/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: 2021-03-21 19:51:15 UTC
  • Revision ID: teddy@recompile.se-20210321195115-qe6g0fyj1kabwlav
Fix theoretical GnuTLS bug

Fix "NameError: global name '_error_code' is not defined" error if
GNUTLS_E_INTERRUPTED or GNUTLS_E_AGAIN was ever returned from
gnutls_record_send().

* mandos (gnutls._retry_on_error): Import "_error_code" from outer
  class scope to local function scope via a keyword argument.

Show diffs side-by-side

added added

removed removed

Lines of Context:
189
189
        facility=logging.handlers.SysLogHandler.LOG_DAEMON,
190
190
        address="/dev/log"))
191
191
    syslogger.setFormatter(logging.Formatter
192
 
                           ("Mandos [%(process)d]: %(levelname)s:"
193
 
                            " %(message)s"))
 
192
                           ('Mandos [%(process)d]: %(levelname)s:'
 
193
                            ' %(message)s'))
194
194
    logger.addHandler(syslogger)
195
195
 
196
196
    if debug:
197
197
        console = logging.StreamHandler()
198
 
        console.setFormatter(logging.Formatter("%(asctime)s %(name)s"
199
 
                                               " [%(process)d]:"
200
 
                                               " %(levelname)s:"
201
 
                                               " %(message)s"))
 
198
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
199
                                               ' [%(process)d]:'
 
200
                                               ' %(levelname)s:'
 
201
                                               ' %(message)s'))
202
202
        logger.addHandler(console)
203
203
    logger.setLevel(level)
204
204
 
224
224
        except OSError as e:
225
225
            if e.errno != errno.ENOENT:
226
226
                raise
227
 
        self.gnupgargs = ["--batch",
228
 
                          "--homedir", self.tempdir,
229
 
                          "--force-mdc",
230
 
                          "--quiet"]
 
227
        self.gnupgargs = ['--batch',
 
228
                          '--homedir', self.tempdir,
 
229
                          '--force-mdc',
 
230
                          '--quiet']
231
231
        # Only GPG version 1 has the --no-use-agent option.
232
232
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
233
233
            self.gnupgargs.append("--no-use-agent")
272
272
                dir=self.tempdir) as passfile:
273
273
            passfile.write(passphrase)
274
274
            passfile.flush()
275
 
            proc = subprocess.Popen([self.gpg, "--symmetric",
276
 
                                     "--passphrase-file",
 
275
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
276
                                     '--passphrase-file',
277
277
                                     passfile.name]
278
278
                                    + self.gnupgargs,
279
279
                                    stdin=subprocess.PIPE,
290
290
                dir=self.tempdir) as passfile:
291
291
            passfile.write(passphrase)
292
292
            passfile.flush()
293
 
            proc = subprocess.Popen([self.gpg, "--decrypt",
294
 
                                     "--passphrase-file",
 
293
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
294
                                     '--passphrase-file',
295
295
                                     passfile.name]
296
296
                                    + self.gnupgargs,
297
297
                                    stdin=subprocess.PIPE,
350
350
    Attributes:
351
351
    interface: integer; avahi.IF_UNSPEC or an interface index.
352
352
               Used to optionally bind to the specified interface.
353
 
    name: string; Example: "Mandos"
354
 
    type: string; Example: "_mandos._tcp".
 
353
    name: string; Example: 'Mandos'
 
354
    type: string; Example: '_mandos._tcp'.
355
355
     See <https://www.iana.org/assignments/service-names-port-numbers>
356
356
    port: integer; what port to announce
357
357
    TXT: list of strings; TXT record for the service
435
435
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
436
436
        self.entry_group_state_changed_match = (
437
437
            self.group.connect_to_signal(
438
 
                "StateChanged", self.entry_group_state_changed))
 
438
                'StateChanged', self.entry_group_state_changed))
439
439
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
440
440
                     self.name, self.type)
441
441
        self.group.AddService(
527
527
        ret = super(AvahiServiceToSyslog, self).rename(*args,
528
528
                                                       **kwargs)
529
529
        syslogger.setFormatter(logging.Formatter(
530
 
            "Mandos ({}) [%(process)d]: %(levelname)s: %(message)s"
 
530
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
531
531
            .format(self.name)))
532
532
        return ret
533
533
 
563
563
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
564
564
 
565
565
    # Types
566
 
    class _session_int(ctypes.Structure):
 
566
    class session_int(ctypes.Structure):
567
567
        _fields_ = []
568
 
    session_t = ctypes.POINTER(_session_int)
 
568
    session_t = ctypes.POINTER(session_int)
569
569
 
570
570
    class certificate_credentials_st(ctypes.Structure):
571
571
        _fields_ = []
574
574
    certificate_type_t = ctypes.c_int
575
575
 
576
576
    class datum_t(ctypes.Structure):
577
 
        _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)),
578
 
                    ("size", ctypes.c_uint)]
 
577
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
578
                    ('size', ctypes.c_uint)]
579
579
 
580
 
    class _openpgp_crt_int(ctypes.Structure):
 
580
    class openpgp_crt_int(ctypes.Structure):
581
581
        _fields_ = []
582
 
    openpgp_crt_t = ctypes.POINTER(_openpgp_crt_int)
 
582
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
583
583
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
584
584
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
585
585
    credentials_type_t = ctypes.c_int
594
594
            # gnutls.strerror()
595
595
            self.code = code
596
596
            if message is None and code is not None:
597
 
                message = gnutls.strerror(code).decode(
598
 
                    "utf-8", errors="replace")
 
597
                message = gnutls.strerror(code)
599
598
            return super(gnutls.Error, self).__init__(
600
599
                message, *args)
601
600
 
602
601
    class CertificateSecurityError(Error):
603
602
        pass
604
603
 
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
604
    # Classes
631
 
    class Credentials(With_from_param):
 
605
    class Credentials:
632
606
        def __init__(self):
633
 
            self._as_parameter_ = gnutls.certificate_credentials_t()
634
 
            gnutls.certificate_allocate_credentials(self)
 
607
            self._c_object = gnutls.certificate_credentials_t()
 
608
            gnutls.certificate_allocate_credentials(
 
609
                ctypes.byref(self._c_object))
635
610
            self.type = gnutls.CRD_CERTIFICATE
636
611
 
637
612
        def __del__(self):
638
 
            gnutls.certificate_free_credentials(self)
 
613
            gnutls.certificate_free_credentials(self._c_object)
639
614
 
640
 
    class ClientSession(With_from_param):
 
615
    class ClientSession:
641
616
        def __init__(self, socket, credentials=None):
642
 
            self._as_parameter_ = gnutls.session_t()
 
617
            self._c_object = gnutls.session_t()
643
618
            gnutls_flags = gnutls.CLIENT
644
619
            if gnutls.check_version(b"3.5.6"):
645
620
                gnutls_flags |= gnutls.NO_TICKETS
646
621
            if gnutls.has_rawpk:
647
622
                gnutls_flags |= gnutls.ENABLE_RAWPK
648
 
            gnutls.init(self, gnutls_flags)
 
623
            gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
649
624
            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)
 
625
            gnutls.set_default_priority(self._c_object)
 
626
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
 
627
            gnutls.handshake_set_private_extensions(self._c_object,
 
628
                                                    True)
653
629
            self.socket = socket
654
630
            if credentials is None:
655
631
                credentials = gnutls.Credentials()
656
 
            gnutls.credentials_set(self, credentials.type,
657
 
                                   credentials)
 
632
            gnutls.credentials_set(self._c_object, credentials.type,
 
633
                                   ctypes.cast(credentials._c_object,
 
634
                                               ctypes.c_void_p))
658
635
            self.credentials = credentials
659
636
 
660
637
        def __del__(self):
661
 
            gnutls.deinit(self)
 
638
            gnutls.deinit(self._c_object)
662
639
 
663
640
        def handshake(self):
664
 
            return gnutls.handshake(self)
 
641
            return gnutls.handshake(self._c_object)
665
642
 
666
643
        def send(self, data):
667
644
            data = bytes(data)
668
645
            data_len = len(data)
669
646
            while data_len > 0:
670
 
                data_len -= gnutls.record_send(self, data[-data_len:],
 
647
                data_len -= gnutls.record_send(self._c_object,
 
648
                                               data[-data_len:],
671
649
                                               data_len)
672
650
 
673
651
        def bye(self):
674
 
            return gnutls.bye(self, gnutls.SHUT_RDWR)
 
652
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
675
653
 
676
654
    # Error handling functions
677
655
    def _error_code(result):
678
656
        """A function to raise exceptions on errors, suitable
679
 
        for the "restype" attribute on ctypes functions"""
680
 
        if result >= gnutls.E_SUCCESS:
 
657
        for the 'restype' attribute on ctypes functions"""
 
658
        if result >= 0:
681
659
            return result
682
660
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
683
661
            raise gnutls.CertificateSecurityError(code=result)
686
664
    def _retry_on_error(result, func, arguments,
687
665
                        _error_code=_error_code):
688
666
        """A function to retry on some errors, suitable
689
 
        for the "errcheck" attribute on ctypes functions"""
690
 
        while result < gnutls.E_SUCCESS:
 
667
        for the 'errcheck' attribute on ctypes functions"""
 
668
        while result < 0:
691
669
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
692
670
                return _error_code(result)
693
671
            result = func(*arguments)
698
676
 
699
677
    # Functions
700
678
    priority_set_direct = _library.gnutls_priority_set_direct
701
 
    priority_set_direct.argtypes = [ClientSession, ctypes.c_char_p,
 
679
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
702
680
                                    ctypes.POINTER(ctypes.c_char_p)]
703
681
    priority_set_direct.restype = _error_code
704
682
 
705
683
    init = _library.gnutls_init
706
 
    init.argtypes = [PointerTo(ClientSession), ctypes.c_int]
 
684
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
707
685
    init.restype = _error_code
708
686
 
709
687
    set_default_priority = _library.gnutls_set_default_priority
710
 
    set_default_priority.argtypes = [ClientSession]
 
688
    set_default_priority.argtypes = [session_t]
711
689
    set_default_priority.restype = _error_code
712
690
 
713
691
    record_send = _library.gnutls_record_send
714
 
    record_send.argtypes = [ClientSession, ctypes.c_void_p,
 
692
    record_send.argtypes = [session_t, ctypes.c_void_p,
715
693
                            ctypes.c_size_t]
716
694
    record_send.restype = ctypes.c_ssize_t
717
695
    record_send.errcheck = _retry_on_error
719
697
    certificate_allocate_credentials = (
720
698
        _library.gnutls_certificate_allocate_credentials)
721
699
    certificate_allocate_credentials.argtypes = [
722
 
        PointerTo(Credentials)]
 
700
        ctypes.POINTER(certificate_credentials_t)]
723
701
    certificate_allocate_credentials.restype = _error_code
724
702
 
725
703
    certificate_free_credentials = (
726
704
        _library.gnutls_certificate_free_credentials)
727
 
    certificate_free_credentials.argtypes = [Credentials]
 
705
    certificate_free_credentials.argtypes = [
 
706
        certificate_credentials_t]
728
707
    certificate_free_credentials.restype = None
729
708
 
730
709
    handshake_set_private_extensions = (
731
710
        _library.gnutls_handshake_set_private_extensions)
732
 
    handshake_set_private_extensions.argtypes = [ClientSession,
 
711
    handshake_set_private_extensions.argtypes = [session_t,
733
712
                                                 ctypes.c_int]
734
713
    handshake_set_private_extensions.restype = None
735
714
 
736
715
    credentials_set = _library.gnutls_credentials_set
737
 
    credentials_set.argtypes = [ClientSession, credentials_type_t,
738
 
                                CastToVoidPointer(Credentials)]
 
716
    credentials_set.argtypes = [session_t, credentials_type_t,
 
717
                                ctypes.c_void_p]
739
718
    credentials_set.restype = _error_code
740
719
 
741
720
    strerror = _library.gnutls_strerror
743
722
    strerror.restype = ctypes.c_char_p
744
723
 
745
724
    certificate_type_get = _library.gnutls_certificate_type_get
746
 
    certificate_type_get.argtypes = [ClientSession]
 
725
    certificate_type_get.argtypes = [session_t]
747
726
    certificate_type_get.restype = _error_code
748
727
 
749
728
    certificate_get_peers = _library.gnutls_certificate_get_peers
750
 
    certificate_get_peers.argtypes = [ClientSession,
 
729
    certificate_get_peers.argtypes = [session_t,
751
730
                                      ctypes.POINTER(ctypes.c_uint)]
752
731
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
753
732
 
760
739
    global_set_log_function.restype = None
761
740
 
762
741
    deinit = _library.gnutls_deinit
763
 
    deinit.argtypes = [ClientSession]
 
742
    deinit.argtypes = [session_t]
764
743
    deinit.restype = None
765
744
 
766
745
    handshake = _library.gnutls_handshake
767
 
    handshake.argtypes = [ClientSession]
768
 
    handshake.restype = ctypes.c_int
 
746
    handshake.argtypes = [session_t]
 
747
    handshake.restype = _error_code
769
748
    handshake.errcheck = _retry_on_error
770
749
 
771
750
    transport_set_ptr = _library.gnutls_transport_set_ptr
772
 
    transport_set_ptr.argtypes = [ClientSession, transport_ptr_t]
 
751
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
773
752
    transport_set_ptr.restype = None
774
753
 
775
754
    bye = _library.gnutls_bye
776
 
    bye.argtypes = [ClientSession, close_request_t]
777
 
    bye.restype = ctypes.c_int
 
755
    bye.argtypes = [session_t, close_request_t]
 
756
    bye.restype = _error_code
778
757
    bye.errcheck = _retry_on_error
779
758
 
780
759
    check_version = _library.gnutls_check_version
854
833
 
855
834
    if check_version(b"3.6.4"):
856
835
        certificate_type_get2 = _library.gnutls_certificate_type_get2
857
 
        certificate_type_get2.argtypes = [ClientSession, ctypes.c_int]
 
836
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
858
837
        certificate_type_get2.restype = _error_code
859
838
 
860
839
    # Remove non-public functions
876
855
    """A representation of a client host served by this server.
877
856
 
878
857
    Attributes:
879
 
    approved:   bool(); None if not yet approved/disapproved
 
858
    approved:   bool(); 'None' if not yet approved/disapproved
880
859
    approval_delay: datetime.timedelta(); Time to wait for approval
881
860
    approval_duration: datetime.timedelta(); Duration of one approval
882
861
    checker: multiprocessing.Process(); a running checker process used
883
 
             to see if the client lives. None if no process is
 
862
             to see if the client lives. 'None' if no process is
884
863
             running.
885
864
    checker_callback_tag: a GLib event source tag, or None
886
865
    checker_command: string; External command which is run to check
1242
1221
        func._dbus_name = func.__name__
1243
1222
        if func._dbus_name.endswith("_dbus_property"):
1244
1223
            func._dbus_name = func._dbus_name[:-14]
1245
 
        func._dbus_get_args_options = {"byte_arrays": byte_arrays}
 
1224
        func._dbus_get_args_options = {'byte_arrays': byte_arrays}
1246
1225
        return func
1247
1226
 
1248
1227
    return decorator
1337
1316
 
1338
1317
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1339
1318
                         out_signature="s",
1340
 
                         path_keyword="object_path",
1341
 
                         connection_keyword="connection")
 
1319
                         path_keyword='object_path',
 
1320
                         connection_keyword='connection')
1342
1321
    def Introspect(self, object_path, connection):
1343
1322
        """Overloading of standard D-Bus method.
1344
1323
 
1497
1476
 
1498
1477
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1499
1478
                         out_signature="s",
1500
 
                         path_keyword="object_path",
1501
 
                         connection_keyword="connection")
 
1479
                         path_keyword='object_path',
 
1480
                         connection_keyword='connection')
1502
1481
    def Introspect(self, object_path, connection):
1503
1482
        """Overloading of standard D-Bus method.
1504
1483
 
1599
1578
 
1600
1579
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1601
1580
                         out_signature="s",
1602
 
                         path_keyword="object_path",
1603
 
                         connection_keyword="connection")
 
1581
                         path_keyword='object_path',
 
1582
                         connection_keyword='connection')
1604
1583
    def Introspect(self, object_path, connection):
1605
1584
        """Overloading of standard D-Bus method.
1606
1585
 
2272
2251
class ProxyClient:
2273
2252
    def __init__(self, child_pipe, key_id, fpr, address):
2274
2253
        self._pipe = child_pipe
2275
 
        self._pipe.send(("init", key_id, fpr, address))
 
2254
        self._pipe.send(('init', key_id, fpr, address))
2276
2255
        if not self._pipe.recv():
2277
2256
            raise KeyError(key_id or fpr)
2278
2257
 
2279
2258
    def __getattribute__(self, name):
2280
 
        if name == "_pipe":
 
2259
        if name == '_pipe':
2281
2260
            return super(ProxyClient, self).__getattribute__(name)
2282
 
        self._pipe.send(("getattr", name))
 
2261
        self._pipe.send(('getattr', name))
2283
2262
        data = self._pipe.recv()
2284
 
        if data[0] == "data":
 
2263
        if data[0] == 'data':
2285
2264
            return data[1]
2286
 
        if data[0] == "function":
 
2265
        if data[0] == 'function':
2287
2266
 
2288
2267
            def func(*args, **kwargs):
2289
 
                self._pipe.send(("funcall", name, args, kwargs))
 
2268
                self._pipe.send(('funcall', name, args, kwargs))
2290
2269
                return self._pipe.recv()[1]
2291
2270
 
2292
2271
            return func
2293
2272
 
2294
2273
    def __setattr__(self, name, value):
2295
 
        if name == "_pipe":
 
2274
        if name == '_pipe':
2296
2275
            return super(ProxyClient, self).__setattr__(name, value)
2297
 
        self._pipe.send(("setattr", name, value))
 
2276
        self._pipe.send(('setattr', name, value))
2298
2277
 
2299
2278
 
2300
2279
class ClientHandler(socketserver.BaseRequestHandler, object):
2312
2291
 
2313
2292
            session = gnutls.ClientSession(self.request)
2314
2293
 
2315
 
            # priority = ":".join(("NONE", "+VERS-TLS1.1",
 
2294
            # priority = ':'.join(("NONE", "+VERS-TLS1.1",
2316
2295
            #                       "+AES-256-CBC", "+SHA1",
2317
2296
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
2318
2297
            #                       "+DHE-DSS"))
2320
2299
            priority = self.server.gnutls_priority
2321
2300
            if priority is None:
2322
2301
                priority = "NORMAL"
2323
 
            gnutls.priority_set_direct(session,
2324
 
                                       priority.encode("utf-8"), None)
 
2302
            gnutls.priority_set_direct(session._c_object,
 
2303
                                       priority.encode("utf-8"),
 
2304
                                       None)
2325
2305
 
2326
2306
            # Start communication using the Mandos protocol
2327
2307
            # Get protocol number
2354
2334
                    except (TypeError, gnutls.Error) as error:
2355
2335
                        logger.warning("Bad certificate: %s", error)
2356
2336
                        return
2357
 
                    logger.debug("Key ID: %s",
2358
 
                                 key_id.decode("utf-8",
2359
 
                                               errors="replace"))
 
2337
                    logger.debug("Key ID: %s", key_id)
2360
2338
 
2361
2339
                else:
2362
2340
                    key_id = b""
2454
2432
    def peer_certificate(session):
2455
2433
        "Return the peer's certificate as a bytestring"
2456
2434
        try:
2457
 
            cert_type = gnutls.certificate_type_get2(
2458
 
                session, gnutls.CTYPE_PEERS)
 
2435
            cert_type = gnutls.certificate_type_get2(session._c_object,
 
2436
                                                     gnutls.CTYPE_PEERS)
2459
2437
        except AttributeError:
2460
 
            cert_type = gnutls.certificate_type_get(session)
 
2438
            cert_type = gnutls.certificate_type_get(session._c_object)
2461
2439
        if gnutls.has_rawpk:
2462
2440
            valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
2463
2441
        else:
2470
2448
            return b""
2471
2449
        list_size = ctypes.c_uint(1)
2472
2450
        cert_list = (gnutls.certificate_get_peers
2473
 
                     (session, ctypes.byref(list_size)))
 
2451
                     (session._c_object, ctypes.byref(list_size)))
2474
2452
        if not bool(cert_list) and list_size.value != 0:
2475
2453
            raise gnutls.Error("error getting peer certificate")
2476
2454
        if list_size.value == 0:
2590
2568
 
2591
2569
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2592
2570
                     socketserver.TCPServer):
2593
 
    """IPv6-capable TCP server.  Accepts None as address and/or port
 
2571
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2594
2572
 
2595
2573
    Attributes:
2596
2574
        enabled:        Boolean; whether this server is activated yet
2748
2726
        request = parent_pipe.recv()
2749
2727
        command = request[0]
2750
2728
 
2751
 
        if command == "init":
 
2729
        if command == 'init':
2752
2730
            key_id = request[1].decode("ascii")
2753
2731
            fpr = request[2].decode("ascii")
2754
2732
            address = request[3]
2784
2762
            # remove the old hook in favor of the new above hook on
2785
2763
            # same fileno
2786
2764
            return False
2787
 
        if command == "funcall":
 
2765
        if command == 'funcall':
2788
2766
            funcname = request[1]
2789
2767
            args = request[2]
2790
2768
            kwargs = request[3]
2791
2769
 
2792
 
            parent_pipe.send(("data", getattr(client_object,
 
2770
            parent_pipe.send(('data', getattr(client_object,
2793
2771
                                              funcname)(*args,
2794
2772
                                                        **kwargs)))
2795
2773
 
2796
 
        if command == "getattr":
 
2774
        if command == 'getattr':
2797
2775
            attrname = request[1]
2798
2776
            if isinstance(client_object.__getattribute__(attrname),
2799
2777
                          collections.abc.Callable):
2800
 
                parent_pipe.send(("function", ))
 
2778
                parent_pipe.send(('function', ))
2801
2779
            else:
2802
2780
                parent_pipe.send((
2803
 
                    "data", client_object.__getattribute__(attrname)))
 
2781
                    'data', client_object.__getattribute__(attrname)))
2804
2782
 
2805
 
        if command == "setattr":
 
2783
        if command == 'setattr':
2806
2784
            attrname = request[1]
2807
2785
            value = request[2]
2808
2786
            setattr(client_object, attrname, value)
2914
2892
def string_to_delta(interval):
2915
2893
    """Parse a string and return a datetime.timedelta
2916
2894
 
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)
 
2895
    >>> string_to_delta('7d') == datetime.timedelta(7)
 
2896
    True
 
2897
    >>> string_to_delta('60s') == datetime.timedelta(0, 60)
 
2898
    True
 
2899
    >>> string_to_delta('60m') == datetime.timedelta(0, 3600)
 
2900
    True
 
2901
    >>> string_to_delta('24h') == datetime.timedelta(1)
 
2902
    True
 
2903
    >>> string_to_delta('1w') == datetime.timedelta(7)
 
2904
    True
 
2905
    >>> string_to_delta('5m 30s') == datetime.timedelta(0, 330)
2928
2906
    True
2929
2907
    """
2930
2908
 
3134
3112
 
3135
3113
    if server_settings["servicename"] != "Mandos":
3136
3114
        syslogger.setFormatter(
3137
 
            logging.Formatter("Mandos ({}) [%(process)d]:"
3138
 
                              " %(levelname)s: %(message)s".format(
 
3115
            logging.Formatter('Mandos ({}) [%(process)d]:'
 
3116
                              ' %(levelname)s: %(message)s'.format(
3139
3117
                                  server_settings["servicename"])))
3140
3118
 
3141
3119
    # Parse config file with clients
3201
3179
 
3202
3180
        @gnutls.log_func
3203
3181
        def debug_gnutls(level, string):
3204
 
            logger.debug("GnuTLS: %s",
3205
 
                         string[:-1].decode("utf-8",
3206
 
                                            errors="replace"))
 
3182
            logger.debug("GnuTLS: %s", string[:-1])
3207
3183
 
3208
3184
        gnutls.global_set_log_function(debug_gnutls)
3209
3185
 
3584
3560
 
3585
3561
        try:
3586
3562
            with tempfile.NamedTemporaryFile(
3587
 
                    mode="wb",
 
3563
                    mode='wb',
3588
3564
                    suffix=".pickle",
3589
 
                    prefix="clients-",
 
3565
                    prefix='clients-',
3590
3566
                    dir=os.path.dirname(stored_state_path),
3591
3567
                    delete=False) as stored_state:
3592
3568
                pickle.dump((clients, client_settings), stored_state,
3679
3655
 
3680
3656
def should_only_run_tests():
3681
3657
    parser = argparse.ArgumentParser(add_help=False)
3682
 
    parser.add_argument("--check", action="store_true")
 
3658
    parser.add_argument("--check", action='store_true')
3683
3659
    args, unknown_args = parser.parse_known_args()
3684
3660
    run_tests = args.check
3685
3661
    if run_tests:
3693
3669
    tests.addTests(doctest.DocTestSuite())
3694
3670
    return tests
3695
3671
 
3696
 
if __name__ == "__main__":
 
3672
if __name__ == '__main__':
3697
3673
    try:
3698
3674
        if should_only_run_tests():
3699
3675
            # Call using ./mandos --check [--verbose]