/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-11-03 19:17:57 UTC
  • Revision ID: teddy@recompile.se-20191103191757-1hdpp0u5fxa8iumo
INSTALL: Add "-" argument to "su" invocations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# "AvahiService" class, and some lines in "main".
12
12
#
13
13
# Everything else is
14
 
# Copyright © 2008-2020 Teddy Hogeborn
15
 
# Copyright © 2008-2020 Björn Påhlsson
 
14
# Copyright © 2008-2019 Teddy Hogeborn
 
15
# Copyright © 2008-2019 Björn Påhlsson
16
16
#
17
17
# This file is part of Mandos.
18
18
#
78
78
import collections
79
79
import codecs
80
80
import unittest
81
 
import random
82
 
import shlex
83
81
 
84
82
import dbus
85
83
import dbus.service
93
91
 
94
92
if sys.version_info.major == 2:
95
93
    __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
94
 
113
95
# Show warnings by default
114
96
if not sys.warnoptions:
140
122
            # No value found
141
123
            SO_BINDTODEVICE = None
142
124
 
 
125
if sys.version_info.major == 2:
 
126
    str = unicode
 
127
 
143
128
if sys.version_info < (3, 2):
144
129
    configparser.Configparser = configparser.SafeConfigParser
145
130
 
146
 
version = "1.8.14"
 
131
version = "1.8.9"
147
132
stored_state_file = "clients.pickle"
148
133
 
149
134
logger = logging.getLogger()
189
174
        facility=logging.handlers.SysLogHandler.LOG_DAEMON,
190
175
        address="/dev/log"))
191
176
    syslogger.setFormatter(logging.Formatter
192
 
                           ("Mandos [%(process)d]: %(levelname)s:"
193
 
                            " %(message)s"))
 
177
                           ('Mandos [%(process)d]: %(levelname)s:'
 
178
                            ' %(message)s'))
194
179
    logger.addHandler(syslogger)
195
180
 
196
181
    if debug:
197
182
        console = logging.StreamHandler()
198
 
        console.setFormatter(logging.Formatter("%(asctime)s %(name)s"
199
 
                                               " [%(process)d]:"
200
 
                                               " %(levelname)s:"
201
 
                                               " %(message)s"))
 
183
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
184
                                               ' [%(process)d]:'
 
185
                                               ' %(levelname)s:'
 
186
                                               ' %(message)s'))
202
187
        logger.addHandler(console)
203
188
    logger.setLevel(level)
204
189
 
224
209
        except OSError as e:
225
210
            if e.errno != errno.ENOENT:
226
211
                raise
227
 
        self.gnupgargs = ["--batch",
228
 
                          "--homedir", self.tempdir,
229
 
                          "--force-mdc",
230
 
                          "--quiet"]
 
212
        self.gnupgargs = ['--batch',
 
213
                          '--homedir', self.tempdir,
 
214
                          '--force-mdc',
 
215
                          '--quiet']
231
216
        # Only GPG version 1 has the --no-use-agent option.
232
217
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
233
218
            self.gnupgargs.append("--no-use-agent")
272
257
                dir=self.tempdir) as passfile:
273
258
            passfile.write(passphrase)
274
259
            passfile.flush()
275
 
            proc = subprocess.Popen([self.gpg, "--symmetric",
276
 
                                     "--passphrase-file",
 
260
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
261
                                     '--passphrase-file',
277
262
                                     passfile.name]
278
263
                                    + self.gnupgargs,
279
264
                                    stdin=subprocess.PIPE,
290
275
                dir=self.tempdir) as passfile:
291
276
            passfile.write(passphrase)
292
277
            passfile.flush()
293
 
            proc = subprocess.Popen([self.gpg, "--decrypt",
294
 
                                     "--passphrase-file",
 
278
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
279
                                     '--passphrase-file',
295
280
                                     passfile.name]
296
281
                                    + self.gnupgargs,
297
282
                                    stdin=subprocess.PIPE,
350
335
    Attributes:
351
336
    interface: integer; avahi.IF_UNSPEC or an interface index.
352
337
               Used to optionally bind to the specified interface.
353
 
    name: string; Example: "Mandos"
354
 
    type: string; Example: "_mandos._tcp".
 
338
    name: string; Example: 'Mandos'
 
339
    type: string; Example: '_mandos._tcp'.
355
340
     See <https://www.iana.org/assignments/service-names-port-numbers>
356
341
    port: integer; what port to announce
357
342
    TXT: list of strings; TXT record for the service
435
420
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
436
421
        self.entry_group_state_changed_match = (
437
422
            self.group.connect_to_signal(
438
 
                "StateChanged", self.entry_group_state_changed))
 
423
                'StateChanged', self.entry_group_state_changed))
439
424
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
440
425
                     self.name, self.type)
441
426
        self.group.AddService(
524
509
class AvahiServiceToSyslog(AvahiService):
525
510
    def rename(self, *args, **kwargs):
526
511
        """Add the new name to the syslog messages"""
527
 
        ret = super(AvahiServiceToSyslog, self).rename(*args,
528
 
                                                       **kwargs)
 
512
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
529
513
        syslogger.setFormatter(logging.Formatter(
530
 
            "Mandos ({}) [%(process)d]: %(levelname)s: %(message)s"
 
514
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
531
515
            .format(self.name)))
532
516
        return ret
533
517
 
563
547
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
564
548
 
565
549
    # Types
566
 
    class _session_int(ctypes.Structure):
 
550
    class session_int(ctypes.Structure):
567
551
        _fields_ = []
568
 
    session_t = ctypes.POINTER(_session_int)
 
552
    session_t = ctypes.POINTER(session_int)
569
553
 
570
554
    class certificate_credentials_st(ctypes.Structure):
571
555
        _fields_ = []
574
558
    certificate_type_t = ctypes.c_int
575
559
 
576
560
    class datum_t(ctypes.Structure):
577
 
        _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)),
578
 
                    ("size", ctypes.c_uint)]
 
561
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
562
                    ('size', ctypes.c_uint)]
579
563
 
580
 
    class _openpgp_crt_int(ctypes.Structure):
 
564
    class openpgp_crt_int(ctypes.Structure):
581
565
        _fields_ = []
582
 
    openpgp_crt_t = ctypes.POINTER(_openpgp_crt_int)
 
566
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
583
567
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
584
568
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
585
569
    credentials_type_t = ctypes.c_int
594
578
            # gnutls.strerror()
595
579
            self.code = code
596
580
            if message is None and code is not None:
597
 
                message = gnutls.strerror(code).decode(
598
 
                    "utf-8", errors="replace")
 
581
                message = gnutls.strerror(code)
599
582
            return super(gnutls.Error, self).__init__(
600
583
                message, *args)
601
584
 
602
585
    class CertificateSecurityError(Error):
603
586
        pass
604
587
 
605
 
    class PointerTo:
606
 
        def __init__(self, cls):
607
 
            self.cls = cls
608
 
 
609
 
        def from_param(self, obj):
610
 
            if not isinstance(obj, self.cls):
611
 
                raise TypeError("Not of type {}: {!r}"
612
 
                                .format(self.cls.__name__, obj))
613
 
            return ctypes.byref(obj.from_param(obj))
614
 
 
615
 
    class CastToVoidPointer:
616
 
        def __init__(self, cls):
617
 
            self.cls = cls
618
 
 
619
 
        def from_param(self, obj):
620
 
            if not isinstance(obj, self.cls):
621
 
                raise TypeError("Not of type {}: {!r}"
622
 
                                .format(self.cls.__name__, obj))
623
 
            return ctypes.cast(obj.from_param(obj), ctypes.c_void_p)
624
 
 
625
 
    class With_from_param:
626
 
        @classmethod
627
 
        def from_param(cls, obj):
628
 
            return obj._as_parameter_
629
 
 
630
588
    # Classes
631
 
    class Credentials(With_from_param):
 
589
    class Credentials:
632
590
        def __init__(self):
633
 
            self._as_parameter_ = gnutls.certificate_credentials_t()
634
 
            gnutls.certificate_allocate_credentials(self)
 
591
            self._c_object = gnutls.certificate_credentials_t()
 
592
            gnutls.certificate_allocate_credentials(
 
593
                ctypes.byref(self._c_object))
635
594
            self.type = gnutls.CRD_CERTIFICATE
636
595
 
637
596
        def __del__(self):
638
 
            gnutls.certificate_free_credentials(self)
 
597
            gnutls.certificate_free_credentials(self._c_object)
639
598
 
640
 
    class ClientSession(With_from_param):
 
599
    class ClientSession:
641
600
        def __init__(self, socket, credentials=None):
642
 
            self._as_parameter_ = gnutls.session_t()
 
601
            self._c_object = gnutls.session_t()
643
602
            gnutls_flags = gnutls.CLIENT
644
603
            if gnutls.check_version(b"3.5.6"):
645
604
                gnutls_flags |= gnutls.NO_TICKETS
646
605
            if gnutls.has_rawpk:
647
606
                gnutls_flags |= gnutls.ENABLE_RAWPK
648
 
            gnutls.init(self, gnutls_flags)
 
607
            gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
649
608
            del gnutls_flags
650
 
            gnutls.set_default_priority(self)
651
 
            gnutls.transport_set_ptr(self, socket.fileno())
652
 
            gnutls.handshake_set_private_extensions(self, True)
 
609
            gnutls.set_default_priority(self._c_object)
 
610
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
 
611
            gnutls.handshake_set_private_extensions(self._c_object,
 
612
                                                    True)
653
613
            self.socket = socket
654
614
            if credentials is None:
655
615
                credentials = gnutls.Credentials()
656
 
            gnutls.credentials_set(self, credentials.type,
657
 
                                   credentials)
 
616
            gnutls.credentials_set(self._c_object, credentials.type,
 
617
                                   ctypes.cast(credentials._c_object,
 
618
                                               ctypes.c_void_p))
658
619
            self.credentials = credentials
659
620
 
660
621
        def __del__(self):
661
 
            gnutls.deinit(self)
 
622
            gnutls.deinit(self._c_object)
662
623
 
663
624
        def handshake(self):
664
 
            return gnutls.handshake(self)
 
625
            return gnutls.handshake(self._c_object)
665
626
 
666
627
        def send(self, data):
667
628
            data = bytes(data)
668
629
            data_len = len(data)
669
630
            while data_len > 0:
670
 
                data_len -= gnutls.record_send(self, data[-data_len:],
 
631
                data_len -= gnutls.record_send(self._c_object,
 
632
                                               data[-data_len:],
671
633
                                               data_len)
672
634
 
673
635
        def bye(self):
674
 
            return gnutls.bye(self, gnutls.SHUT_RDWR)
 
636
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
675
637
 
676
638
    # Error handling functions
677
639
    def _error_code(result):
678
640
        """A function to raise exceptions on errors, suitable
679
 
        for the "restype" attribute on ctypes functions"""
680
 
        if result >= gnutls.E_SUCCESS:
 
641
        for the 'restype' attribute on ctypes functions"""
 
642
        if result >= 0:
681
643
            return result
682
644
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
683
645
            raise gnutls.CertificateSecurityError(code=result)
684
646
        raise gnutls.Error(code=result)
685
647
 
686
 
    def _retry_on_error(result, func, arguments,
687
 
                        _error_code=_error_code):
 
648
    def _retry_on_error(result, func, arguments):
688
649
        """A function to retry on some errors, suitable
689
 
        for the "errcheck" attribute on ctypes functions"""
690
 
        while result < gnutls.E_SUCCESS:
 
650
        for the 'errcheck' attribute on ctypes functions"""
 
651
        while result < 0:
691
652
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
692
653
                return _error_code(result)
693
654
            result = func(*arguments)
698
659
 
699
660
    # Functions
700
661
    priority_set_direct = _library.gnutls_priority_set_direct
701
 
    priority_set_direct.argtypes = [ClientSession, ctypes.c_char_p,
 
662
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
702
663
                                    ctypes.POINTER(ctypes.c_char_p)]
703
664
    priority_set_direct.restype = _error_code
704
665
 
705
666
    init = _library.gnutls_init
706
 
    init.argtypes = [PointerTo(ClientSession), ctypes.c_int]
 
667
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
707
668
    init.restype = _error_code
708
669
 
709
670
    set_default_priority = _library.gnutls_set_default_priority
710
 
    set_default_priority.argtypes = [ClientSession]
 
671
    set_default_priority.argtypes = [session_t]
711
672
    set_default_priority.restype = _error_code
712
673
 
713
674
    record_send = _library.gnutls_record_send
714
 
    record_send.argtypes = [ClientSession, ctypes.c_void_p,
 
675
    record_send.argtypes = [session_t, ctypes.c_void_p,
715
676
                            ctypes.c_size_t]
716
677
    record_send.restype = ctypes.c_ssize_t
717
678
    record_send.errcheck = _retry_on_error
719
680
    certificate_allocate_credentials = (
720
681
        _library.gnutls_certificate_allocate_credentials)
721
682
    certificate_allocate_credentials.argtypes = [
722
 
        PointerTo(Credentials)]
 
683
        ctypes.POINTER(certificate_credentials_t)]
723
684
    certificate_allocate_credentials.restype = _error_code
724
685
 
725
686
    certificate_free_credentials = (
726
687
        _library.gnutls_certificate_free_credentials)
727
 
    certificate_free_credentials.argtypes = [Credentials]
 
688
    certificate_free_credentials.argtypes = [
 
689
        certificate_credentials_t]
728
690
    certificate_free_credentials.restype = None
729
691
 
730
692
    handshake_set_private_extensions = (
731
693
        _library.gnutls_handshake_set_private_extensions)
732
 
    handshake_set_private_extensions.argtypes = [ClientSession,
 
694
    handshake_set_private_extensions.argtypes = [session_t,
733
695
                                                 ctypes.c_int]
734
696
    handshake_set_private_extensions.restype = None
735
697
 
736
698
    credentials_set = _library.gnutls_credentials_set
737
 
    credentials_set.argtypes = [ClientSession, credentials_type_t,
738
 
                                CastToVoidPointer(Credentials)]
 
699
    credentials_set.argtypes = [session_t, credentials_type_t,
 
700
                                ctypes.c_void_p]
739
701
    credentials_set.restype = _error_code
740
702
 
741
703
    strerror = _library.gnutls_strerror
743
705
    strerror.restype = ctypes.c_char_p
744
706
 
745
707
    certificate_type_get = _library.gnutls_certificate_type_get
746
 
    certificate_type_get.argtypes = [ClientSession]
 
708
    certificate_type_get.argtypes = [session_t]
747
709
    certificate_type_get.restype = _error_code
748
710
 
749
711
    certificate_get_peers = _library.gnutls_certificate_get_peers
750
 
    certificate_get_peers.argtypes = [ClientSession,
 
712
    certificate_get_peers.argtypes = [session_t,
751
713
                                      ctypes.POINTER(ctypes.c_uint)]
752
714
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
753
715
 
760
722
    global_set_log_function.restype = None
761
723
 
762
724
    deinit = _library.gnutls_deinit
763
 
    deinit.argtypes = [ClientSession]
 
725
    deinit.argtypes = [session_t]
764
726
    deinit.restype = None
765
727
 
766
728
    handshake = _library.gnutls_handshake
767
 
    handshake.argtypes = [ClientSession]
768
 
    handshake.restype = ctypes.c_int
 
729
    handshake.argtypes = [session_t]
 
730
    handshake.restype = _error_code
769
731
    handshake.errcheck = _retry_on_error
770
732
 
771
733
    transport_set_ptr = _library.gnutls_transport_set_ptr
772
 
    transport_set_ptr.argtypes = [ClientSession, transport_ptr_t]
 
734
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
773
735
    transport_set_ptr.restype = None
774
736
 
775
737
    bye = _library.gnutls_bye
776
 
    bye.argtypes = [ClientSession, close_request_t]
777
 
    bye.restype = ctypes.c_int
 
738
    bye.argtypes = [session_t, close_request_t]
 
739
    bye.restype = _error_code
778
740
    bye.errcheck = _retry_on_error
779
741
 
780
742
    check_version = _library.gnutls_check_version
797
759
 
798
760
        x509_crt_fmt_t = ctypes.c_int
799
761
 
800
 
        # All the function declarations below are from
801
 
        # gnutls/abstract.h
 
762
        # All the function declarations below are from gnutls/abstract.h
802
763
        pubkey_init = _library.gnutls_pubkey_init
803
764
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
804
765
        pubkey_init.restype = _error_code
818
779
        pubkey_deinit.argtypes = [pubkey_t]
819
780
        pubkey_deinit.restype = None
820
781
    else:
821
 
        # All the function declarations below are from
822
 
        # gnutls/openpgp.h
 
782
        # All the function declarations below are from gnutls/openpgp.h
823
783
 
824
784
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
825
785
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
831
791
                                       openpgp_crt_fmt_t]
832
792
        openpgp_crt_import.restype = _error_code
833
793
 
834
 
        openpgp_crt_verify_self = \
835
 
            _library.gnutls_openpgp_crt_verify_self
836
 
        openpgp_crt_verify_self.argtypes = [
837
 
            openpgp_crt_t,
838
 
            ctypes.c_uint,
839
 
            ctypes.POINTER(ctypes.c_uint),
840
 
        ]
 
794
        openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
795
        openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
796
                                            ctypes.POINTER(ctypes.c_uint)]
841
797
        openpgp_crt_verify_self.restype = _error_code
842
798
 
843
799
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
854
810
 
855
811
    if check_version(b"3.6.4"):
856
812
        certificate_type_get2 = _library.gnutls_certificate_type_get2
857
 
        certificate_type_get2.argtypes = [ClientSession, ctypes.c_int]
 
813
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
858
814
        certificate_type_get2.restype = _error_code
859
815
 
860
816
    # Remove non-public functions
876
832
    """A representation of a client host served by this server.
877
833
 
878
834
    Attributes:
879
 
    approved:   bool(); None if not yet approved/disapproved
 
835
    approved:   bool(); 'None' if not yet approved/disapproved
880
836
    approval_delay: datetime.timedelta(); Time to wait for approval
881
837
    approval_duration: datetime.timedelta(); Duration of one approval
882
838
    checker: multiprocessing.Process(); a running checker process used
883
 
             to see if the client lives. None if no process is
 
839
             to see if the client lives. 'None' if no process is
884
840
             running.
885
841
    checker_callback_tag: a GLib event source tag, or None
886
842
    checker_command: string; External command which is run to check
1081
1037
        if self.checker_initiator_tag is not None:
1082
1038
            GLib.source_remove(self.checker_initiator_tag)
1083
1039
        self.checker_initiator_tag = GLib.timeout_add(
1084
 
            random.randrange(int(self.interval.total_seconds() * 1000
1085
 
                                 + 1)),
 
1040
            int(self.interval.total_seconds() * 1000),
1086
1041
            self.start_checker)
1087
1042
        # Schedule a disable() when 'timeout' has passed
1088
1043
        if self.disable_initiator_tag is not None:
1163
1118
        if self.checker is None:
1164
1119
            # Escape attributes for the shell
1165
1120
            escaped_attrs = {
1166
 
                attr: shlex.quote(str(getattr(self, attr)))
 
1121
                attr: re.escape(str(getattr(self, attr)))
1167
1122
                for attr in self.runtime_expansions}
1168
1123
            try:
1169
1124
                command = self.checker_command % escaped_attrs
1242
1197
        func._dbus_name = func.__name__
1243
1198
        if func._dbus_name.endswith("_dbus_property"):
1244
1199
            func._dbus_name = func._dbus_name[:-14]
1245
 
        func._dbus_get_args_options = {"byte_arrays": byte_arrays}
 
1200
        func._dbus_get_args_options = {'byte_arrays': byte_arrays}
1246
1201
        return func
1247
1202
 
1248
1203
    return decorator
1337
1292
 
1338
1293
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1339
1294
                         out_signature="s",
1340
 
                         path_keyword="object_path",
1341
 
                         connection_keyword="connection")
 
1295
                         path_keyword='object_path',
 
1296
                         connection_keyword='connection')
1342
1297
    def Introspect(self, object_path, connection):
1343
1298
        """Overloading of standard D-Bus method.
1344
1299
 
1458
1413
                raise ValueError("Byte arrays not supported for non-"
1459
1414
                                 "'ay' signature {!r}"
1460
1415
                                 .format(prop._dbus_signature))
1461
 
            value = dbus.ByteArray(bytes(value))
 
1416
            value = dbus.ByteArray(b''.join(chr(byte)
 
1417
                                            for byte in value))
1462
1418
        prop(value)
1463
1419
 
1464
1420
    @dbus.service.method(dbus.PROPERTIES_IFACE,
1497
1453
 
1498
1454
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1499
1455
                         out_signature="s",
1500
 
                         path_keyword="object_path",
1501
 
                         connection_keyword="connection")
 
1456
                         path_keyword='object_path',
 
1457
                         connection_keyword='connection')
1502
1458
    def Introspect(self, object_path, connection):
1503
1459
        """Overloading of standard D-Bus method.
1504
1460
 
1599
1555
 
1600
1556
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1601
1557
                         out_signature="s",
1602
 
                         path_keyword="object_path",
1603
 
                         connection_keyword="connection")
 
1558
                         path_keyword='object_path',
 
1559
                         connection_keyword='connection')
1604
1560
    def Introspect(self, object_path, connection):
1605
1561
        """Overloading of standard D-Bus method.
1606
1562
 
2272
2228
class ProxyClient:
2273
2229
    def __init__(self, child_pipe, key_id, fpr, address):
2274
2230
        self._pipe = child_pipe
2275
 
        self._pipe.send(("init", key_id, fpr, address))
 
2231
        self._pipe.send(('init', key_id, fpr, address))
2276
2232
        if not self._pipe.recv():
2277
2233
            raise KeyError(key_id or fpr)
2278
2234
 
2279
2235
    def __getattribute__(self, name):
2280
 
        if name == "_pipe":
 
2236
        if name == '_pipe':
2281
2237
            return super(ProxyClient, self).__getattribute__(name)
2282
 
        self._pipe.send(("getattr", name))
 
2238
        self._pipe.send(('getattr', name))
2283
2239
        data = self._pipe.recv()
2284
 
        if data[0] == "data":
 
2240
        if data[0] == 'data':
2285
2241
            return data[1]
2286
 
        if data[0] == "function":
 
2242
        if data[0] == 'function':
2287
2243
 
2288
2244
            def func(*args, **kwargs):
2289
 
                self._pipe.send(("funcall", name, args, kwargs))
 
2245
                self._pipe.send(('funcall', name, args, kwargs))
2290
2246
                return self._pipe.recv()[1]
2291
2247
 
2292
2248
            return func
2293
2249
 
2294
2250
    def __setattr__(self, name, value):
2295
 
        if name == "_pipe":
 
2251
        if name == '_pipe':
2296
2252
            return super(ProxyClient, self).__setattr__(name, value)
2297
 
        self._pipe.send(("setattr", name, value))
 
2253
        self._pipe.send(('setattr', name, value))
2298
2254
 
2299
2255
 
2300
2256
class ClientHandler(socketserver.BaseRequestHandler, object):
2312
2268
 
2313
2269
            session = gnutls.ClientSession(self.request)
2314
2270
 
2315
 
            # priority = ":".join(("NONE", "+VERS-TLS1.1",
 
2271
            # priority = ':'.join(("NONE", "+VERS-TLS1.1",
2316
2272
            #                       "+AES-256-CBC", "+SHA1",
2317
2273
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
2318
2274
            #                       "+DHE-DSS"))
2320
2276
            priority = self.server.gnutls_priority
2321
2277
            if priority is None:
2322
2278
                priority = "NORMAL"
2323
 
            gnutls.priority_set_direct(session,
2324
 
                                       priority.encode("utf-8"), None)
 
2279
            gnutls.priority_set_direct(session._c_object,
 
2280
                                       priority.encode("utf-8"),
 
2281
                                       None)
2325
2282
 
2326
2283
            # Start communication using the Mandos protocol
2327
2284
            # Get protocol number
2354
2311
                    except (TypeError, gnutls.Error) as error:
2355
2312
                        logger.warning("Bad certificate: %s", error)
2356
2313
                        return
2357
 
                    logger.debug("Key ID: %s",
2358
 
                                 key_id.decode("utf-8",
2359
 
                                               errors="replace"))
 
2314
                    logger.debug("Key ID: %s", key_id)
2360
2315
 
2361
2316
                else:
2362
2317
                    key_id = b""
2454
2409
    def peer_certificate(session):
2455
2410
        "Return the peer's certificate as a bytestring"
2456
2411
        try:
2457
 
            cert_type = gnutls.certificate_type_get2(
2458
 
                session, gnutls.CTYPE_PEERS)
 
2412
            cert_type = gnutls.certificate_type_get2(session._c_object,
 
2413
                                                     gnutls.CTYPE_PEERS)
2459
2414
        except AttributeError:
2460
 
            cert_type = gnutls.certificate_type_get(session)
 
2415
            cert_type = gnutls.certificate_type_get(session._c_object)
2461
2416
        if gnutls.has_rawpk:
2462
2417
            valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
2463
2418
        else:
2470
2425
            return b""
2471
2426
        list_size = ctypes.c_uint(1)
2472
2427
        cert_list = (gnutls.certificate_get_peers
2473
 
                     (session, ctypes.byref(list_size)))
 
2428
                     (session._c_object, ctypes.byref(list_size)))
2474
2429
        if not bool(cert_list) and list_size.value != 0:
2475
2430
            raise gnutls.Error("error getting peer certificate")
2476
2431
        if list_size.value == 0:
2498
2453
        buf = ctypes.create_string_buffer(32)
2499
2454
        buf_len = ctypes.c_size_t(len(buf))
2500
2455
        # Get the key ID from the raw public key into the buffer
2501
 
        gnutls.pubkey_get_key_id(
2502
 
            pubkey,
2503
 
            gnutls.KEYID_USE_SHA256,
2504
 
            ctypes.cast(ctypes.byref(buf),
2505
 
                        ctypes.POINTER(ctypes.c_ubyte)),
2506
 
            ctypes.byref(buf_len))
 
2456
        gnutls.pubkey_get_key_id(pubkey,
 
2457
                                 gnutls.KEYID_USE_SHA256,
 
2458
                                 ctypes.cast(ctypes.byref(buf),
 
2459
                                             ctypes.POINTER(ctypes.c_ubyte)),
 
2460
                                 ctypes.byref(buf_len))
2507
2461
        # Deinit the certificate
2508
2462
        gnutls.pubkey_deinit(pubkey)
2509
2463
 
2590
2544
 
2591
2545
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2592
2546
                     socketserver.TCPServer):
2593
 
    """IPv6-capable TCP server.  Accepts None as address and/or port
 
2547
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2594
2548
 
2595
2549
    Attributes:
2596
2550
        enabled:        Boolean; whether this server is activated yet
2748
2702
        request = parent_pipe.recv()
2749
2703
        command = request[0]
2750
2704
 
2751
 
        if command == "init":
 
2705
        if command == 'init':
2752
2706
            key_id = request[1].decode("ascii")
2753
2707
            fpr = request[2].decode("ascii")
2754
2708
            address = request[3]
2755
2709
 
2756
2710
            for c in self.clients.values():
2757
 
                if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924"
2758
 
                              "27AE41E4649B934CA495991B7852B855"):
 
2711
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
2759
2712
                    continue
2760
2713
                if key_id and c.key_id == key_id:
2761
2714
                    client = c
2784
2737
            # remove the old hook in favor of the new above hook on
2785
2738
            # same fileno
2786
2739
            return False
2787
 
        if command == "funcall":
 
2740
        if command == 'funcall':
2788
2741
            funcname = request[1]
2789
2742
            args = request[2]
2790
2743
            kwargs = request[3]
2791
2744
 
2792
 
            parent_pipe.send(("data", getattr(client_object,
 
2745
            parent_pipe.send(('data', getattr(client_object,
2793
2746
                                              funcname)(*args,
2794
2747
                                                        **kwargs)))
2795
2748
 
2796
 
        if command == "getattr":
 
2749
        if command == 'getattr':
2797
2750
            attrname = request[1]
2798
2751
            if isinstance(client_object.__getattribute__(attrname),
2799
 
                          collections.abc.Callable):
2800
 
                parent_pipe.send(("function", ))
 
2752
                          collections.Callable):
 
2753
                parent_pipe.send(('function', ))
2801
2754
            else:
2802
2755
                parent_pipe.send((
2803
 
                    "data", client_object.__getattribute__(attrname)))
 
2756
                    'data', client_object.__getattribute__(attrname)))
2804
2757
 
2805
 
        if command == "setattr":
 
2758
        if command == 'setattr':
2806
2759
            attrname = request[1]
2807
2760
            value = request[2]
2808
2761
            setattr(client_object, attrname, value)
2813
2766
def rfc3339_duration_to_delta(duration):
2814
2767
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2815
2768
 
2816
 
    >>> timedelta = datetime.timedelta
2817
 
    >>> rfc3339_duration_to_delta("P7D") == timedelta(7)
2818
 
    True
2819
 
    >>> rfc3339_duration_to_delta("PT60S") == timedelta(0, 60)
2820
 
    True
2821
 
    >>> rfc3339_duration_to_delta("PT60M") == timedelta(0, 3600)
2822
 
    True
2823
 
    >>> rfc3339_duration_to_delta("PT24H") == timedelta(1)
2824
 
    True
2825
 
    >>> rfc3339_duration_to_delta("P1W") == timedelta(7)
2826
 
    True
2827
 
    >>> rfc3339_duration_to_delta("PT5M30S") == timedelta(0, 330)
2828
 
    True
2829
 
    >>> rfc3339_duration_to_delta("P1DT3M20S") == timedelta(1, 200)
2830
 
    True
2831
 
    >>> del timedelta
 
2769
    >>> rfc3339_duration_to_delta("P7D") == datetime.timedelta(7)
 
2770
    True
 
2771
    >>> rfc3339_duration_to_delta("PT60S") == datetime.timedelta(0, 60)
 
2772
    True
 
2773
    >>> rfc3339_duration_to_delta("PT60M") == datetime.timedelta(0, 3600)
 
2774
    True
 
2775
    >>> rfc3339_duration_to_delta("PT24H") == datetime.timedelta(1)
 
2776
    True
 
2777
    >>> rfc3339_duration_to_delta("P1W") == datetime.timedelta(7)
 
2778
    True
 
2779
    >>> rfc3339_duration_to_delta("PT5M30S") == datetime.timedelta(0, 330)
 
2780
    True
 
2781
    >>> rfc3339_duration_to_delta("P1DT3M20S") == datetime.timedelta(1, 200)
 
2782
    True
2832
2783
    """
2833
2784
 
2834
2785
    # Parsing an RFC 3339 duration with regular expressions is not
2914
2865
def string_to_delta(interval):
2915
2866
    """Parse a string and return a datetime.timedelta
2916
2867
 
2917
 
    >>> string_to_delta("7d") == datetime.timedelta(7)
2918
 
    True
2919
 
    >>> string_to_delta("60s") == datetime.timedelta(0, 60)
2920
 
    True
2921
 
    >>> string_to_delta("60m") == datetime.timedelta(0, 3600)
2922
 
    True
2923
 
    >>> string_to_delta("24h") == datetime.timedelta(1)
2924
 
    True
2925
 
    >>> string_to_delta("1w") == datetime.timedelta(7)
2926
 
    True
2927
 
    >>> string_to_delta("5m 30s") == datetime.timedelta(0, 330)
 
2868
    >>> string_to_delta('7d') == datetime.timedelta(7)
 
2869
    True
 
2870
    >>> string_to_delta('60s') == datetime.timedelta(0, 60)
 
2871
    True
 
2872
    >>> string_to_delta('60m') == datetime.timedelta(0, 3600)
 
2873
    True
 
2874
    >>> string_to_delta('24h') == datetime.timedelta(1)
 
2875
    True
 
2876
    >>> string_to_delta('1w') == datetime.timedelta(7)
 
2877
    True
 
2878
    >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
2928
2879
    True
2929
2880
    """
2930
2881
 
3134
3085
 
3135
3086
    if server_settings["servicename"] != "Mandos":
3136
3087
        syslogger.setFormatter(
3137
 
            logging.Formatter("Mandos ({}) [%(process)d]:"
3138
 
                              " %(levelname)s: %(message)s".format(
 
3088
            logging.Formatter('Mandos ({}) [%(process)d]:'
 
3089
                              ' %(levelname)s: %(message)s'.format(
3139
3090
                                  server_settings["servicename"])))
3140
3091
 
3141
3092
    # Parse config file with clients
3201
3152
 
3202
3153
        @gnutls.log_func
3203
3154
        def debug_gnutls(level, string):
3204
 
            logger.debug("GnuTLS: %s",
3205
 
                         string[:-1].decode("utf-8",
3206
 
                                            errors="replace"))
 
3155
            logger.debug("GnuTLS: %s", string[:-1])
3207
3156
 
3208
3157
        gnutls.global_set_log_function(debug_gnutls)
3209
3158
 
3584
3533
 
3585
3534
        try:
3586
3535
            with tempfile.NamedTemporaryFile(
3587
 
                    mode="wb",
 
3536
                    mode='wb',
3588
3537
                    suffix=".pickle",
3589
 
                    prefix="clients-",
 
3538
                    prefix='clients-',
3590
3539
                    dir=os.path.dirname(stored_state_path),
3591
3540
                    delete=False) as stored_state:
3592
3541
                pickle.dump((clients, client_settings), stored_state,
3679
3628
 
3680
3629
def should_only_run_tests():
3681
3630
    parser = argparse.ArgumentParser(add_help=False)
3682
 
    parser.add_argument("--check", action="store_true")
 
3631
    parser.add_argument("--check", action='store_true')
3683
3632
    args, unknown_args = parser.parse_known_args()
3684
3633
    run_tests = args.check
3685
3634
    if run_tests:
3693
3642
    tests.addTests(doctest.DocTestSuite())
3694
3643
    return tests
3695
3644
 
3696
 
if __name__ == "__main__":
 
3645
if __name__ == '__main__':
3697
3646
    try:
3698
3647
        if should_only_run_tests():
3699
3648
            # Call using ./mandos --check [--verbose]