/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 at recompile
  • Date: 2020-12-03 20:30:45 UTC
  • Revision ID: teddy@recompile.se-20201203203045-iqd6nq9y5nwalh1x
Minor fix of a test function

In dracut-module/password-agent, the test function
test_send_password_to_socket_EMSGSIZE() (which tests that the
send_password_to_socket() task function aborts properly when getting
EMSGSIZE when writing to the password socket), part of the test code
is supposed to find a message size which definitely does trigger
EMSGSIZE when send()ing to a socket.  Without a "break" in the proper
place, however, the size given is always exactly 1024 bytes too large.

This is very probably not a problem, since a too large message will
still be too large if it is increased by 1024 bytes, and send(2) in
practice checks the size before reading the buffer.  The biggest issue
would be if some version of send(2) would try to look at the last 1024
bytes of the message buffer before checking the message size; this
would then lead to a buffer over-read when running this test function.
(But even then there would be no security implications since the tests
are not run in the normal operation of the program.)

* dracut-module/password-agent.c
  (test_send_password_to_socket_EMSGSIZE): Break out early when ssret
  < 0 and errno == EMSGSIZE; don't allow loop to increase message_size
  again.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
# -*- mode: python; coding: utf-8 -*-
 
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 -*-
3
3
#
4
4
# Mandos server - give out binary blobs to connecting clients.
5
5
#
11
11
# "AvahiService" class, and some lines in "main".
12
12
#
13
13
# Everything else is
14
 
# Copyright © 2008-2019 Teddy Hogeborn
15
 
# Copyright © 2008-2019 Björn Påhlsson
 
14
# Copyright © 2008-2020 Teddy Hogeborn
 
15
# Copyright © 2008-2020 Björn Påhlsson
16
16
#
17
17
# This file is part of Mandos.
18
18
#
77
77
import itertools
78
78
import collections
79
79
import codecs
 
80
import unittest
 
81
import random
 
82
import shlex
80
83
 
81
84
import dbus
82
85
import dbus.service
90
93
 
91
94
if sys.version_info.major == 2:
92
95
    __metaclass__ = type
 
96
    str = unicode
 
97
 
 
98
# Add collections.abc.Callable if it does not exist
 
99
try:
 
100
    collections.abc.Callable
 
101
except AttributeError:
 
102
    class abc:
 
103
        Callable = collections.Callable
 
104
    collections.abc = abc
 
105
    del abc
 
106
 
 
107
# Add shlex.quote if it does not exist
 
108
try:
 
109
    shlex.quote
 
110
except AttributeError:
 
111
    shlex.quote = re.escape
 
112
 
 
113
# Show warnings by default
 
114
if not sys.warnoptions:
 
115
    import warnings
 
116
    warnings.simplefilter("default")
93
117
 
94
118
# Try to find the value of SO_BINDTODEVICE:
95
119
try:
116
140
            # No value found
117
141
            SO_BINDTODEVICE = None
118
142
 
119
 
if sys.version_info.major == 2:
120
 
    str = unicode
121
 
 
122
143
if sys.version_info < (3, 2):
123
144
    configparser.Configparser = configparser.SafeConfigParser
124
145
 
125
 
version = "1.8.7"
 
146
version = "1.8.13"
126
147
stored_state_file = "clients.pickle"
127
148
 
128
149
logger = logging.getLogger()
 
150
logging.captureWarnings(True)   # Show warnings via the logging system
129
151
syslogger = None
130
152
 
131
153
try:
196
218
            output = subprocess.check_output(["gpgconf"])
197
219
            for line in output.splitlines():
198
220
                name, text, path = line.split(b":")
199
 
                if name == "gpg":
 
221
                if name == b"gpg":
200
222
                    self.gpg = path
201
223
                    break
202
224
        except OSError as e:
207
229
                          '--force-mdc',
208
230
                          '--quiet']
209
231
        # Only GPG version 1 has the --no-use-agent option.
210
 
        if self.gpg == "gpg" or self.gpg.endswith("/gpg"):
 
232
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
211
233
            self.gnupgargs.append("--no-use-agent")
212
234
 
213
235
    def __enter__(self):
502
524
class AvahiServiceToSyslog(AvahiService):
503
525
    def rename(self, *args, **kwargs):
504
526
        """Add the new name to the syslog messages"""
505
 
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
 
527
        ret = super(AvahiServiceToSyslog, self).rename(*args,
 
528
                                                       **kwargs)
506
529
        syslogger.setFormatter(logging.Formatter(
507
530
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
508
531
            .format(self.name)))
752
775
 
753
776
        x509_crt_fmt_t = ctypes.c_int
754
777
 
755
 
        # All the function declarations below are from gnutls/abstract.h
 
778
        # All the function declarations below are from
 
779
        # gnutls/abstract.h
756
780
        pubkey_init = _library.gnutls_pubkey_init
757
781
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
758
782
        pubkey_init.restype = _error_code
772
796
        pubkey_deinit.argtypes = [pubkey_t]
773
797
        pubkey_deinit.restype = None
774
798
    else:
775
 
        # All the function declarations below are from gnutls/openpgp.h
 
799
        # All the function declarations below are from
 
800
        # gnutls/openpgp.h
776
801
 
777
802
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
778
803
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
784
809
                                       openpgp_crt_fmt_t]
785
810
        openpgp_crt_import.restype = _error_code
786
811
 
787
 
        openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
788
 
        openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
789
 
                                            ctypes.POINTER(ctypes.c_uint)]
 
812
        openpgp_crt_verify_self = \
 
813
            _library.gnutls_openpgp_crt_verify_self
 
814
        openpgp_crt_verify_self.argtypes = [
 
815
            openpgp_crt_t,
 
816
            ctypes.c_uint,
 
817
            ctypes.POINTER(ctypes.c_uint),
 
818
        ]
790
819
        openpgp_crt_verify_self.restype = _error_code
791
820
 
792
821
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
1030
1059
        if self.checker_initiator_tag is not None:
1031
1060
            GLib.source_remove(self.checker_initiator_tag)
1032
1061
        self.checker_initiator_tag = GLib.timeout_add(
1033
 
            int(self.interval.total_seconds() * 1000),
 
1062
            random.randrange(int(self.interval.total_seconds() * 1000
 
1063
                                 + 1)),
1034
1064
            self.start_checker)
1035
1065
        # Schedule a disable() when 'timeout' has passed
1036
1066
        if self.disable_initiator_tag is not None:
1046
1076
        # Read return code from connection (see call_pipe)
1047
1077
        returncode = connection.recv()
1048
1078
        connection.close()
1049
 
        self.checker.join()
 
1079
        if self.checker is not None:
 
1080
            self.checker.join()
1050
1081
        self.checker_callback_tag = None
1051
1082
        self.checker = None
1052
1083
 
1110
1141
        if self.checker is None:
1111
1142
            # Escape attributes for the shell
1112
1143
            escaped_attrs = {
1113
 
                attr: re.escape(str(getattr(self, attr)))
 
1144
                attr: shlex.quote(str(getattr(self, attr)))
1114
1145
                for attr in self.runtime_expansions}
1115
1146
            try:
1116
1147
                command = self.checker_command % escaped_attrs
1143
1174
                kwargs=popen_args)
1144
1175
            self.checker.start()
1145
1176
            self.checker_callback_tag = GLib.io_add_watch(
1146
 
                pipe[0].fileno(), GLib.IO_IN,
 
1177
                GLib.IOChannel.unix_new(pipe[0].fileno()),
 
1178
                GLib.PRIORITY_DEFAULT, GLib.IO_IN,
1147
1179
                self.checker_callback, pipe[0], command)
1148
1180
        # Re-run this periodically if run by GLib.timeout_add
1149
1181
        return True
1404
1436
                raise ValueError("Byte arrays not supported for non-"
1405
1437
                                 "'ay' signature {!r}"
1406
1438
                                 .format(prop._dbus_signature))
1407
 
            value = dbus.ByteArray(b''.join(chr(byte)
1408
 
                                            for byte in value))
 
1439
            value = dbus.ByteArray(bytes(value))
1409
1440
        prop(value)
1410
1441
 
1411
1442
    @dbus.service.method(dbus.PROPERTIES_IFACE,
2444
2475
        buf = ctypes.create_string_buffer(32)
2445
2476
        buf_len = ctypes.c_size_t(len(buf))
2446
2477
        # Get the key ID from the raw public key into the buffer
2447
 
        gnutls.pubkey_get_key_id(pubkey,
2448
 
                                 gnutls.KEYID_USE_SHA256,
2449
 
                                 ctypes.cast(ctypes.byref(buf),
2450
 
                                             ctypes.POINTER(ctypes.c_ubyte)),
2451
 
                                 ctypes.byref(buf_len))
 
2478
        gnutls.pubkey_get_key_id(
 
2479
            pubkey,
 
2480
            gnutls.KEYID_USE_SHA256,
 
2481
            ctypes.cast(ctypes.byref(buf),
 
2482
                        ctypes.POINTER(ctypes.c_ubyte)),
 
2483
            ctypes.byref(buf_len))
2452
2484
        # Deinit the certificate
2453
2485
        gnutls.pubkey_deinit(pubkey)
2454
2486
 
2673
2705
    def add_pipe(self, parent_pipe, proc):
2674
2706
        # Call "handle_ipc" for both data and EOF events
2675
2707
        GLib.io_add_watch(
2676
 
            parent_pipe.fileno(),
2677
 
            GLib.IO_IN | GLib.IO_HUP,
 
2708
            GLib.IOChannel.unix_new(parent_pipe.fileno()),
 
2709
            GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
2678
2710
            functools.partial(self.handle_ipc,
2679
2711
                              parent_pipe=parent_pipe,
2680
2712
                              proc=proc))
2699
2731
            address = request[3]
2700
2732
 
2701
2733
            for c in self.clients.values():
2702
 
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
 
2734
                if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924"
 
2735
                              "27AE41E4649B934CA495991B7852B855"):
2703
2736
                    continue
2704
2737
                if key_id and c.key_id == key_id:
2705
2738
                    client = c
2718
2751
                return False
2719
2752
 
2720
2753
            GLib.io_add_watch(
2721
 
                parent_pipe.fileno(),
2722
 
                GLib.IO_IN | GLib.IO_HUP,
 
2754
                GLib.IOChannel.unix_new(parent_pipe.fileno()),
 
2755
                GLib.PRIORITY_DEFAULT, GLib.IO_IN | GLib.IO_HUP,
2723
2756
                functools.partial(self.handle_ipc,
2724
2757
                                  parent_pipe=parent_pipe,
2725
2758
                                  proc=proc,
2740
2773
        if command == 'getattr':
2741
2774
            attrname = request[1]
2742
2775
            if isinstance(client_object.__getattribute__(attrname),
2743
 
                          collections.Callable):
 
2776
                          collections.abc.Callable):
2744
2777
                parent_pipe.send(('function', ))
2745
2778
            else:
2746
2779
                parent_pipe.send((
2757
2790
def rfc3339_duration_to_delta(duration):
2758
2791
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2759
2792
 
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)
 
2793
    >>> timedelta = datetime.timedelta
 
2794
    >>> rfc3339_duration_to_delta("P7D") == timedelta(7)
 
2795
    True
 
2796
    >>> rfc3339_duration_to_delta("PT60S") == timedelta(0, 60)
 
2797
    True
 
2798
    >>> rfc3339_duration_to_delta("PT60M") == timedelta(0, 3600)
 
2799
    True
 
2800
    >>> rfc3339_duration_to_delta("PT24H") == timedelta(1)
 
2801
    True
 
2802
    >>> rfc3339_duration_to_delta("P1W") == timedelta(7)
 
2803
    True
 
2804
    >>> rfc3339_duration_to_delta("PT5M30S") == timedelta(0, 330)
 
2805
    True
 
2806
    >>> rfc3339_duration_to_delta("P1DT3M20S") == timedelta(1, 200)
 
2807
    True
 
2808
    >>> del timedelta
2774
2809
    """
2775
2810
 
2776
2811
    # Parsing an RFC 3339 duration with regular expressions is not
2856
2891
def string_to_delta(interval):
2857
2892
    """Parse a string and return a datetime.timedelta
2858
2893
 
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)
 
2894
    >>> string_to_delta('7d') == datetime.timedelta(7)
 
2895
    True
 
2896
    >>> string_to_delta('60s') == datetime.timedelta(0, 60)
 
2897
    True
 
2898
    >>> string_to_delta('60m') == datetime.timedelta(0, 3600)
 
2899
    True
 
2900
    >>> string_to_delta('24h') == datetime.timedelta(1)
 
2901
    True
 
2902
    >>> string_to_delta('1w') == datetime.timedelta(7)
 
2903
    True
 
2904
    >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
 
2905
    True
2871
2906
    """
2872
2907
 
2873
2908
    try:
2975
3010
 
2976
3011
    options = parser.parse_args()
2977
3012
 
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
 
 
2983
3013
    # Default values for config file for server-global settings
2984
3014
    if gnutls.has_rawpk:
2985
3015
        priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
3248
3278
                             if isinstance(s, bytes)
3249
3279
                             else s) for s in
3250
3280
                            value["client_structure"]]
3251
 
                        # .name & .host
3252
 
                        for k in ("name", "host"):
 
3281
                        # .name, .host, and .checker_command
 
3282
                        for k in ("name", "host", "checker_command"):
3253
3283
                            if isinstance(value[k], bytes):
3254
3284
                                value[k] = value[k].decode("utf-8")
3255
3285
                        if "key_id" not in value:
3265
3295
                        for key, value in
3266
3296
                        bytes_old_client_settings.items()}
3267
3297
                    del bytes_old_client_settings
3268
 
                    # .host
 
3298
                    # .host and .checker_command
3269
3299
                    for value in old_client_settings.values():
3270
 
                        if isinstance(value["host"], bytes):
3271
 
                            value["host"] = (value["host"]
3272
 
                                             .decode("utf-8"))
 
3300
                        for attribute in ("host", "checker_command"):
 
3301
                            if isinstance(value[attribute], bytes):
 
3302
                                value[attribute] = (value[attribute]
 
3303
                                                    .decode("utf-8"))
3273
3304
            os.remove(stored_state_path)
3274
3305
        except IOError as e:
3275
3306
            if e.errno == errno.ENOENT:
3600
3631
                sys.exit(1)
3601
3632
            # End of Avahi example code
3602
3633
 
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))
 
3634
        GLib.io_add_watch(
 
3635
            GLib.IOChannel.unix_new(tcp_server.fileno()),
 
3636
            GLib.PRIORITY_DEFAULT, GLib.IO_IN,
 
3637
            lambda *args, **kwargs: (tcp_server.handle_request
 
3638
                                     (*args[2:], **kwargs) or True))
3607
3639
 
3608
3640
        logger.debug("Starting main loop")
3609
3641
        main_loop.run()
3619
3651
    # Must run before the D-Bus bus name gets deregistered
3620
3652
    cleanup()
3621
3653
 
 
3654
 
 
3655
def should_only_run_tests():
 
3656
    parser = argparse.ArgumentParser(add_help=False)
 
3657
    parser.add_argument("--check", action='store_true')
 
3658
    args, unknown_args = parser.parse_known_args()
 
3659
    run_tests = args.check
 
3660
    if run_tests:
 
3661
        # Remove --check argument from sys.argv
 
3662
        sys.argv[1:] = unknown_args
 
3663
    return run_tests
 
3664
 
 
3665
# Add all tests from doctest strings
 
3666
def load_tests(loader, tests, none):
 
3667
    import doctest
 
3668
    tests.addTests(doctest.DocTestSuite())
 
3669
    return tests
3622
3670
 
3623
3671
if __name__ == '__main__':
3624
 
    main()
 
3672
    try:
 
3673
        if should_only_run_tests():
 
3674
            # Call using ./mandos --check [--verbose]
 
3675
            unittest.main()
 
3676
        else:
 
3677
            main()
 
3678
    finally:
 
3679
        logging.shutdown()