2
2
# -*- mode: python; coding: utf-8 -*-
 
4
4
# Mandos server - give out binary blobs to connecting clients.
 
6
6
# This program is partly derived from an example program for an Avahi
 
7
7
# service publisher, downloaded from
 
8
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
 
9
9
# methods "add", "remove", "server_state_changed",
 
10
10
# "entry_group_state_changed", "cleanup", and "activate" in the
 
11
11
# "AvahiService" class, and some lines in "main".
 
13
13
# Everything else is
 
14
 
# Copyright © 2008-2018 Teddy Hogeborn
 
15
 
# Copyright © 2008-2018 Björn Påhlsson
 
17
 
# This file is part of Mandos.
 
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
 
 
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.
 
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.
 
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/>.
 
32
31
# Contact the authors at <mandos@recompile.se>.
 
35
34
from __future__ import (division, absolute_import, print_function,
 
 
87
86
import xml.dom.minidom
 
90
 
# Try to find the value of SO_BINDTODEVICE:
 
92
 
    # This is where SO_BINDTODEVICE is in Python 3.3 (or 3.4?) and
 
93
 
    # newer, and it is also the most natural place for it:
 
94
90
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
 
95
91
except AttributeError:
 
97
 
        # This is where SO_BINDTODEVICE was up to and including Python
 
99
93
        from IN import SO_BINDTODEVICE
 
100
94
    except ImportError:
 
101
 
        # In Python 2.7 it seems to have been removed entirely.
 
102
 
        # Try running the C preprocessor:
 
104
 
            cc = subprocess.Popen(["cc", "--language=c", "-E",
 
106
 
                                  stdin=subprocess.PIPE,
 
107
 
                                  stdout=subprocess.PIPE)
 
108
 
            stdout = cc.communicate(
 
109
 
                "#include <sys/socket.h>\nSO_BINDTODEVICE\n")[0]
 
110
 
            SO_BINDTODEVICE = int(stdout.splitlines()[-1])
 
111
 
        except (OSError, ValueError, IndexError):
 
113
 
            SO_BINDTODEVICE = None
 
 
95
        SO_BINDTODEVICE = None
 
115
97
if sys.version_info.major == 2:
 
119
101
stored_state_file = "clients.pickle"
 
121
103
logger = logging.getLogger()
 
 
247
227
                                     '--passphrase-file',
 
249
229
                                    + self.gnupgargs,
 
250
 
                                    stdin=subprocess.PIPE,
 
251
 
                                    stdout=subprocess.PIPE,
 
252
 
                                    stderr=subprocess.PIPE)
 
253
 
            ciphertext, err = proc.communicate(input=data)
 
 
230
                                    stdin = subprocess.PIPE,
 
 
231
                                    stdout = subprocess.PIPE,
 
 
232
                                    stderr = subprocess.PIPE)
 
 
233
            ciphertext, err = proc.communicate(input = data)
 
254
234
        if proc.returncode != 0:
 
255
235
            raise PGPError(err)
 
256
236
        return ciphertext
 
258
238
    def decrypt(self, data, password):
 
259
239
        passphrase = self.password_encode(password)
 
260
240
        with tempfile.NamedTemporaryFile(
 
261
 
                dir=self.tempdir) as passfile:
 
 
241
                dir = self.tempdir) as passfile:
 
262
242
            passfile.write(passphrase)
 
264
244
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
265
245
                                     '--passphrase-file',
 
267
247
                                    + self.gnupgargs,
 
268
 
                                    stdin=subprocess.PIPE,
 
269
 
                                    stdout=subprocess.PIPE,
 
270
 
                                    stderr=subprocess.PIPE)
 
271
 
            decrypted_plaintext, err = proc.communicate(input=data)
 
 
248
                                    stdin = subprocess.PIPE,
 
 
249
                                    stdout = subprocess.PIPE,
 
 
250
                                    stderr = subprocess.PIPE)
 
 
251
            decrypted_plaintext, err = proc.communicate(input = data)
 
272
252
        if proc.returncode != 0:
 
273
253
            raise PGPError(err)
 
274
254
        return decrypted_plaintext
 
277
256
# Pretend that we have an Avahi module
 
278
257
class Avahi(object):
 
279
258
    """This isn't so much a class as it is a module-like namespace.
 
280
259
    It is instantiated once, and simulates having an Avahi module."""
 
281
 
    IF_UNSPEC = -1               # avahi-common/address.h
 
282
 
    PROTO_UNSPEC = -1            # avahi-common/address.h
 
283
 
    PROTO_INET = 0               # avahi-common/address.h
 
284
 
    PROTO_INET6 = 1              # avahi-common/address.h
 
 
260
    IF_UNSPEC = -1              # avahi-common/address.h
 
 
261
    PROTO_UNSPEC = -1           # avahi-common/address.h
 
 
262
    PROTO_INET = 0              # avahi-common/address.h
 
 
263
    PROTO_INET6 = 1             # avahi-common/address.h
 
285
264
    DBUS_NAME = "org.freedesktop.Avahi"
 
286
265
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
 
287
266
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
 
288
267
    DBUS_PATH_SERVER = "/"
 
290
268
    def string_array_to_txt_array(self, t):
 
291
269
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
 
292
270
                           for s in t), signature="ay")
 
293
 
    ENTRY_GROUP_ESTABLISHED = 2  # avahi-common/defs.h
 
294
 
    ENTRY_GROUP_COLLISION = 3    # avahi-common/defs.h
 
295
 
    ENTRY_GROUP_FAILURE = 4      # avahi-common/defs.h
 
296
 
    SERVER_INVALID = 0           # avahi-common/defs.h
 
297
 
    SERVER_REGISTERING = 1       # avahi-common/defs.h
 
298
 
    SERVER_RUNNING = 2           # avahi-common/defs.h
 
299
 
    SERVER_COLLISION = 3         # avahi-common/defs.h
 
300
 
    SERVER_FAILURE = 4           # avahi-common/defs.h
 
 
271
    ENTRY_GROUP_ESTABLISHED = 2 # avahi-common/defs.h
 
 
272
    ENTRY_GROUP_COLLISION = 3   # avahi-common/defs.h
 
 
273
    ENTRY_GROUP_FAILURE = 4     # avahi-common/defs.h
 
 
274
    SERVER_INVALID = 0          # avahi-common/defs.h
 
 
275
    SERVER_REGISTERING = 1      # avahi-common/defs.h
 
 
276
    SERVER_RUNNING = 2          # avahi-common/defs.h
 
 
277
    SERVER_COLLISION = 3        # avahi-common/defs.h
 
 
278
    SERVER_FAILURE = 4          # avahi-common/defs.h
 
304
281
class AvahiError(Exception):
 
305
282
    def __init__(self, value, *args, **kwargs):
 
306
283
        self.value = value
 
 
496
473
class AvahiServiceToSyslog(AvahiService):
 
497
474
    def rename(self, *args, **kwargs):
 
498
475
        """Add the new name to the syslog messages"""
 
499
 
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
 
 
476
        ret = AvahiService.rename(self, *args, **kwargs)
 
500
477
        syslogger.setFormatter(logging.Formatter(
 
501
478
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
 
502
479
            .format(self.name)))
 
506
482
# Pretend that we have a GnuTLS module
 
507
483
class GnuTLS(object):
 
508
484
    """This isn't so much a class as it is a module-like namespace.
 
509
485
    It is instantiated once, and simulates having a GnuTLS module."""
 
511
 
    library = ctypes.util.find_library("gnutls")
 
513
 
        library = ctypes.util.find_library("gnutls-deb0")
 
514
 
    _library = ctypes.cdll.LoadLibrary(library)
 
 
487
    _library = ctypes.cdll.LoadLibrary(
 
 
488
        ctypes.util.find_library("gnutls"))
 
516
489
    _need_version = b"3.3.0"
 
518
490
    def __init__(self):
 
519
 
        # Need to use "self" here, since this method is called before
 
520
 
        # the assignment to the "gnutls" global variable happens.
 
521
 
        if self.check_version(self._need_version) is None:
 
522
 
            raise self.Error("Needs GnuTLS {} or later"
 
523
 
                             .format(self._need_version))
 
 
491
        # Need to use class name "GnuTLS" here, since this method is
 
 
492
        # called before the assignment to the "gnutls" global variable
 
 
494
        if GnuTLS.check_version(self._need_version) is None:
 
 
495
            raise GnuTLS.Error("Needs GnuTLS {} or later"
 
 
496
                               .format(self._need_version))
 
525
498
    # Unless otherwise indicated, the constants and types below are
 
526
499
    # all from the gnutls/gnutls.h C header file.
 
530
503
    E_INTERRUPTED = -52
 
 
535
508
    CRD_CERTIFICATE = 1
 
536
509
    E_NO_CERTIFICATE_FOUND = -49
 
537
510
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
 
540
513
    class session_int(ctypes.Structure):
 
542
515
    session_t = ctypes.POINTER(session_int)
 
544
516
    class certificate_credentials_st(ctypes.Structure):
 
546
518
    certificate_credentials_t = ctypes.POINTER(
 
547
519
        certificate_credentials_st)
 
548
520
    certificate_type_t = ctypes.c_int
 
550
521
    class datum_t(ctypes.Structure):
 
551
522
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
552
523
                    ('size', ctypes.c_uint)]
 
554
524
    class openpgp_crt_int(ctypes.Structure):
 
556
526
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
 
557
 
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
 
 
527
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
 
558
528
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
 
559
529
    credentials_type_t = ctypes.c_int
 
560
530
    transport_ptr_t = ctypes.c_void_p
 
561
531
    close_request_t = ctypes.c_int
 
564
534
    class Error(Exception):
 
565
535
        # We need to use the class name "GnuTLS" here, since this
 
566
536
        # exception might be raised from within GnuTLS.__init__,
 
567
537
        # which is called before the assignment to the "gnutls"
 
568
538
        # global variable has happened.
 
569
 
        def __init__(self, message=None, code=None, args=()):
 
 
539
        def __init__(self, message = None, code = None, args=()):
 
570
540
            # Default usage is by a message string, but if a return
 
571
541
            # code is passed, convert it to a string with
 
572
542
            # gnutls.strerror()
 
 
641
611
                return _error_code(result)
 
642
612
            result = func(*arguments)
 
645
615
    # Unless otherwise indicated, the function declarations below are
 
646
616
    # all from the gnutls/gnutls.h C header file.
 
649
619
    priority_set_direct = _library.gnutls_priority_set_direct
 
650
620
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
 
651
621
                                    ctypes.POINTER(ctypes.c_char_p)]
 
652
622
    priority_set_direct.restype = _error_code
 
654
624
    init = _library.gnutls_init
 
655
625
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
 
656
626
    init.restype = _error_code
 
658
628
    set_default_priority = _library.gnutls_set_default_priority
 
659
629
    set_default_priority.argtypes = [session_t]
 
660
630
    set_default_priority.restype = _error_code
 
662
632
    record_send = _library.gnutls_record_send
 
663
633
    record_send.argtypes = [session_t, ctypes.c_void_p,
 
665
635
    record_send.restype = ctypes.c_ssize_t
 
666
636
    record_send.errcheck = _retry_on_error
 
668
638
    certificate_allocate_credentials = (
 
669
639
        _library.gnutls_certificate_allocate_credentials)
 
670
640
    certificate_allocate_credentials.argtypes = [
 
671
641
        ctypes.POINTER(certificate_credentials_t)]
 
672
642
    certificate_allocate_credentials.restype = _error_code
 
674
644
    certificate_free_credentials = (
 
675
645
        _library.gnutls_certificate_free_credentials)
 
676
 
    certificate_free_credentials.argtypes = [
 
677
 
        certificate_credentials_t]
 
 
646
    certificate_free_credentials.argtypes = [certificate_credentials_t]
 
678
647
    certificate_free_credentials.restype = None
 
680
649
    handshake_set_private_extensions = (
 
681
650
        _library.gnutls_handshake_set_private_extensions)
 
682
651
    handshake_set_private_extensions.argtypes = [session_t,
 
684
653
    handshake_set_private_extensions.restype = None
 
686
655
    credentials_set = _library.gnutls_credentials_set
 
687
656
    credentials_set.argtypes = [session_t, credentials_type_t,
 
689
658
    credentials_set.restype = _error_code
 
691
660
    strerror = _library.gnutls_strerror
 
692
661
    strerror.argtypes = [ctypes.c_int]
 
693
662
    strerror.restype = ctypes.c_char_p
 
695
664
    certificate_type_get = _library.gnutls_certificate_type_get
 
696
665
    certificate_type_get.argtypes = [session_t]
 
697
666
    certificate_type_get.restype = _error_code
 
699
668
    certificate_get_peers = _library.gnutls_certificate_get_peers
 
700
669
    certificate_get_peers.argtypes = [session_t,
 
701
670
                                      ctypes.POINTER(ctypes.c_uint)]
 
702
671
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
 
704
673
    global_set_log_level = _library.gnutls_global_set_log_level
 
705
674
    global_set_log_level.argtypes = [ctypes.c_int]
 
706
675
    global_set_log_level.restype = None
 
708
677
    global_set_log_function = _library.gnutls_global_set_log_function
 
709
678
    global_set_log_function.argtypes = [log_func]
 
710
679
    global_set_log_function.restype = None
 
712
681
    deinit = _library.gnutls_deinit
 
713
682
    deinit.argtypes = [session_t]
 
714
683
    deinit.restype = None
 
716
685
    handshake = _library.gnutls_handshake
 
717
686
    handshake.argtypes = [session_t]
 
718
687
    handshake.restype = _error_code
 
719
688
    handshake.errcheck = _retry_on_error
 
721
690
    transport_set_ptr = _library.gnutls_transport_set_ptr
 
722
691
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
 
723
692
    transport_set_ptr.restype = None
 
725
694
    bye = _library.gnutls_bye
 
726
695
    bye.argtypes = [session_t, close_request_t]
 
727
696
    bye.restype = _error_code
 
728
697
    bye.errcheck = _retry_on_error
 
730
699
    check_version = _library.gnutls_check_version
 
731
700
    check_version.argtypes = [ctypes.c_char_p]
 
732
701
    check_version.restype = ctypes.c_char_p
 
734
703
    # All the function declarations below are from gnutls/openpgp.h
 
736
705
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
737
706
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
738
707
    openpgp_crt_init.restype = _error_code
 
740
709
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
741
710
    openpgp_crt_import.argtypes = [openpgp_crt_t,
 
742
711
                                   ctypes.POINTER(datum_t),
 
743
712
                                   openpgp_crt_fmt_t]
 
744
713
    openpgp_crt_import.restype = _error_code
 
746
715
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
747
716
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
748
717
                                        ctypes.POINTER(ctypes.c_uint)]
 
749
718
    openpgp_crt_verify_self.restype = _error_code
 
751
720
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
752
721
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
753
722
    openpgp_crt_deinit.restype = None
 
755
724
    openpgp_crt_get_fingerprint = (
 
756
725
        _library.gnutls_openpgp_crt_get_fingerprint)
 
757
726
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
 
1079
1046
            # The exception is when not debugging but nevertheless
 
1080
1047
            # running in the foreground; use the previously
 
1081
1048
            # created wnull.
 
1082
 
            popen_args = {"close_fds": True,
 
 
1049
            popen_args = { "close_fds": True,
 
1085
1052
            if (not self.server_settings["debug"]
 
1086
1053
                and self.server_settings["foreground"]):
 
1087
1054
                popen_args.update({"stdout": wnull,
 
1089
 
            pipe = multiprocessing.Pipe(duplex=False)
 
 
1056
            pipe = multiprocessing.Pipe(duplex = False)
 
1090
1057
            self.checker = multiprocessing.Process(
 
1092
 
                args=(pipe[1], subprocess.call, command),
 
 
1059
                args = (pipe[1], subprocess.call, command),
 
 
1060
                kwargs = popen_args)
 
1094
1061
            self.checker.start()
 
1095
1062
            self.checker_callback_tag = GLib.io_add_watch(
 
1096
1063
                pipe[0].fileno(), GLib.IO_IN,
 
1097
1064
                self.checker_callback, pipe[0], command)
 
1098
1065
        # Re-run this periodically if run by GLib.timeout_add
 
1101
1068
    def stop_checker(self):
 
1102
1069
        """Force the checker process, if any, to stop."""
 
1103
1070
        if self.checker_callback_tag:
 
 
1138
1105
        func._dbus_name = func.__name__
 
1139
1106
        if func._dbus_name.endswith("_dbus_property"):
 
1140
1107
            func._dbus_name = func._dbus_name[:-14]
 
1141
 
        func._dbus_get_args_options = {'byte_arrays': byte_arrays}
 
 
1108
        func._dbus_get_args_options = {'byte_arrays': byte_arrays }
 
1144
1111
    return decorator
 
1147
1114
def dbus_interface_annotations(dbus_interface):
 
1148
1115
    """Decorator for marking functions returning interface annotations
 
1152
1119
    @dbus_interface_annotations("org.example.Interface")
 
1153
1120
    def _foo(self):  # Function name does not matter
 
1154
1121
        return {"org.freedesktop.DBus.Deprecated": "true",
 
1155
1122
                "org.freedesktop.DBus.Property.EmitsChangedSignal":
 
1159
1126
    def decorator(func):
 
1160
1127
        func._dbus_is_interface = True
 
1161
1128
        func._dbus_interface = dbus_interface
 
1162
1129
        func._dbus_name = dbus_interface
 
1165
1132
    return decorator
 
1168
1135
def dbus_annotations(annotations):
 
1169
1136
    """Decorator to annotate D-Bus methods, signals or properties
 
1172
1139
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true",
 
1173
1140
                       "org.freedesktop.DBus.Property."
 
1174
1141
                       "EmitsChangedSignal": "false"})
 
 
1230
1197
                for cls in self.__class__.__mro__
 
1231
1198
                for name, athing in
 
1232
1199
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
 
1234
1201
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
1236
 
                         path_keyword='object_path',
 
1237
 
                         connection_keyword='connection')
 
 
1202
                         out_signature = "s",
 
 
1203
                         path_keyword = 'object_path',
 
 
1204
                         connection_keyword = 'connection')
 
1238
1205
    def Introspect(self, object_path, connection):
 
1239
1206
        """Overloading of standard D-Bus method.
 
1241
1208
        Inserts annotation tags on methods and signals.
 
1243
1210
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
1246
1213
            document = xml.dom.minidom.parseString(xmlstring)
 
1248
1215
            for if_tag in document.getElementsByTagName("interface"):
 
1249
1216
                # Add annotation tags
 
1250
1217
                for typ in ("method", "signal"):
 
 
1461
1428
                         exc_info=error)
 
1462
1429
        return xmlstring
 
1466
1432
    dbus.OBJECT_MANAGER_IFACE
 
1467
1433
except AttributeError:
 
1468
1434
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
 
1471
1436
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
 
1472
1437
    """A D-Bus object with an ObjectManager.
 
1474
1439
    Classes inheriting from this exposes the standard
 
1475
1440
    GetManagedObjects call and the InterfacesAdded and
 
1476
1441
    InterfacesRemoved signals on the standard
 
1477
1442
    "org.freedesktop.DBus.ObjectManager" interface.
 
1479
1444
    Note: No signals are sent automatically; they must be sent
 
1482
1447
    @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
 
1483
 
                         out_signature="a{oa{sa{sv}}}")
 
 
1448
                         out_signature = "a{oa{sa{sv}}}")
 
1484
1449
    def GetManagedObjects(self):
 
1485
1450
        """This function must be overridden"""
 
1486
1451
        raise NotImplementedError()
 
1488
1453
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
 
1489
 
                         signature="oa{sa{sv}}")
 
 
1454
                         signature = "oa{sa{sv}}")
 
1490
1455
    def InterfacesAdded(self, object_path, interfaces_and_properties):
 
1493
 
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature="oas")
 
 
1458
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
 
1494
1459
    def InterfacesRemoved(self, object_path, interfaces):
 
1497
1462
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
1499
 
                         path_keyword='object_path',
 
1500
 
                         connection_keyword='connection')
 
 
1463
                         out_signature = "s",
 
 
1464
                         path_keyword = 'object_path',
 
 
1465
                         connection_keyword = 'connection')
 
1501
1466
    def Introspect(self, object_path, connection):
 
1502
1467
        """Overloading of standard D-Bus method.
 
1504
1469
        Override return argument name of GetManagedObjects to be
 
1505
1470
        "objpath_interfaces_and_properties"
 
 
1545
1509
    dbus.service.Object, it will add alternate D-Bus attributes with
 
1546
1510
    interface names according to the "alt_interface_names" mapping.
 
1549
1513
    @alternate_dbus_interfaces({"org.example.Interface":
 
1550
1514
                                    "net.example.AlternateInterface"})
 
1551
1515
    class SampleDBusObject(dbus.service.Object):
 
1552
1516
        @dbus.service.method("org.example.Interface")
 
1553
1517
        def SampleDBusMethod():
 
1556
1520
    The above "SampleDBusMethod" on "SampleDBusObject" will be
 
1557
1521
    reachable via two interfaces: "org.example.Interface" and
 
1558
1522
    "net.example.AlternateInterface", the latter of which will have
 
1559
1523
    its D-Bus annotation "org.freedesktop.DBus.Deprecated" set to
 
1560
1524
    "true", unless "deprecate" is passed with a False value.
 
1562
1526
    This works for methods and signals, and also for D-Bus properties
 
1563
1527
    (from DBusObjectWithProperties) and interfaces (from the
 
1564
1528
    dbus_interface_annotations decorator).
 
1567
1531
    def wrapper(cls):
 
1568
1532
        for orig_interface_name, alt_interface_name in (
 
1569
1533
                alt_interface_names.items()):
 
 
1713
1675
                            "se.bsnet.fukt.Mandos"})
 
1714
1676
class ClientDBus(Client, DBusObjectWithProperties):
 
1715
1677
    """A Client class using D-Bus
 
1718
1680
    dbus_object_path: dbus.ObjectPath
 
1719
1681
    bus: dbus.SystemBus()
 
1722
1684
    runtime_expansions = (Client.runtime_expansions
 
1723
1685
                          + ("dbus_object_path", ))
 
1725
1687
    _interface = "se.recompile.Mandos.Client"
 
1727
1689
    # dbus.service.Object doesn't use super(), so we can't either.
 
1729
 
    def __init__(self, bus=None, *args, **kwargs):
 
 
1691
    def __init__(self, bus = None, *args, **kwargs):
 
1731
1693
        Client.__init__(self, *args, **kwargs)
 
1732
1694
        # Only now, when this client is initialized, can it show up on
 
 
1769
1731
                        dbus_value = transform_func(
 
1770
1732
                            type_func(value),
 
1771
 
                            variant_level=variant_level)
 
 
1733
                            variant_level = variant_level)
 
1772
1734
                        self.PropertyChanged(dbus.String(dbus_name),
 
1774
1736
                        self.PropertiesChanged(
 
1776
 
                            dbus.Dictionary({dbus.String(dbus_name):
 
 
1738
                            dbus.Dictionary({ dbus.String(dbus_name):
 
1779
1741
            setattr(self, attrname, value)
 
1781
1743
        return property(lambda self: getattr(self, attrname), setter)
 
1783
1745
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
 
1784
1746
    approvals_pending = notifychangeproperty(dbus.Boolean,
 
1785
1747
                                             "ApprovalPending",
 
1787
1749
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
 
1788
1750
    last_enabled = notifychangeproperty(datetime_to_dbus,
 
1790
1752
    checker = notifychangeproperty(
 
1791
1753
        dbus.Boolean, "CheckerRunning",
 
1792
 
        type_func=lambda checker: checker is not None)
 
 
1754
        type_func = lambda checker: checker is not None)
 
1793
1755
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
 
1794
1756
                                           "LastCheckedOK")
 
1795
1757
    last_checker_status = notifychangeproperty(dbus.Int16,
 
 
1800
1762
                                               "ApprovedByDefault")
 
1801
1763
    approval_delay = notifychangeproperty(
 
1802
1764
        dbus.UInt64, "ApprovalDelay",
 
1803
 
        type_func=lambda td: td.total_seconds() * 1000)
 
 
1765
        type_func = lambda td: td.total_seconds() * 1000)
 
1804
1766
    approval_duration = notifychangeproperty(
 
1805
1767
        dbus.UInt64, "ApprovalDuration",
 
1806
 
        type_func=lambda td: td.total_seconds() * 1000)
 
 
1768
        type_func = lambda td: td.total_seconds() * 1000)
 
1807
1769
    host = notifychangeproperty(dbus.String, "Host")
 
1808
1770
    timeout = notifychangeproperty(
 
1809
1771
        dbus.UInt64, "Timeout",
 
1810
 
        type_func=lambda td: td.total_seconds() * 1000)
 
 
1772
        type_func = lambda td: td.total_seconds() * 1000)
 
1811
1773
    extended_timeout = notifychangeproperty(
 
1812
1774
        dbus.UInt64, "ExtendedTimeout",
 
1813
 
        type_func=lambda td: td.total_seconds() * 1000)
 
 
1775
        type_func = lambda td: td.total_seconds() * 1000)
 
1814
1776
    interval = notifychangeproperty(
 
1815
1777
        dbus.UInt64, "Interval",
 
1816
 
        type_func=lambda td: td.total_seconds() * 1000)
 
 
1778
        type_func = lambda td: td.total_seconds() * 1000)
 
1817
1779
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
1818
1780
    secret = notifychangeproperty(dbus.ByteArray, "Secret",
 
1819
1781
                                  invalidate_only=True)
 
1821
1783
    del notifychangeproperty
 
1823
1785
    def __del__(self, *args, **kwargs):
 
1825
1787
            self.remove_from_connection()
 
 
1860
1822
            # Emit D-Bus signal
 
1861
1823
            self.CheckerStarted(self.current_checker_command)
 
1864
1826
    def _reset_approved(self):
 
1865
1827
        self.approved = None
 
1868
1830
    def approve(self, value=True):
 
1869
1831
        self.approved = value
 
1870
1832
        GLib.timeout_add(int(self.approval_duration.total_seconds()
 
1871
1833
                             * 1000), self._reset_approved)
 
1872
1834
        self.send_changedstate()
 
1874
 
    #  D-Bus methods, signals & properties
 
 
1836
    ## D-Bus methods, signals & properties
 
1880
1842
    # CheckerCompleted - signal
 
1881
1843
    @dbus.service.signal(_interface, signature="nxs")
 
1882
1844
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
1886
1848
    # CheckerStarted - signal
 
1887
1849
    @dbus.service.signal(_interface, signature="s")
 
1888
1850
    def CheckerStarted(self, command):
 
1892
1854
    # PropertyChanged - signal
 
1893
1855
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
 
1894
1856
    @dbus.service.signal(_interface, signature="sv")
 
1895
1857
    def PropertyChanged(self, property, value):
 
1899
1861
    # GotSecret - signal
 
1900
1862
    @dbus.service.signal(_interface)
 
1901
1863
    def GotSecret(self):
 
 
1904
1866
        server to mandos-client
 
1908
1870
    # Rejected - signal
 
1909
1871
    @dbus.service.signal(_interface, signature="s")
 
1910
1872
    def Rejected(self, reason):
 
1914
1876
    # NeedApproval - signal
 
1915
1877
    @dbus.service.signal(_interface, signature="tb")
 
1916
1878
    def NeedApproval(self, timeout, default):
 
1918
1880
        return self.need_approval()
 
1922
1884
    # Approve - method
 
1923
1885
    @dbus.service.method(_interface, in_signature="b")
 
1924
1886
    def Approve(self, value):
 
1925
1887
        self.approve(value)
 
1927
1889
    # CheckedOK - method
 
1928
1890
    @dbus.service.method(_interface)
 
1929
1891
    def CheckedOK(self):
 
1930
1892
        self.checked_ok()
 
1932
1894
    # Enable - method
 
1933
1895
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
 
1934
1896
    @dbus.service.method(_interface)
 
1935
1897
    def Enable(self):
 
1939
1901
    # StartChecker - method
 
1940
1902
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
 
1941
1903
    @dbus.service.method(_interface)
 
1942
1904
    def StartChecker(self):
 
1944
1906
        self.start_checker()
 
1946
1908
    # Disable - method
 
1947
1909
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
 
1948
1910
    @dbus.service.method(_interface)
 
1949
1911
    def Disable(self):
 
1953
1915
    # StopChecker - method
 
1954
1916
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
 
1955
1917
    @dbus.service.method(_interface)
 
1956
1918
    def StopChecker(self):
 
1957
1919
        self.stop_checker()
 
1961
1923
    # ApprovalPending - property
 
1962
1924
    @dbus_service_property(_interface, signature="b", access="read")
 
1963
1925
    def ApprovalPending_dbus_property(self):
 
1964
1926
        return dbus.Boolean(bool(self.approvals_pending))
 
1966
1928
    # ApprovedByDefault - property
 
1967
1929
    @dbus_service_property(_interface,
 
 
2048
2010
            self.checked_ok()
 
2050
2012
        return datetime_to_dbus(self.last_checked_ok)
 
2052
2014
    # LastCheckerStatus - property
 
2053
2015
    @dbus_service_property(_interface, signature="n", access="read")
 
2054
2016
    def LastCheckerStatus_dbus_property(self):
 
2055
2017
        return dbus.Int16(self.last_checker_status)
 
2057
2019
    # Expires - property
 
2058
2020
    @dbus_service_property(_interface, signature="s", access="read")
 
2059
2021
    def Expires_dbus_property(self):
 
2060
2022
        return datetime_to_dbus(self.expires)
 
2062
2024
    # LastApprovalRequest - property
 
2063
2025
    @dbus_service_property(_interface, signature="s", access="read")
 
2064
2026
    def LastApprovalRequest_dbus_property(self):
 
2065
2027
        return datetime_to_dbus(self.last_approval_request)
 
2067
2029
    # Timeout - property
 
2068
2030
    @dbus_service_property(_interface,
 
 
2190
2152
class ClientHandler(socketserver.BaseRequestHandler, object):
 
2191
2153
    """A class to handle client connections.
 
2193
2155
    Instantiated once for each connection to handle it.
 
2194
2156
    Note: This will run in its own forked process."""
 
2196
2158
    def handle(self):
 
2197
2159
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
2198
2160
            logger.info("TCP connection from: %s",
 
2199
2161
                        str(self.client_address))
 
2200
2162
            logger.debug("Pipe FD: %d",
 
2201
2163
                         self.server.child_pipe.fileno())
 
2203
2165
            session = gnutls.ClientSession(self.request)
 
2205
 
            # priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
2206
 
            #                       "+AES-256-CBC", "+SHA1",
 
2207
 
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
 
 
2167
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
 
2168
            #                      "+AES-256-CBC", "+SHA1",
 
 
2169
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
 
2209
2171
            # Use a fallback default, since this MUST be set.
 
2210
2172
            priority = self.server.gnutls_priority
 
2211
2173
            if priority is None:
 
2212
2174
                priority = "NORMAL"
 
2213
 
            gnutls.priority_set_direct(session._c_object,
 
2214
 
                                       priority.encode("utf-8"),
 
 
2175
            gnutls.priority_set_direct(session._c_object, priority,
 
2217
2178
            # Start communication using the Mandos protocol
 
2218
2179
            # Get protocol number
 
2219
2180
            line = self.request.makefile().readline()
 
 
2385
2345
class MultiprocessingMixIn(object):
 
2386
2346
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
2388
2348
    def sub_process_main(self, request, address):
 
2390
2350
            self.finish_request(request, address)
 
2391
2351
        except Exception:
 
2392
2352
            self.handle_error(request, address)
 
2393
2353
        self.close_request(request)
 
2395
2355
    def process_request(self, request, address):
 
2396
2356
        """Start a new process to process the request."""
 
2397
 
        proc = multiprocessing.Process(target=self.sub_process_main,
 
2398
 
                                       args=(request, address))
 
 
2357
        proc = multiprocessing.Process(target = self.sub_process_main,
 
 
2358
                                       args = (request, address))
 
2403
2363
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
2404
2364
    """ adds a pipe to the MixIn """
 
2406
2366
    def process_request(self, request, client_address):
 
2407
2367
        """Overrides and wraps the original process_request().
 
2409
2369
        This function creates a new pipe in self.pipe
 
2411
2371
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
 
2413
2373
        proc = MultiprocessingMixIn.process_request(self, request,
 
2414
2374
                                                    client_address)
 
2415
2375
        self.child_pipe.close()
 
2416
2376
        self.add_pipe(parent_pipe, proc)
 
2418
2378
    def add_pipe(self, parent_pipe, proc):
 
2419
2379
        """Dummy function; override as necessary"""
 
2420
2380
        raise NotImplementedError()
 
 
2469
2428
        # socket_wrapper(), if socketfd was set.
 
2470
2429
        socketserver.TCPServer.__init__(self, server_address,
 
2471
2430
                                        RequestHandlerClass)
 
2473
2432
    def server_bind(self):
 
2474
2433
        """This overrides the normal server_bind() function
 
2475
2434
        to bind to an interface if one was specified, and also NOT to
 
2476
2435
        bind to an address or port if they were not specified."""
 
2477
 
        global SO_BINDTODEVICE
 
2478
2436
        if self.interface is not None:
 
2479
2437
            if SO_BINDTODEVICE is None:
 
2480
 
                # Fall back to a hard-coded value which seems to be
 
2482
 
                logger.warning("SO_BINDTODEVICE not found, trying 25")
 
2483
 
                SO_BINDTODEVICE = 25
 
2485
 
                self.socket.setsockopt(
 
2486
 
                    socket.SOL_SOCKET, SO_BINDTODEVICE,
 
2487
 
                    (self.interface + "\0").encode("utf-8"))
 
2488
 
            except socket.error as error:
 
2489
 
                if error.errno == errno.EPERM:
 
2490
 
                    logger.error("No permission to bind to"
 
2491
 
                                 " interface %s", self.interface)
 
2492
 
                elif error.errno == errno.ENOPROTOOPT:
 
2493
 
                    logger.error("SO_BINDTODEVICE not available;"
 
2494
 
                                 " cannot bind to interface %s",
 
2496
 
                elif error.errno == errno.ENODEV:
 
2497
 
                    logger.error("Interface %s does not exist,"
 
2498
 
                                 " cannot bind", self.interface)
 
 
2438
                logger.error("SO_BINDTODEVICE does not exist;"
 
 
2439
                             " cannot bind to interface %s",
 
 
2443
                    self.socket.setsockopt(
 
 
2444
                        socket.SOL_SOCKET, SO_BINDTODEVICE,
 
 
2445
                        (self.interface + "\0").encode("utf-8"))
 
 
2446
                except socket.error as error:
 
 
2447
                    if error.errno == errno.EPERM:
 
 
2448
                        logger.error("No permission to bind to"
 
 
2449
                                     " interface %s", self.interface)
 
 
2450
                    elif error.errno == errno.ENOPROTOOPT:
 
 
2451
                        logger.error("SO_BINDTODEVICE not available;"
 
 
2452
                                     " cannot bind to interface %s",
 
 
2454
                    elif error.errno == errno.ENODEV:
 
 
2455
                        logger.error("Interface %s does not exist,"
 
 
2456
                                     " cannot bind", self.interface)
 
2501
2459
        # Only bind(2) the socket if we really need to.
 
2502
2460
        if self.server_address[0] or self.server_address[1]:
 
2503
2461
            if not self.server_address[0]:
 
2504
2462
                if self.address_family == socket.AF_INET6:
 
2505
 
                    any_address = "::"  # in6addr_any
 
 
2463
                    any_address = "::" # in6addr_any
 
2507
 
                    any_address = "0.0.0.0"  # INADDR_ANY
 
 
2465
                    any_address = "0.0.0.0" # INADDR_ANY
 
2508
2466
                self.server_address = (any_address,
 
2509
2467
                                       self.server_address[1])
 
2510
2468
            elif not self.server_address[1]:
 
 
2544
2502
        self.gnutls_priority = gnutls_priority
 
2545
2503
        IPv6_TCPServer.__init__(self, server_address,
 
2546
2504
                                RequestHandlerClass,
 
2547
 
                                interface=interface,
 
 
2505
                                interface = interface,
 
 
2506
                                use_ipv6 = use_ipv6,
 
 
2507
                                socketfd = socketfd)
 
2551
2509
    def server_activate(self):
 
2552
2510
        if self.enabled:
 
2553
2511
            return socketserver.TCPServer.server_activate(self)
 
2555
2513
    def enable(self):
 
2556
2514
        self.enabled = True
 
2558
2516
    def add_pipe(self, parent_pipe, proc):
 
2559
2517
        # Call "handle_ipc" for both data and EOF events
 
2560
2518
        GLib.io_add_watch(
 
2561
2519
            parent_pipe.fileno(),
 
2562
2520
            GLib.IO_IN | GLib.IO_HUP,
 
2563
2521
            functools.partial(self.handle_ipc,
 
2564
 
                              parent_pipe=parent_pipe,
 
 
2522
                              parent_pipe = parent_pipe,
 
2567
2525
    def handle_ipc(self, source, condition,
 
2568
2526
                   parent_pipe=None,
 
2570
2528
                   client_object=None):
 
2571
2529
        # error, or the other end of multiprocessing.Pipe has closed
 
2572
2530
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2573
2531
            # Wait for other process to exit
 
2577
2535
        # Read a request from the child
 
2578
2536
        request = parent_pipe.recv()
 
2579
2537
        command = request[0]
 
2581
2539
        if command == 'init':
 
2582
 
            fpr = request[1].decode("ascii")
 
2583
2541
            address = request[2]
 
2585
2543
            for c in self.clients.values():
 
2586
2544
                if c.fingerprint == fpr:
 
 
2651
2609
    >>> rfc3339_duration_to_delta("P1DT3M20S")
 
2652
2610
    datetime.timedelta(1, 200)
 
2655
2613
    # Parsing an RFC 3339 duration with regular expressions is not
 
2656
2614
    # possible - there would have to be multiple places for the same
 
2657
2615
    # values, like seconds.  The current code, while more esoteric, is
 
2658
2616
    # cleaner without depending on a parsing library.  If Python had a
 
2659
2617
    # built-in library for parsing we would use it, but we'd like to
 
2660
2618
    # avoid excessive use of external libraries.
 
2662
2620
    # New type for defining tokens, syntax, and semantics all-in-one
 
2663
2621
    Token = collections.namedtuple("Token", (
 
2664
2622
        "regexp",  # To match token; if "value" is not None, must have
 
 
2851
2806
    parser.add_argument("--no-zeroconf", action="store_false",
 
2852
2807
                        dest="zeroconf", help="Do not use Zeroconf",
 
2855
2810
    options = parser.parse_args()
 
2857
2812
    if options.check:
 
2859
2814
        fail_count, test_count = doctest.testmod()
 
2860
2815
        sys.exit(os.EX_OK if fail_count == 0 else 1)
 
2862
2817
    # Default values for config file for server-global settings
 
2863
 
    server_defaults = {"interface": "",
 
2868
 
                       "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
 
2869
 
                       ":+SIGN-DSA-SHA256",
 
2870
 
                       "servicename": "Mandos",
 
2876
 
                       "statedir": "/var/lib/mandos",
 
2877
 
                       "foreground": "False",
 
 
2818
    server_defaults = { "interface": "",
 
 
2823
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP:!RSA"
 
 
2824
                        ":+SIGN-DSA-SHA256",
 
 
2825
                        "servicename": "Mandos",
 
 
2831
                        "statedir": "/var/lib/mandos",
 
 
2832
                        "foreground": "False",
 
2881
2836
    # Parse config file for server-global settings
 
2882
2837
    server_config = configparser.SafeConfigParser(server_defaults)
 
2883
2838
    del server_defaults
 
 
3013
2967
                       .format(uid, gid, os.strerror(error.errno)))
 
3014
2968
        if error.errno != errno.EPERM:
 
3018
2972
        # Enable all possible GnuTLS debugging
 
3020
2974
        # "Use a log level over 10 to enable all debugging options."
 
3021
2975
        # - GnuTLS manual
 
3022
2976
        gnutls.global_set_log_level(11)
 
3024
2978
        @gnutls.log_func
 
3025
2979
        def debug_gnutls(level, string):
 
3026
2980
            logger.debug("GnuTLS: %s", string[:-1])
 
3028
2982
        gnutls.global_set_log_function(debug_gnutls)
 
3030
2984
        # Redirect stdin so all checkers get /dev/null
 
3031
2985
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
 
3032
2986
        os.dup2(null, sys.stdin.fileno())
 
3036
2990
    # Need to fork before connecting to D-Bus
 
3037
2991
    if not foreground:
 
3038
2992
        # Close all input and output, do double fork, etc.
 
3041
2995
    # multiprocessing will use threads, so before we use GLib we need
 
3042
2996
    # to inform GLib that threads will be used.
 
3043
2997
    GLib.threads_init()
 
3045
2999
    global main_loop
 
3046
3000
    # From the Avahi example code
 
3047
3001
    DBusGMainLoop(set_as_default=True)
 
 
3065
3019
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
3066
3020
        service = AvahiServiceToSyslog(
 
3067
 
            name=server_settings["servicename"],
 
3068
 
            servicetype="_mandos._tcp",
 
 
3021
            name = server_settings["servicename"],
 
 
3022
            servicetype = "_mandos._tcp",
 
 
3023
            protocol = protocol,
 
3071
3025
        if server_settings["interface"]:
 
3072
3026
            service.interface = if_nametoindex(
 
3073
3027
                server_settings["interface"].encode("utf-8"))
 
3075
3029
    global multiprocessing_manager
 
3076
3030
    multiprocessing_manager = multiprocessing.Manager()
 
3078
3032
    client_class = Client
 
3080
 
        client_class = functools.partial(ClientDBus, bus=bus)
 
 
3034
        client_class = functools.partial(ClientDBus, bus = bus)
 
3082
3036
    client_settings = Client.config_parser(client_config)
 
3083
3037
    old_client_settings = {}
 
3084
3038
    clients_data = {}
 
3086
3040
    # This is used to redirect stdout and stderr for checker processes
 
3088
 
    wnull = open(os.devnull, "w")  # A writable /dev/null
 
 
3042
    wnull = open(os.devnull, "w") # A writable /dev/null
 
3089
3043
    # Only used if server is running in foreground but not in debug
 
3091
3045
    if debug or not foreground:
 
3094
3048
    # Get client data and settings from last running state.
 
3095
3049
    if server_settings["restore"]:
 
3097
3051
            with open(stored_state_path, "rb") as stored_state:
 
3098
 
                if sys.version_info.major == 2:
 
 
3052
                if sys.version_info.major == 2:                
 
3099
3053
                    clients_data, old_client_settings = pickle.load(
 
3102
3056
                    bytes_clients_data, bytes_old_client_settings = (
 
3103
 
                        pickle.load(stored_state, encoding="bytes"))
 
3104
 
                    #   Fix bytes to strings
 
 
3057
                        pickle.load(stored_state, encoding = "bytes"))
 
 
3058
                    ### Fix bytes to strings
 
3107
 
                    clients_data = {(key.decode("utf-8")
 
3108
 
                                     if isinstance(key, bytes)
 
3111
 
                                    bytes_clients_data.items()}
 
 
3061
                    clients_data = { (key.decode("utf-8")
 
 
3062
                                      if isinstance(key, bytes)
 
 
3065
                                     bytes_clients_data.items() }
 
3112
3066
                    del bytes_clients_data
 
3113
3067
                    for key in clients_data:
 
3114
 
                        value = {(k.decode("utf-8")
 
3115
 
                                  if isinstance(k, bytes) else k): v
 
3117
 
                                 clients_data[key].items()}
 
 
3068
                        value = { (k.decode("utf-8")
 
 
3069
                                   if isinstance(k, bytes) else k): v
 
 
3071
                                  clients_data[key].items() }
 
3118
3072
                        clients_data[key] = value
 
3119
3073
                        # .client_structure
 
3120
3074
                        value["client_structure"] = [
 
3121
3075
                            (s.decode("utf-8")
 
3122
3076
                             if isinstance(s, bytes)
 
3123
3077
                             else s) for s in
 
3124
 
                            value["client_structure"]]
 
 
3078
                            value["client_structure"] ]
 
3125
3079
                        # .name & .host
 
3126
3080
                        for k in ("name", "host"):
 
3127
3081
                            if isinstance(value[k], bytes):
 
3128
3082
                                value[k] = value[k].decode("utf-8")
 
3129
 
                    #  old_client_settings
 
 
3083
                    ## old_client_settings
 
3131
3085
                    old_client_settings = {
 
3132
3086
                        (key.decode("utf-8")
 
3133
3087
                         if isinstance(key, bytes)
 
3134
3088
                         else key): value
 
3135
3089
                        for key, value in
 
3136
 
                        bytes_old_client_settings.items()}
 
 
3090
                        bytes_old_client_settings.items() }
 
3137
3091
                    del bytes_old_client_settings
 
3139
3093
                    for value in old_client_settings.values():
 
 
3245
3199
                             pidfilename, pid)
 
3247
3201
        del pidfilename
 
3249
3203
    for termsig in (signal.SIGHUP, signal.SIGTERM):
 
3250
3204
        GLib.unix_signal_add(GLib.PRIORITY_HIGH, termsig,
 
3251
3205
                             lambda: main_loop.quit() and False)
 
3255
3209
        @alternate_dbus_interfaces(
 
3256
 
            {"se.recompile.Mandos": "se.bsnet.fukt.Mandos"})
 
 
3210
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
 
3257
3211
        class MandosDBusService(DBusObjectWithObjectManager):
 
3258
3212
            """A D-Bus proxy object"""
 
3260
3214
            def __init__(self):
 
3261
3215
                dbus.service.Object.__init__(self, bus, "/")
 
3263
3217
            _interface = "se.recompile.Mandos"
 
3265
3219
            @dbus.service.signal(_interface, signature="o")
 
3266
3220
            def ClientAdded(self, objpath):
 
3270
3224
            @dbus.service.signal(_interface, signature="ss")
 
3271
3225
            def ClientNotFound(self, fingerprint, address):
 
3275
3229
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
 
3277
3231
            @dbus.service.signal(_interface, signature="os")
 
3278
3232
            def ClientRemoved(self, objpath, name):
 
3282
3236
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
 
3284
3238
            @dbus.service.method(_interface, out_signature="ao")
 
 
3312
3266
                        self.client_removed_signal(c)
 
3314
3268
                raise KeyError(object_path)
 
3318
3272
            @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
 
3319
 
                                 out_signature="a{oa{sa{sv}}}")
 
 
3273
                                 out_signature = "a{oa{sa{sv}}}")
 
3320
3274
            def GetManagedObjects(self):
 
3321
3275
                """D-Bus method"""
 
3322
3276
                return dbus.Dictionary(
 
3323
 
                    {client.dbus_object_path:
 
3325
 
                         {interface: client.GetAll(interface)
 
3327
 
                          client._get_all_interface_names()})
 
3328
 
                     for client in tcp_server.clients.values()})
 
 
3277
                    { client.dbus_object_path:
 
 
3279
                          { interface: client.GetAll(interface)
 
 
3281
                                 client._get_all_interface_names()})
 
 
3282
                      for client in tcp_server.clients.values()})
 
3330
3284
            def client_added_signal(self, client):
 
3331
3285
                """Send the new standard signal and the old signal"""
 
 
3378
3327
                client.encrypted_secret = pgp.encrypt(client.secret,
 
3380
3329
                client_dict = {}
 
3382
3331
                # A list of attributes that can not be pickled
 
3384
 
                exclude = {"bus", "changedstate", "secret",
 
3385
 
                           "checker", "server_settings"}
 
 
3333
                exclude = { "bus", "changedstate", "secret",
 
 
3334
                            "checker", "server_settings" }
 
3386
3335
                for name, typ in inspect.getmembers(dbus.service
 
3388
3337
                    exclude.add(name)
 
3390
3339
                client_dict["encrypted_secret"] = (client
 
3391
3340
                                                   .encrypted_secret)
 
3392
3341
                for attr in client.client_structure:
 
3393
3342
                    if attr not in exclude:
 
3394
3343
                        client_dict[attr] = getattr(client, attr)
 
3396
3345
                clients[client.name] = client_dict
 
3397
3346
                del client_settings[client.name]["secret"]
 
3400
3349
            with tempfile.NamedTemporaryFile(