/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-02-07 20:53:34 UTC
  • Revision ID: teddy@recompile.se-20200207205334-dp41p8c8vw0ytik5
Allow users to more easily alter mandos.service

The sysvinit script uses /etc/default/mandos as an environment file,
and supports adding additional server options to a DAEMON_ARGS
environment variable.  This should be supported by the systemd
service, too.

* mandos.service ([Service]/EnvironmentFile): New; set to
  "/etc/default/mandos ".
  ([Service]/ExecStart): Append "$DAEMON_ARGS".

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
#
79
79
import codecs
80
80
import unittest
81
81
import random
82
 
import shlex
83
82
 
84
83
import dbus
85
84
import dbus.service
104
103
    collections.abc = abc
105
104
    del abc
106
105
 
107
 
# Add shlex.quote if it does not exist
108
 
try:
109
 
    shlex.quote
110
 
except AttributeError:
111
 
    shlex.quote = re.escape
112
 
 
113
106
# Show warnings by default
114
107
if not sys.warnoptions:
115
108
    import warnings
143
136
if sys.version_info < (3, 2):
144
137
    configparser.Configparser = configparser.SafeConfigParser
145
138
 
146
 
version = "1.8.14"
 
139
version = "1.8.9"
147
140
stored_state_file = "clients.pickle"
148
141
 
149
142
logger = logging.getLogger()
189
182
        facility=logging.handlers.SysLogHandler.LOG_DAEMON,
190
183
        address="/dev/log"))
191
184
    syslogger.setFormatter(logging.Formatter
192
 
                           ("Mandos [%(process)d]: %(levelname)s:"
193
 
                            " %(message)s"))
 
185
                           ('Mandos [%(process)d]: %(levelname)s:'
 
186
                            ' %(message)s'))
194
187
    logger.addHandler(syslogger)
195
188
 
196
189
    if debug:
197
190
        console = logging.StreamHandler()
198
 
        console.setFormatter(logging.Formatter("%(asctime)s %(name)s"
199
 
                                               " [%(process)d]:"
200
 
                                               " %(levelname)s:"
201
 
                                               " %(message)s"))
 
191
        console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
 
192
                                               ' [%(process)d]:'
 
193
                                               ' %(levelname)s:'
 
194
                                               ' %(message)s'))
202
195
        logger.addHandler(console)
203
196
    logger.setLevel(level)
204
197
 
224
217
        except OSError as e:
225
218
            if e.errno != errno.ENOENT:
226
219
                raise
227
 
        self.gnupgargs = ["--batch",
228
 
                          "--homedir", self.tempdir,
229
 
                          "--force-mdc",
230
 
                          "--quiet"]
 
220
        self.gnupgargs = ['--batch',
 
221
                          '--homedir', self.tempdir,
 
222
                          '--force-mdc',
 
223
                          '--quiet']
231
224
        # Only GPG version 1 has the --no-use-agent option.
232
225
        if self.gpg == b"gpg" or self.gpg.endswith(b"/gpg"):
233
226
            self.gnupgargs.append("--no-use-agent")
272
265
                dir=self.tempdir) as passfile:
273
266
            passfile.write(passphrase)
274
267
            passfile.flush()
275
 
            proc = subprocess.Popen([self.gpg, "--symmetric",
276
 
                                     "--passphrase-file",
 
268
            proc = subprocess.Popen([self.gpg, '--symmetric',
 
269
                                     '--passphrase-file',
277
270
                                     passfile.name]
278
271
                                    + self.gnupgargs,
279
272
                                    stdin=subprocess.PIPE,
290
283
                dir=self.tempdir) as passfile:
291
284
            passfile.write(passphrase)
292
285
            passfile.flush()
293
 
            proc = subprocess.Popen([self.gpg, "--decrypt",
294
 
                                     "--passphrase-file",
 
286
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
287
                                     '--passphrase-file',
295
288
                                     passfile.name]
296
289
                                    + self.gnupgargs,
297
290
                                    stdin=subprocess.PIPE,
350
343
    Attributes:
351
344
    interface: integer; avahi.IF_UNSPEC or an interface index.
352
345
               Used to optionally bind to the specified interface.
353
 
    name: string; Example: "Mandos"
354
 
    type: string; Example: "_mandos._tcp".
 
346
    name: string; Example: 'Mandos'
 
347
    type: string; Example: '_mandos._tcp'.
355
348
     See <https://www.iana.org/assignments/service-names-port-numbers>
356
349
    port: integer; what port to announce
357
350
    TXT: list of strings; TXT record for the service
435
428
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
436
429
        self.entry_group_state_changed_match = (
437
430
            self.group.connect_to_signal(
438
 
                "StateChanged", self.entry_group_state_changed))
 
431
                'StateChanged', self.entry_group_state_changed))
439
432
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
440
433
                     self.name, self.type)
441
434
        self.group.AddService(
524
517
class AvahiServiceToSyslog(AvahiService):
525
518
    def rename(self, *args, **kwargs):
526
519
        """Add the new name to the syslog messages"""
527
 
        ret = super(AvahiServiceToSyslog, self).rename(*args,
528
 
                                                       **kwargs)
 
520
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
529
521
        syslogger.setFormatter(logging.Formatter(
530
 
            "Mandos ({}) [%(process)d]: %(levelname)s: %(message)s"
 
522
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
531
523
            .format(self.name)))
532
524
        return ret
533
525
 
563
555
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
564
556
 
565
557
    # Types
566
 
    class _session_int(ctypes.Structure):
 
558
    class session_int(ctypes.Structure):
567
559
        _fields_ = []
568
 
    session_t = ctypes.POINTER(_session_int)
 
560
    session_t = ctypes.POINTER(session_int)
569
561
 
570
562
    class certificate_credentials_st(ctypes.Structure):
571
563
        _fields_ = []
574
566
    certificate_type_t = ctypes.c_int
575
567
 
576
568
    class datum_t(ctypes.Structure):
577
 
        _fields_ = [("data", ctypes.POINTER(ctypes.c_ubyte)),
578
 
                    ("size", ctypes.c_uint)]
 
569
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
570
                    ('size', ctypes.c_uint)]
579
571
 
580
 
    class _openpgp_crt_int(ctypes.Structure):
 
572
    class openpgp_crt_int(ctypes.Structure):
581
573
        _fields_ = []
582
 
    openpgp_crt_t = ctypes.POINTER(_openpgp_crt_int)
 
574
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
583
575
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
584
576
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
585
577
    credentials_type_t = ctypes.c_int
594
586
            # gnutls.strerror()
595
587
            self.code = code
596
588
            if message is None and code is not None:
597
 
                message = gnutls.strerror(code).decode(
598
 
                    "utf-8", errors="replace")
 
589
                message = gnutls.strerror(code)
599
590
            return super(gnutls.Error, self).__init__(
600
591
                message, *args)
601
592
 
602
593
    class CertificateSecurityError(Error):
603
594
        pass
604
595
 
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
596
    # Classes
631
 
    class Credentials(With_from_param):
 
597
    class Credentials:
632
598
        def __init__(self):
633
 
            self._as_parameter_ = gnutls.certificate_credentials_t()
634
 
            gnutls.certificate_allocate_credentials(self)
 
599
            self._c_object = gnutls.certificate_credentials_t()
 
600
            gnutls.certificate_allocate_credentials(
 
601
                ctypes.byref(self._c_object))
635
602
            self.type = gnutls.CRD_CERTIFICATE
636
603
 
637
604
        def __del__(self):
638
 
            gnutls.certificate_free_credentials(self)
 
605
            gnutls.certificate_free_credentials(self._c_object)
639
606
 
640
 
    class ClientSession(With_from_param):
 
607
    class ClientSession:
641
608
        def __init__(self, socket, credentials=None):
642
 
            self._as_parameter_ = gnutls.session_t()
 
609
            self._c_object = gnutls.session_t()
643
610
            gnutls_flags = gnutls.CLIENT
644
611
            if gnutls.check_version(b"3.5.6"):
645
612
                gnutls_flags |= gnutls.NO_TICKETS
646
613
            if gnutls.has_rawpk:
647
614
                gnutls_flags |= gnutls.ENABLE_RAWPK
648
 
            gnutls.init(self, gnutls_flags)
 
615
            gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
649
616
            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)
 
617
            gnutls.set_default_priority(self._c_object)
 
618
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
 
619
            gnutls.handshake_set_private_extensions(self._c_object,
 
620
                                                    True)
653
621
            self.socket = socket
654
622
            if credentials is None:
655
623
                credentials = gnutls.Credentials()
656
 
            gnutls.credentials_set(self, credentials.type,
657
 
                                   credentials)
 
624
            gnutls.credentials_set(self._c_object, credentials.type,
 
625
                                   ctypes.cast(credentials._c_object,
 
626
                                               ctypes.c_void_p))
658
627
            self.credentials = credentials
659
628
 
660
629
        def __del__(self):
661
 
            gnutls.deinit(self)
 
630
            gnutls.deinit(self._c_object)
662
631
 
663
632
        def handshake(self):
664
 
            return gnutls.handshake(self)
 
633
            return gnutls.handshake(self._c_object)
665
634
 
666
635
        def send(self, data):
667
636
            data = bytes(data)
668
637
            data_len = len(data)
669
638
            while data_len > 0:
670
 
                data_len -= gnutls.record_send(self, data[-data_len:],
 
639
                data_len -= gnutls.record_send(self._c_object,
 
640
                                               data[-data_len:],
671
641
                                               data_len)
672
642
 
673
643
        def bye(self):
674
 
            return gnutls.bye(self, gnutls.SHUT_RDWR)
 
644
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
675
645
 
676
646
    # Error handling functions
677
647
    def _error_code(result):
678
648
        """A function to raise exceptions on errors, suitable
679
 
        for the "restype" attribute on ctypes functions"""
680
 
        if result >= gnutls.E_SUCCESS:
 
649
        for the 'restype' attribute on ctypes functions"""
 
650
        if result >= 0:
681
651
            return result
682
652
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
683
653
            raise gnutls.CertificateSecurityError(code=result)
684
654
        raise gnutls.Error(code=result)
685
655
 
686
 
    def _retry_on_error(result, func, arguments,
687
 
                        _error_code=_error_code):
 
656
    def _retry_on_error(result, func, arguments):
688
657
        """A function to retry on some errors, suitable
689
 
        for the "errcheck" attribute on ctypes functions"""
690
 
        while result < gnutls.E_SUCCESS:
 
658
        for the 'errcheck' attribute on ctypes functions"""
 
659
        while result < 0:
691
660
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
692
661
                return _error_code(result)
693
662
            result = func(*arguments)
698
667
 
699
668
    # Functions
700
669
    priority_set_direct = _library.gnutls_priority_set_direct
701
 
    priority_set_direct.argtypes = [ClientSession, ctypes.c_char_p,
 
670
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
702
671
                                    ctypes.POINTER(ctypes.c_char_p)]
703
672
    priority_set_direct.restype = _error_code
704
673
 
705
674
    init = _library.gnutls_init
706
 
    init.argtypes = [PointerTo(ClientSession), ctypes.c_int]
 
675
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
707
676
    init.restype = _error_code
708
677
 
709
678
    set_default_priority = _library.gnutls_set_default_priority
710
 
    set_default_priority.argtypes = [ClientSession]
 
679
    set_default_priority.argtypes = [session_t]
711
680
    set_default_priority.restype = _error_code
712
681
 
713
682
    record_send = _library.gnutls_record_send
714
 
    record_send.argtypes = [ClientSession, ctypes.c_void_p,
 
683
    record_send.argtypes = [session_t, ctypes.c_void_p,
715
684
                            ctypes.c_size_t]
716
685
    record_send.restype = ctypes.c_ssize_t
717
686
    record_send.errcheck = _retry_on_error
719
688
    certificate_allocate_credentials = (
720
689
        _library.gnutls_certificate_allocate_credentials)
721
690
    certificate_allocate_credentials.argtypes = [
722
 
        PointerTo(Credentials)]
 
691
        ctypes.POINTER(certificate_credentials_t)]
723
692
    certificate_allocate_credentials.restype = _error_code
724
693
 
725
694
    certificate_free_credentials = (
726
695
        _library.gnutls_certificate_free_credentials)
727
 
    certificate_free_credentials.argtypes = [Credentials]
 
696
    certificate_free_credentials.argtypes = [
 
697
        certificate_credentials_t]
728
698
    certificate_free_credentials.restype = None
729
699
 
730
700
    handshake_set_private_extensions = (
731
701
        _library.gnutls_handshake_set_private_extensions)
732
 
    handshake_set_private_extensions.argtypes = [ClientSession,
 
702
    handshake_set_private_extensions.argtypes = [session_t,
733
703
                                                 ctypes.c_int]
734
704
    handshake_set_private_extensions.restype = None
735
705
 
736
706
    credentials_set = _library.gnutls_credentials_set
737
 
    credentials_set.argtypes = [ClientSession, credentials_type_t,
738
 
                                CastToVoidPointer(Credentials)]
 
707
    credentials_set.argtypes = [session_t, credentials_type_t,
 
708
                                ctypes.c_void_p]
739
709
    credentials_set.restype = _error_code
740
710
 
741
711
    strerror = _library.gnutls_strerror
743
713
    strerror.restype = ctypes.c_char_p
744
714
 
745
715
    certificate_type_get = _library.gnutls_certificate_type_get
746
 
    certificate_type_get.argtypes = [ClientSession]
 
716
    certificate_type_get.argtypes = [session_t]
747
717
    certificate_type_get.restype = _error_code
748
718
 
749
719
    certificate_get_peers = _library.gnutls_certificate_get_peers
750
 
    certificate_get_peers.argtypes = [ClientSession,
 
720
    certificate_get_peers.argtypes = [session_t,
751
721
                                      ctypes.POINTER(ctypes.c_uint)]
752
722
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
753
723
 
760
730
    global_set_log_function.restype = None
761
731
 
762
732
    deinit = _library.gnutls_deinit
763
 
    deinit.argtypes = [ClientSession]
 
733
    deinit.argtypes = [session_t]
764
734
    deinit.restype = None
765
735
 
766
736
    handshake = _library.gnutls_handshake
767
 
    handshake.argtypes = [ClientSession]
768
 
    handshake.restype = ctypes.c_int
 
737
    handshake.argtypes = [session_t]
 
738
    handshake.restype = _error_code
769
739
    handshake.errcheck = _retry_on_error
770
740
 
771
741
    transport_set_ptr = _library.gnutls_transport_set_ptr
772
 
    transport_set_ptr.argtypes = [ClientSession, transport_ptr_t]
 
742
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
773
743
    transport_set_ptr.restype = None
774
744
 
775
745
    bye = _library.gnutls_bye
776
 
    bye.argtypes = [ClientSession, close_request_t]
777
 
    bye.restype = ctypes.c_int
 
746
    bye.argtypes = [session_t, close_request_t]
 
747
    bye.restype = _error_code
778
748
    bye.errcheck = _retry_on_error
779
749
 
780
750
    check_version = _library.gnutls_check_version
797
767
 
798
768
        x509_crt_fmt_t = ctypes.c_int
799
769
 
800
 
        # All the function declarations below are from
801
 
        # gnutls/abstract.h
 
770
        # All the function declarations below are from gnutls/abstract.h
802
771
        pubkey_init = _library.gnutls_pubkey_init
803
772
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
804
773
        pubkey_init.restype = _error_code
818
787
        pubkey_deinit.argtypes = [pubkey_t]
819
788
        pubkey_deinit.restype = None
820
789
    else:
821
 
        # All the function declarations below are from
822
 
        # gnutls/openpgp.h
 
790
        # All the function declarations below are from gnutls/openpgp.h
823
791
 
824
792
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
825
793
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
831
799
                                       openpgp_crt_fmt_t]
832
800
        openpgp_crt_import.restype = _error_code
833
801
 
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
 
        ]
 
802
        openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
803
        openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
804
                                            ctypes.POINTER(ctypes.c_uint)]
841
805
        openpgp_crt_verify_self.restype = _error_code
842
806
 
843
807
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
854
818
 
855
819
    if check_version(b"3.6.4"):
856
820
        certificate_type_get2 = _library.gnutls_certificate_type_get2
857
 
        certificate_type_get2.argtypes = [ClientSession, ctypes.c_int]
 
821
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
858
822
        certificate_type_get2.restype = _error_code
859
823
 
860
824
    # Remove non-public functions
876
840
    """A representation of a client host served by this server.
877
841
 
878
842
    Attributes:
879
 
    approved:   bool(); None if not yet approved/disapproved
 
843
    approved:   bool(); 'None' if not yet approved/disapproved
880
844
    approval_delay: datetime.timedelta(); Time to wait for approval
881
845
    approval_duration: datetime.timedelta(); Duration of one approval
882
846
    checker: multiprocessing.Process(); a running checker process used
883
 
             to see if the client lives. None if no process is
 
847
             to see if the client lives. 'None' if no process is
884
848
             running.
885
849
    checker_callback_tag: a GLib event source tag, or None
886
850
    checker_command: string; External command which is run to check
1163
1127
        if self.checker is None:
1164
1128
            # Escape attributes for the shell
1165
1129
            escaped_attrs = {
1166
 
                attr: shlex.quote(str(getattr(self, attr)))
 
1130
                attr: re.escape(str(getattr(self, attr)))
1167
1131
                for attr in self.runtime_expansions}
1168
1132
            try:
1169
1133
                command = self.checker_command % escaped_attrs
1242
1206
        func._dbus_name = func.__name__
1243
1207
        if func._dbus_name.endswith("_dbus_property"):
1244
1208
            func._dbus_name = func._dbus_name[:-14]
1245
 
        func._dbus_get_args_options = {"byte_arrays": byte_arrays}
 
1209
        func._dbus_get_args_options = {'byte_arrays': byte_arrays}
1246
1210
        return func
1247
1211
 
1248
1212
    return decorator
1337
1301
 
1338
1302
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1339
1303
                         out_signature="s",
1340
 
                         path_keyword="object_path",
1341
 
                         connection_keyword="connection")
 
1304
                         path_keyword='object_path',
 
1305
                         connection_keyword='connection')
1342
1306
    def Introspect(self, object_path, connection):
1343
1307
        """Overloading of standard D-Bus method.
1344
1308
 
1497
1461
 
1498
1462
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1499
1463
                         out_signature="s",
1500
 
                         path_keyword="object_path",
1501
 
                         connection_keyword="connection")
 
1464
                         path_keyword='object_path',
 
1465
                         connection_keyword='connection')
1502
1466
    def Introspect(self, object_path, connection):
1503
1467
        """Overloading of standard D-Bus method.
1504
1468
 
1599
1563
 
1600
1564
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
1601
1565
                         out_signature="s",
1602
 
                         path_keyword="object_path",
1603
 
                         connection_keyword="connection")
 
1566
                         path_keyword='object_path',
 
1567
                         connection_keyword='connection')
1604
1568
    def Introspect(self, object_path, connection):
1605
1569
        """Overloading of standard D-Bus method.
1606
1570
 
2272
2236
class ProxyClient:
2273
2237
    def __init__(self, child_pipe, key_id, fpr, address):
2274
2238
        self._pipe = child_pipe
2275
 
        self._pipe.send(("init", key_id, fpr, address))
 
2239
        self._pipe.send(('init', key_id, fpr, address))
2276
2240
        if not self._pipe.recv():
2277
2241
            raise KeyError(key_id or fpr)
2278
2242
 
2279
2243
    def __getattribute__(self, name):
2280
 
        if name == "_pipe":
 
2244
        if name == '_pipe':
2281
2245
            return super(ProxyClient, self).__getattribute__(name)
2282
 
        self._pipe.send(("getattr", name))
 
2246
        self._pipe.send(('getattr', name))
2283
2247
        data = self._pipe.recv()
2284
 
        if data[0] == "data":
 
2248
        if data[0] == 'data':
2285
2249
            return data[1]
2286
 
        if data[0] == "function":
 
2250
        if data[0] == 'function':
2287
2251
 
2288
2252
            def func(*args, **kwargs):
2289
 
                self._pipe.send(("funcall", name, args, kwargs))
 
2253
                self._pipe.send(('funcall', name, args, kwargs))
2290
2254
                return self._pipe.recv()[1]
2291
2255
 
2292
2256
            return func
2293
2257
 
2294
2258
    def __setattr__(self, name, value):
2295
 
        if name == "_pipe":
 
2259
        if name == '_pipe':
2296
2260
            return super(ProxyClient, self).__setattr__(name, value)
2297
 
        self._pipe.send(("setattr", name, value))
 
2261
        self._pipe.send(('setattr', name, value))
2298
2262
 
2299
2263
 
2300
2264
class ClientHandler(socketserver.BaseRequestHandler, object):
2312
2276
 
2313
2277
            session = gnutls.ClientSession(self.request)
2314
2278
 
2315
 
            # priority = ":".join(("NONE", "+VERS-TLS1.1",
 
2279
            # priority = ':'.join(("NONE", "+VERS-TLS1.1",
2316
2280
            #                       "+AES-256-CBC", "+SHA1",
2317
2281
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
2318
2282
            #                       "+DHE-DSS"))
2320
2284
            priority = self.server.gnutls_priority
2321
2285
            if priority is None:
2322
2286
                priority = "NORMAL"
2323
 
            gnutls.priority_set_direct(session,
2324
 
                                       priority.encode("utf-8"), None)
 
2287
            gnutls.priority_set_direct(session._c_object,
 
2288
                                       priority.encode("utf-8"),
 
2289
                                       None)
2325
2290
 
2326
2291
            # Start communication using the Mandos protocol
2327
2292
            # Get protocol number
2354
2319
                    except (TypeError, gnutls.Error) as error:
2355
2320
                        logger.warning("Bad certificate: %s", error)
2356
2321
                        return
2357
 
                    logger.debug("Key ID: %s",
2358
 
                                 key_id.decode("utf-8",
2359
 
                                               errors="replace"))
 
2322
                    logger.debug("Key ID: %s", key_id)
2360
2323
 
2361
2324
                else:
2362
2325
                    key_id = b""
2454
2417
    def peer_certificate(session):
2455
2418
        "Return the peer's certificate as a bytestring"
2456
2419
        try:
2457
 
            cert_type = gnutls.certificate_type_get2(
2458
 
                session, gnutls.CTYPE_PEERS)
 
2420
            cert_type = gnutls.certificate_type_get2(session._c_object,
 
2421
                                                     gnutls.CTYPE_PEERS)
2459
2422
        except AttributeError:
2460
 
            cert_type = gnutls.certificate_type_get(session)
 
2423
            cert_type = gnutls.certificate_type_get(session._c_object)
2461
2424
        if gnutls.has_rawpk:
2462
2425
            valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
2463
2426
        else:
2470
2433
            return b""
2471
2434
        list_size = ctypes.c_uint(1)
2472
2435
        cert_list = (gnutls.certificate_get_peers
2473
 
                     (session, ctypes.byref(list_size)))
 
2436
                     (session._c_object, ctypes.byref(list_size)))
2474
2437
        if not bool(cert_list) and list_size.value != 0:
2475
2438
            raise gnutls.Error("error getting peer certificate")
2476
2439
        if list_size.value == 0:
2498
2461
        buf = ctypes.create_string_buffer(32)
2499
2462
        buf_len = ctypes.c_size_t(len(buf))
2500
2463
        # 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))
 
2464
        gnutls.pubkey_get_key_id(pubkey,
 
2465
                                 gnutls.KEYID_USE_SHA256,
 
2466
                                 ctypes.cast(ctypes.byref(buf),
 
2467
                                             ctypes.POINTER(ctypes.c_ubyte)),
 
2468
                                 ctypes.byref(buf_len))
2507
2469
        # Deinit the certificate
2508
2470
        gnutls.pubkey_deinit(pubkey)
2509
2471
 
2590
2552
 
2591
2553
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2592
2554
                     socketserver.TCPServer):
2593
 
    """IPv6-capable TCP server.  Accepts None as address and/or port
 
2555
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2594
2556
 
2595
2557
    Attributes:
2596
2558
        enabled:        Boolean; whether this server is activated yet
2748
2710
        request = parent_pipe.recv()
2749
2711
        command = request[0]
2750
2712
 
2751
 
        if command == "init":
 
2713
        if command == 'init':
2752
2714
            key_id = request[1].decode("ascii")
2753
2715
            fpr = request[2].decode("ascii")
2754
2716
            address = request[3]
2755
2717
 
2756
2718
            for c in self.clients.values():
2757
 
                if key_id == ("E3B0C44298FC1C149AFBF4C8996FB924"
2758
 
                              "27AE41E4649B934CA495991B7852B855"):
 
2719
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
2759
2720
                    continue
2760
2721
                if key_id and c.key_id == key_id:
2761
2722
                    client = c
2784
2745
            # remove the old hook in favor of the new above hook on
2785
2746
            # same fileno
2786
2747
            return False
2787
 
        if command == "funcall":
 
2748
        if command == 'funcall':
2788
2749
            funcname = request[1]
2789
2750
            args = request[2]
2790
2751
            kwargs = request[3]
2791
2752
 
2792
 
            parent_pipe.send(("data", getattr(client_object,
 
2753
            parent_pipe.send(('data', getattr(client_object,
2793
2754
                                              funcname)(*args,
2794
2755
                                                        **kwargs)))
2795
2756
 
2796
 
        if command == "getattr":
 
2757
        if command == 'getattr':
2797
2758
            attrname = request[1]
2798
2759
            if isinstance(client_object.__getattribute__(attrname),
2799
2760
                          collections.abc.Callable):
2800
 
                parent_pipe.send(("function", ))
 
2761
                parent_pipe.send(('function', ))
2801
2762
            else:
2802
2763
                parent_pipe.send((
2803
 
                    "data", client_object.__getattribute__(attrname)))
 
2764
                    'data', client_object.__getattribute__(attrname)))
2804
2765
 
2805
 
        if command == "setattr":
 
2766
        if command == 'setattr':
2806
2767
            attrname = request[1]
2807
2768
            value = request[2]
2808
2769
            setattr(client_object, attrname, value)
2813
2774
def rfc3339_duration_to_delta(duration):
2814
2775
    """Parse an RFC 3339 "duration" and return a datetime.timedelta
2815
2776
 
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
 
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
2832
2791
    """
2833
2792
 
2834
2793
    # Parsing an RFC 3339 duration with regular expressions is not
2914
2873
def string_to_delta(interval):
2915
2874
    """Parse a string and return a datetime.timedelta
2916
2875
 
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)
 
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)
2928
2887
    True
2929
2888
    """
2930
2889
 
3134
3093
 
3135
3094
    if server_settings["servicename"] != "Mandos":
3136
3095
        syslogger.setFormatter(
3137
 
            logging.Formatter("Mandos ({}) [%(process)d]:"
3138
 
                              " %(levelname)s: %(message)s".format(
 
3096
            logging.Formatter('Mandos ({}) [%(process)d]:'
 
3097
                              ' %(levelname)s: %(message)s'.format(
3139
3098
                                  server_settings["servicename"])))
3140
3099
 
3141
3100
    # Parse config file with clients
3201
3160
 
3202
3161
        @gnutls.log_func
3203
3162
        def debug_gnutls(level, string):
3204
 
            logger.debug("GnuTLS: %s",
3205
 
                         string[:-1].decode("utf-8",
3206
 
                                            errors="replace"))
 
3163
            logger.debug("GnuTLS: %s", string[:-1])
3207
3164
 
3208
3165
        gnutls.global_set_log_function(debug_gnutls)
3209
3166
 
3584
3541
 
3585
3542
        try:
3586
3543
            with tempfile.NamedTemporaryFile(
3587
 
                    mode="wb",
 
3544
                    mode='wb',
3588
3545
                    suffix=".pickle",
3589
 
                    prefix="clients-",
 
3546
                    prefix='clients-',
3590
3547
                    dir=os.path.dirname(stored_state_path),
3591
3548
                    delete=False) as stored_state:
3592
3549
                pickle.dump((clients, client_settings), stored_state,
3679
3636
 
3680
3637
def should_only_run_tests():
3681
3638
    parser = argparse.ArgumentParser(add_help=False)
3682
 
    parser.add_argument("--check", action="store_true")
 
3639
    parser.add_argument("--check", action='store_true')
3683
3640
    args, unknown_args = parser.parse_known_args()
3684
3641
    run_tests = args.check
3685
3642
    if run_tests:
3693
3650
    tests.addTests(doctest.DocTestSuite())
3694
3651
    return tests
3695
3652
 
3696
 
if __name__ == "__main__":
 
3653
if __name__ == '__main__':
3697
3654
    try:
3698
3655
        if should_only_run_tests():
3699
3656
            # Call using ./mandos --check [--verbose]