/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: Björn Påhlsson
  • Date: 2011-10-02 13:45:45 UTC
  • mto: This revision was merged to the branch mainline in revision 505.
  • Revision ID: belorn@fukt.bsnet.se-20111002134545-oytmfbl15r8lsm6p
working transition code for going between se.bsnet.fukt to se.recompile

Show diffs side-by-side

added added

removed removed

Lines of Context:
62
62
import functools
63
63
import cPickle as pickle
64
64
import multiprocessing
 
65
import types
65
66
 
66
67
import dbus
67
68
import dbus.service
313
314
                          "created", "enabled", "fingerprint",
314
315
                          "host", "interval", "last_checked_ok",
315
316
                          "last_enabled", "name", "timeout")
316
 
        
 
317
    
317
318
    def timeout_milliseconds(self):
318
319
        "Return the 'timeout' attribute in milliseconds"
319
320
        return _timedelta_to_milliseconds(self.timeout)
320
 
 
 
321
    
321
322
    def extended_timeout_milliseconds(self):
322
323
        "Return the 'extended_timeout' attribute in milliseconds"
323
324
        return _timedelta_to_milliseconds(self.extended_timeout)    
325
326
    def interval_milliseconds(self):
326
327
        "Return the 'interval' attribute in milliseconds"
327
328
        return _timedelta_to_milliseconds(self.interval)
328
 
 
 
329
    
329
330
    def approval_delay_milliseconds(self):
330
331
        return _timedelta_to_milliseconds(self.approval_delay)
331
332
    
509
510
                                       'replace')))
510
511
                    for attr in
511
512
                    self.runtime_expansions)
512
 
 
 
513
                
513
514
                try:
514
515
                    command = self.checker_command % escaped_attrs
515
516
                except TypeError as error:
561
562
                raise
562
563
        self.checker = None
563
564
 
 
565
 
564
566
def dbus_service_property(dbus_interface, signature="v",
565
567
                          access="readwrite", byte_arrays=False):
566
568
    """Decorators for marking methods of a DBusObjectWithProperties to
612
614
 
613
615
class DBusObjectWithProperties(dbus.service.Object):
614
616
    """A D-Bus object with properties.
615
 
 
 
617
    
616
618
    Classes inheriting from this can use the dbus_service_property
617
619
    decorator to expose methods as D-Bus properties.  It exposes the
618
620
    standard Get(), Set(), and GetAll() methods on the D-Bus.
629
631
                for name, prop in
630
632
                inspect.getmembers(self, self._is_dbus_property))
631
633
    
 
634
#    def _get_dbus_property(self, interface_name, property_name):
 
635
#        """Returns a bound method if one exists which is a D-Bus
 
636
#        property with the specified name and interface.
 
637
#        """
 
638
#        print("get_property({0!r}, {1!r}".format(interface_name, property_name),file=sys.stderr)
 
639
#        print(dir(self), sys.stderr)
 
640
#        for name in (property_name,
 
641
#                     property_name + "_dbus_property"):
 
642
#            prop = getattr(self, name, None)
 
643
#            if (prop is None
 
644
#                or not self._is_dbus_property(prop)
 
645
#                or prop._dbus_name != property_name
 
646
#                or (interface_name and prop._dbus_interface
 
647
#                    and interface_name != prop._dbus_interface)):
 
648
#                continue
 
649
#            return prop
 
650
#        # No such property
 
651
#        raise DBusPropertyNotFound(self.dbus_object_path + ":"
 
652
#                                   + interface_name + "."
 
653
#                                   + property_name)
 
654
 
632
655
    def _get_dbus_property(self, interface_name, property_name):
633
656
        """Returns a bound method if one exists which is a D-Bus
634
657
        property with the specified name and interface.
635
658
        """
636
 
        for name in (property_name,
637
 
                     property_name + "_dbus_property"):
638
 
            prop = getattr(self, name, None)
639
 
            if (prop is None
640
 
                or not self._is_dbus_property(prop)
641
 
                or prop._dbus_name != property_name
642
 
                or (interface_name and prop._dbus_interface
643
 
                    and interface_name != prop._dbus_interface)):
644
 
                continue
645
 
            return prop
 
659
        for name, value in inspect.getmembers(self, self._is_dbus_property):
 
660
            if value._dbus_name == property_name and value._dbus_interface == interface_name:
 
661
                return value
 
662
        
646
663
        # No such property
647
664
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
648
665
                                   + interface_name + "."
649
666
                                   + property_name)
 
667
 
650
668
    
651
669
    @dbus.service.method(dbus.PROPERTIES_IFACE, in_signature="ss",
652
670
                         out_signature="v")
682
700
    def GetAll(self, interface_name):
683
701
        """Standard D-Bus property GetAll() method, see D-Bus
684
702
        standard.
685
 
 
 
703
        
686
704
        Note: Will not include properties with access="write".
687
705
        """
688
706
        all = {}
757
775
    return dbus.String(dt.isoformat(),
758
776
                       variant_level=variant_level)
759
777
 
 
778
class transitional_clientdbus(DBusObjectWithProperties.__metaclass__):
 
779
    def __new__(mcs, name, bases, attr):
 
780
        for key, old_dbusobj in attr.items():
 
781
            new_interface = getattr(old_dbusobj, "_dbus_interface", "").replace("se.bsnet.fukt.", "se.recompile.")
 
782
            if getattr(old_dbusobj, "_dbus_is_signal", False):
 
783
                unwrappedfunc = dict(zip(old_dbusobj.func_code.co_freevars,
 
784
                                    old_dbusobj.__closure__))["func"].cell_contents
 
785
                newfunc = types.FunctionType(unwrappedfunc.func_code,
 
786
                                             unwrappedfunc.func_globals,
 
787
                                             unwrappedfunc.func_name,
 
788
                                             unwrappedfunc.func_defaults,
 
789
                                             unwrappedfunc.func_closure)
 
790
                new_dbusfunc = dbus.service.signal(
 
791
                    new_interface, old_dbusobj._dbus_signature)(newfunc)            
 
792
                attr["_transitional_{0}_1".format(key)] = new_dbusfunc
 
793
                attr["_transitional_{0}_0".format(key)] = old_dbusobj                
 
794
                def fixscope(func1, func2):
 
795
                    def newcall(*args, **kwargs):
 
796
                        func1(*args, **kwargs)
 
797
                        func2(*args, **kwargs)
 
798
                    return newcall
 
799
 
 
800
                attr[key] = fixscope(
 
801
                    old_dbusobj, attr["_transitional_{0}_1".format(key)])
 
802
            
 
803
            if getattr(old_dbusobj, "_dbus_is_method", False):
 
804
                new_dbusfunc = (dbus.service.method
 
805
                                (new_interface,
 
806
                                 old_dbusobj._dbus_in_signature,
 
807
                                 old_dbusobj._dbus_out_signature)
 
808
                                (types.FunctionType
 
809
                                 (old_dbusobj.func_code,
 
810
                                  old_dbusobj.func_globals,
 
811
                                  old_dbusobj.func_name,
 
812
                                  old_dbusobj.func_defaults,
 
813
                                  old_dbusobj.func_closure)))
 
814
 
 
815
                attr["_transitional_{0}".format(key)] = new_dbusfunc
 
816
            if getattr(old_dbusobj, "_dbus_is_property", False):
 
817
                new_dbusfunc = (dbus_service_property
 
818
                                (new_interface,
 
819
                                 old_dbusobj._dbus_signature,
 
820
                                 old_dbusobj._dbus_access,
 
821
                                 old_dbusobj._dbus_get_args_options["byte_arrays"])
 
822
                                (types.FunctionType
 
823
                                 (old_dbusobj.func_code,
 
824
                                  old_dbusobj.func_globals,
 
825
                                  old_dbusobj.func_name,
 
826
                                  old_dbusobj.func_defaults,
 
827
                                  old_dbusobj.func_closure)))
 
828
 
 
829
                attr["_transitional_{0}".format(key)] = new_dbusfunc
 
830
        return type.__new__(mcs, name, bases, attr)
 
831
 
760
832
class ClientDBus(Client, DBusObjectWithProperties):
761
833
    """A Client class using D-Bus
762
834
    
767
839
    
768
840
    runtime_expansions = (Client.runtime_expansions
769
841
                          + ("dbus_object_path",))
 
842
 
 
843
    __metaclass__ = transitional_clientdbus
770
844
    
771
845
    # dbus.service.Object doesn't use super(), so we can't either.
772
846
    
806
880
                                                variant_level)
807
881
                    self.PropertyChanged(dbus.String(dbus_name),
808
882
                                         dbus_value)
809
 
 
 
883
        
810
884
        return property(lambda self: real_value[0], setter)
811
 
 
812
 
 
 
885
    
 
886
    
813
887
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
814
888
    approvals_pending = notifychangeproperty(dbus.Boolean,
815
889
                                             "ApprovalPending",
867
941
        
868
942
        return Client.checker_callback(self, pid, condition, command,
869
943
                                       *args, **kwargs)
870
 
 
 
944
    
871
945
    def start_checker(self, *args, **kwargs):
872
946
        old_checker = self.checker
873
947
        if self.checker is not None:
896
970
    
897
971
    ## D-Bus methods, signals & properties
898
972
    _interface = "se.bsnet.fukt.Mandos.Client"
899
 
    
 
973
 
900
974
    ## Signals
901
975
    
902
976
    # CheckerCompleted - signal
1090
1164
                            + datetime.timedelta(milliseconds = time_to_die))
1091
1165
            self.disable_initiator_tag = (gobject.timeout_add
1092
1166
                                          (time_to_die, self.disable))
1093
 
 
 
1167
    
1094
1168
    # ExtendedTimeout - property
1095
1169
    @dbus_service_property(_interface, signature="t",
1096
1170
                           access="readwrite")
1098
1172
        if value is None:       # get
1099
1173
            return dbus.UInt64(self.extended_timeout_milliseconds())
1100
1174
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1101
 
 
 
1175
    
1102
1176
    # Interval - property
1103
1177
    @dbus_service_property(_interface, signature="t",
1104
1178
                           access="readwrite")
1113
1187
        self.checker_initiator_tag = (gobject.timeout_add
1114
1188
                                      (value, self.start_checker))
1115
1189
        self.start_checker()    # Start one now, too
1116
 
 
 
1190
    
1117
1191
    # Checker - property
1118
1192
    @dbus_service_property(_interface, signature="s",
1119
1193
                           access="readwrite")
1153
1227
        self._pipe.send(('init', fpr, address))
1154
1228
        if not self._pipe.recv():
1155
1229
            raise KeyError()
1156
 
 
 
1230
    
1157
1231
    def __getattribute__(self, name):
1158
1232
        if(name == '_pipe'):
1159
1233
            return super(ProxyClient, self).__getattribute__(name)
1166
1240
                self._pipe.send(('funcall', name, args, kwargs))
1167
1241
                return self._pipe.recv()[1]
1168
1242
            return func
1169
 
 
 
1243
    
1170
1244
    def __setattr__(self, name, value):
1171
1245
        if(name == '_pipe'):
1172
1246
            return super(ProxyClient, self).__setattr__(name, value)
1185
1259
                        unicode(self.client_address))
1186
1260
            logger.debug("Pipe FD: %d",
1187
1261
                         self.server.child_pipe.fileno())
1188
 
 
 
1262
            
1189
1263
            session = (gnutls.connection
1190
1264
                       .ClientSession(self.request,
1191
1265
                                      gnutls.connection
1192
1266
                                      .X509Credentials()))
1193
 
 
 
1267
            
1194
1268
            # Note: gnutls.connection.X509Credentials is really a
1195
1269
            # generic GnuTLS certificate credentials object so long as
1196
1270
            # no X.509 keys are added to it.  Therefore, we can use it
1197
1271
            # here despite using OpenPGP certificates.
1198
 
 
 
1272
            
1199
1273
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1200
1274
            #                      "+AES-256-CBC", "+SHA1",
1201
1275
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1207
1281
            (gnutls.library.functions
1208
1282
             .gnutls_priority_set_direct(session._c_object,
1209
1283
                                         priority, None))
1210
 
 
 
1284
            
1211
1285
            # Start communication using the Mandos protocol
1212
1286
            # Get protocol number
1213
1287
            line = self.request.makefile().readline()
1218
1292
            except (ValueError, IndexError, RuntimeError) as error:
1219
1293
                logger.error("Unknown protocol version: %s", error)
1220
1294
                return
1221
 
 
 
1295
            
1222
1296
            # Start GnuTLS connection
1223
1297
            try:
1224
1298
                session.handshake()
1228
1302
                # established.  Just abandon the request.
1229
1303
                return
1230
1304
            logger.debug("Handshake succeeded")
1231
 
 
 
1305
            
1232
1306
            approval_required = False
1233
1307
            try:
1234
1308
                try:
1239
1313
                    logger.warning("Bad certificate: %s", error)
1240
1314
                    return
1241
1315
                logger.debug("Fingerprint: %s", fpr)
1242
 
 
 
1316
                
1243
1317
                try:
1244
1318
                    client = ProxyClient(child_pipe, fpr,
1245
1319
                                         self.client_address)
1311
1385
                                 sent, len(client.secret)
1312
1386
                                 - (sent_size + sent))
1313
1387
                    sent_size += sent
1314
 
 
 
1388
                
1315
1389
                logger.info("Sending secret to %s", client.name)
1316
1390
                # bump the timeout as if seen
1317
1391
                client.checked_ok(client.extended_timeout)
1405
1479
        multiprocessing.Process(target = self.sub_process_main,
1406
1480
                                args = (request, address)).start()
1407
1481
 
 
1482
 
1408
1483
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1409
1484
    """ adds a pipe to the MixIn """
1410
1485
    def process_request(self, request, client_address):
1413
1488
        This function creates a new pipe in self.pipe
1414
1489
        """
1415
1490
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1416
 
 
 
1491
        
1417
1492
        super(MultiprocessingMixInWithPipe,
1418
1493
              self).process_request(request, client_address)
1419
1494
        self.child_pipe.close()
1420
1495
        self.add_pipe(parent_pipe)
1421
 
 
 
1496
    
1422
1497
    def add_pipe(self, parent_pipe):
1423
1498
        """Dummy function; override as necessary"""
1424
1499
        raise NotImplementedError
1425
1500
 
 
1501
 
1426
1502
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1427
1503
                     socketserver.TCPServer, object):
1428
1504
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1576
1652
            kwargs = request[3]
1577
1653
            
1578
1654
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1579
 
 
 
1655
        
1580
1656
        if command == 'getattr':
1581
1657
            attrname = request[1]
1582
1658
            if callable(client_object.__getattribute__(attrname)):
1588
1664
            attrname = request[1]
1589
1665
            value = request[2]
1590
1666
            setattr(client_object, attrname, value)
1591
 
 
 
1667
        
1592
1668
        return True
1593
1669
 
1594
1670
 
1773
1849
    debuglevel = server_settings["debuglevel"]
1774
1850
    use_dbus = server_settings["use_dbus"]
1775
1851
    use_ipv6 = server_settings["use_ipv6"]
1776
 
 
 
1852
    
1777
1853
    if server_settings["servicename"] != "Mandos":
1778
1854
        syslogger.setFormatter(logging.Formatter
1779
1855
                               ('Mandos (%s) [%%(process)d]:'
1840
1916
        level = getattr(logging, debuglevel.upper())
1841
1917
        syslogger.setLevel(level)
1842
1918
        console.setLevel(level)
1843
 
 
 
1919
    
1844
1920
    if debug:
1845
1921
        # Enable all possible GnuTLS debugging
1846
1922
        
1879
1955
        try:
1880
1956
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1881
1957
                                            bus, do_not_queue=True)
 
1958
            bus_name2 = dbus.service.BusName("se.recompile.Mandos",
 
1959
                                            bus, do_not_queue=True)
1882
1960
        except dbus.exceptions.NameExistsException as e:
1883
1961
            logger.error(unicode(e) + ", disabling D-Bus")
1884
1962
            use_dbus = False
1933
2011
        del pidfilename
1934
2012
        
1935
2013
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1936
 
 
 
2014
    
1937
2015
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1938
2016
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1939
2017
    
2060
2138
    # Must run before the D-Bus bus name gets deregistered
2061
2139
    cleanup()
2062
2140
 
 
2141
 
2063
2142
if __name__ == '__main__':
2064
2143
    main()