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-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
 
 
14
# Copyright © 2008-2019 Teddy Hogeborn
 
 
15
# Copyright © 2008-2019 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
 
19
21
# the Free Software Foundation, either version 3 of the License, or
 
20
22
# (at your option) any later version.
 
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.
 
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/>.
 
31
32
# Contact the authors at <mandos@recompile.se>.
 
34
35
from __future__ import (division, absolute_import, print_function,
 
 
227
247
                                     '--passphrase-file',
 
229
249
                                    + self.gnupgargs,
 
230
 
                                    stdin = subprocess.PIPE,
 
231
 
                                    stdout = subprocess.PIPE,
 
232
 
                                    stderr = subprocess.PIPE)
 
233
 
            ciphertext, err = proc.communicate(input = data)
 
 
250
                                    stdin=subprocess.PIPE,
 
 
251
                                    stdout=subprocess.PIPE,
 
 
252
                                    stderr=subprocess.PIPE)
 
 
253
            ciphertext, err = proc.communicate(input=data)
 
234
254
        if proc.returncode != 0:
 
235
255
            raise PGPError(err)
 
236
256
        return ciphertext
 
238
258
    def decrypt(self, data, password):
 
239
259
        passphrase = self.password_encode(password)
 
240
260
        with tempfile.NamedTemporaryFile(
 
241
 
                dir = self.tempdir) as passfile:
 
 
261
                dir=self.tempdir) as passfile:
 
242
262
            passfile.write(passphrase)
 
244
264
            proc = subprocess.Popen([self.gpg, '--decrypt',
 
245
265
                                     '--passphrase-file',
 
247
267
                                    + self.gnupgargs,
 
248
 
                                    stdin = subprocess.PIPE,
 
249
 
                                    stdout = subprocess.PIPE,
 
250
 
                                    stderr = subprocess.PIPE)
 
251
 
            decrypted_plaintext, err = proc.communicate(input = data)
 
 
268
                                    stdin=subprocess.PIPE,
 
 
269
                                    stdout=subprocess.PIPE,
 
 
270
                                    stderr=subprocess.PIPE)
 
 
271
            decrypted_plaintext, err = proc.communicate(input=data)
 
252
272
        if proc.returncode != 0:
 
253
273
            raise PGPError(err)
 
254
274
        return decrypted_plaintext
 
256
277
# Pretend that we have an Avahi module
 
258
 
    """This isn't so much a class as it is a module-like namespace.
 
259
 
    It is instantiated once, and simulates having an Avahi module."""
 
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
 
 
279
    """This isn't so much a class as it is a module-like namespace."""
 
 
280
    IF_UNSPEC = -1               # avahi-common/address.h
 
 
281
    PROTO_UNSPEC = -1            # avahi-common/address.h
 
 
282
    PROTO_INET = 0               # avahi-common/address.h
 
 
283
    PROTO_INET6 = 1              # avahi-common/address.h
 
264
284
    DBUS_NAME = "org.freedesktop.Avahi"
 
265
285
    DBUS_INTERFACE_ENTRY_GROUP = DBUS_NAME + ".EntryGroup"
 
266
286
    DBUS_INTERFACE_SERVER = DBUS_NAME + ".Server"
 
267
287
    DBUS_PATH_SERVER = "/"
 
268
 
    def string_array_to_txt_array(self, t):
 
 
290
    def string_array_to_txt_array(t):
 
269
291
        return dbus.Array((dbus.ByteArray(s.encode("utf-8"))
 
270
292
                           for s in t), signature="ay")
 
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
 
 
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
 
281
303
class AvahiError(Exception):
 
282
304
    def __init__(self, value, *args, **kwargs):
 
 
473
495
class AvahiServiceToSyslog(AvahiService):
 
474
496
    def rename(self, *args, **kwargs):
 
475
497
        """Add the new name to the syslog messages"""
 
476
 
        ret = AvahiService.rename(self, *args, **kwargs)
 
 
498
        ret = super(AvahiServiceToSyslog, self).rename(*args, **kwargs)
 
477
499
        syslogger.setFormatter(logging.Formatter(
 
478
500
            'Mandos ({}) [%(process)d]: %(levelname)s: %(message)s'
 
479
501
            .format(self.name)))
 
482
505
# Pretend that we have a GnuTLS module
 
483
 
class GnuTLS(object):
 
484
 
    """This isn't so much a class as it is a module-like namespace.
 
485
 
    It is instantiated once, and simulates having a GnuTLS module."""
 
487
 
    _library = ctypes.cdll.LoadLibrary(
 
488
 
        ctypes.util.find_library("gnutls"))
 
489
 
    _need_version = b"3.3.0"
 
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))
 
 
506
class gnutls(object):
 
 
507
    """This isn't so much a class as it is a module-like namespace."""
 
 
509
    library = ctypes.util.find_library("gnutls")
 
 
511
        library = ctypes.util.find_library("gnutls-deb0")
 
 
512
    _library = ctypes.cdll.LoadLibrary(library)
 
498
515
    # Unless otherwise indicated, the constants and types below are
 
499
516
    # all from the gnutls/gnutls.h C header file.
 
503
520
    E_INTERRUPTED = -52
 
508
526
    CRD_CERTIFICATE = 1
 
509
527
    E_NO_CERTIFICATE_FOUND = -49
 
 
532
    KEYID_USE_SHA256 = 1        # gnutls/x509.h
 
510
533
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
 
513
536
    class session_int(ctypes.Structure):
 
515
538
    session_t = ctypes.POINTER(session_int)
 
516
540
    class certificate_credentials_st(ctypes.Structure):
 
518
542
    certificate_credentials_t = ctypes.POINTER(
 
519
543
        certificate_credentials_st)
 
520
544
    certificate_type_t = ctypes.c_int
 
521
546
    class datum_t(ctypes.Structure):
 
522
547
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
523
548
                    ('size', ctypes.c_uint)]
 
524
550
    class openpgp_crt_int(ctypes.Structure):
 
526
552
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
 
527
 
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
 
 
553
    openpgp_crt_fmt_t = ctypes.c_int  # gnutls/openpgp.h
 
528
554
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
 
529
555
    credentials_type_t = ctypes.c_int
 
530
556
    transport_ptr_t = ctypes.c_void_p
 
531
557
    close_request_t = ctypes.c_int
 
534
560
    class Error(Exception):
 
535
 
        # We need to use the class name "GnuTLS" here, since this
 
536
 
        # exception might be raised from within GnuTLS.__init__,
 
537
 
        # which is called before the assignment to the "gnutls"
 
538
 
        # global variable has happened.
 
539
 
        def __init__(self, message = None, code = None, args=()):
 
 
561
        def __init__(self, message=None, code=None, args=()):
 
540
562
            # Default usage is by a message string, but if a return
 
541
563
            # code is passed, convert it to a string with
 
542
564
            # gnutls.strerror()
 
544
566
            if message is None and code is not None:
 
545
 
                message = GnuTLS.strerror(code)
 
546
 
            return super(GnuTLS.Error, self).__init__(
 
 
567
                message = gnutls.strerror(code)
 
 
568
            return super(gnutls.Error, self).__init__(
 
549
571
    class CertificateSecurityError(Error):
 
553
575
    class Credentials(object):
 
554
576
        def __init__(self):
 
 
611
639
                return _error_code(result)
 
612
640
            result = func(*arguments)
 
615
643
    # Unless otherwise indicated, the function declarations below are
 
616
644
    # all from the gnutls/gnutls.h C header file.
 
619
647
    priority_set_direct = _library.gnutls_priority_set_direct
 
620
648
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
 
621
649
                                    ctypes.POINTER(ctypes.c_char_p)]
 
622
650
    priority_set_direct.restype = _error_code
 
624
652
    init = _library.gnutls_init
 
625
653
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
 
626
654
    init.restype = _error_code
 
628
656
    set_default_priority = _library.gnutls_set_default_priority
 
629
657
    set_default_priority.argtypes = [session_t]
 
630
658
    set_default_priority.restype = _error_code
 
632
660
    record_send = _library.gnutls_record_send
 
633
661
    record_send.argtypes = [session_t, ctypes.c_void_p,
 
635
663
    record_send.restype = ctypes.c_ssize_t
 
636
664
    record_send.errcheck = _retry_on_error
 
638
666
    certificate_allocate_credentials = (
 
639
667
        _library.gnutls_certificate_allocate_credentials)
 
640
668
    certificate_allocate_credentials.argtypes = [
 
641
669
        ctypes.POINTER(certificate_credentials_t)]
 
642
670
    certificate_allocate_credentials.restype = _error_code
 
644
672
    certificate_free_credentials = (
 
645
673
        _library.gnutls_certificate_free_credentials)
 
646
 
    certificate_free_credentials.argtypes = [certificate_credentials_t]
 
 
674
    certificate_free_credentials.argtypes = [
 
 
675
        certificate_credentials_t]
 
647
676
    certificate_free_credentials.restype = None
 
649
678
    handshake_set_private_extensions = (
 
650
679
        _library.gnutls_handshake_set_private_extensions)
 
651
680
    handshake_set_private_extensions.argtypes = [session_t,
 
653
682
    handshake_set_private_extensions.restype = None
 
655
684
    credentials_set = _library.gnutls_credentials_set
 
656
685
    credentials_set.argtypes = [session_t, credentials_type_t,
 
658
687
    credentials_set.restype = _error_code
 
660
689
    strerror = _library.gnutls_strerror
 
661
690
    strerror.argtypes = [ctypes.c_int]
 
662
691
    strerror.restype = ctypes.c_char_p
 
664
693
    certificate_type_get = _library.gnutls_certificate_type_get
 
665
694
    certificate_type_get.argtypes = [session_t]
 
666
695
    certificate_type_get.restype = _error_code
 
668
697
    certificate_get_peers = _library.gnutls_certificate_get_peers
 
669
698
    certificate_get_peers.argtypes = [session_t,
 
670
699
                                      ctypes.POINTER(ctypes.c_uint)]
 
671
700
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
 
673
702
    global_set_log_level = _library.gnutls_global_set_log_level
 
674
703
    global_set_log_level.argtypes = [ctypes.c_int]
 
675
704
    global_set_log_level.restype = None
 
677
706
    global_set_log_function = _library.gnutls_global_set_log_function
 
678
707
    global_set_log_function.argtypes = [log_func]
 
679
708
    global_set_log_function.restype = None
 
681
710
    deinit = _library.gnutls_deinit
 
682
711
    deinit.argtypes = [session_t]
 
683
712
    deinit.restype = None
 
685
714
    handshake = _library.gnutls_handshake
 
686
715
    handshake.argtypes = [session_t]
 
687
716
    handshake.restype = _error_code
 
688
717
    handshake.errcheck = _retry_on_error
 
690
719
    transport_set_ptr = _library.gnutls_transport_set_ptr
 
691
720
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
 
692
721
    transport_set_ptr.restype = None
 
694
723
    bye = _library.gnutls_bye
 
695
724
    bye.argtypes = [session_t, close_request_t]
 
696
725
    bye.restype = _error_code
 
697
726
    bye.errcheck = _retry_on_error
 
699
728
    check_version = _library.gnutls_check_version
 
700
729
    check_version.argtypes = [ctypes.c_char_p]
 
701
730
    check_version.restype = ctypes.c_char_p
 
703
 
    # All the function declarations below are from gnutls/openpgp.h
 
705
 
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
706
 
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
707
 
    openpgp_crt_init.restype = _error_code
 
709
 
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
710
 
    openpgp_crt_import.argtypes = [openpgp_crt_t,
 
711
 
                                   ctypes.POINTER(datum_t),
 
713
 
    openpgp_crt_import.restype = _error_code
 
715
 
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
716
 
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
717
 
                                        ctypes.POINTER(ctypes.c_uint)]
 
718
 
    openpgp_crt_verify_self.restype = _error_code
 
720
 
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
721
 
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
722
 
    openpgp_crt_deinit.restype = None
 
724
 
    openpgp_crt_get_fingerprint = (
 
725
 
        _library.gnutls_openpgp_crt_get_fingerprint)
 
726
 
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
730
 
    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))
 
 
737
    _tls_rawpk_version = b"3.6.6"
 
 
738
    has_rawpk = bool(check_version(_tls_rawpk_version))
 
 
742
        class pubkey_st(ctypes.Structure):
 
 
744
        pubkey_t = ctypes.POINTER(pubkey_st)
 
 
746
        x509_crt_fmt_t = ctypes.c_int
 
 
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
 
 
753
        pubkey_import = _library.gnutls_pubkey_import
 
 
754
        pubkey_import.argtypes = [pubkey_t, ctypes.POINTER(datum_t),
 
 
756
        pubkey_import.restype = _error_code
 
 
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
 
 
764
        pubkey_deinit = _library.gnutls_pubkey_deinit
 
 
765
        pubkey_deinit.argtypes = [pubkey_t]
 
 
766
        pubkey_deinit.restype = None
 
 
768
        # All the function declarations below are from gnutls/openpgp.h
 
 
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
 
 
774
        openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
 
775
        openpgp_crt_import.argtypes = [openpgp_crt_t,
 
 
776
                                       ctypes.POINTER(datum_t),
 
 
778
        openpgp_crt_import.restype = _error_code
 
 
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
 
 
785
        openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
 
786
        openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
 
787
        openpgp_crt_deinit.restype = None
 
 
789
        openpgp_crt_get_fingerprint = (
 
 
790
            _library.gnutls_openpgp_crt_get_fingerprint)
 
 
791
        openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
 
795
        openpgp_crt_get_fingerprint.restype = _error_code
 
 
797
    if check_version("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
 
732
802
    # Remove non-public functions
 
733
803
    del _error_code, _retry_on_error
 
734
 
# Create the global "gnutls" object, simulating a module
 
737
806
def call_pipe(connection,       # : multiprocessing.Connection
 
738
807
              func, *args, **kwargs):
 
739
808
    """This function is meant to be called by multiprocessing.Process
 
741
810
    This function runs func(*args, **kwargs), and writes the resulting
 
742
811
    return value on the provided multiprocessing.Connection.
 
744
813
    connection.send(func(*args, **kwargs))
 
745
814
    connection.close()
 
747
817
class Client(object):
 
748
818
    """A representation of a client host served by this server.
 
751
821
    approved:   bool(); 'None' if not yet approved/disapproved
 
752
822
    approval_delay: datetime.timedelta(); Time to wait for approval
 
 
1428
1503
                         exc_info=error)
 
1429
1504
        return xmlstring
 
1432
1508
    dbus.OBJECT_MANAGER_IFACE
 
1433
1509
except AttributeError:
 
1434
1510
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
 
1436
1513
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
 
1437
1514
    """A D-Bus object with an ObjectManager.
 
1439
1516
    Classes inheriting from this exposes the standard
 
1440
1517
    GetManagedObjects call and the InterfacesAdded and
 
1441
1518
    InterfacesRemoved signals on the standard
 
1442
1519
    "org.freedesktop.DBus.ObjectManager" interface.
 
1444
1521
    Note: No signals are sent automatically; they must be sent
 
1447
1524
    @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
 
1448
 
                         out_signature = "a{oa{sa{sv}}}")
 
 
1525
                         out_signature="a{oa{sa{sv}}}")
 
1449
1526
    def GetManagedObjects(self):
 
1450
1527
        """This function must be overridden"""
 
1451
1528
        raise NotImplementedError()
 
1453
1530
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
 
1454
 
                         signature = "oa{sa{sv}}")
 
 
1531
                         signature="oa{sa{sv}}")
 
1455
1532
    def InterfacesAdded(self, object_path, interfaces_and_properties):
 
1458
 
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
 
 
1535
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature="oas")
 
1459
1536
    def InterfacesRemoved(self, object_path, interfaces):
 
1462
1539
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
1463
 
                         out_signature = "s",
 
1464
 
                         path_keyword = 'object_path',
 
1465
 
                         connection_keyword = 'connection')
 
 
1541
                         path_keyword='object_path',
 
 
1542
                         connection_keyword='connection')
 
1466
1543
    def Introspect(self, object_path, connection):
 
1467
1544
        """Overloading of standard D-Bus method.
 
1469
1546
        Override return argument name of GetManagedObjects to be
 
1470
1547
        "objpath_interfaces_and_properties"
 
 
1762
1842
                                               "ApprovedByDefault")
 
1763
1843
    approval_delay = notifychangeproperty(
 
1764
1844
        dbus.UInt64, "ApprovalDelay",
 
1765
 
        type_func = lambda td: td.total_seconds() * 1000)
 
 
1845
        type_func=lambda td: td.total_seconds() * 1000)
 
1766
1846
    approval_duration = notifychangeproperty(
 
1767
1847
        dbus.UInt64, "ApprovalDuration",
 
1768
 
        type_func = lambda td: td.total_seconds() * 1000)
 
 
1848
        type_func=lambda td: td.total_seconds() * 1000)
 
1769
1849
    host = notifychangeproperty(dbus.String, "Host")
 
1770
1850
    timeout = notifychangeproperty(
 
1771
1851
        dbus.UInt64, "Timeout",
 
1772
 
        type_func = lambda td: td.total_seconds() * 1000)
 
 
1852
        type_func=lambda td: td.total_seconds() * 1000)
 
1773
1853
    extended_timeout = notifychangeproperty(
 
1774
1854
        dbus.UInt64, "ExtendedTimeout",
 
1775
 
        type_func = lambda td: td.total_seconds() * 1000)
 
 
1855
        type_func=lambda td: td.total_seconds() * 1000)
 
1776
1856
    interval = notifychangeproperty(
 
1777
1857
        dbus.UInt64, "Interval",
 
1778
 
        type_func = lambda td: td.total_seconds() * 1000)
 
 
1858
        type_func=lambda td: td.total_seconds() * 1000)
 
1779
1859
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
1780
1860
    secret = notifychangeproperty(dbus.ByteArray, "Secret",
 
1781
1861
                                  invalidate_only=True)
 
1783
1863
    del notifychangeproperty
 
1785
1865
    def __del__(self, *args, **kwargs):
 
1787
1867
            self.remove_from_connection()
 
 
2152
2239
class ClientHandler(socketserver.BaseRequestHandler, object):
 
2153
2240
    """A class to handle client connections.
 
2155
2242
    Instantiated once for each connection to handle it.
 
2156
2243
    Note: This will run in its own forked process."""
 
2158
2245
    def handle(self):
 
2159
2246
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
2160
2247
            logger.info("TCP connection from: %s",
 
2161
2248
                        str(self.client_address))
 
2162
2249
            logger.debug("Pipe FD: %d",
 
2163
2250
                         self.server.child_pipe.fileno())
 
2165
2252
            session = gnutls.ClientSession(self.request)
 
2167
 
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
2168
 
            #                      "+AES-256-CBC", "+SHA1",
 
2169
 
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
 
 
2254
            # priority = ':'.join(("NONE", "+VERS-TLS1.1",
 
 
2255
            #                       "+AES-256-CBC", "+SHA1",
 
 
2256
            #                       "+COMP-NULL", "+CTYPE-OPENPGP",
 
2171
2258
            # Use a fallback default, since this MUST be set.
 
2172
2259
            priority = self.server.gnutls_priority
 
2173
2260
            if priority is None:
 
2174
2261
                priority = "NORMAL"
 
2175
 
            gnutls.priority_set_direct(session._c_object, priority,
 
 
2262
            gnutls.priority_set_direct(session._c_object,
 
 
2263
                                       priority.encode("utf-8"),
 
2178
2266
            # Start communication using the Mandos protocol
 
2179
2267
            # Get protocol number
 
2180
2268
            line = self.request.makefile().readline()
 
 
2306
2416
        cert = cert_list[0]
 
2307
2417
        return ctypes.string_at(cert.data, cert.size)
 
 
2420
    def key_id(certificate):
 
 
2421
        "Convert a certificate bytestring to a hexdigit key ID"
 
 
2422
        # New GnuTLS "datum" with the public key
 
 
2423
        datum = gnutls.datum_t(
 
 
2424
            ctypes.cast(ctypes.c_char_p(certificate),
 
 
2425
                        ctypes.POINTER(ctypes.c_ubyte)),
 
 
2426
            ctypes.c_uint(len(certificate)))
 
 
2427
        # XXX all these need to be created in the gnutls "module"
 
 
2428
        # New empty GnuTLS certificate
 
 
2429
        pubkey = gnutls.pubkey_t()
 
 
2430
        gnutls.pubkey_init(ctypes.byref(pubkey))
 
 
2431
        # Import the raw public key into the certificate
 
 
2432
        gnutls.pubkey_import(pubkey,
 
 
2433
                             ctypes.byref(datum),
 
 
2434
                             gnutls.X509_FMT_DER)
 
 
2435
        # New buffer for the key ID
 
 
2436
        buf = ctypes.create_string_buffer(32)
 
 
2437
        buf_len = ctypes.c_size_t(len(buf))
 
 
2438
        # Get the key ID from the raw public key into the buffer
 
 
2439
        gnutls.pubkey_get_key_id(pubkey,
 
 
2440
                                 gnutls.KEYID_USE_SHA256,
 
 
2441
                                 ctypes.cast(ctypes.byref(buf),
 
 
2442
                                             ctypes.POINTER(ctypes.c_ubyte)),
 
 
2443
                                 ctypes.byref(buf_len))
 
 
2444
        # Deinit the certificate
 
 
2445
        gnutls.pubkey_deinit(pubkey)
 
 
2447
        # Convert the buffer to a Python bytestring
 
 
2448
        key_id = ctypes.string_at(buf, buf_len.value)
 
 
2449
        # Convert the bytestring to hexadecimal notation
 
 
2450
        hex_key_id = binascii.hexlify(key_id).upper()
 
2310
2454
    def fingerprint(openpgp):
 
2311
2455
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
 
 
2345
2490
class MultiprocessingMixIn(object):
 
2346
2491
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
2348
2493
    def sub_process_main(self, request, address):
 
2350
2495
            self.finish_request(request, address)
 
2351
2496
        except Exception:
 
2352
2497
            self.handle_error(request, address)
 
2353
2498
        self.close_request(request)
 
2355
2500
    def process_request(self, request, address):
 
2356
2501
        """Start a new process to process the request."""
 
2357
 
        proc = multiprocessing.Process(target = self.sub_process_main,
 
2358
 
                                       args = (request, address))
 
 
2502
        proc = multiprocessing.Process(target=self.sub_process_main,
 
 
2503
                                       args=(request, address))
 
2363
2508
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
2364
2509
    """ adds a pipe to the MixIn """
 
2366
2511
    def process_request(self, request, client_address):
 
2367
2512
        """Overrides and wraps the original process_request().
 
2369
2514
        This function creates a new pipe in self.pipe
 
2371
2516
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
 
2373
2518
        proc = MultiprocessingMixIn.process_request(self, request,
 
2374
2519
                                                    client_address)
 
2375
2520
        self.child_pipe.close()
 
2376
2521
        self.add_pipe(parent_pipe, proc)
 
2378
2523
    def add_pipe(self, parent_pipe, proc):
 
2379
2524
        """Dummy function; override as necessary"""
 
2380
2525
        raise NotImplementedError()
 
 
2428
2574
        # socket_wrapper(), if socketfd was set.
 
2429
2575
        socketserver.TCPServer.__init__(self, server_address,
 
2430
2576
                                        RequestHandlerClass)
 
2432
2578
    def server_bind(self):
 
2433
2579
        """This overrides the normal server_bind() function
 
2434
2580
        to bind to an interface if one was specified, and also NOT to
 
2435
2581
        bind to an address or port if they were not specified."""
 
 
2582
        global SO_BINDTODEVICE
 
2436
2583
        if self.interface is not None:
 
2437
2584
            if SO_BINDTODEVICE is None:
 
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)
 
 
2585
                # Fall back to a hard-coded value which seems to be
 
 
2587
                logger.warning("SO_BINDTODEVICE not found, trying 25")
 
 
2588
                SO_BINDTODEVICE = 25
 
 
2590
                self.socket.setsockopt(
 
 
2591
                    socket.SOL_SOCKET, SO_BINDTODEVICE,
 
 
2592
                    (self.interface + "\0").encode("utf-8"))
 
 
2593
            except socket.error as error:
 
 
2594
                if error.errno == errno.EPERM:
 
 
2595
                    logger.error("No permission to bind to"
 
 
2596
                                 " interface %s", self.interface)
 
 
2597
                elif error.errno == errno.ENOPROTOOPT:
 
 
2598
                    logger.error("SO_BINDTODEVICE not available;"
 
 
2599
                                 " cannot bind to interface %s",
 
 
2601
                elif error.errno == errno.ENODEV:
 
 
2602
                    logger.error("Interface %s does not exist,"
 
 
2603
                                 " cannot bind", self.interface)
 
2459
2606
        # Only bind(2) the socket if we really need to.
 
2460
2607
        if self.server_address[0] or self.server_address[1]:
 
2461
2608
            if not self.server_address[0]:
 
2462
2609
                if self.address_family == socket.AF_INET6:
 
2463
 
                    any_address = "::" # in6addr_any
 
 
2610
                    any_address = "::"  # in6addr_any
 
2465
 
                    any_address = "0.0.0.0" # INADDR_ANY
 
 
2612
                    any_address = "0.0.0.0"  # INADDR_ANY
 
2466
2613
                self.server_address = (any_address,
 
2467
2614
                                       self.server_address[1])
 
2468
2615
            elif not self.server_address[1]:
 
 
2502
2649
        self.gnutls_priority = gnutls_priority
 
2503
2650
        IPv6_TCPServer.__init__(self, server_address,
 
2504
2651
                                RequestHandlerClass,
 
2505
 
                                interface = interface,
 
2506
 
                                use_ipv6 = use_ipv6,
 
2507
 
                                socketfd = socketfd)
 
 
2652
                                interface=interface,
 
2509
2656
    def server_activate(self):
 
2510
2657
        if self.enabled:
 
2511
2658
            return socketserver.TCPServer.server_activate(self)
 
2513
2660
    def enable(self):
 
2514
2661
        self.enabled = True
 
2516
2663
    def add_pipe(self, parent_pipe, proc):
 
2517
2664
        # Call "handle_ipc" for both data and EOF events
 
2518
2665
        GLib.io_add_watch(
 
2519
2666
            parent_pipe.fileno(),
 
2520
2667
            GLib.IO_IN | GLib.IO_HUP,
 
2521
2668
            functools.partial(self.handle_ipc,
 
2522
 
                              parent_pipe = parent_pipe,
 
 
2669
                              parent_pipe=parent_pipe,
 
2525
2672
    def handle_ipc(self, source, condition,
 
2526
2673
                   parent_pipe=None,
 
2528
2675
                   client_object=None):
 
2529
2676
        # error, or the other end of multiprocessing.Pipe has closed
 
2530
2677
        if condition & (GLib.IO_ERR | GLib.IO_HUP):
 
2531
2678
            # Wait for other process to exit
 
2535
2682
        # Read a request from the child
 
2536
2683
        request = parent_pipe.recv()
 
2537
2684
        command = request[0]
 
2539
2686
        if command == 'init':
 
2541
 
            address = request[2]
 
 
2687
            key_id = request[1].decode("ascii")
 
 
2688
            fpr = request[2].decode("ascii")
 
 
2689
            address = request[3]
 
2543
2691
            for c in self.clients.values():
 
2544
 
                if c.fingerprint == fpr:
 
 
2692
                if key_id == "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855":
 
 
2694
                if key_id and c.key_id == key_id:
 
 
2697
                if fpr and c.fingerprint == fpr:
 
2548
 
                logger.info("Client not found for fingerprint: %s, ad"
 
2549
 
                            "dress: %s", fpr, address)
 
 
2701
                logger.info("Client not found for key ID: %s, address"
 
 
2702
                            ": %s", key_id or fpr, address)
 
2550
2703
                if self.use_dbus:
 
2551
2704
                    # Emit D-Bus signal
 
2552
 
                    mandos_dbus_service.ClientNotFound(fpr,
 
 
2705
                    mandos_dbus_service.ClientNotFound(key_id or fpr,
 
2554
2707
                parent_pipe.send(False)
 
2557
2710
            GLib.io_add_watch(
 
2558
2711
                parent_pipe.fileno(),
 
2559
2712
                GLib.IO_IN | GLib.IO_HUP,
 
2560
2713
                functools.partial(self.handle_ipc,
 
2561
 
                                  parent_pipe = parent_pipe,
 
2563
 
                                  client_object = client))
 
 
2714
                                  parent_pipe=parent_pipe,
 
 
2716
                                  client_object=client))
 
2564
2717
            parent_pipe.send(True)
 
2565
2718
            # remove the old hook in favor of the new above hook on
 
 
3019
3181
        protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
 
3020
3182
        service = AvahiServiceToSyslog(
 
3021
 
            name = server_settings["servicename"],
 
3022
 
            servicetype = "_mandos._tcp",
 
3023
 
            protocol = protocol,
 
 
3183
            name=server_settings["servicename"],
 
 
3184
            servicetype="_mandos._tcp",
 
3025
3187
        if server_settings["interface"]:
 
3026
3188
            service.interface = if_nametoindex(
 
3027
3189
                server_settings["interface"].encode("utf-8"))
 
3029
3191
    global multiprocessing_manager
 
3030
3192
    multiprocessing_manager = multiprocessing.Manager()
 
3032
3194
    client_class = Client
 
3034
 
        client_class = functools.partial(ClientDBus, bus = bus)
 
 
3196
        client_class = functools.partial(ClientDBus, bus=bus)
 
3036
3198
    client_settings = Client.config_parser(client_config)
 
3037
3199
    old_client_settings = {}
 
3038
3200
    clients_data = {}
 
3040
3202
    # This is used to redirect stdout and stderr for checker processes
 
3042
 
    wnull = open(os.devnull, "w") # A writable /dev/null
 
 
3204
    wnull = open(os.devnull, "w")  # A writable /dev/null
 
3043
3205
    # Only used if server is running in foreground but not in debug
 
3045
3207
    if debug or not foreground:
 
3048
3210
    # Get client data and settings from last running state.
 
3049
3211
    if server_settings["restore"]:
 
3051
3213
            with open(stored_state_path, "rb") as stored_state:
 
3052
 
                if sys.version_info.major == 2:                
 
 
3214
                if sys.version_info.major == 2:
 
3053
3215
                    clients_data, old_client_settings = pickle.load(
 
3056
3218
                    bytes_clients_data, bytes_old_client_settings = (
 
3057
 
                        pickle.load(stored_state, encoding = "bytes"))
 
3058
 
                    ### Fix bytes to strings
 
 
3219
                        pickle.load(stored_state, encoding="bytes"))
 
 
3220
                    #   Fix bytes to strings
 
3061
 
                    clients_data = { (key.decode("utf-8")
 
3062
 
                                      if isinstance(key, bytes)
 
3065
 
                                     bytes_clients_data.items() }
 
 
3223
                    clients_data = {(key.decode("utf-8")
 
 
3224
                                     if isinstance(key, bytes)
 
 
3227
                                    bytes_clients_data.items()}
 
3066
3228
                    del bytes_clients_data
 
3067
3229
                    for key in clients_data:
 
3068
 
                        value = { (k.decode("utf-8")
 
3069
 
                                   if isinstance(k, bytes) else k): v
 
3071
 
                                  clients_data[key].items() }
 
 
3230
                        value = {(k.decode("utf-8")
 
 
3231
                                  if isinstance(k, bytes) else k): v
 
 
3233
                                 clients_data[key].items()}
 
3072
3234
                        clients_data[key] = value
 
3073
3235
                        # .client_structure
 
3074
3236
                        value["client_structure"] = [
 
3075
3237
                            (s.decode("utf-8")
 
3076
3238
                             if isinstance(s, bytes)
 
3077
3239
                             else s) for s in
 
3078
 
                            value["client_structure"] ]
 
 
3240
                            value["client_structure"]]
 
3079
3241
                        # .name & .host
 
3080
3242
                        for k in ("name", "host"):
 
3081
3243
                            if isinstance(value[k], bytes):
 
3082
3244
                                value[k] = value[k].decode("utf-8")
 
3083
 
                    ## old_client_settings
 
 
3245
                        if not value.has_key("key_id"):
 
 
3246
                            value["key_id"] = ""
 
 
3247
                        elif not value.has_key("fingerprint"):
 
 
3248
                            value["fingerprint"] = ""
 
 
3249
                    #  old_client_settings
 
3085
3251
                    old_client_settings = {
 
3086
3252
                        (key.decode("utf-8")
 
3087
3253
                         if isinstance(key, bytes)
 
3088
3254
                         else key): value
 
3089
3255
                        for key, value in
 
3090
 
                        bytes_old_client_settings.items() }
 
 
3256
                        bytes_old_client_settings.items()}
 
3091
3257
                    del bytes_old_client_settings
 
3093
3259
                    for value in old_client_settings.values():