/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2017-01-25 19:24:00 UTC
  • mfrom: (237.7.439 trunk)
  • Revision ID: teddy@recompile.se-20170125192400-qska3g89wkwg0ccu
Merge from trunk

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-2019 Teddy Hogeborn
15
 
# Copyright © 2008-2019 Björn Påhlsson
16
 
#
17
 
# This file is part of Mandos.
18
 
#
19
 
# Mandos is free software: you can redistribute it and/or modify it
20
 
# under the terms of the GNU General Public License as published by
 
14
# Copyright © 2008-2016 Teddy Hogeborn
 
15
# Copyright © 2008-2016 Björn Påhlsson
 
16
#
 
17
# This program is free software: you can redistribute it and/or modify
 
18
# it under the terms of the GNU General Public License as published by
21
19
# the Free Software Foundation, either version 3 of the License, or
22
20
# (at your option) any later version.
23
21
#
24
 
#     Mandos is distributed in the hope that it will be useful, but
25
 
#     WITHOUT ANY WARRANTY; without even the implied warranty of
 
22
#     This program is distributed in the hope that it will be useful,
 
23
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
26
24
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27
25
#     GNU General Public License for more details.
28
26
#
29
27
# You should have received a copy of the GNU General Public License
30
 
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
 
28
# along with this program.  If not, see
 
29
# <http://www.gnu.org/licenses/>.
31
30
#
32
31
# Contact the authors at <mandos@recompile.se>.
33
32
#
80
79
 
81
80
import dbus
82
81
import dbus.service
83
 
import gi
84
82
from gi.repository import GLib
85
83
from dbus.mainloop.glib import DBusGMainLoop
86
84
import ctypes
88
86
import xml.dom.minidom
89
87
import inspect
90
88
 
91
 
if sys.version_info.major == 2:
92
 
    __metaclass__ = type
93
 
 
94
89
# Try to find the value of SO_BINDTODEVICE:
95
90
try:
96
91
    # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and
119
114
if sys.version_info.major == 2:
120
115
    str = unicode
121
116
 
122
 
if sys.version_info < (3, 2):
123
 
    configparser.Configparser = configparser.SafeConfigParser
124
 
 
125
 
version = "1.8.7"
 
117
version = "1.7.13"
126
118
stored_state_file = "clients.pickle"
127
119
 
128
120
logger = logging.getLogger()
186
178
    pass
187
179
 
188
180
 
189
 
class PGPEngine:
 
181
class PGPEngine(object):
190
182
    """A simple class for OpenPGP symmetric encryption & decryption"""
191
183
 
192
184
    def __init__(self):
282
274
 
283
275
 
284
276
# Pretend that we have an Avahi module
285
 
class avahi:
286
 
    """This isn't so much a class as it is a module-like namespace."""
 
277
class Avahi(object):
 
278
    """This isn't so much a class as it is a module-like namespace.
 
279
    It is instantiated once, and simulates having an Avahi module."""
287
280
    IF_UNSPEC = -1               # avahi-common/address.h
288
281
    PROTO_UNSPEC = -1            # avahi-common/address.h
289
282
    PROTO_INET = 0               # avahi-common/address.h
293
286
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
294
287
    DBUS_PATH_SERVER = "/"
295
288
 
296
 
    @staticmethod
297
 
    def string_array_to_txt_array(t):
 
289
    def string_array_to_txt_array(self, t):
298
290
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
299
291
                           for s in t), signature="ay")
300
292
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
305
297
    SERVER_RUNNING = 2           # avahi-common/defs.h
306
298
    SERVER_COLLISION = 3         # avahi-common/defs.h
307
299
    SERVER_FAILURE = 4           # avahi-common/defs.h
 
300
avahi = Avahi()
308
301
 
309
302
 
310
303
class AvahiError(Exception):
322
315
    pass
323
316
 
324
317
 
325
 
class AvahiService:
 
318
class AvahiService(object):
326
319
    """An Avahi (Zeroconf) service.
327
320
 
328
321
    Attributes:
502
495
class AvahiServiceToSyslog(AvahiService):
503
496
    def rename(self, *args, **kwargs):
504
497
        """Add the new name to the syslog messages"""
505
 
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
 
498
        ret = AvahiService.rename(self, *args, **kwargs)
506
499
        syslogger.setFormatter(logging.Formatter(
507
500
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
508
501
            .format(self.name)))
510
503
 
511
504
 
512
505
# Pretend that we have a GnuTLS module
513
 
class gnutls:
514
 
    """This isn't so much a class as it is a module-like namespace."""
 
506
class GnuTLS(object):
 
507
    """This isn't so much a class as it is a module-like namespace.
 
508
    It is instantiated once, and simulates having a GnuTLS module."""
515
509
 
516
510
    library = ctypes.util.find_library("gnutls")
517
511
    if library is None:
518
512
        library = ctypes.util.find_library("gnutls-deb0")
519
513
    _library = ctypes.cdll.LoadLibrary(library)
520
514
    del library
 
515
    _need_version = b"3.3.0"
 
516
 
 
517
    def __init__(self):
 
518
        # Need to use "self" here, since this method is called before
 
519
        # the assignment to the "gnutls" global variable happens.
 
520
        if self.check_version(self._need_version) is None:
 
521
            raise self.Error("Needs GnuTLS {} or later"
 
522
                             .format(self._need_version))
521
523
 
522
524
    # Unless otherwise indicated, the constants and types below are
523
525
    # all from the gnutls/gnutls.h C header file.
527
529
    E_INTERRUPTED = -52
528
530
    E_AGAIN = -28
529
531
    CRT_OPENPGP = 2
530
 
    CRT_RAWPK = 3
531
532
    CLIENT = 2
532
533
    SHUT_RDWR = 0
533
534
    CRD_CERTIFICATE = 1
534
535
    E_NO_CERTIFICATE_FOUND = -49
535
 
    X509_FMT_DER = 0
536
 
    NO_TICKETS = 1<<10
537
 
    ENABLE_RAWPK = 1<<18
538
 
    CTYPE_PEERS = 3
539
 
    KEYID_USE_SHA256 = 1        # gnutls/x509.h
540
536
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
541
537
 
542
538
    # Types
565
561
 
566
562
    # Exceptions
567
563
    class Error(Exception):
 
564
        # We need to use the class name "GnuTLS" here, since this
 
565
        # exception might be raised from within GnuTLS.__init__,
 
566
        # which is called before the assignment to the "gnutls"
 
567
        # global variable has happened.
568
568
        def __init__(self, message=None, code=None, args=()):
569
569
            # Default usage is by a message string, but if a return
570
570
            # code is passed, convert it to a string with
571
571
            # gnutls.strerror()
572
572
            self.code = code
573
573
            if message is None and code is not None:
574
 
                message = gnutls.strerror(code)
575
 
            return super(gnutls.Error, self).__init__(
 
574
                message = GnuTLS.strerror(code)
 
575
            return super(GnuTLS.Error, self).__init__(
576
576
                message, *args)
577
577
 
578
578
    class CertificateSecurityError(Error):
579
579
        pass
580
580
 
581
581
    # Classes
582
 
    class Credentials:
 
582
    class Credentials(object):
583
583
        def __init__(self):
584
584
            self._c_object = gnutls.certificate_credentials_t()
585
585
            gnutls.certificate_allocate_credentials(
589
589
        def __del__(self):
590
590
            gnutls.certificate_free_credentials(self._c_object)
591
591
 
592
 
    class ClientSession:
 
592
    class ClientSession(object):
593
593
        def __init__(self, socket, credentials=None):
594
594
            self._c_object = gnutls.session_t()
595
 
            gnutls_flags = gnutls.CLIENT
596
 
            if gnutls.check_version(b"3.5.6"):
597
 
                gnutls_flags |= gnutls.NO_TICKETS
598
 
            if gnutls.has_rawpk:
599
 
                gnutls_flags |= gnutls.ENABLE_RAWPK
600
 
            gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
601
 
            del gnutls_flags
 
595
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
602
596
            gnutls.set_default_priority(self._c_object)
603
597
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
604
598
            gnutls.handshake_set_private_extensions(self._c_object,
736
730
    check_version.argtypes = [ctypes.c_char_p]
737
731
    check_version.restype = ctypes.c_char_p
738
732
 
739
 
    _need_version = b"3.3.0"
740
 
    if check_version(_need_version) is None:
741
 
        raise self.Error("Needs GnuTLS {} or later"
742
 
                         .format(_need_version))
743
 
 
744
 
    _tls_rawpk_version = b"3.6.6"
745
 
    has_rawpk = bool(check_version(_tls_rawpk_version))
746
 
 
747
 
    if has_rawpk:
748
 
        # Types
749
 
        class pubkey_st(ctypes.Structure):
750
 
            _fields = []
751
 
        pubkey_t = ctypes.POINTER(pubkey_st)
752
 
 
753
 
        x509_crt_fmt_t = ctypes.c_int
754
 
 
755
 
        # All the function declarations below are from gnutls/abstract.h
756
 
        pubkey_init = _library.gnutls_pubkey_init
757
 
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
758
 
        pubkey_init.restype = _error_code
759
 
 
760
 
        pubkey_import = _library.gnutls_pubkey_import
761
 
        pubkey_import.argtypes = [pubkey_t, ctypes.POINTER(datum_t),
762
 
                                  x509_crt_fmt_t]
763
 
        pubkey_import.restype = _error_code
764
 
 
765
 
        pubkey_get_key_id = _library.gnutls_pubkey_get_key_id
766
 
        pubkey_get_key_id.argtypes = [pubkey_t, ctypes.c_int,
767
 
                                      ctypes.POINTER(ctypes.c_ubyte),
768
 
                                      ctypes.POINTER(ctypes.c_size_t)]
769
 
        pubkey_get_key_id.restype = _error_code
770
 
 
771
 
        pubkey_deinit = _library.gnutls_pubkey_deinit
772
 
        pubkey_deinit.argtypes = [pubkey_t]
773
 
        pubkey_deinit.restype = None
774
 
    else:
775
 
        # All the function declarations below are from gnutls/openpgp.h
776
 
 
777
 
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
778
 
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
779
 
        openpgp_crt_init.restype = _error_code
780
 
 
781
 
        openpgp_crt_import = _library.gnutls_openpgp_crt_import
782
 
        openpgp_crt_import.argtypes = [openpgp_crt_t,
783
 
                                       ctypes.POINTER(datum_t),
784
 
                                       openpgp_crt_fmt_t]
785
 
        openpgp_crt_import.restype = _error_code
786
 
 
787
 
        openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
788
 
        openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
789
 
                                            ctypes.POINTER(ctypes.c_uint)]
790
 
        openpgp_crt_verify_self.restype = _error_code
791
 
 
792
 
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
793
 
        openpgp_crt_deinit.argtypes = [openpgp_crt_t]
794
 
        openpgp_crt_deinit.restype = None
795
 
 
796
 
        openpgp_crt_get_fingerprint = (
797
 
            _library.gnutls_openpgp_crt_get_fingerprint)
798
 
        openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
799
 
                                                ctypes.c_void_p,
800
 
                                                ctypes.POINTER(
801
 
                                                    ctypes.c_size_t)]
802
 
        openpgp_crt_get_fingerprint.restype = _error_code
803
 
 
804
 
    if check_version(b"3.6.4"):
805
 
        certificate_type_get2 = _library.gnutls_certificate_type_get2
806
 
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
807
 
        certificate_type_get2.restype = _error_code
 
733
    # All the function declarations below are from gnutls/openpgp.h
 
734
 
 
735
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
736
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
737
    openpgp_crt_init.restype = _error_code
 
738
 
 
739
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
740
    openpgp_crt_import.argtypes = [openpgp_crt_t,
 
741
                                   ctypes.POINTER(datum_t),
 
742
                                   openpgp_crt_fmt_t]
 
743
    openpgp_crt_import.restype = _error_code
 
744
 
 
745
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
746
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
747
                                        ctypes.POINTER(ctypes.c_uint)]
 
748
    openpgp_crt_verify_self.restype = _error_code
 
749
 
 
750
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
751
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
752
    openpgp_crt_deinit.restype = None
 
753
 
 
754
    openpgp_crt_get_fingerprint = (
 
755
        _library.gnutls_openpgp_crt_get_fingerprint)
 
756
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
757
                                            ctypes.c_void_p,
 
758
                                            ctypes.POINTER(
 
759
                                                ctypes.c_size_t)]
 
760
    openpgp_crt_get_fingerprint.restype = _error_code
808
761
 
809
762
    # Remove non-public functions
810
763
    del _error_code, _retry_on_error
 
764
# Create the global "gnutls" object, simulating a module
 
765
gnutls = GnuTLS()
811
766
 
812
767
 
813
768
def call_pipe(connection,       # : multiprocessing.Connection
821
776
    connection.close()
822
777
 
823
778
 
824
 
class Client:
 
779
class Client(object):
825
780
    """A representation of a client host served by this server.
826
781
 
827
782
    Attributes:
828
783
    approved:   bool(); 'None' if not yet approved/disapproved
829
784
    approval_delay: datetime.timedelta(); Time to wait for approval
830
785
    approval_duration: datetime.timedelta(); Duration of one approval
831
 
    checker: multiprocessing.Process(); a running checker process used
832
 
             to see if the client lives. 'None' if no process is
833
 
             running.
 
786
    checker:    subprocess.Popen(); a running checker process used
 
787
                                    to see if the client lives.
 
788
                                    'None' if no process is running.
834
789
    checker_callback_tag: a GLib event source tag, or None
835
790
    checker_command: string; External command which is run to check
836
791
                     if client lives.  %() expansions are done at
844
799
    disable_initiator_tag: a GLib event source tag, or None
845
800
    enabled:    bool()
846
801
    fingerprint: string (40 or 32 hexadecimal digits); used to
847
 
                 uniquely identify an OpenPGP client
848
 
    key_id: string (64 hexadecimal digits); used to uniquely identify
849
 
            a client using raw public keys
 
802
                 uniquely identify the client
850
803
    host:       string; available for use by the checker command
851
804
    interval:   datetime.timedelta(); How often to start a new checker
852
805
    last_approval_request: datetime.datetime(); (UTC) or None
870
823
    """
871
824
 
872
825
    runtime_expansions = ("approval_delay", "approval_duration",
873
 
                          "created", "enabled", "expires", "key_id",
 
826
                          "created", "enabled", "expires",
874
827
                          "fingerprint", "host", "interval",
875
828
                          "last_approval_request", "last_checked_ok",
876
829
                          "last_enabled", "name", "timeout")
906
859
            client["enabled"] = config.getboolean(client_name,
907
860
                                                  "enabled")
908
861
 
909
 
            # Uppercase and remove spaces from key_id and fingerprint
910
 
            # for later comparison purposes with return value from the
911
 
            # key_id() and fingerprint() functions
912
 
            client["key_id"] = (section.get("key_id", "").upper()
913
 
                                .replace(" ", ""))
 
862
            # Uppercase and remove spaces from fingerprint for later
 
863
            # comparison purposes with return value from the
 
864
            # fingerprint() function
914
865
            client["fingerprint"] = (section["fingerprint"].upper()
915
866
                                     .replace(" ", ""))
916
867
            if "secret" in section:
960
911
            self.expires = None
961
912
 
962
913
        logger.debug("Creating client %r", self.name)
963
 
        logger.debug("  Key ID: %s", self.key_id)
964
914
        logger.debug("  Fingerprint: %s", self.fingerprint)
965
915
        self.created = settings.get("created",
966
916
                                    datetime.datetime.utcnow())
1043
993
    def checker_callback(self, source, condition, connection,
1044
994
                         command):
1045
995
        """The checker has completed, so take appropriate actions."""
 
996
        self.checker_callback_tag = None
 
997
        self.checker = None
1046
998
        # Read return code from connection (see call_pipe)
1047
999
        returncode = connection.recv()
1048
1000
        connection.close()
1049
 
        self.checker.join()
1050
 
        self.checker_callback_tag = None
1051
 
        self.checker = None
1052
1001
 
1053
1002
        if returncode >= 0:
1054
1003
            self.last_checker_status = returncode
2049
1998
    def Name_dbus_property(self):
2050
1999
        return dbus.String(self.name)
2051
2000
 
2052
 
    # KeyID - property
2053
 
    @dbus_annotations(
2054
 
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
2055
 
    @dbus_service_property(_interface, signature="s", access="read")
2056
 
    def KeyID_dbus_property(self):
2057
 
        return dbus.String(self.key_id)
2058
 
 
2059
2001
    # Fingerprint - property
2060
2002
    @dbus_annotations(
2061
2003
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
2216
2158
    del _interface
2217
2159
 
2218
2160
 
2219
 
class ProxyClient:
2220
 
    def __init__(self, child_pipe, key_id, fpr, address):
 
2161
class ProxyClient(object):
 
2162
    def __init__(self, child_pipe, fpr, address):
2221
2163
        self._pipe = child_pipe
2222
 
        self._pipe.send(('init', key_id, fpr, address))
 
2164
        self._pipe.send(('init', fpr, address))
2223
2165
        if not self._pipe.recv():
2224
 
            raise KeyError(key_id or fpr)
 
2166
            raise KeyError(fpr)
2225
2167
 
2226
2168
    def __getattribute__(self, name):
2227
2169
        if name == '_pipe':
2294
2236
 
2295
2237
            approval_required = False
2296
2238
            try:
2297
 
                if gnutls.has_rawpk:
2298
 
                    fpr = b""
2299
 
                    try:
2300
 
                        key_id = self.key_id(
2301
 
                            self.peer_certificate(session))
2302
 
                    except (TypeError, gnutls.Error) as error:
2303
 
                        logger.warning("Bad certificate: %s", error)
2304
 
                        return
2305
 
                    logger.debug("Key ID: %s", key_id)
2306
 
 
2307
 
                else:
2308
 
                    key_id = b""
2309
 
                    try:
2310
 
                        fpr = self.fingerprint(
2311
 
                            self.peer_certificate(session))
2312
 
                    except (TypeError, gnutls.Error) as error:
2313
 
                        logger.warning("Bad certificate: %s", error)
2314
 
                        return
2315
 
                    logger.debug("Fingerprint: %s", fpr)
2316
 
 
2317
 
                try:
2318
 
                    client = ProxyClient(child_pipe, key_id, fpr,
 
2239
                try:
 
2240
                    fpr = self.fingerprint(
 
2241
                        self.peer_certificate(session))
 
2242
                except (TypeError, gnutls.Error) as error:
 
2243
                    logger.warning("Bad certificate: %s", error)
 
2244
                    return
 
2245
                logger.debug("Fingerprint: %s", fpr)
 
2246
 
 
2247
                try:
 
2248
                    client = ProxyClient(child_pipe, fpr,
2319
2249
                                         self.client_address)
2320
2250
                except KeyError:
2321
2251
                    return
2398
2328
 
2399
2329
    @staticmethod
2400
2330
    def peer_certificate(session):
2401
 
        "Return the peer's certificate as a bytestring"
2402
 
        try:
2403
 
            cert_type = gnutls.certificate_type_get2(session._c_object,
2404
 
                                                     gnutls.CTYPE_PEERS)
2405
 
        except AttributeError:
2406
 
            cert_type = gnutls.certificate_type_get(session._c_object)
2407
 
        if gnutls.has_rawpk:
2408
 
            valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
2409
 
        else:
2410
 
            valid_cert_types = frozenset((gnutls.CRT_OPENPGP,))
2411
 
        # If not a valid certificate type...
2412
 
        if cert_type not in valid_cert_types:
2413
 
            logger.info("Cert type %r not in %r", cert_type,
2414
 
                        valid_cert_types)
 
2331
        "Return the peer's OpenPGP certificate as a bytestring"
 
2332
        # If not an OpenPGP certificate...
 
2333
        if (gnutls.certificate_type_get(session._c_object)
 
2334
            != gnutls.CRT_OPENPGP):
2415
2335
            # ...return invalid data
2416
2336
            return b""
2417
2337
        list_size = ctypes.c_uint(1)
2425
2345
        return ctypes.string_at(cert.data, cert.size)
2426
2346
 
2427
2347
    @staticmethod
2428
 
    def key_id(certificate):
2429
 
        "Convert a certificate bytestring to a hexdigit key ID"
2430
 
        # New GnuTLS "datum" with the public key
2431
 
        datum = gnutls.datum_t(
2432
 
            ctypes.cast(ctypes.c_char_p(certificate),
2433
 
                        ctypes.POINTER(ctypes.c_ubyte)),
2434
 
            ctypes.c_uint(len(certificate)))
2435
 
        # XXX all these need to be created in the gnutls "module"
2436
 
        # New empty GnuTLS certificate
2437
 
        pubkey = gnutls.pubkey_t()
2438
 
        gnutls.pubkey_init(ctypes.byref(pubkey))
2439
 
        # Import the raw public key into the certificate
2440
 
        gnutls.pubkey_import(pubkey,
2441
 
                             ctypes.byref(datum),
2442
 
                             gnutls.X509_FMT_DER)
2443
 
        # New buffer for the key ID
2444
 
        buf = ctypes.create_string_buffer(32)
2445
 
        buf_len = ctypes.c_size_t(len(buf))
2446
 
        # Get the key ID from the raw public key into the buffer
2447
 
        gnutls.pubkey_get_key_id(pubkey,
2448
 
                                 gnutls.KEYID_USE_SHA256,
2449
 
                                 ctypes.cast(ctypes.byref(buf),
2450
 
                                             ctypes.POINTER(ctypes.c_ubyte)),
2451
 
                                 ctypes.byref(buf_len))
2452
 
        # Deinit the certificate
2453
 
        gnutls.pubkey_deinit(pubkey)
2454
 
 
2455
 
        # Convert the buffer to a Python bytestring
2456
 
        key_id = ctypes.string_at(buf, buf_len.value)
2457
 
        # Convert the bytestring to hexadecimal notation
2458
 
        hex_key_id = binascii.hexlify(key_id).upper()
2459
 
        return hex_key_id
2460
 
 
2461
 
    @staticmethod
2462
2348
    def fingerprint(openpgp):
2463
2349
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
2464
2350
        # New GnuTLS "datum" with the OpenPGP public key
2478
2364
                                       ctypes.byref(crtverify))
2479
2365
        if crtverify.value != 0:
2480
2366
            gnutls.openpgp_crt_deinit(crt)
2481
 
            raise gnutls.CertificateSecurityError(code
2482
 
                                                  =crtverify.value)
 
2367
            raise gnutls.CertificateSecurityError("Verify failed")
2483
2368
        # New buffer for the fingerprint
2484
2369
        buf = ctypes.create_string_buffer(20)
2485
2370
        buf_len = ctypes.c_size_t()
2495
2380
        return hex_fpr
2496
2381
 
2497
2382
 
2498
 
class MultiprocessingMixIn:
 
2383
class MultiprocessingMixIn(object):
2499
2384
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
2500
2385
 
2501
2386
    def sub_process_main(self, request, address):
2513
2398
        return proc
2514
2399
 
2515
2400
 
2516
 
class MultiprocessingMixInWithPipe(MultiprocessingMixIn):
 
2401
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
2517
2402
    """ adds a pipe to the MixIn """
2518
2403
 
2519
2404
    def process_request(self, request, client_address):
2534
2419
 
2535
2420
 
2536
2421
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
2537
 
                     socketserver.TCPServer):
 
2422
                     socketserver.TCPServer, object):
2538
2423
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
2539
2424
 
2540
2425
    Attributes:
2613
2498
                    raise
2614
2499
        # Only bind(2) the socket if we really need to.
2615
2500
        if self.server_address[0] or self.server_address[1]:
2616
 
            if self.server_address[1]:
2617
 
                self.allow_reuse_address = True
2618
2501
            if not self.server_address[0]:
2619
2502
                if self.address_family == socket.AF_INET6:
2620
2503
                    any_address = "::"  # in6addr_any
2694
2577
        command = request[0]
2695
2578
 
2696
2579
        if command == 'init':
2697
 
            key_id = request[1].decode("ascii")
2698
 
            fpr = request[2].decode("ascii")
2699
 
            address = request[3]
 
2580
            fpr = request[1]
 
2581
            address = request[2]
2700
2582
 
2701
2583
            for c in self.clients.values():
2702
 
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
2703
 
                    continue
2704
 
                if key_id and c.key_id == key_id:
2705
 
                    client = c
2706
 
                    break
2707
 
                if fpr and c.fingerprint == fpr:
 
2584
                if c.fingerprint == fpr:
2708
2585
                    client = c
2709
2586
                    break
2710
2587
            else:
2711
 
                logger.info("Client not found for key ID: %s, address"
2712
 
                            ": %s", key_id or fpr, address)
 
2588
                logger.info("Client not found for fingerprint: %s, ad"
 
2589
                            "dress: %s", fpr, address)
2713
2590
                if self.use_dbus:
2714
2591
                    # Emit D-Bus signal
2715
 
                    mandos_dbus_service.ClientNotFound(key_id or fpr,
 
2592
                    mandos_dbus_service.ClientNotFound(fpr,
2716
2593
                                                       address[0])
2717
2594
                parent_pipe.send(False)
2718
2595
                return False
2981
2858
        sys.exit(os.EX_OK if fail_count == 0 else 1)
2982
2859
 
2983
2860
    # Default values for config file for server-global settings
2984
 
    if gnutls.has_rawpk:
2985
 
        priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
2986
 
                    ":!VERS-ALL:+VERS-TLS1.3:%PROFILE_ULTRA")
2987
 
    else:
2988
 
        priority = ("SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
2989
 
                    ":+SIGN-DSA-SHA256")
2990
2861
    server_defaults = {"interface": "",
2991
2862
                       "address": "",
2992
2863
                       "port": "",
2993
2864
                       "debug": "False",
2994
 
                       "priority": priority,
 
2865
                       "priority":
 
2866
                       "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
 
2867
                       ":+SIGN-DSA-SHA256",
2995
2868
                       "servicename": "Mandos",
2996
2869
                       "use_dbus": "True",
2997
2870
                       "use_ipv6": "True",
3002
2875
                       "foreground": "False",
3003
2876
                       "zeroconf": "True",
3004
2877
                       }
3005
 
    del priority
3006
2878
 
3007
2879
    # Parse config file for server-global settings
3008
 
    server_config = configparser.ConfigParser(server_defaults)
 
2880
    server_config = configparser.SafeConfigParser(server_defaults)
3009
2881
    del server_defaults
3010
2882
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
3011
 
    # Convert the ConfigParser object to a dict
 
2883
    # Convert the SafeConfigParser object to a dict
3012
2884
    server_settings = server_config.defaults()
3013
2885
    # Use the appropriate methods on the non-string config options
3014
 
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
3015
 
                   "foreground", "zeroconf"):
 
2886
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
3016
2887
        server_settings[option] = server_config.getboolean("DEFAULT",
3017
2888
                                                           option)
3018
2889
    if server_settings["port"]:
3086
2957
                                  server_settings["servicename"])))
3087
2958
 
3088
2959
    # Parse config file with clients
3089
 
    client_config = configparser.ConfigParser(Client.client_defaults)
 
2960
    client_config = configparser.SafeConfigParser(Client
 
2961
                                                  .client_defaults)
3090
2962
    client_config.read(os.path.join(server_settings["configdir"],
3091
2963
                                    "clients.conf"))
3092
2964
 
3163
3035
        # Close all input and output, do double fork, etc.
3164
3036
        daemon()
3165
3037
 
3166
 
    if gi.version_info < (3, 10, 2):
3167
 
        # multiprocessing will use threads, so before we use GLib we
3168
 
        # need to inform GLib that threads will be used.
3169
 
        GLib.threads_init()
 
3038
    # multiprocessing will use threads, so before we use GLib we need
 
3039
    # to inform GLib that threads will be used.
 
3040
    GLib.threads_init()
3170
3041
 
3171
3042
    global main_loop
3172
3043
    # From the Avahi example code
3252
3123
                        for k in ("name", "host"):
3253
3124
                            if isinstance(value[k], bytes):
3254
3125
                                value[k] = value[k].decode("utf-8")
3255
 
                        if "key_id" not in value:
3256
 
                            value["key_id"] = ""
3257
 
                        elif "fingerprint" not in value:
3258
 
                            value["fingerprint"] = ""
3259
3126
                    #  old_client_settings
3260
3127
                    # .keys()
3261
3128
                    old_client_settings = {
3398
3265
                pass
3399
3266
 
3400
3267
            @dbus.service.signal(_interface, signature="ss")
3401
 
            def ClientNotFound(self, key_id, address):
 
3268
            def ClientNotFound(self, fingerprint, address):
3402
3269
                "D-Bus signal"
3403
3270
                pass
3404
3271