/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2019-02-10 03:50:20 UTC
  • Revision ID: teddy@recompile.se-20190210035020-nttr1tybgwwixueu
Show debconf note about new TLS key IDs

If mandos-client did not see TLS keys and had to create them, or if
mandos sees GnuTLS version 3.6.6 or later, show an important notice on
package installation about the importance of adding the new key_id
options to clients.conf on the Mandos server.

* debian/control (Package: mandos, Package: mandos-client): Depend on
                                                            debconf.
* debian/mandos-client.lintian-overrides: Override warnings.
* debian/mandos-client.postinst (create_keys): Show notice if new TLS
                                               key files were created.
* debian/mandos-client.templates: New.
* debian/mandos.lintian-overrides: Override warnings.
* debian/mandos.postinst (configure): If GnuTLS 3.6.6 or later is
                                      detected, show an important
                                      notice (once) about the new
                                      key_id option required in
                                      clients.conf.
* debian/mandos.templates: New.

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
 
14
# Copyright © 2008-2018 Teddy Hogeborn
 
15
# Copyright © 2008-2018 Björn Påhlsson
16
16
#
17
17
# This file is part of Mandos.
18
18
#
80
80
 
81
81
import dbus
82
82
import dbus.service
83
 
import gi
84
83
from gi.repository import GLib
85
84
from dbus.mainloop.glib import DBusGMainLoop
86
85
import ctypes
116
115
if sys.version_info.major == 2:
117
116
    str = unicode
118
117
 
119
 
if sys.version_info < (3, 2):
120
 
    configparser.Configparser = configparser.SafeConfigParser
121
 
 
122
 
version = "1.8.5"
 
118
version = "1.7.20"
123
119
stored_state_file = "clients.pickle"
124
120
 
125
121
logger = logging.getLogger()
279
275
 
280
276
 
281
277
# Pretend that we have an Avahi module
282
 
class avahi(object):
283
 
    """This isn't so much a class as it is a module-like namespace."""
 
278
class Avahi(object):
 
279
    """This isn't so much a class as it is a module-like namespace.
 
280
    It is instantiated once, and simulates having an Avahi module."""
284
281
    IF_UNSPEC = -1               # avahi-common/address.h
285
282
    PROTO_UNSPEC = -1            # avahi-common/address.h
286
283
    PROTO_INET = 0               # avahi-common/address.h
290
287
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
291
288
    DBUS_PATH_SERVER = "/"
292
289
 
293
 
    @staticmethod
294
 
    def string_array_to_txt_array(t):
 
290
    def string_array_to_txt_array(self, t):
295
291
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
296
292
                           for s in t), signature="ay")
297
293
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
302
298
    SERVER_RUNNING = 2           # avahi-common/defs.h
303
299
    SERVER_COLLISION = 3         # avahi-common/defs.h
304
300
    SERVER_FAILURE = 4           # avahi-common/defs.h
 
301
avahi = Avahi()
305
302
 
306
303
 
307
304
class AvahiError(Exception):
507
504
 
508
505
 
509
506
# Pretend that we have a GnuTLS module
510
 
class gnutls(object):
511
 
    """This isn't so much a class as it is a module-like namespace."""
 
507
class GnuTLS(object):
 
508
    """This isn't so much a class as it is a module-like namespace.
 
509
    It is instantiated once, and simulates having a GnuTLS module."""
512
510
 
513
511
    library = ctypes.util.find_library("gnutls")
514
512
    if library is None:
515
513
        library = ctypes.util.find_library("gnutls-deb0")
516
514
    _library = ctypes.cdll.LoadLibrary(library)
517
515
    del library
 
516
    _need_version = b"3.3.0"
 
517
    _tls_rawpk_version = b"3.6.6"
 
518
 
 
519
    def __init__(self):
 
520
        # Need to use "self" here, since this method is called before
 
521
        # the assignment to the "gnutls" global variable happens.
 
522
        if self.check_version(self._need_version) is None:
 
523
            raise self.Error("Needs GnuTLS {} or later"
 
524
                             .format(self._need_version))
518
525
 
519
526
    # Unless otherwise indicated, the constants and types below are
520
527
    # all from the gnutls/gnutls.h C header file.
562
569
 
563
570
    # Exceptions
564
571
    class Error(Exception):
 
572
        # We need to use the class name "GnuTLS" here, since this
 
573
        # exception might be raised from within GnuTLS.__init__,
 
574
        # which is called before the assignment to the "gnutls"
 
575
        # global variable has happened.
565
576
        def __init__(self, message=None, code=None, args=()):
566
577
            # Default usage is by a message string, but if a return
567
578
            # code is passed, convert it to a string with
568
579
            # gnutls.strerror()
569
580
            self.code = code
570
581
            if message is None and code is not None:
571
 
                message = gnutls.strerror(code)
572
 
            return super(gnutls.Error, self).__init__(
 
582
                message = GnuTLS.strerror(code)
 
583
            return super(GnuTLS.Error, self).__init__(
573
584
                message, *args)
574
585
 
575
586
    class CertificateSecurityError(Error):
590
601
        def __init__(self, socket, credentials=None):
591
602
            self._c_object = gnutls.session_t()
592
603
            gnutls_flags = gnutls.CLIENT
593
 
            if gnutls.check_version(b"3.5.6"):
 
604
            if gnutls.check_version("3.5.6"):
594
605
                gnutls_flags |= gnutls.NO_TICKETS
595
606
            if gnutls.has_rawpk:
596
607
                gnutls_flags |= gnutls.ENABLE_RAWPK
733
744
    check_version.argtypes = [ctypes.c_char_p]
734
745
    check_version.restype = ctypes.c_char_p
735
746
 
736
 
    _need_version = b"3.3.0"
737
 
    if check_version(_need_version) is None:
738
 
        raise self.Error("Needs GnuTLS {} or later"
739
 
                         .format(_need_version))
740
 
 
741
 
    _tls_rawpk_version = b"3.6.6"
742
747
    has_rawpk = bool(check_version(_tls_rawpk_version))
743
748
 
744
749
    if has_rawpk:
798
803
                                                    ctypes.c_size_t)]
799
804
        openpgp_crt_get_fingerprint.restype = _error_code
800
805
 
801
 
    if check_version(b"3.6.4"):
 
806
    if check_version("3.6.4"):
802
807
        certificate_type_get2 = _library.gnutls_certificate_type_get2
803
808
        certificate_type_get2.argtypes = [session_t, ctypes.c_int]
804
809
        certificate_type_get2.restype = _error_code
805
810
 
806
811
    # Remove non-public functions
807
812
    del _error_code, _retry_on_error
 
813
# Create the global "gnutls" object, simulating a module
 
814
gnutls = GnuTLS()
808
815
 
809
816
 
810
817
def call_pipe(connection,       # : multiprocessing.Connection
825
832
    approved:   bool(); 'None' if not yet approved/disapproved
826
833
    approval_delay: datetime.timedelta(); Time to wait for approval
827
834
    approval_duration: datetime.timedelta(); Duration of one approval
828
 
    checker: multiprocessing.Process(); a running checker process used
829
 
             to see if the client lives. 'None' if no process is
830
 
             running.
 
835
    checker:    subprocess.Popen(); a running checker process used
 
836
                                    to see if the client lives.
 
837
                                    'None' if no process is running.
831
838
    checker_callback_tag: a GLib event source tag, or None
832
839
    checker_command: string; External command which is run to check
833
840
                     if client lives.  %() expansions are done at
1040
1047
    def checker_callback(self, source, condition, connection,
1041
1048
                         command):
1042
1049
        """The checker has completed, so take appropriate actions."""
 
1050
        self.checker_callback_tag = None
 
1051
        self.checker = None
1043
1052
        # Read return code from connection (see call_pipe)
1044
1053
        returncode = connection.recv()
1045
1054
        connection.close()
1046
 
        self.checker.join()
1047
 
        self.checker_callback_tag = None
1048
 
        self.checker = None
1049
1055
 
1050
1056
        if returncode >= 0:
1051
1057
            self.last_checker_status = returncode
2292
2298
            approval_required = False
2293
2299
            try:
2294
2300
                if gnutls.has_rawpk:
2295
 
                    fpr = b""
 
2301
                    fpr = ""
2296
2302
                    try:
2297
2303
                        key_id = self.key_id(
2298
2304
                            self.peer_certificate(session))
2302
2308
                    logger.debug("Key ID: %s", key_id)
2303
2309
 
2304
2310
                else:
2305
 
                    key_id = b""
 
2311
                    key_id = ""
2306
2312
                    try:
2307
2313
                        fpr = self.fingerprint(
2308
2314
                            self.peer_certificate(session))
2610
2616
                    raise
2611
2617
        # Only bind(2) the socket if we really need to.
2612
2618
        if self.server_address[0] or self.server_address[1]:
2613
 
            if self.server_address[1]:
2614
 
                self.allow_reuse_address = True
2615
2619
            if not self.server_address[0]:
2616
2620
                if self.address_family == socket.AF_INET6:
2617
2621
                    any_address = "::"  # in6addr_any
2696
2700
            address = request[3]
2697
2701
 
2698
2702
            for c in self.clients.values():
2699
 
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
2700
 
                    continue
2701
2703
                if key_id and c.key_id == key_id:
2702
2704
                    client = c
2703
2705
                    break
3002
3004
    del priority
3003
3005
 
3004
3006
    # Parse config file for server-global settings
3005
 
    server_config = configparser.ConfigParser(server_defaults)
 
3007
    server_config = configparser.SafeConfigParser(server_defaults)
3006
3008
    del server_defaults
3007
3009
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
3008
 
    # Convert the ConfigParser object to a dict
 
3010
    # Convert the SafeConfigParser object to a dict
3009
3011
    server_settings = server_config.defaults()
3010
3012
    # Use the appropriate methods on the non-string config options
3011
3013
    for option in ("debug", "use_dbus", "use_ipv6", "restore",
3083
3085
                                  server_settings["servicename"])))
3084
3086
 
3085
3087
    # Parse config file with clients
3086
 
    client_config = configparser.ConfigParser(Client.client_defaults)
 
3088
    client_config = configparser.SafeConfigParser(Client
 
3089
                                                  .client_defaults)
3087
3090
    client_config.read(os.path.join(server_settings["configdir"],
3088
3091
                                    "clients.conf"))
3089
3092
 
3160
3163
        # Close all input and output, do double fork, etc.
3161
3164
        daemon()
3162
3165
 
3163
 
    if gi.version_info < (3, 10, 2):
3164
 
        # multiprocessing will use threads, so before we use GLib we
3165
 
        # need to inform GLib that threads will be used.
3166
 
        GLib.threads_init()
 
3166
    # multiprocessing will use threads, so before we use GLib we need
 
3167
    # to inform GLib that threads will be used.
 
3168
    GLib.threads_init()
3167
3169
 
3168
3170
    global main_loop
3169
3171
    # From the Avahi example code
3249
3251
                        for k in ("name", "host"):
3250
3252
                            if isinstance(value[k], bytes):
3251
3253
                                value[k] = value[k].decode("utf-8")
3252
 
                        if "key_id" not in value:
 
3254
                        if not value.has_key("key_id"):
3253
3255
                            value["key_id"] = ""
3254
 
                        elif "fingerprint" not in value:
 
3256
                        elif not value.has_key("fingerprint"):
3255
3257
                            value["fingerprint"] = ""
3256
3258
                    #  old_client_settings
3257
3259
                    # .keys()