/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: 2019-08-02 22:16:53 UTC
  • mto: This revision was merged to the branch mainline in revision 386.
  • Revision ID: teddy@recompile.se-20190802221653-ic1iko9hbefzwsk7
Fix bug in server Debian package: Fails to start on first install

There has been a very long-standing bug where installation of the
server (the "mandos" Debian package) would fail to start the server
properly right after installation.  It would work on manual (re)start
after installation, or after reboot, and even after package purge and
reinstall, it would then work the first time.  The problem, it turns
out, is when the new "_mandos" user (and corresponding group) is
created, the D-Bus server is not reloaded, and is therefore not aware
of that user, and does not recognize the user and group name in the
/etc/dbus-1/system.d/mandos.conf file.  The Mandos server, when it
tries to start and access the D-Bus, is then not permitted to connect
to its D-Bus bus name, and disables D-Bus use as a fallback measure;
i.e. the server works, but it is not controllable via D-Bus commands
(via mandos-ctl or mandos-monitor).  The next time the D-Bus daemon is
reloaded for any reason, the new user & group would become visible to
the D-Bus daemon and after that, any restart of the Mandos server
would succeed and it would bind to its D-Bus name properly, and
thereby be visible and controllable by mandos-ctl & mandos-monitor.
This was mostly invisible when using sysvinit, but systemd makes the
problem visible since the systemd service file for the Mandos server
is configured to not consider the Mandos server "started" until the
D-Bus name has been bound; this makes the starting of the service wait
for 90 seconds and then fail with a timeout error.

Fixing this should also make the Debian CI autopkgtest tests work.

* debian/mandos.postinst (configure): After creating (or renaming)
                                      user & group, reload D-Bus
                                      daemon (if present).

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-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
 
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
19
21
# the Free Software Foundation, either version 3 of the License, or
20
22
# (at your option) any later version.
21
23
#
22
 
#     This program is distributed in the hope that it will be useful,
23
 
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
 
24
#     Mandos is distributed in the hope that it will be useful, but
 
25
#     WITHOUT ANY WARRANTY; without even the implied warranty of
24
26
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25
27
#     GNU General Public License for more details.
26
28
#
27
29
# You should have received a copy of the GNU General Public License
28
 
# along with this program.  If not, see
29
 
# <http://www.gnu.org/licenses/>.
 
30
# along with Mandos.  If not, see <http://www.gnu.org/licenses/>.
30
31
#
31
32
# Contact the authors at <mandos@recompile.se>.
32
33
#
114
115
if sys.version_info.major == 2:
115
116
    str = unicode
116
117
 
117
 
version = "1.7.10"
 
118
version = "1.8.5"
118
119
stored_state_file = "clients.pickle"
119
120
 
120
121
logger = logging.getLogger()
274
275
 
275
276
 
276
277
# Pretend that we have an Avahi module
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."""
 
278
class avahi(object):
 
279
    """This isn't so much a class as it is a module-like namespace."""
280
280
    IF_UNSPEC = -1               # avahi-common/address.h
281
281
    PROTO_UNSPEC = -1            # avahi-common/address.h
282
282
    PROTO_INET = 0               # avahi-common/address.h
286
286
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
287
287
    DBUS_PATH_SERVER = "/"
288
288
 
289
 
    def string_array_to_txt_array(self, t):
 
289
    @staticmethod
 
290
    def string_array_to_txt_array(t):
290
291
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
291
292
                           for s in t), signature="ay")
292
293
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
297
298
    SERVER_RUNNING = 2           # avahi-common/defs.h
298
299
    SERVER_COLLISION = 3         # avahi-common/defs.h
299
300
    SERVER_FAILURE = 4           # avahi-common/defs.h
300
 
avahi = Avahi()
301
301
 
302
302
 
303
303
class AvahiError(Exception):
495
495
class AvahiServiceToSyslog(AvahiService):
496
496
    def rename(self, *args, **kwargs):
497
497
        """Add the new name to the syslog messages"""
498
 
        ret = AvahiService.rename(self, *args, **kwargs)
 
498
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
499
499
        syslogger.setFormatter(logging.Formatter(
500
500
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
501
501
            .format(self.name)))
503
503
 
504
504
 
505
505
# Pretend that we have a GnuTLS module
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."""
 
506
class gnutls(object):
 
507
    """This isn't so much a class as it is a module-like namespace."""
509
508
 
510
509
    library = ctypes.util.find_library("gnutls")
511
510
    if library is None:
512
511
        library = ctypes.util.find_library("gnutls-deb0")
513
512
    _library = ctypes.cdll.LoadLibrary(library)
514
513
    del library
515
 
    _need_version = b"3.3.0"
516
 
 
517
 
    def __init__(self):
518
 
        # Need to use class name "GnuTLS" here, since this method is
519
 
        # called before the assignment to the "gnutls" global variable
520
 
        # happens.
521
 
        if GnuTLS.check_version(self._need_version) is None:
522
 
            raise GnuTLS.Error("Needs GnuTLS {} or later"
523
 
                               .format(self._need_version))
524
514
 
525
515
    # Unless otherwise indicated, the constants and types below are
526
516
    # all from the gnutls/gnutls.h C header file.
530
520
    E_INTERRUPTED = -52
531
521
    E_AGAIN = -28
532
522
    CRT_OPENPGP = 2
 
523
    CRT_RAWPK = 3
533
524
    CLIENT = 2
534
525
    SHUT_RDWR = 0
535
526
    CRD_CERTIFICATE = 1
536
527
    E_NO_CERTIFICATE_FOUND = -49
 
528
    X509_FMT_DER = 0
 
529
    NO_TICKETS = 1<<10
 
530
    ENABLE_RAWPK = 1<<18
 
531
    CTYPE_PEERS = 3
 
532
    KEYID_USE_SHA256 = 1        # gnutls/x509.h
537
533
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
538
534
 
539
535
    # Types
562
558
 
563
559
    # Exceptions
564
560
    class Error(Exception):
565
 
        # We need to use the class name "GnuTLS" here, since this
566
 
        # exception might be raised from within GnuTLS.__init__,
567
 
        # which is called before the assignment to the "gnutls"
568
 
        # global variable has happened.
569
561
        def __init__(self, message=None, code=None, args=()):
570
562
            # Default usage is by a message string, but if a return
571
563
            # code is passed, convert it to a string with
572
564
            # gnutls.strerror()
573
565
            self.code = code
574
566
            if message is None and code is not None:
575
 
                message = GnuTLS.strerror(code)
576
 
            return super(GnuTLS.Error, self).__init__(
 
567
                message = gnutls.strerror(code)
 
568
            return super(gnutls.Error, self).__init__(
577
569
                message, *args)
578
570
 
579
571
    class CertificateSecurityError(Error):
593
585
    class ClientSession(object):
594
586
        def __init__(self, socket, credentials=None):
595
587
            self._c_object = gnutls.session_t()
596
 
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
 
588
            gnutls_flags = gnutls.CLIENT
 
589
            if gnutls.check_version(b"3.5.6"):
 
590
                gnutls_flags |= gnutls.NO_TICKETS
 
591
            if gnutls.has_rawpk:
 
592
                gnutls_flags |= gnutls.ENABLE_RAWPK
 
593
            gnutls.init(ctypes.byref(self._c_object), gnutls_flags)
 
594
            del gnutls_flags
597
595
            gnutls.set_default_priority(self._c_object)
598
596
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
599
597
            gnutls.handshake_set_private_extensions(self._c_object,
731
729
    check_version.argtypes = [ctypes.c_char_p]
732
730
    check_version.restype = ctypes.c_char_p
733
731
 
734
 
    # All the function declarations below are from gnutls/openpgp.h
735
 
 
736
 
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
737
 
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
738
 
    openpgp_crt_init.restype = _error_code
739
 
 
740
 
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
741
 
    openpgp_crt_import.argtypes = [openpgp_crt_t,
742
 
                                   ctypes.POINTER(datum_t),
743
 
                                   openpgp_crt_fmt_t]
744
 
    openpgp_crt_import.restype = _error_code
745
 
 
746
 
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
747
 
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
748
 
                                        ctypes.POINTER(ctypes.c_uint)]
749
 
    openpgp_crt_verify_self.restype = _error_code
750
 
 
751
 
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
752
 
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
753
 
    openpgp_crt_deinit.restype = None
754
 
 
755
 
    openpgp_crt_get_fingerprint = (
756
 
        _library.gnutls_openpgp_crt_get_fingerprint)
757
 
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
758
 
                                            ctypes.c_void_p,
759
 
                                            ctypes.POINTER(
760
 
                                                ctypes.c_size_t)]
761
 
    openpgp_crt_get_fingerprint.restype = _error_code
 
732
    _need_version = b"3.3.0"
 
733
    if check_version(_need_version) is None:
 
734
        raise self.Error("Needs GnuTLS {} or later"
 
735
                         .format(_need_version))
 
736
 
 
737
    _tls_rawpk_version = b"3.6.6"
 
738
    has_rawpk = bool(check_version(_tls_rawpk_version))
 
739
 
 
740
    if has_rawpk:
 
741
        # Types
 
742
        class pubkey_st(ctypes.Structure):
 
743
            _fields = []
 
744
        pubkey_t = ctypes.POINTER(pubkey_st)
 
745
 
 
746
        x509_crt_fmt_t = ctypes.c_int
 
747
 
 
748
        # All the function declarations below are from gnutls/abstract.h
 
749
        pubkey_init = _library.gnutls_pubkey_init
 
750
        pubkey_init.argtypes = [ctypes.POINTER(pubkey_t)]
 
751
        pubkey_init.restype = _error_code
 
752
 
 
753
        pubkey_import = _library.gnutls_pubkey_import
 
754
        pubkey_import.argtypes = [pubkey_t, ctypes.POINTER(datum_t),
 
755
                                  x509_crt_fmt_t]
 
756
        pubkey_import.restype = _error_code
 
757
 
 
758
        pubkey_get_key_id = _library.gnutls_pubkey_get_key_id
 
759
        pubkey_get_key_id.argtypes = [pubkey_t, ctypes.c_int,
 
760
                                      ctypes.POINTER(ctypes.c_ubyte),
 
761
                                      ctypes.POINTER(ctypes.c_size_t)]
 
762
        pubkey_get_key_id.restype = _error_code
 
763
 
 
764
        pubkey_deinit = _library.gnutls_pubkey_deinit
 
765
        pubkey_deinit.argtypes = [pubkey_t]
 
766
        pubkey_deinit.restype = None
 
767
    else:
 
768
        # All the function declarations below are from gnutls/openpgp.h
 
769
 
 
770
        openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
771
        openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
772
        openpgp_crt_init.restype = _error_code
 
773
 
 
774
        openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
775
        openpgp_crt_import.argtypes = [openpgp_crt_t,
 
776
                                       ctypes.POINTER(datum_t),
 
777
                                       openpgp_crt_fmt_t]
 
778
        openpgp_crt_import.restype = _error_code
 
779
 
 
780
        openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
781
        openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
782
                                            ctypes.POINTER(ctypes.c_uint)]
 
783
        openpgp_crt_verify_self.restype = _error_code
 
784
 
 
785
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
786
        openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
787
        openpgp_crt_deinit.restype = None
 
788
 
 
789
        openpgp_crt_get_fingerprint = (
 
790
            _library.gnutls_openpgp_crt_get_fingerprint)
 
791
        openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
792
                                                ctypes.c_void_p,
 
793
                                                ctypes.POINTER(
 
794
                                                    ctypes.c_size_t)]
 
795
        openpgp_crt_get_fingerprint.restype = _error_code
 
796
 
 
797
    if check_version(b"3.6.4"):
 
798
        certificate_type_get2 = _library.gnutls_certificate_type_get2
 
799
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
 
800
        certificate_type_get2.restype = _error_code
762
801
 
763
802
    # Remove non-public functions
764
803
    del _error_code, _retry_on_error
765
 
# Create the global "gnutls" object, simulating a module
766
 
gnutls = GnuTLS()
767
804
 
768
805
 
769
806
def call_pipe(connection,       # : multiprocessing.Connection
784
821
    approved:   bool(); 'None' if not yet approved/disapproved
785
822
    approval_delay: datetime.timedelta(); Time to wait for approval
786
823
    approval_duration: datetime.timedelta(); Duration of one approval
787
 
    checker:    subprocess.Popen(); a running checker process used
788
 
                                    to see if the client lives.
789
 
                                    'None' if no process is running.
 
824
    checker: multiprocessing.Process(); a running checker process used
 
825
             to see if the client lives. 'None' if no process is
 
826
             running.
790
827
    checker_callback_tag: a GLib event source tag, or None
791
828
    checker_command: string; External command which is run to check
792
829
                     if client lives.  %() expansions are done at
800
837
    disable_initiator_tag: a GLib event source tag, or None
801
838
    enabled:    bool()
802
839
    fingerprint: string (40 or 32 hexadecimal digits); used to
803
 
                 uniquely identify the client
 
840
                 uniquely identify an OpenPGP client
 
841
    key_id: string (64 hexadecimal digits); used to uniquely identify
 
842
            a client using raw public keys
804
843
    host:       string; available for use by the checker command
805
844
    interval:   datetime.timedelta(); How often to start a new checker
806
845
    last_approval_request: datetime.datetime(); (UTC) or None
824
863
    """
825
864
 
826
865
    runtime_expansions = ("approval_delay", "approval_duration",
827
 
                          "created", "enabled", "expires",
 
866
                          "created", "enabled", "expires", "key_id",
828
867
                          "fingerprint", "host", "interval",
829
868
                          "last_approval_request", "last_checked_ok",
830
869
                          "last_enabled", "name", "timeout")
860
899
            client["enabled"] = config.getboolean(client_name,
861
900
                                                  "enabled")
862
901
 
863
 
            # Uppercase and remove spaces from fingerprint for later
864
 
            # comparison purposes with return value from the
865
 
            # fingerprint() function
 
902
            # Uppercase and remove spaces from key_id and fingerprint
 
903
            # for later comparison purposes with return value from the
 
904
            # key_id() and fingerprint() functions
 
905
            client["key_id"] = (section.get("key_id", "").upper()
 
906
                                .replace(" ", ""))
866
907
            client["fingerprint"] = (section["fingerprint"].upper()
867
908
                                     .replace(" ", ""))
868
909
            if "secret" in section:
912
953
            self.expires = None
913
954
 
914
955
        logger.debug("Creating client %r", self.name)
 
956
        logger.debug("  Key ID: %s", self.key_id)
915
957
        logger.debug("  Fingerprint: %s", self.fingerprint)
916
958
        self.created = settings.get("created",
917
959
                                    datetime.datetime.utcnow())
994
1036
    def checker_callback(self, source, condition, connection,
995
1037
                         command):
996
1038
        """The checker has completed, so take appropriate actions."""
997
 
        self.checker_callback_tag = None
998
 
        self.checker = None
999
1039
        # Read return code from connection (see call_pipe)
1000
1040
        returncode = connection.recv()
1001
1041
        connection.close()
 
1042
        self.checker.join()
 
1043
        self.checker_callback_tag = None
 
1044
        self.checker = None
1002
1045
 
1003
1046
        if returncode >= 0:
1004
1047
            self.last_checker_status = returncode
1461
1504
                         exc_info=error)
1462
1505
        return xmlstring
1463
1506
 
 
1507
 
1464
1508
try:
1465
1509
    dbus.OBJECT_MANAGER_IFACE
1466
1510
except AttributeError:
1998
2042
    def Name_dbus_property(self):
1999
2043
        return dbus.String(self.name)
2000
2044
 
 
2045
    # KeyID - property
 
2046
    @dbus_annotations(
 
2047
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
 
2048
    @dbus_service_property(_interface, signature="s", access="read")
 
2049
    def KeyID_dbus_property(self):
 
2050
        return dbus.String(self.key_id)
 
2051
 
2001
2052
    # Fingerprint - property
2002
2053
    @dbus_annotations(
2003
2054
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
2159
2210
 
2160
2211
 
2161
2212
class ProxyClient(object):
2162
 
    def __init__(self, child_pipe, fpr, address):
 
2213
    def __init__(self, child_pipe, key_id, fpr, address):
2163
2214
        self._pipe = child_pipe
2164
 
        self._pipe.send(('init', fpr, address))
 
2215
        self._pipe.send(('init', key_id, fpr, address))
2165
2216
        if not self._pipe.recv():
2166
 
            raise KeyError(fpr)
 
2217
            raise KeyError(key_id or fpr)
2167
2218
 
2168
2219
    def __getattribute__(self, name):
2169
2220
        if name == '_pipe':
2236
2287
 
2237
2288
            approval_required = False
2238
2289
            try:
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,
 
2290
                if gnutls.has_rawpk:
 
2291
                    fpr = b""
 
2292
                    try:
 
2293
                        key_id = self.key_id(
 
2294
                            self.peer_certificate(session))
 
2295
                    except (TypeError, gnutls.Error) as error:
 
2296
                        logger.warning("Bad certificate: %s", error)
 
2297
                        return
 
2298
                    logger.debug("Key ID: %s", key_id)
 
2299
 
 
2300
                else:
 
2301
                    key_id = b""
 
2302
                    try:
 
2303
                        fpr = self.fingerprint(
 
2304
                            self.peer_certificate(session))
 
2305
                    except (TypeError, gnutls.Error) as error:
 
2306
                        logger.warning("Bad certificate: %s", error)
 
2307
                        return
 
2308
                    logger.debug("Fingerprint: %s", fpr)
 
2309
 
 
2310
                try:
 
2311
                    client = ProxyClient(child_pipe, key_id, fpr,
2249
2312
                                         self.client_address)
2250
2313
                except KeyError:
2251
2314
                    return
2328
2391
 
2329
2392
    @staticmethod
2330
2393
    def peer_certificate(session):
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):
 
2394
        "Return the peer's certificate as a bytestring"
 
2395
        try:
 
2396
            cert_type = gnutls.certificate_type_get2(session._c_object,
 
2397
                                                     gnutls.CTYPE_PEERS)
 
2398
        except AttributeError:
 
2399
            cert_type = gnutls.certificate_type_get(session._c_object)
 
2400
        if gnutls.has_rawpk:
 
2401
            valid_cert_types = frozenset((gnutls.CRT_RAWPK,))
 
2402
        else:
 
2403
            valid_cert_types = frozenset((gnutls.CRT_OPENPGP,))
 
2404
        # If not a valid certificate type...
 
2405
        if cert_type not in valid_cert_types:
 
2406
            logger.info("Cert type %r not in %r", cert_type,
 
2407
                        valid_cert_types)
2335
2408
            # ...return invalid data
2336
2409
            return b""
2337
2410
        list_size = ctypes.c_uint(1)
2345
2418
        return ctypes.string_at(cert.data, cert.size)
2346
2419
 
2347
2420
    @staticmethod
 
2421
    def key_id(certificate):
 
2422
        "Convert a certificate bytestring to a hexdigit key ID"
 
2423
        # New GnuTLS "datum" with the public key
 
2424
        datum = gnutls.datum_t(
 
2425
            ctypes.cast(ctypes.c_char_p(certificate),
 
2426
                        ctypes.POINTER(ctypes.c_ubyte)),
 
2427
            ctypes.c_uint(len(certificate)))
 
2428
        # XXX all these need to be created in the gnutls "module"
 
2429
        # New empty GnuTLS certificate
 
2430
        pubkey = gnutls.pubkey_t()
 
2431
        gnutls.pubkey_init(ctypes.byref(pubkey))
 
2432
        # Import the raw public key into the certificate
 
2433
        gnutls.pubkey_import(pubkey,
 
2434
                             ctypes.byref(datum),
 
2435
                             gnutls.X509_FMT_DER)
 
2436
        # New buffer for the key ID
 
2437
        buf = ctypes.create_string_buffer(32)
 
2438
        buf_len = ctypes.c_size_t(len(buf))
 
2439
        # Get the key ID from the raw public key into the buffer
 
2440
        gnutls.pubkey_get_key_id(pubkey,
 
2441
                                 gnutls.KEYID_USE_SHA256,
 
2442
                                 ctypes.cast(ctypes.byref(buf),
 
2443
                                             ctypes.POINTER(ctypes.c_ubyte)),
 
2444
                                 ctypes.byref(buf_len))
 
2445
        # Deinit the certificate
 
2446
        gnutls.pubkey_deinit(pubkey)
 
2447
 
 
2448
        # Convert the buffer to a Python bytestring
 
2449
        key_id = ctypes.string_at(buf, buf_len.value)
 
2450
        # Convert the bytestring to hexadecimal notation
 
2451
        hex_key_id = binascii.hexlify(key_id).upper()
 
2452
        return hex_key_id
 
2453
 
 
2454
    @staticmethod
2348
2455
    def fingerprint(openpgp):
2349
2456
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
2350
2457
        # New GnuTLS "datum" with the OpenPGP public key
2364
2471
                                       ctypes.byref(crtverify))
2365
2472
        if crtverify.value != 0:
2366
2473
            gnutls.openpgp_crt_deinit(crt)
2367
 
            raise gnutls.CertificateSecurityError("Verify failed")
 
2474
            raise gnutls.CertificateSecurityError(code
 
2475
                                                  =crtverify.value)
2368
2476
        # New buffer for the fingerprint
2369
2477
        buf = ctypes.create_string_buffer(20)
2370
2478
        buf_len = ctypes.c_size_t()
2498
2606
                    raise
2499
2607
        # Only bind(2) the socket if we really need to.
2500
2608
        if self.server_address[0] or self.server_address[1]:
 
2609
            if self.server_address[1]:
 
2610
                self.allow_reuse_address = True
2501
2611
            if not self.server_address[0]:
2502
2612
                if self.address_family == socket.AF_INET6:
2503
2613
                    any_address = "::"  # in6addr_any
2577
2687
        command = request[0]
2578
2688
 
2579
2689
        if command == 'init':
2580
 
            fpr = request[1]
2581
 
            address = request[2]
 
2690
            key_id = request[1].decode("ascii")
 
2691
            fpr = request[2].decode("ascii")
 
2692
            address = request[3]
2582
2693
 
2583
2694
            for c in self.clients.values():
2584
 
                if c.fingerprint == fpr:
 
2695
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
 
2696
                    continue
 
2697
                if key_id and c.key_id == key_id:
 
2698
                    client = c
 
2699
                    break
 
2700
                if fpr and c.fingerprint == fpr:
2585
2701
                    client = c
2586
2702
                    break
2587
2703
            else:
2588
 
                logger.info("Client not found for fingerprint: %s, ad"
2589
 
                            "dress: %s", fpr, address)
 
2704
                logger.info("Client not found for key ID: %s, address"
 
2705
                            ": %s", key_id or fpr, address)
2590
2706
                if self.use_dbus:
2591
2707
                    # Emit D-Bus signal
2592
 
                    mandos_dbus_service.ClientNotFound(fpr,
 
2708
                    mandos_dbus_service.ClientNotFound(key_id or fpr,
2593
2709
                                                       address[0])
2594
2710
                parent_pipe.send(False)
2595
2711
                return False
2858
2974
        sys.exit(os.EX_OK if fail_count == 0 else 1)
2859
2975
 
2860
2976
    # Default values for config file for server-global settings
 
2977
    if gnutls.has_rawpk:
 
2978
        priority = ("SECURE128:!CTYPE-X.509:+CTYPE-RAWPK:!RSA"
 
2979
                    ":!VERS-ALL:+VERS-TLS1.3:%PROFILE_ULTRA")
 
2980
    else:
 
2981
        priority = ("SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
 
2982
                    ":+SIGN-DSA-SHA256")
2861
2983
    server_defaults = {"interface": "",
2862
2984
                       "address": "",
2863
2985
                       "port": "",
2864
2986
                       "debug": "False",
2865
 
                       "priority":
2866
 
                       "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
2867
 
                       ":+SIGN-DSA-SHA256",
 
2987
                       "priority": priority,
2868
2988
                       "servicename": "Mandos",
2869
2989
                       "use_dbus": "True",
2870
2990
                       "use_ipv6": "True",
2875
2995
                       "foreground": "False",
2876
2996
                       "zeroconf": "True",
2877
2997
                       }
 
2998
    del priority
2878
2999
 
2879
3000
    # Parse config file for server-global settings
2880
3001
    server_config = configparser.SafeConfigParser(server_defaults)
2883
3004
    # Convert the SafeConfigParser object to a dict
2884
3005
    server_settings = server_config.defaults()
2885
3006
    # Use the appropriate methods on the non-string config options
2886
 
    for option in ("debug", "use_dbus", "use_ipv6", "foreground"):
 
3007
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
 
3008
                   "foreground", "zeroconf"):
2887
3009
        server_settings[option] = server_config.getboolean("DEFAULT",
2888
3010
                                                           option)
2889
3011
    if server_settings["port"]:
3123
3245
                        for k in ("name", "host"):
3124
3246
                            if isinstance(value[k], bytes):
3125
3247
                                value[k] = value[k].decode("utf-8")
 
3248
                        if "key_id" not in value:
 
3249
                            value["key_id"] = ""
 
3250
                        elif "fingerprint" not in value:
 
3251
                            value["fingerprint"] = ""
3126
3252
                    #  old_client_settings
3127
3253
                    # .keys()
3128
3254
                    old_client_settings = {
3265
3391
                pass
3266
3392
 
3267
3393
            @dbus.service.signal(_interface, signature="ss")
3268
 
            def ClientNotFound(self, fingerprint, address):
 
3394
            def ClientNotFound(self, key_id, address):
3269
3395
                "D-Bus signal"
3270
3396
                pass
3271
3397