/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2016-02-28 12:29:12 UTC
  • Revision ID: teddy@recompile.se-20160228122912-6yxw70vq8oze7ogc
Remove version specific packages from Debian dependencies.

* debian/control (Source: mandos/Build-Depends-Indep): Remove
  "python2.7", "python2.7-dbus", "python2.7-avahi", and
  "python2.7-gobject"; replace with "python (= 2.7)", "python-dbus",
  "python-avahi", "python-gobject".

Thanks: Scott Kitterman <debian@kitterman.com>
Closes: #811159

Show diffs side-by-side

added added

removed removed

Lines of Context:
44
44
import argparse
45
45
import datetime
46
46
import errno
47
 
import gnutls.crypto
48
 
import gnutls.connection
49
 
import gnutls.errors
50
 
import gnutls.library.functions
51
 
import gnutls.library.constants
52
 
import gnutls.library.types
53
47
try:
54
48
    import ConfigParser as configparser
55
49
except ImportError:
104
98
if sys.version_info.major == 2:
105
99
    str = unicode
106
100
 
107
 
version = "1.6.9"
 
101
version = "1.7.1"
108
102
stored_state_file = "clients.pickle"
109
103
 
110
104
logger = logging.getLogger()
435
429
            .format(self.name)))
436
430
        return ret
437
431
 
 
432
# Pretend that we have a GnuTLS module
 
433
class GnuTLS(object):
 
434
    """This isn't so much a class as it is a module-like namespace.
 
435
    It is instantiated once, and simulates having a GnuTLS module."""
 
436
    
 
437
    _library = ctypes.cdll.LoadLibrary(
 
438
        ctypes.util.find_library("gnutls"))
 
439
    _need_version = "3.3.0"
 
440
    def __init__(self):
 
441
        # Need to use class name "GnuTLS" here, since this method is
 
442
        # called before the assignment to the "gnutls" global variable
 
443
        # happens.
 
444
        if GnuTLS.check_version(self._need_version) is None:
 
445
            raise GnuTLS.Error("Needs GnuTLS {} or later"
 
446
                               .format(self._need_version))
 
447
    
 
448
    # Unless otherwise indicated, the constants and types below are
 
449
    # all from the gnutls/gnutls.h C header file.
 
450
    
 
451
    # Constants
 
452
    E_SUCCESS = 0
 
453
    E_INTERRUPTED = -52
 
454
    E_AGAIN = -28
 
455
    CRT_OPENPGP = 2
 
456
    CLIENT = 2
 
457
    SHUT_RDWR = 0
 
458
    CRD_CERTIFICATE = 1
 
459
    E_NO_CERTIFICATE_FOUND = -49
 
460
    OPENPGP_FMT_RAW = 0         # gnutls/openpgp.h
 
461
    
 
462
    # Types
 
463
    class session_int(ctypes.Structure):
 
464
        _fields_ = []
 
465
    session_t = ctypes.POINTER(session_int)
 
466
    class certificate_credentials_st(ctypes.Structure):
 
467
        _fields_ = []
 
468
    certificate_credentials_t = ctypes.POINTER(
 
469
        certificate_credentials_st)
 
470
    certificate_type_t = ctypes.c_int
 
471
    class datum_t(ctypes.Structure):
 
472
        _fields_ = [('data', ctypes.POINTER(ctypes.c_ubyte)),
 
473
                    ('size', ctypes.c_uint)]
 
474
    class openpgp_crt_int(ctypes.Structure):
 
475
        _fields_ = []
 
476
    openpgp_crt_t = ctypes.POINTER(openpgp_crt_int)
 
477
    openpgp_crt_fmt_t = ctypes.c_int # gnutls/openpgp.h
 
478
    log_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_char_p)
 
479
    credentials_type_t = ctypes.c_int # 
 
480
    transport_ptr_t = ctypes.c_void_p
 
481
    close_request_t = ctypes.c_int
 
482
    
 
483
    # Exceptions
 
484
    class Error(Exception):
 
485
        # We need to use the class name "GnuTLS" here, since this
 
486
        # exception might be raised from within GnuTLS.__init__,
 
487
        # which is called before the assignment to the "gnutls"
 
488
        # global variable has happened.
 
489
        def __init__(self, message = None, code = None, args=()):
 
490
            # Default usage is by a message string, but if a return
 
491
            # code is passed, convert it to a string with
 
492
            # gnutls.strerror()
 
493
            self.code = code
 
494
            if message is None and code is not None:
 
495
                message = GnuTLS.strerror(code)
 
496
            return super(GnuTLS.Error, self).__init__(
 
497
                message, *args)
 
498
    
 
499
    class CertificateSecurityError(Error):
 
500
        pass
 
501
    
 
502
    # Classes
 
503
    class Credentials(object):
 
504
        def __init__(self):
 
505
            self._c_object = gnutls.certificate_credentials_t()
 
506
            gnutls.certificate_allocate_credentials(
 
507
                ctypes.byref(self._c_object))
 
508
            self.type = gnutls.CRD_CERTIFICATE
 
509
        
 
510
        def __del__(self):
 
511
            gnutls.certificate_free_credentials(self._c_object)
 
512
    
 
513
    class ClientSession(object):
 
514
        def __init__(self, socket, credentials = None):
 
515
            self._c_object = gnutls.session_t()
 
516
            gnutls.init(ctypes.byref(self._c_object), gnutls.CLIENT)
 
517
            gnutls.set_default_priority(self._c_object)
 
518
            gnutls.transport_set_ptr(self._c_object, socket.fileno())
 
519
            gnutls.handshake_set_private_extensions(self._c_object,
 
520
                                                    True)
 
521
            self.socket = socket
 
522
            if credentials is None:
 
523
                credentials = gnutls.Credentials()
 
524
            gnutls.credentials_set(self._c_object, credentials.type,
 
525
                                   ctypes.cast(credentials._c_object,
 
526
                                               ctypes.c_void_p))
 
527
            self.credentials = credentials
 
528
        
 
529
        def __del__(self):
 
530
            gnutls.deinit(self._c_object)
 
531
        
 
532
        def handshake(self):
 
533
            return gnutls.handshake(self._c_object)
 
534
        
 
535
        def send(self, data):
 
536
            data = bytes(data)
 
537
            data_len = len(data)
 
538
            while data_len > 0:
 
539
                data_len -= gnutls.record_send(self._c_object,
 
540
                                               data[-data_len:],
 
541
                                               data_len)
 
542
        
 
543
        def bye(self):
 
544
            return gnutls.bye(self._c_object, gnutls.SHUT_RDWR)
 
545
    
 
546
    # Error handling functions
 
547
    def _error_code(result):
 
548
        """A function to raise exceptions on errors, suitable
 
549
        for the 'restype' attribute on ctypes functions"""
 
550
        if result >= 0:
 
551
            return result
 
552
        if result == gnutls.E_NO_CERTIFICATE_FOUND:
 
553
            raise gnutls.CertificateSecurityError(code = result)
 
554
        raise gnutls.Error(code = result)
 
555
    
 
556
    def _retry_on_error(result, func, arguments):
 
557
        """A function to retry on some errors, suitable
 
558
        for the 'errcheck' attribute on ctypes functions"""
 
559
        while result < 0:
 
560
            if result not in (gnutls.E_INTERRUPTED, gnutls.E_AGAIN):
 
561
                return _error_code(result)
 
562
            result = func(*arguments)
 
563
        return result
 
564
    
 
565
    # Unless otherwise indicated, the function declarations below are
 
566
    # all from the gnutls/gnutls.h C header file.
 
567
    
 
568
    # Functions
 
569
    priority_set_direct = _library.gnutls_priority_set_direct
 
570
    priority_set_direct.argtypes = [session_t, ctypes.c_char_p,
 
571
                                    ctypes.POINTER(ctypes.c_char_p)]
 
572
    priority_set_direct.restype = _error_code
 
573
    
 
574
    init = _library.gnutls_init
 
575
    init.argtypes = [ctypes.POINTER(session_t), ctypes.c_int]
 
576
    init.restype = _error_code
 
577
    
 
578
    set_default_priority = _library.gnutls_set_default_priority
 
579
    set_default_priority.argtypes = [session_t]
 
580
    set_default_priority.restype = _error_code
 
581
    
 
582
    record_send = _library.gnutls_record_send
 
583
    record_send.argtypes = [session_t, ctypes.c_void_p,
 
584
                            ctypes.c_size_t]
 
585
    record_send.restype = ctypes.c_ssize_t
 
586
    record_send.errcheck = _retry_on_error
 
587
    
 
588
    certificate_allocate_credentials = (
 
589
        _library.gnutls_certificate_allocate_credentials)
 
590
    certificate_allocate_credentials.argtypes = [
 
591
        ctypes.POINTER(certificate_credentials_t)]
 
592
    certificate_allocate_credentials.restype = _error_code
 
593
    
 
594
    certificate_free_credentials = (
 
595
        _library.gnutls_certificate_free_credentials)
 
596
    certificate_free_credentials.argtypes = [certificate_credentials_t]
 
597
    certificate_free_credentials.restype = None
 
598
    
 
599
    handshake_set_private_extensions = (
 
600
        _library.gnutls_handshake_set_private_extensions)
 
601
    handshake_set_private_extensions.argtypes = [session_t,
 
602
                                                 ctypes.c_int]
 
603
    handshake_set_private_extensions.restype = None
 
604
    
 
605
    credentials_set = _library.gnutls_credentials_set
 
606
    credentials_set.argtypes = [session_t, credentials_type_t,
 
607
                                ctypes.c_void_p]
 
608
    credentials_set.restype = _error_code
 
609
    
 
610
    strerror = _library.gnutls_strerror
 
611
    strerror.argtypes = [ctypes.c_int]
 
612
    strerror.restype = ctypes.c_char_p
 
613
    
 
614
    certificate_type_get = _library.gnutls_certificate_type_get
 
615
    certificate_type_get.argtypes = [session_t]
 
616
    certificate_type_get.restype = _error_code
 
617
    
 
618
    certificate_get_peers = _library.gnutls_certificate_get_peers
 
619
    certificate_get_peers.argtypes = [session_t,
 
620
                                      ctypes.POINTER(ctypes.c_uint)]
 
621
    certificate_get_peers.restype = ctypes.POINTER(datum_t)
 
622
    
 
623
    global_set_log_level = _library.gnutls_global_set_log_level
 
624
    global_set_log_level.argtypes = [ctypes.c_int]
 
625
    global_set_log_level.restype = None
 
626
    
 
627
    global_set_log_function = _library.gnutls_global_set_log_function
 
628
    global_set_log_function.argtypes = [log_func]
 
629
    global_set_log_function.restype = None
 
630
    
 
631
    deinit = _library.gnutls_deinit
 
632
    deinit.argtypes = [session_t]
 
633
    deinit.restype = None
 
634
    
 
635
    handshake = _library.gnutls_handshake
 
636
    handshake.argtypes = [session_t]
 
637
    handshake.restype = _error_code
 
638
    handshake.errcheck = _retry_on_error
 
639
    
 
640
    transport_set_ptr = _library.gnutls_transport_set_ptr
 
641
    transport_set_ptr.argtypes = [session_t, transport_ptr_t]
 
642
    transport_set_ptr.restype = None
 
643
    
 
644
    bye = _library.gnutls_bye
 
645
    bye.argtypes = [session_t, close_request_t]
 
646
    bye.restype = _error_code
 
647
    bye.errcheck = _retry_on_error
 
648
    
 
649
    check_version = _library.gnutls_check_version
 
650
    check_version.argtypes = [ctypes.c_char_p]
 
651
    check_version.restype = ctypes.c_char_p
 
652
    
 
653
    # All the function declarations below are from gnutls/openpgp.h
 
654
    
 
655
    openpgp_crt_init = _library.gnutls_openpgp_crt_init
 
656
    openpgp_crt_init.argtypes = [ctypes.POINTER(openpgp_crt_t)]
 
657
    openpgp_crt_init.restype = _error_code
 
658
    
 
659
    openpgp_crt_import = _library.gnutls_openpgp_crt_import
 
660
    openpgp_crt_import.argtypes = [openpgp_crt_t,
 
661
                                   ctypes.POINTER(datum_t),
 
662
                                   openpgp_crt_fmt_t]
 
663
    openpgp_crt_import.restype = _error_code
 
664
    
 
665
    openpgp_crt_verify_self = _library.gnutls_openpgp_crt_verify_self
 
666
    openpgp_crt_verify_self.argtypes = [openpgp_crt_t, ctypes.c_uint,
 
667
                                        ctypes.POINTER(ctypes.c_uint)]
 
668
    openpgp_crt_verify_self.restype = _error_code
 
669
    
 
670
    openpgp_crt_deinit = _library.gnutls_openpgp_crt_deinit
 
671
    openpgp_crt_deinit.argtypes = [openpgp_crt_t]
 
672
    openpgp_crt_deinit.restype = None
 
673
    
 
674
    openpgp_crt_get_fingerprint = (
 
675
        _library.gnutls_openpgp_crt_get_fingerprint)
 
676
    openpgp_crt_get_fingerprint.argtypes = [openpgp_crt_t,
 
677
                                            ctypes.c_void_p,
 
678
                                            ctypes.POINTER(
 
679
                                                ctypes.c_size_t)]
 
680
    openpgp_crt_get_fingerprint.restype = _error_code
 
681
    
 
682
    # Remove non-public functions
 
683
    del _error_code, _retry_on_error
 
684
# Create the global "gnutls" object, simulating a module
 
685
gnutls = GnuTLS()
 
686
 
438
687
def call_pipe(connection,       # : multiprocessing.Connection
439
688
              func, *args, **kwargs):
440
689
    """This function is meant to be called by multiprocessing.Process
842
1091
                           access="r")
843
1092
    def Property_dbus_property(self):
844
1093
        return dbus.Boolean(False)
 
1094
    
 
1095
    See also the DBusObjectWithAnnotations class.
845
1096
    """
846
1097
    
847
1098
    def decorator(func):
869
1120
    pass
870
1121
 
871
1122
 
872
 
class DBusObjectWithProperties(dbus.service.Object):
873
 
    """A D-Bus object with properties.
 
1123
class DBusObjectWithAnnotations(dbus.service.Object):
 
1124
    """A D-Bus object with annotations.
874
1125
    
875
 
    Classes inheriting from this can use the dbus_service_property
876
 
    decorator to expose methods as D-Bus properties.  It exposes the
877
 
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
1126
    Classes inheriting from this can use the dbus_annotations
 
1127
    decorator to add annotations to methods or signals.
878
1128
    """
879
1129
    
880
1130
    @staticmethod
896
1146
                for name, athing in
897
1147
                inspect.getmembers(cls, self._is_dbus_thing(thing)))
898
1148
    
 
1149
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
1150
                         out_signature = "s",
 
1151
                         path_keyword = 'object_path',
 
1152
                         connection_keyword = 'connection')
 
1153
    def Introspect(self, object_path, connection):
 
1154
        """Overloading of standard D-Bus method.
 
1155
        
 
1156
        Inserts annotation tags on methods and signals.
 
1157
        """
 
1158
        xmlstring = dbus.service.Object.Introspect(self, object_path,
 
1159
                                                   connection)
 
1160
        try:
 
1161
            document = xml.dom.minidom.parseString(xmlstring)
 
1162
            
 
1163
            for if_tag in document.getElementsByTagName("interface"):
 
1164
                # Add annotation tags
 
1165
                for typ in ("method", "signal"):
 
1166
                    for tag in if_tag.getElementsByTagName(typ):
 
1167
                        annots = dict()
 
1168
                        for name, prop in (self.
 
1169
                                           _get_all_dbus_things(typ)):
 
1170
                            if (name == tag.getAttribute("name")
 
1171
                                and prop._dbus_interface
 
1172
                                == if_tag.getAttribute("name")):
 
1173
                                annots.update(getattr(
 
1174
                                    prop, "_dbus_annotations", {}))
 
1175
                        for name, value in annots.items():
 
1176
                            ann_tag = document.createElement(
 
1177
                                "annotation")
 
1178
                            ann_tag.setAttribute("name", name)
 
1179
                            ann_tag.setAttribute("value", value)
 
1180
                            tag.appendChild(ann_tag)
 
1181
                # Add interface annotation tags
 
1182
                for annotation, value in dict(
 
1183
                    itertools.chain.from_iterable(
 
1184
                        annotations().items()
 
1185
                        for name, annotations
 
1186
                        in self._get_all_dbus_things("interface")
 
1187
                        if name == if_tag.getAttribute("name")
 
1188
                        )).items():
 
1189
                    ann_tag = document.createElement("annotation")
 
1190
                    ann_tag.setAttribute("name", annotation)
 
1191
                    ann_tag.setAttribute("value", value)
 
1192
                    if_tag.appendChild(ann_tag)
 
1193
                # Fix argument name for the Introspect method itself
 
1194
                if (if_tag.getAttribute("name")
 
1195
                                == dbus.INTROSPECTABLE_IFACE):
 
1196
                    for cn in if_tag.getElementsByTagName("method"):
 
1197
                        if cn.getAttribute("name") == "Introspect":
 
1198
                            for arg in cn.getElementsByTagName("arg"):
 
1199
                                if (arg.getAttribute("direction")
 
1200
                                    == "out"):
 
1201
                                    arg.setAttribute("name",
 
1202
                                                     "xml_data")
 
1203
            xmlstring = document.toxml("utf-8")
 
1204
            document.unlink()
 
1205
        except (AttributeError, xml.dom.DOMException,
 
1206
                xml.parsers.expat.ExpatError) as error:
 
1207
            logger.error("Failed to override Introspection method",
 
1208
                         exc_info=error)
 
1209
        return xmlstring
 
1210
 
 
1211
 
 
1212
class DBusObjectWithProperties(DBusObjectWithAnnotations):
 
1213
    """A D-Bus object with properties.
 
1214
    
 
1215
    Classes inheriting from this can use the dbus_service_property
 
1216
    decorator to expose methods as D-Bus properties.  It exposes the
 
1217
    standard Get(), Set(), and GetAll() methods on the D-Bus.
 
1218
    """
 
1219
    
899
1220
    def _get_dbus_property(self, interface_name, property_name):
900
1221
        """Returns a bound method if one exists which is a D-Bus
901
1222
        property with the specified name and interface.
911
1232
        raise DBusPropertyNotFound("{}:{}.{}".format(
912
1233
            self.dbus_object_path, interface_name, property_name))
913
1234
    
 
1235
    @classmethod
 
1236
    def _get_all_interface_names(cls):
 
1237
        """Get a sequence of all interfaces supported by an object"""
 
1238
        return (name for name in set(getattr(getattr(x, attr),
 
1239
                                             "_dbus_interface", None)
 
1240
                                     for x in (inspect.getmro(cls))
 
1241
                                     for attr in dir(x))
 
1242
                if name is not None)
 
1243
    
914
1244
    @dbus.service.method(dbus.PROPERTIES_IFACE,
915
1245
                         in_signature="ss",
916
1246
                         out_signature="v")
986
1316
        
987
1317
        Inserts property tags and interface annotation tags.
988
1318
        """
989
 
        xmlstring = dbus.service.Object.Introspect(self, object_path,
990
 
                                                   connection)
 
1319
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
 
1320
                                                         object_path,
 
1321
                                                         connection)
991
1322
        try:
992
1323
            document = xml.dom.minidom.parseString(xmlstring)
993
1324
            
1006
1337
                            if prop._dbus_interface
1007
1338
                            == if_tag.getAttribute("name")):
1008
1339
                    if_tag.appendChild(tag)
1009
 
                # Add annotation tags
1010
 
                for typ in ("method", "signal", "property"):
1011
 
                    for tag in if_tag.getElementsByTagName(typ):
1012
 
                        annots = dict()
1013
 
                        for name, prop in (self.
1014
 
                                           _get_all_dbus_things(typ)):
1015
 
                            if (name == tag.getAttribute("name")
1016
 
                                and prop._dbus_interface
1017
 
                                == if_tag.getAttribute("name")):
1018
 
                                annots.update(getattr(
1019
 
                                    prop, "_dbus_annotations", {}))
1020
 
                        for name, value in annots.items():
1021
 
                            ann_tag = document.createElement(
1022
 
                                "annotation")
1023
 
                            ann_tag.setAttribute("name", name)
1024
 
                            ann_tag.setAttribute("value", value)
1025
 
                            tag.appendChild(ann_tag)
1026
 
                # Add interface annotation tags
1027
 
                for annotation, value in dict(
1028
 
                    itertools.chain.from_iterable(
1029
 
                        annotations().items()
1030
 
                        for name, annotations
1031
 
                        in self._get_all_dbus_things("interface")
1032
 
                        if name == if_tag.getAttribute("name")
1033
 
                        )).items():
1034
 
                    ann_tag = document.createElement("annotation")
1035
 
                    ann_tag.setAttribute("name", annotation)
1036
 
                    ann_tag.setAttribute("value", value)
1037
 
                    if_tag.appendChild(ann_tag)
 
1340
                # Add annotation tags for properties
 
1341
                for tag in if_tag.getElementsByTagName("property"):
 
1342
                    annots = dict()
 
1343
                    for name, prop in self._get_all_dbus_things(
 
1344
                            "property"):
 
1345
                        if (name == tag.getAttribute("name")
 
1346
                            and prop._dbus_interface
 
1347
                            == if_tag.getAttribute("name")):
 
1348
                            annots.update(getattr(
 
1349
                                prop, "_dbus_annotations", {}))
 
1350
                    for name, value in annots.items():
 
1351
                        ann_tag = document.createElement(
 
1352
                            "annotation")
 
1353
                        ann_tag.setAttribute("name", name)
 
1354
                        ann_tag.setAttribute("value", value)
 
1355
                        tag.appendChild(ann_tag)
1038
1356
                # Add the names to the return values for the
1039
1357
                # "org.freedesktop.DBus.Properties" methods
1040
1358
                if (if_tag.getAttribute("name")
1058
1376
                         exc_info=error)
1059
1377
        return xmlstring
1060
1378
 
 
1379
try:
 
1380
    dbus.OBJECT_MANAGER_IFACE
 
1381
except AttributeError:
 
1382
    dbus.OBJECT_MANAGER_IFACE = "org.freedesktop.DBus.ObjectManager"
 
1383
 
 
1384
class DBusObjectWithObjectManager(DBusObjectWithAnnotations):
 
1385
    """A D-Bus object with an ObjectManager.
 
1386
    
 
1387
    Classes inheriting from this exposes the standard
 
1388
    GetManagedObjects call and the InterfacesAdded and
 
1389
    InterfacesRemoved signals on the standard
 
1390
    "org.freedesktop.DBus.ObjectManager" interface.
 
1391
    
 
1392
    Note: No signals are sent automatically; they must be sent
 
1393
    manually.
 
1394
    """
 
1395
    @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
 
1396
                         out_signature = "a{oa{sa{sv}}}")
 
1397
    def GetManagedObjects(self):
 
1398
        """This function must be overridden"""
 
1399
        raise NotImplementedError()
 
1400
    
 
1401
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE,
 
1402
                         signature = "oa{sa{sv}}")
 
1403
    def InterfacesAdded(self, object_path, interfaces_and_properties):
 
1404
        pass
 
1405
    
 
1406
    @dbus.service.signal(dbus.OBJECT_MANAGER_IFACE, signature = "oas")
 
1407
    def InterfacesRemoved(self, object_path, interfaces):
 
1408
        pass
 
1409
    
 
1410
    @dbus.service.method(dbus.INTROSPECTABLE_IFACE,
 
1411
                         out_signature = "s",
 
1412
                         path_keyword = 'object_path',
 
1413
                         connection_keyword = 'connection')
 
1414
    def Introspect(self, object_path, connection):
 
1415
        """Overloading of standard D-Bus method.
 
1416
        
 
1417
        Override return argument name of GetManagedObjects to be
 
1418
        "objpath_interfaces_and_properties"
 
1419
        """
 
1420
        xmlstring = DBusObjectWithAnnotations.Introspect(self,
 
1421
                                                         object_path,
 
1422
                                                         connection)
 
1423
        try:
 
1424
            document = xml.dom.minidom.parseString(xmlstring)
 
1425
            
 
1426
            for if_tag in document.getElementsByTagName("interface"):
 
1427
                # Fix argument name for the GetManagedObjects method
 
1428
                if (if_tag.getAttribute("name")
 
1429
                                == dbus.OBJECT_MANAGER_IFACE):
 
1430
                    for cn in if_tag.getElementsByTagName("method"):
 
1431
                        if (cn.getAttribute("name")
 
1432
                            == "GetManagedObjects"):
 
1433
                            for arg in cn.getElementsByTagName("arg"):
 
1434
                                if (arg.getAttribute("direction")
 
1435
                                    == "out"):
 
1436
                                    arg.setAttribute(
 
1437
                                        "name",
 
1438
                                        "objpath_interfaces"
 
1439
                                        "_and_properties")
 
1440
            xmlstring = document.toxml("utf-8")
 
1441
            document.unlink()
 
1442
        except (AttributeError, xml.dom.DOMException,
 
1443
                xml.parsers.expat.ExpatError) as error:
 
1444
            logger.error("Failed to override Introspection method",
 
1445
                         exc_info = error)
 
1446
        return xmlstring
1061
1447
 
1062
1448
def datetime_to_dbus(dt, variant_level=0):
1063
1449
    """Convert a UTC datetime.datetime() to a D-Bus type."""
1154
1540
                        func1 and func2 to the "call_both" function
1155
1541
                        outside of its arguments"""
1156
1542
                        
 
1543
                        @functools.wraps(func2)
1157
1544
                        def call_both(*args, **kwargs):
1158
1545
                            """This function will emit two D-Bus
1159
1546
                            signals by calling func1 and func2"""
1160
1547
                            func1(*args, **kwargs)
1161
1548
                            func2(*args, **kwargs)
 
1549
                        # Make wrapper function look like a D-Bus signal
 
1550
                        for name, attr in inspect.getmembers(func2):
 
1551
                            if name.startswith("_dbus_"):
 
1552
                                setattr(call_both, name, attr)
1162
1553
                        
1163
1554
                        return call_both
1164
1555
                    # Create the "call_both" function and add it to
1376
1767
        if exitstatus >= 0:
1377
1768
            # Emit D-Bus signal
1378
1769
            self.CheckerCompleted(dbus.Int16(exitstatus),
1379
 
                                  dbus.Int64(0),
 
1770
                                  # This is specific to GNU libC
 
1771
                                  dbus.Int64(exitstatus << 8),
1380
1772
                                  dbus.String(command))
1381
1773
        else:
1382
1774
            # Emit D-Bus signal
1383
1775
            self.CheckerCompleted(dbus.Int16(-1),
1384
1776
                                  dbus.Int64(
1385
 
                                      self.last_checker_signal),
 
1777
                                      # This is specific to GNU libC
 
1778
                                      (exitstatus << 8)
 
1779
                                      | self.last_checker_signal),
1386
1780
                                  dbus.String(command))
1387
1781
        return ret
1388
1782
    
1465
1859
        self.checked_ok()
1466
1860
    
1467
1861
    # Enable - method
 
1862
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1468
1863
    @dbus.service.method(_interface)
1469
1864
    def Enable(self):
1470
1865
        "D-Bus method"
1471
1866
        self.enable()
1472
1867
    
1473
1868
    # StartChecker - method
 
1869
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1474
1870
    @dbus.service.method(_interface)
1475
1871
    def StartChecker(self):
1476
1872
        "D-Bus method"
1477
1873
        self.start_checker()
1478
1874
    
1479
1875
    # Disable - method
 
1876
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1480
1877
    @dbus.service.method(_interface)
1481
1878
    def Disable(self):
1482
1879
        "D-Bus method"
1483
1880
        self.disable()
1484
1881
    
1485
1882
    # StopChecker - method
 
1883
    @dbus_annotations({"org.freedesktop.DBus.Deprecated": "true"})
1486
1884
    @dbus.service.method(_interface)
1487
1885
    def StopChecker(self):
1488
1886
        self.stop_checker()
1524
1922
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
1525
1923
    
1526
1924
    # Name - property
 
1925
    @dbus_annotations(
 
1926
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1527
1927
    @dbus_service_property(_interface, signature="s", access="read")
1528
1928
    def Name_dbus_property(self):
1529
1929
        return dbus.String(self.name)
1530
1930
    
1531
1931
    # Fingerprint - property
 
1932
    @dbus_annotations(
 
1933
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1532
1934
    @dbus_service_property(_interface, signature="s", access="read")
1533
1935
    def Fingerprint_dbus_property(self):
1534
1936
        return dbus.String(self.fingerprint)
1543
1945
        self.host = str(value)
1544
1946
    
1545
1947
    # Created - property
 
1948
    @dbus_annotations(
 
1949
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const"})
1546
1950
    @dbus_service_property(_interface, signature="s", access="read")
1547
1951
    def Created_dbus_property(self):
1548
1952
        return datetime_to_dbus(self.created)
1663
2067
            self.stop_checker()
1664
2068
    
1665
2069
    # ObjectPath - property
 
2070
    @dbus_annotations(
 
2071
        {"org.freedesktop.DBus.Property.EmitsChangedSignal": "const",
 
2072
         "org.freedesktop.DBus.Deprecated": "true"})
1666
2073
    @dbus_service_property(_interface, signature="o", access="read")
1667
2074
    def ObjectPath_dbus_property(self):
1668
2075
        return self.dbus_object_path # is already a dbus.ObjectPath
1669
2076
    
1670
2077
    # Secret = property
 
2078
    @dbus_annotations(
 
2079
        {"org.freedesktop.DBus.Property.EmitsChangedSignal":
 
2080
         "invalidates"})
1671
2081
    @dbus_service_property(_interface,
1672
2082
                           signature="ay",
1673
2083
                           access="write",
1719
2129
            logger.debug("Pipe FD: %d",
1720
2130
                         self.server.child_pipe.fileno())
1721
2131
            
1722
 
            session = gnutls.connection.ClientSession(
1723
 
                self.request, gnutls.connection .X509Credentials())
1724
 
            
1725
 
            # Note: gnutls.connection.X509Credentials is really a
1726
 
            # generic GnuTLS certificate credentials object so long as
1727
 
            # no X.509 keys are added to it.  Therefore, we can use it
1728
 
            # here despite using OpenPGP certificates.
 
2132
            session = gnutls.ClientSession(self.request)
1729
2133
            
1730
2134
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1731
2135
            #                      "+AES-256-CBC", "+SHA1",
1735
2139
            priority = self.server.gnutls_priority
1736
2140
            if priority is None:
1737
2141
                priority = "NORMAL"
1738
 
            gnutls.library.functions.gnutls_priority_set_direct(
1739
 
                session._c_object, priority, None)
 
2142
            gnutls.priority_set_direct(session._c_object, priority,
 
2143
                                       None)
1740
2144
            
1741
2145
            # Start communication using the Mandos protocol
1742
2146
            # Get protocol number
1752
2156
            # Start GnuTLS connection
1753
2157
            try:
1754
2158
                session.handshake()
1755
 
            except gnutls.errors.GNUTLSError as error:
 
2159
            except gnutls.Error as error:
1756
2160
                logger.warning("Handshake failed: %s", error)
1757
2161
                # Do not run session.bye() here: the session is not
1758
2162
                # established.  Just abandon the request.
1764
2168
                try:
1765
2169
                    fpr = self.fingerprint(
1766
2170
                        self.peer_certificate(session))
1767
 
                except (TypeError,
1768
 
                        gnutls.errors.GNUTLSError) as error:
 
2171
                except (TypeError, gnutls.Error) as error:
1769
2172
                    logger.warning("Bad certificate: %s", error)
1770
2173
                    return
1771
2174
                logger.debug("Fingerprint: %s", fpr)
1829
2232
                    else:
1830
2233
                        delay -= time2 - time
1831
2234
                
1832
 
                sent_size = 0
1833
 
                while sent_size < len(client.secret):
1834
 
                    try:
1835
 
                        sent = session.send(client.secret[sent_size:])
1836
 
                    except gnutls.errors.GNUTLSError as error:
1837
 
                        logger.warning("gnutls send failed",
1838
 
                                       exc_info=error)
1839
 
                        return
1840
 
                    logger.debug("Sent: %d, remaining: %d", sent,
1841
 
                                 len(client.secret) - (sent_size
1842
 
                                                       + sent))
1843
 
                    sent_size += sent
 
2235
                try:
 
2236
                    session.send(client.secret)
 
2237
                except gnutls.Error as error:
 
2238
                    logger.warning("gnutls send failed",
 
2239
                                   exc_info = error)
 
2240
                    return
1844
2241
                
1845
2242
                logger.info("Sending secret to %s", client.name)
1846
2243
                # bump the timeout using extended_timeout
1854
2251
                    client.approvals_pending -= 1
1855
2252
                try:
1856
2253
                    session.bye()
1857
 
                except gnutls.errors.GNUTLSError as error:
 
2254
                except gnutls.Error as error:
1858
2255
                    logger.warning("GnuTLS bye failed",
1859
2256
                                   exc_info=error)
1860
2257
    
1862
2259
    def peer_certificate(session):
1863
2260
        "Return the peer's OpenPGP certificate as a bytestring"
1864
2261
        # If not an OpenPGP certificate...
1865
 
        if (gnutls.library.functions.gnutls_certificate_type_get(
1866
 
                session._c_object)
1867
 
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
1868
 
            # ...do the normal thing
1869
 
            return session.peer_certificate
 
2262
        if (gnutls.certificate_type_get(session._c_object)
 
2263
            != gnutls.CRT_OPENPGP):
 
2264
            # ...return invalid data
 
2265
            return b""
1870
2266
        list_size = ctypes.c_uint(1)
1871
 
        cert_list = (gnutls.library.functions
1872
 
                     .gnutls_certificate_get_peers
 
2267
        cert_list = (gnutls.certificate_get_peers
1873
2268
                     (session._c_object, ctypes.byref(list_size)))
1874
2269
        if not bool(cert_list) and list_size.value != 0:
1875
 
            raise gnutls.errors.GNUTLSError("error getting peer"
1876
 
                                            " certificate")
 
2270
            raise gnutls.Error("error getting peer certificate")
1877
2271
        if list_size.value == 0:
1878
2272
            return None
1879
2273
        cert = cert_list[0]
1883
2277
    def fingerprint(openpgp):
1884
2278
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
1885
2279
        # New GnuTLS "datum" with the OpenPGP public key
1886
 
        datum = gnutls.library.types.gnutls_datum_t(
 
2280
        datum = gnutls.datum_t(
1887
2281
            ctypes.cast(ctypes.c_char_p(openpgp),
1888
2282
                        ctypes.POINTER(ctypes.c_ubyte)),
1889
2283
            ctypes.c_uint(len(openpgp)))
1890
2284
        # New empty GnuTLS certificate
1891
 
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
1892
 
        gnutls.library.functions.gnutls_openpgp_crt_init(
1893
 
            ctypes.byref(crt))
 
2285
        crt = gnutls.openpgp_crt_t()
 
2286
        gnutls.openpgp_crt_init(ctypes.byref(crt))
1894
2287
        # Import the OpenPGP public key into the certificate
1895
 
        gnutls.library.functions.gnutls_openpgp_crt_import(
1896
 
            crt, ctypes.byref(datum),
1897
 
            gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
2288
        gnutls.openpgp_crt_import(crt, ctypes.byref(datum),
 
2289
                                  gnutls.OPENPGP_FMT_RAW)
1898
2290
        # Verify the self signature in the key
1899
2291
        crtverify = ctypes.c_uint()
1900
 
        gnutls.library.functions.gnutls_openpgp_crt_verify_self(
1901
 
            crt, 0, ctypes.byref(crtverify))
 
2292
        gnutls.openpgp_crt_verify_self(crt, 0,
 
2293
                                       ctypes.byref(crtverify))
1902
2294
        if crtverify.value != 0:
1903
 
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
1904
 
            raise gnutls.errors.CertificateSecurityError(
1905
 
                "Verify failed")
 
2295
            gnutls.openpgp_crt_deinit(crt)
 
2296
            raise gnutls.CertificateSecurityError("Verify failed")
1906
2297
        # New buffer for the fingerprint
1907
2298
        buf = ctypes.create_string_buffer(20)
1908
2299
        buf_len = ctypes.c_size_t()
1909
2300
        # Get the fingerprint from the certificate into the buffer
1910
 
        gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint(
1911
 
            crt, ctypes.byref(buf), ctypes.byref(buf_len))
 
2301
        gnutls.openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
 
2302
                                           ctypes.byref(buf_len))
1912
2303
        # Deinit the certificate
1913
 
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
2304
        gnutls.openpgp_crt_deinit(crt)
1914
2305
        # Convert the buffer to a Python bytestring
1915
2306
        fpr = ctypes.string_at(buf, buf_len.value)
1916
2307
        # Convert the bytestring to hexadecimal notation
2542
2933
        
2543
2934
        # "Use a log level over 10 to enable all debugging options."
2544
2935
        # - GnuTLS manual
2545
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
 
2936
        gnutls.global_set_log_level(11)
2546
2937
        
2547
 
        @gnutls.library.types.gnutls_log_func
 
2938
        @gnutls.log_func
2548
2939
        def debug_gnutls(level, string):
2549
2940
            logger.debug("GnuTLS: %s", string[:-1])
2550
2941
        
2551
 
        gnutls.library.functions.gnutls_global_set_log_function(
2552
 
            debug_gnutls)
 
2942
        gnutls.global_set_log_function(debug_gnutls)
2553
2943
        
2554
2944
        # Redirect stdin so all checkers get /dev/null
2555
2945
        null = os.open(os.devnull, os.O_NOCTTY | os.O_RDWR)
2734
3124
        
2735
3125
        @alternate_dbus_interfaces(
2736
3126
            { "se.recompile.Mandos": "se.bsnet.fukt.Mandos" })
2737
 
        class MandosDBusService(DBusObjectWithProperties):
 
3127
        class MandosDBusService(DBusObjectWithObjectManager):
2738
3128
            """A D-Bus proxy object"""
2739
3129
            
2740
3130
            def __init__(self):
2742
3132
            
2743
3133
            _interface = "se.recompile.Mandos"
2744
3134
            
2745
 
            @dbus_interface_annotations(_interface)
2746
 
            def _foo(self):
2747
 
                return {
2748
 
                    "org.freedesktop.DBus.Property.EmitsChangedSignal":
2749
 
                    "false" }
2750
 
            
2751
3135
            @dbus.service.signal(_interface, signature="o")
2752
3136
            def ClientAdded(self, objpath):
2753
3137
                "D-Bus signal"
2758
3142
                "D-Bus signal"
2759
3143
                pass
2760
3144
            
 
3145
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
 
3146
                               "true"})
2761
3147
            @dbus.service.signal(_interface, signature="os")
2762
3148
            def ClientRemoved(self, objpath, name):
2763
3149
                "D-Bus signal"
2764
3150
                pass
2765
3151
            
 
3152
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
 
3153
                               "true"})
2766
3154
            @dbus.service.method(_interface, out_signature="ao")
2767
3155
            def GetAllClients(self):
2768
3156
                "D-Bus method"
2769
3157
                return dbus.Array(c.dbus_object_path for c in
2770
3158
                                  tcp_server.clients.itervalues())
2771
3159
            
 
3160
            @dbus_annotations({"org.freedesktop.DBus.Deprecated":
 
3161
                               "true"})
2772
3162
            @dbus.service.method(_interface,
2773
3163
                                 out_signature="a{oa{sv}}")
2774
3164
            def GetAllClientsWithProperties(self):
2775
3165
                "D-Bus method"
2776
3166
                return dbus.Dictionary(
2777
 
                    { c.dbus_object_path: c.GetAll("")
 
3167
                    { c.dbus_object_path: c.GetAll(
 
3168
                        "se.recompile.Mandos.Client")
2778
3169
                      for c in tcp_server.clients.itervalues() },
2779
3170
                    signature="oa{sv}")
2780
3171
            
2785
3176
                    if c.dbus_object_path == object_path:
2786
3177
                        del tcp_server.clients[c.name]
2787
3178
                        c.remove_from_connection()
2788
 
                        # Don't signal anything except ClientRemoved
 
3179
                        # Don't signal the disabling
2789
3180
                        c.disable(quiet=True)
2790
 
                        # Emit D-Bus signal
2791
 
                        self.ClientRemoved(object_path, c.name)
 
3181
                        # Emit D-Bus signal for removal
 
3182
                        self.client_removed_signal(c)
2792
3183
                        return
2793
3184
                raise KeyError(object_path)
2794
3185
            
2795
3186
            del _interface
 
3187
            
 
3188
            @dbus.service.method(dbus.OBJECT_MANAGER_IFACE,
 
3189
                                 out_signature = "a{oa{sa{sv}}}")
 
3190
            def GetManagedObjects(self):
 
3191
                """D-Bus method"""
 
3192
                return dbus.Dictionary(
 
3193
                    { client.dbus_object_path:
 
3194
                      dbus.Dictionary(
 
3195
                          { interface: client.GetAll(interface)
 
3196
                            for interface in
 
3197
                                 client._get_all_interface_names()})
 
3198
                      for client in tcp_server.clients.values()})
 
3199
            
 
3200
            def client_added_signal(self, client):
 
3201
                """Send the new standard signal and the old signal"""
 
3202
                if use_dbus:
 
3203
                    # New standard signal
 
3204
                    self.InterfacesAdded(
 
3205
                        client.dbus_object_path,
 
3206
                        dbus.Dictionary(
 
3207
                            { interface: client.GetAll(interface)
 
3208
                              for interface in
 
3209
                              client._get_all_interface_names()}))
 
3210
                    # Old signal
 
3211
                    self.ClientAdded(client.dbus_object_path)
 
3212
            
 
3213
            def client_removed_signal(self, client):
 
3214
                """Send the new standard signal and the old signal"""
 
3215
                if use_dbus:
 
3216
                    # New standard signal
 
3217
                    self.InterfacesRemoved(
 
3218
                        client.dbus_object_path,
 
3219
                        client._get_all_interface_names())
 
3220
                    # Old signal
 
3221
                    self.ClientRemoved(client.dbus_object_path,
 
3222
                                       client.name)
2796
3223
        
2797
3224
        mandos_dbus_service = MandosDBusService()
2798
3225
    
2863
3290
            name, client = tcp_server.clients.popitem()
2864
3291
            if use_dbus:
2865
3292
                client.remove_from_connection()
2866
 
            # Don't signal anything except ClientRemoved
 
3293
            # Don't signal the disabling
2867
3294
            client.disable(quiet=True)
 
3295
            # Emit D-Bus signal for removal
2868
3296
            if use_dbus:
2869
 
                # Emit D-Bus signal
2870
 
                mandos_dbus_service.ClientRemoved(
2871
 
                    client.dbus_object_path, client.name)
 
3297
                mandos_dbus_service.client_removed_signal(client)
2872
3298
        client_settings.clear()
2873
3299
    
2874
3300
    atexit.register(cleanup)
2875
3301
    
2876
3302
    for client in tcp_server.clients.itervalues():
2877
3303
        if use_dbus:
2878
 
            # Emit D-Bus signal
2879
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
 
3304
            # Emit D-Bus signal for adding
 
3305
            mandos_dbus_service.client_added_signal(client)
2880
3306
        # Need to initiate checking of clients
2881
3307
        if client.enabled:
2882
3308
            client.init_checker()