/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-04 12:42:49 UTC
  • Revision ID: teddy@recompile.se-20190804124249-69cq19d1bgbmm0gh
Describe role of password-agent(8mandos) in intro(8mandos)

Describe the role of password-agent(8mandos) in the intro(8mandos)
manual.

* intro.xml (SYSTEMD): New section.
  (SEE ALSO): Add reference to password-agent(8mandos).

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
 
import random
82
80
 
83
81
import dbus
84
82
import dbus.service
90
88
import xml.dom.minidom
91
89
import inspect
92
90
 
93
 
if sys.version_info.major == 2:
94
 
    __metaclass__ = type
95
 
    str = unicode
96
 
 
97
 
# Add collections.abc.Callable if it does not exist
98
 
try:
99
 
    collections.abc.Callable
100
 
except AttributeError:
101
 
    class abc:
102
 
        Callable = collections.Callable
103
 
    collections.abc = abc
104
 
    del abc
105
 
 
106
 
# Show warnings by default
107
 
if not sys.warnoptions:
108
 
    import warnings
109
 
    warnings.simplefilter("default")
110
 
 
111
91
# Try to find the value of SO_BINDTODEVICE:
112
92
try:
113
93
    # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and
133
113
            # No value found
134
114
            SO_BINDTODEVICE = None
135
115
 
 
116
if sys.version_info.major == 2:
 
117
    str = unicode
 
118
 
136
119
if sys.version_info < (3, 2):
137
120
    configparser.Configparser = configparser.SafeConfigParser
138
121
 
139
 
version = "1.8.9"
 
122
version = "1.8.6"
140
123
stored_state_file = "clients.pickle"
141
124
 
142
125
logger = logging.getLogger()
143
 
logging.captureWarnings(True)   # Show warnings via the logging system
144
126
syslogger = None
145
127
 
146
128
try:
201
183
    pass
202
184
 
203
185
 
204
 
class PGPEngine:
 
186
class PGPEngine(object):
205
187
    """A simple class for OpenPGP symmetric encryption & decryption"""
206
188
 
207
189
    def __init__(self):
211
193
            output = subprocess.check_output(["gpgconf"])
212
194
            for line in output.splitlines():
213
195
                name, text, path = line.split(b":")
214
 
                if name == b"gpg":
 
196
                if name == "gpg":
215
197
                    self.gpg = path
216
198
                    break
217
199
        except OSError as e:
222
204
                          '--force-mdc',
223
205
                          '--quiet']
224
206
        # Only GPG version 1 has the --no-use-agent option.
225
 
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
 
207
        if self.gpg == "gpg" or self.gpg.endswith("/gpg"):
226
208
            self.gnupgargs.append("--no-use-agent")
227
209
 
228
210
    def __enter__(self):
297
279
 
298
280
 
299
281
# Pretend that we have an Avahi module
300
 
class avahi:
 
282
class avahi(object):
301
283
    """This isn't so much a class as it is a module-like namespace."""
302
284
    IF_UNSPEC = -1               # avahi-common/address.h
303
285
    PROTO_UNSPEC = -1            # avahi-common/address.h
337
319
    pass
338
320
 
339
321
 
340
 
class AvahiService:
 
322
class AvahiService(object):
341
323
    """An Avahi (Zeroconf) service.
342
324
 
343
325
    Attributes:
525
507
 
526
508
 
527
509
# Pretend that we have a GnuTLS module
528
 
class gnutls:
 
510
class gnutls(object):
529
511
    """This isn't so much a class as it is a module-like namespace."""
530
512
 
531
513
    library = ctypes.util.find_library("gnutls")
594
576
        pass
595
577
 
596
578
    # Classes
597
 
    class Credentials:
 
579
    class Credentials(object):
598
580
        def __init__(self):
599
581
            self._c_object = gnutls.certificate_credentials_t()
600
582
            gnutls.certificate_allocate_credentials(
604
586
        def __del__(self):
605
587
            gnutls.certificate_free_credentials(self._c_object)
606
588
 
607
 
    class ClientSession:
 
589
    class ClientSession(object):
608
590
        def __init__(self, socket, credentials=None):
609
591
            self._c_object = gnutls.session_t()
610
592
            gnutls_flags = gnutls.CLIENT
836
818
    connection.close()
837
819
 
838
820
 
839
 
class Client:
 
821
class Client(object):
840
822
    """A representation of a client host served by this server.
841
823
 
842
824
    Attributes:
1045
1027
        if self.checker_initiator_tag is not None:
1046
1028
            GLib.source_remove(self.checker_initiator_tag)
1047
1029
        self.checker_initiator_tag = GLib.timeout_add(
1048
 
            random.randrange(int(self.interval.total_seconds() * 1000
1049
 
                                 + 1)),
 
1030
            int(self.interval.total_seconds() * 1000),
1050
1031
            self.start_checker)
1051
1032
        # Schedule a disable() when 'timeout' has passed
1052
1033
        if self.disable_initiator_tag is not None:
1062
1043
        # Read return code from connection (see call_pipe)
1063
1044
        returncode = connection.recv()
1064
1045
        connection.close()
1065
 
        if self.checker is not None:
1066
 
            self.checker.join()
 
1046
        self.checker.join()
1067
1047
        self.checker_callback_tag = None
1068
1048
        self.checker = None
1069
1049
 
1160
1140
                kwargs=popen_args)
1161
1141
            self.checker.start()
1162
1142
            self.checker_callback_tag = GLib.io_add_watch(
1163
 
                GLib.IOChannel.unix_new(pipe[0].fileno()),
1164
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
 
1143
                pipe[0].fileno(), GLib.IO_IN,
1165
1144
                self.checker_callback, pipe[0], command)
1166
1145
        # Re-run this periodically if run by GLib.timeout_add
1167
1146
        return True
1422
1401
                raise ValueError("Byte arrays not supported for non-"
1423
1402
                                 "'ay' signature {!r}"
1424
1403
                                 .format(prop._dbus_signature))
1425
 
            value = dbus.ByteArray(bytes(value))
 
1404
            value = dbus.ByteArray(b''.join(chr(byte)
 
1405
                                            for byte in value))
1426
1406
        prop(value)
1427
1407
 
1428
1408
    @dbus.service.method(dbus.PROPERTIES_IFACE,
2233
2213
    del _interface
2234
2214
 
2235
2215
 
2236
 
class ProxyClient:
 
2216
class ProxyClient(object):
2237
2217
    def __init__(self, child_pipe, key_id, fpr, address):
2238
2218
        self._pipe = child_pipe
2239
2219
        self._pipe.send(('init', key_id, fpr, address))
2512
2492
        return hex_fpr
2513
2493
 
2514
2494
 
2515
 
class MultiprocessingMixIn:
 
2495
class MultiprocessingMixIn(object):
2516
2496
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
2517
2497
 
2518
2498
    def sub_process_main(self, request, address):
2530
2510
        return proc
2531
2511
 
2532
2512
 
2533
 
class MultiprocessingMixInWithPipe(MultiprocessingMixIn):
 
2513
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
2534
2514
    """ adds a pipe to the MixIn """
2535
2515
 
2536
2516
    def process_request(self, request, client_address):
2551
2531
 
2552
2532
 
2553
2533
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2554
 
                     socketserver.TCPServer):
 
2534
                     socketserver.TCPServer, object):
2555
2535
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2556
2536
 
2557
2537
    Attributes:
2690
2670
    def add_pipe(self, parent_pipe, proc):
2691
2671
        # Call "handle_ipc" for both data and EOF events
2692
2672
        GLib.io_add_watch(
2693
 
            GLib.IOChannel.unix_new(parent_pipe.fileno()),
2694
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
2673
            parent_pipe.fileno(),
 
2674
            GLib.IO_IN | GLib.IO_HUP,
2695
2675
            functools.partial(self.handle_ipc,
2696
2676
                              parent_pipe=parent_pipe,
2697
2677
                              proc=proc))
2735
2715
                return False
2736
2716
 
2737
2717
            GLib.io_add_watch(
2738
 
                GLib.IOChannel.unix_new(parent_pipe.fileno()),
2739
 
                GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
 
2718
                parent_pipe.fileno(),
 
2719
                GLib.IO_IN | GLib.IO_HUP,
2740
2720
                functools.partial(self.handle_ipc,
2741
2721
                                  parent_pipe=parent_pipe,
2742
2722
                                  proc=proc,
2757
2737
        if command == 'getattr':
2758
2738
            attrname = request[1]
2759
2739
            if isinstance(client_object.__getattribute__(attrname),
2760
 
                          collections.abc.Callable):
 
2740
                          collections.Callable):
2761
2741
                parent_pipe.send(('function', ))
2762
2742
            else:
2763
2743
                parent_pipe.send((
2774
2754
def rfc3339_duration_to_delta(duration):
2775
2755
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2776
2756
 
2777
 
    >>> rfc3339_duration_to_delta("P7D") == datetime.timedelta(7)
2778
 
    True
2779
 
    >>> rfc3339_duration_to_delta("PT60S") == datetime.timedelta(0, 60)
2780
 
    True
2781
 
    >>> rfc3339_duration_to_delta("PT60M") == datetime.timedelta(0, 3600)
2782
 
    True
2783
 
    >>> rfc3339_duration_to_delta("PT24H") == datetime.timedelta(1)
2784
 
    True
2785
 
    >>> rfc3339_duration_to_delta("P1W") == datetime.timedelta(7)
2786
 
    True
2787
 
    >>> rfc3339_duration_to_delta("PT5M30S") == datetime.timedelta(0, 330)
2788
 
    True
2789
 
    >>> rfc3339_duration_to_delta("P1DT3M20S") == datetime.timedelta(1, 200)
2790
 
    True
 
2757
    >>> rfc3339_duration_to_delta("P7D")
 
2758
    datetime.timedelta(7)
 
2759
    >>> rfc3339_duration_to_delta("PT60S")
 
2760
    datetime.timedelta(0, 60)
 
2761
    >>> rfc3339_duration_to_delta("PT60M")
 
2762
    datetime.timedelta(0, 3600)
 
2763
    >>> rfc3339_duration_to_delta("PT24H")
 
2764
    datetime.timedelta(1)
 
2765
    >>> rfc3339_duration_to_delta("P1W")
 
2766
    datetime.timedelta(7)
 
2767
    >>> rfc3339_duration_to_delta("PT5M30S")
 
2768
    datetime.timedelta(0, 330)
 
2769
    >>> rfc3339_duration_to_delta("P1DT3M20S")
 
2770
    datetime.timedelta(1, 200)
2791
2771
    """
2792
2772
 
2793
2773
    # Parsing an RFC 3339 duration with regular expressions is not
2873
2853
def string_to_delta(interval):
2874
2854
    """Parse a string and return a datetime.timedelta
2875
2855
 
2876
 
    >>> string_to_delta('7d') == datetime.timedelta(7)
2877
 
    True
2878
 
    >>> string_to_delta('60s') == datetime.timedelta(0, 60)
2879
 
    True
2880
 
    >>> string_to_delta('60m') == datetime.timedelta(0, 3600)
2881
 
    True
2882
 
    >>> string_to_delta('24h') == datetime.timedelta(1)
2883
 
    True
2884
 
    >>> string_to_delta('1w') == datetime.timedelta(7)
2885
 
    True
2886
 
    >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
2887
 
    True
 
2856
    >>> string_to_delta('7d')
 
2857
    datetime.timedelta(7)
 
2858
    >>> string_to_delta('60s')
 
2859
    datetime.timedelta(0, 60)
 
2860
    >>> string_to_delta('60m')
 
2861
    datetime.timedelta(0, 3600)
 
2862
    >>> string_to_delta('24h')
 
2863
    datetime.timedelta(1)
 
2864
    >>> string_to_delta('1w')
 
2865
    datetime.timedelta(7)
 
2866
    >>> string_to_delta('5m 30s')
 
2867
    datetime.timedelta(0, 330)
2888
2868
    """
2889
2869
 
2890
2870
    try:
2992
2972
 
2993
2973
    options = parser.parse_args()
2994
2974
 
 
2975
    if options.check:
 
2976
        import doctest
 
2977
        fail_count, test_count = doctest.testmod()
 
2978
        sys.exit(os.EX_OK if fail_count == 0 else 1)
 
2979
 
2995
2980
    # Default values for config file for server-global settings
2996
2981
    if gnutls.has_rawpk:
2997
2982
        priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
3260
3245
                             if isinstance(s, bytes)
3261
3246
                             else s) for s in
3262
3247
                            value["client_structure"]]
3263
 
                        # .name, .host, and .checker_command
3264
 
                        for k in ("name", "host", "checker_command"):
 
3248
                        # .name & .host
 
3249
                        for k in ("name", "host"):
3265
3250
                            if isinstance(value[k], bytes):
3266
3251
                                value[k] = value[k].decode("utf-8")
3267
3252
                        if "key_id" not in value:
3277
3262
                        for key, value in
3278
3263
                        bytes_old_client_settings.items()}
3279
3264
                    del bytes_old_client_settings
3280
 
                    # .host and .checker_command
 
3265
                    # .host
3281
3266
                    for value in old_client_settings.values():
3282
 
                        for attribute in ("host", "checker_command"):
3283
 
                            if isinstance(value[attribute], bytes):
3284
 
                                value[attribute] = (value[attribute]
3285
 
                                                    .decode("utf-8"))
 
3267
                        if isinstance(value["host"], bytes):
 
3268
                            value["host"] = (value["host"]
 
3269
                                             .decode("utf-8"))
3286
3270
            os.remove(stored_state_path)
3287
3271
        except IOError as e:
3288
3272
            if e.errno == errno.ENOENT:
3613
3597
                sys.exit(1)
3614
3598
            # End of Avahi example code
3615
3599
 
3616
 
        GLib.io_add_watch(
3617
 
            GLib.IOChannel.unix_new(tcp_server.fileno()),
3618
 
            GLib.PRIORITY_DEFAULT, GLib.IO_IN,
3619
 
            lambda *args, **kwargs: (tcp_server.handle_request
3620
 
                                     (*args[2:], **kwargs) or True))
 
3600
        GLib.io_add_watch(tcp_server.fileno(), GLib.IO_IN,
 
3601
                          lambda *args, **kwargs:
 
3602
                          (tcp_server.handle_request
 
3603
                           (*args[2:], **kwargs) or True))
3621
3604
 
3622
3605
        logger.debug("Starting main loop")
3623
3606
        main_loop.run()
3633
3616
    # Must run before the D-Bus bus name gets deregistered
3634
3617
    cleanup()
3635
3618
 
3636
 
 
3637
 
def should_only_run_tests():
3638
 
    parser = argparse.ArgumentParser(add_help=False)
3639
 
    parser.add_argument("--check", action='store_true')
3640
 
    args, unknown_args = parser.parse_known_args()
3641
 
    run_tests = args.check
3642
 
    if run_tests:
3643
 
        # Remove --check argument from sys.argv
3644
 
        sys.argv[1:] = unknown_args
3645
 
    return run_tests
3646
 
 
3647
 
# Add all tests from doctest strings
3648
 
def load_tests(loader, tests, none):
3649
 
    import doctest
3650
 
    tests.addTests(doctest.DocTestSuite())
3651
 
    return tests
3652
3619
 
3653
3620
if __name__ == '__main__':
3654
 
    try:
3655
 
        if should_only_run_tests():
3656
 
            # Call using ./mandos --check [--verbose]
3657
 
            unittest.main()
3658
 
        else:
3659
 
            main()
3660
 
    finally:
3661
 
        logging.shutdown()
 
3621
    main()