/mandos/trunk

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/trunk

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2019-08-18 00:42:22 UTC
  • Revision ID: teddy@recompile.se-20190818004222-lfrgtnmqz766a08e
Client: Use the systemd sysusers.d mechanism, if present

* Makefile (install-client-nokey): Also install sysusers.d file, if
                                   $(SYSUSERS) exists.
* sysusers.d-mandos.conf: Adjust comment to match reality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python3 -bI
2
 
# -*- mode: python; after-save-hook: (lambda () (let ((command (if (fboundp 'file-local-name) (file-local-name (buffer-file-name)) (or (file-remote-p (buffer-file-name) 'localname) (buffer-file-name))))) (if (= (progn (if (get-buffer "*Test*") (kill-buffer "*Test*")) (process-file-shell-command (format "%s --check" (shell-quote-argument command)) nil "*Test*")) 0) (let ((w (get-buffer-window "*Test*"))) (if w (delete-window w))) (progn (with-current-buffer "*Test*" (compilation-mode)) (display-buffer "*Test*" '(display-buffer-in-side-window)))))); coding: utf-8 -*-
 
1
#!/usr/bin/python
 
2
# -*- mode: python; coding: utf-8 -*-
3
3
#
4
4
# Mandos server - give out binary blobs to connecting clients.
5
5
#
77
77
import itertools
78
78
import collections
79
79
import codecs
80
 
import unittest
81
80
 
82
81
import dbus
83
82
import dbus.service
91
90
 
92
91
if sys.version_info.major == 2:
93
92
    __metaclass__ = type
94
 
    str = unicode
95
 
 
96
 
# Show warnings by default
97
 
if not sys.warnoptions:
98
 
    import warnings
99
 
    warnings.simplefilter("default")
100
93
 
101
94
# Try to find the value of SO_BINDTODEVICE:
102
95
try:
123
116
            # No value found
124
117
            SO_BINDTODEVICE = None
125
118
 
 
119
if sys.version_info.major == 2:
 
120
    str = unicode
 
121
 
126
122
if sys.version_info < (3, 2):
127
123
    configparser.Configparser = configparser.SafeConfigParser
128
124
 
129
 
version = "1.8.9"
 
125
version = "1.8.7"
130
126
stored_state_file = "clients.pickle"
131
127
 
132
128
logger = logging.getLogger()
133
 
logging.captureWarnings(True)   # Show warnings via the logging system
134
129
syslogger = None
135
130
 
136
131
try:
201
196
            output = subprocess.check_output(["gpgconf"])
202
197
            for line in output.splitlines():
203
198
                name, text, path = line.split(b":")
204
 
                if name == b"gpg":
 
199
                if name == "gpg":
205
200
                    self.gpg = path
206
201
                    break
207
202
        except OSError as e:
212
207
                          '--force-mdc',
213
208
                          '--quiet']
214
209
        # Only GPG version 1 has the --no-use-agent option.
215
 
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
 
210
        if self.gpg == "gpg" or self.gpg.endswith("/gpg"):
216
211
            self.gnupgargs.append("--no-use-agent")
217
212
 
218
213
    def __enter__(self):
1051
1046
        # Read return code from connection (see call_pipe)
1052
1047
        returncode = connection.recv()
1053
1048
        connection.close()
1054
 
        if self.checker is not None:
1055
 
            self.checker.join()
 
1049
        self.checker.join()
1056
1050
        self.checker_callback_tag = None
1057
1051
        self.checker = None
1058
1052
 
1149
1143
                kwargs=popen_args)
1150
1144
            self.checker.start()
1151
1145
            self.checker_callback_tag = GLib.io_add_watch(
1152
 
                GLib.IOChannel.unix_new(pipe[0].fileno()),
1153
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
 
1146
                pipe[0].fileno(), GLib.IO_IN,
1154
1147
                self.checker_callback, pipe[0], command)
1155
1148
        # Re-run this periodically if run by GLib.timeout_add
1156
1149
        return True
1411
1404
                raise ValueError("Byte arrays not supported for non-"
1412
1405
                                 "'ay' signature {!r}"
1413
1406
                                 .format(prop._dbus_signature))
1414
 
            value = dbus.ByteArray(bytes(value))
 
1407
            value = dbus.ByteArray(b''.join(chr(byte)
 
1408
                                            for byte in value))
1415
1409
        prop(value)
1416
1410
 
1417
1411
    @dbus.service.method(dbus.PROPERTIES_IFACE,
2679
2673
    def add_pipe(self, parent_pipe, proc):
2680
2674
        # Call "handle_ipc" for both data and EOF events
2681
2675
        GLib.io_add_watch(
2682
 
            GLib.IOChannel.unix_new(parent_pipe.fileno()),
2683
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
2676
            parent_pipe.fileno(),
 
2677
            GLib.IO_IN | GLib.IO_HUP,
2684
2678
            functools.partial(self.handle_ipc,
2685
2679
                              parent_pipe=parent_pipe,
2686
2680
                              proc=proc))
2724
2718
                return False
2725
2719
 
2726
2720
            GLib.io_add_watch(
2727
 
                GLib.IOChannel.unix_new(parent_pipe.fileno()),
2728
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
2721
                parent_pipe.fileno(),
 
2722
                GLib.IO_IN | GLib.IO_HUP,
2729
2723
                functools.partial(self.handle_ipc,
2730
2724
                                  parent_pipe=parent_pipe,
2731
2725
                                  proc=proc,
2763
2757
def rfc3339_duration_to_delta(duration):
2764
2758
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2765
2759
 
2766
 
    >>> rfc3339_duration_to_delta("P7D") == datetime.timedelta(7)
2767
 
    True
2768
 
    >>> rfc3339_duration_to_delta("PT60S") == datetime.timedelta(0, 60)
2769
 
    True
2770
 
    >>> rfc3339_duration_to_delta("PT60M") == datetime.timedelta(0, 3600)
2771
 
    True
2772
 
    >>> rfc3339_duration_to_delta("PT24H") == datetime.timedelta(1)
2773
 
    True
2774
 
    >>> rfc3339_duration_to_delta("P1W") == datetime.timedelta(7)
2775
 
    True
2776
 
    >>> rfc3339_duration_to_delta("PT5M30S") == datetime.timedelta(0, 330)
2777
 
    True
2778
 
    >>> rfc3339_duration_to_delta("P1DT3M20S") == datetime.timedelta(1, 200)
2779
 
    True
 
2760
    >>> rfc3339_duration_to_delta("P7D")
 
2761
    datetime.timedelta(7)
 
2762
    >>> rfc3339_duration_to_delta("PT60S")
 
2763
    datetime.timedelta(0, 60)
 
2764
    >>> rfc3339_duration_to_delta("PT60M")
 
2765
    datetime.timedelta(0, 3600)
 
2766
    >>> rfc3339_duration_to_delta("PT24H")
 
2767
    datetime.timedelta(1)
 
2768
    >>> rfc3339_duration_to_delta("P1W")
 
2769
    datetime.timedelta(7)
 
2770
    >>> rfc3339_duration_to_delta("PT5M30S")
 
2771
    datetime.timedelta(0, 330)
 
2772
    >>> rfc3339_duration_to_delta("P1DT3M20S")
 
2773
    datetime.timedelta(1, 200)
2780
2774
    """
2781
2775
 
2782
2776
    # Parsing an RFC 3339 duration with regular expressions is not
2862
2856
def string_to_delta(interval):
2863
2857
    """Parse a string and return a datetime.timedelta
2864
2858
 
2865
 
    >>> string_to_delta('7d') == datetime.timedelta(7)
2866
 
    True
2867
 
    >>> string_to_delta('60s') == datetime.timedelta(0, 60)
2868
 
    True
2869
 
    >>> string_to_delta('60m') == datetime.timedelta(0, 3600)
2870
 
    True
2871
 
    >>> string_to_delta('24h') == datetime.timedelta(1)
2872
 
    True
2873
 
    >>> string_to_delta('1w') == datetime.timedelta(7)
2874
 
    True
2875
 
    >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
2876
 
    True
 
2859
    >>> string_to_delta('7d')
 
2860
    datetime.timedelta(7)
 
2861
    >>> string_to_delta('60s')
 
2862
    datetime.timedelta(0, 60)
 
2863
    >>> string_to_delta('60m')
 
2864
    datetime.timedelta(0, 3600)
 
2865
    >>> string_to_delta('24h')
 
2866
    datetime.timedelta(1)
 
2867
    >>> string_to_delta('1w')
 
2868
    datetime.timedelta(7)
 
2869
    >>> string_to_delta('5m 30s')
 
2870
    datetime.timedelta(0, 330)
2877
2871
    """
2878
2872
 
2879
2873
    try:
2981
2975
 
2982
2976
    options = parser.parse_args()
2983
2977
 
 
2978
    if options.check:
 
2979
        import doctest
 
2980
        fail_count, test_count = doctest.testmod()
 
2981
        sys.exit(os.EX_OK if fail_count == 0 else 1)
 
2982
 
2984
2983
    # Default values for config file for server-global settings
2985
2984
    if gnutls.has_rawpk:
2986
2985
        priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
3249
3248
                             if isinstance(s, bytes)
3250
3249
                             else s) for s in
3251
3250
                            value["client_structure"]]
3252
 
                        # .name, .host, and .checker_command
3253
 
                        for k in ("name", "host", "checker_command"):
 
3251
                        # .name & .host
 
3252
                        for k in ("name", "host"):
3254
3253
                            if isinstance(value[k], bytes):
3255
3254
                                value[k] = value[k].decode("utf-8")
3256
3255
                        if "key_id" not in value:
3266
3265
                        for key, value in
3267
3266
                        bytes_old_client_settings.items()}
3268
3267
                    del bytes_old_client_settings
3269
 
                    # .host and .checker_command
 
3268
                    # .host
3270
3269
                    for value in old_client_settings.values():
3271
 
                        for attribute in ("host", "checker_command"):
3272
 
                            if isinstance(value[attribute], bytes):
3273
 
                                value[attribute] = (value[attribute]
3274
 
                                                    .decode("utf-8"))
 
3270
                        if isinstance(value["host"], bytes):
 
3271
                            value["host"] = (value["host"]
 
3272
                                             .decode("utf-8"))
3275
3273
            os.remove(stored_state_path)
3276
3274
        except IOError as e:
3277
3275
            if e.errno == errno.ENOENT:
3602
3600
                sys.exit(1)
3603
3601
            # End of Avahi example code
3604
3602
 
3605
 
        GLib.io_add_watch(
3606
 
            GLib.IOChannel.unix_new(tcp_server.fileno()),
3607
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN,
3608
 
            lambda *args, **kwargs: (tcp_server.handle_request
3609
 
                                     (*args[2:], **kwargs) or True))
 
3603
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
 
3604
                          lambda *args, **kwargs:
 
3605
                          (tcp_server.handle_request
 
3606
                           (*args[2:], **kwargs) or True))
3610
3607
 
3611
3608
        logger.debug("Starting main loop")
3612
3609
        main_loop.run()
3622
3619
    # Must run before the D-Bus bus name gets deregistered
3623
3620
    cleanup()
3624
3621
 
3625
 
 
3626
 
def should_only_run_tests():
3627
 
    parser = argparse.ArgumentParser(add_help=False)
3628
 
    parser.add_argument("--check", action='store_true')
3629
 
    args, unknown_args = parser.parse_known_args()
3630
 
    run_tests = args.check
3631
 
    if run_tests:
3632
 
        # Remove --check argument from sys.argv
3633
 
        sys.argv[1:] = unknown_args
3634
 
    return run_tests
3635
 
 
3636
 
# Add all tests from doctest strings
3637
 
def load_tests(loader, tests, none):
3638
 
    import doctest
3639
 
    tests.addTests(doctest.DocTestSuite())
3640
 
    return tests
3641
3622
 
3642
3623
if __name__ == '__main__':
3643
 
    try:
3644
 
        if should_only_run_tests():
3645
 
            # Call using ./mandos --check [--verbose]
3646
 
            unittest.main()
3647
 
        else:
3648
 
            main()
3649
 
    finally:
3650
 
        logging.shutdown()
 
3624
    main()