/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

Intermediate commit - this does *not* work yet.

* plugins.d/mandos-client.c (HOOKDIR): New.
  (up_interface): New.
  (runnable_hook): New.
  (main): Run network hooks with "start" argument.

Show diffs side-by-side

added added

removed removed

Lines of Context:
83
83
        SO_BINDTODEVICE = None
84
84
 
85
85
 
86
 
version = "1.3.1"
 
86
version = "1.4.1"
87
87
 
88
88
#logger = logging.getLogger('mandos')
89
89
logger = logging.Logger('mandos')
160
160
                            " after %i retries, exiting.",
161
161
                            self.rename_count)
162
162
            raise AvahiServiceError("Too many renames")
163
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
163
        self.name = unicode(self.server
 
164
                            .GetAlternativeServiceName(self.name))
164
165
        logger.info("Changing Zeroconf service name to %r ...",
165
166
                    self.name)
166
167
        syslogger.setFormatter(logging.Formatter
321
322
    
322
323
    def extended_timeout_milliseconds(self):
323
324
        "Return the 'extended_timeout' attribute in milliseconds"
324
 
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
325
        return _timedelta_to_milliseconds(self.extended_timeout)
325
326
    
326
327
    def interval_milliseconds(self):
327
328
        "Return the 'interval' attribute in milliseconds"
361
362
        self.last_enabled = None
362
363
        self.last_checked_ok = None
363
364
        self.timeout = string_to_delta(config["timeout"])
364
 
        self.extended_timeout = string_to_delta(config["extended_timeout"])
 
365
        self.extended_timeout = string_to_delta(config
 
366
                                                ["extended_timeout"])
365
367
        self.interval = string_to_delta(config["interval"])
366
368
        self.disable_hook = disable_hook
367
369
        self.checker = None
380
382
            config["approval_delay"])
381
383
        self.approval_duration = string_to_delta(
382
384
            config["approval_duration"])
383
 
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
 
385
        self.changedstate = (multiprocessing_manager
 
386
                             .Condition(multiprocessing_manager
 
387
                                        .Lock()))
384
388
    
385
389
    def send_changedstate(self):
386
390
        self.changedstate.acquire()
387
391
        self.changedstate.notify_all()
388
392
        self.changedstate.release()
389
 
        
 
393
    
390
394
    def enable(self):
391
395
        """Start this client's checker and timeout hooks"""
392
396
        if getattr(self, "enabled", False):
460
464
        if timeout is None:
461
465
            timeout = self.timeout
462
466
        self.last_checked_ok = datetime.datetime.utcnow()
463
 
        gobject.source_remove(self.disable_initiator_tag)
464
 
        self.expires = datetime.datetime.utcnow() + timeout
465
 
        self.disable_initiator_tag = (gobject.timeout_add
466
 
                                      (_timedelta_to_milliseconds(timeout),
467
 
                                       self.disable))
 
467
        if self.disable_initiator_tag is not None:
 
468
            gobject.source_remove(self.disable_initiator_tag)
 
469
        if getattr(self, "enabled", False):
 
470
            self.disable_initiator_tag = (gobject.timeout_add
 
471
                                          (_timedelta_to_milliseconds
 
472
                                           (timeout), self.disable))
 
473
            self.expires = datetime.datetime.utcnow() + timeout
468
474
    
469
475
    def need_approval(self):
470
476
        self.last_approval_request = datetime.datetime.utcnow()
629
635
        """
630
636
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
631
637
                for cls in self.__class__.__mro__
632
 
                for name, prop in inspect.getmembers(cls, self._is_dbus_property))
 
638
                for name, prop in
 
639
                inspect.getmembers(cls, self._is_dbus_property))
633
640
    
634
641
    def _get_dbus_property(self, interface_name, property_name):
635
642
        """Returns a bound method if one exists which is a D-Bus
636
643
        property with the specified name and interface.
637
644
        """
638
645
        for cls in  self.__class__.__mro__:
639
 
            for name, value in inspect.getmembers(cls, self._is_dbus_property):
640
 
                if value._dbus_name == property_name and value._dbus_interface == interface_name:
 
646
            for name, value in (inspect.getmembers
 
647
                                (cls, self._is_dbus_property)):
 
648
                if (value._dbus_name == property_name
 
649
                    and value._dbus_interface == interface_name):
641
650
                    return value.__get__(self)
642
651
        
643
652
        # No such property
754
763
    return dbus.String(dt.isoformat(),
755
764
                       variant_level=variant_level)
756
765
 
757
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties.__metaclass__):
 
766
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
 
767
                                  .__metaclass__):
758
768
    """Applied to an empty subclass of a D-Bus object, this metaclass
759
769
    will add additional D-Bus attributes matching a certain pattern.
760
770
    """
883
893
        """ Modify a variable so that it's a property which announces
884
894
        its changes to DBus.
885
895
 
886
 
        transform_fun: Function that takes a value and transforms it
887
 
                       to a D-Bus type.
 
896
        transform_fun: Function that takes a value and a variant_level
 
897
                       and transforms it to a D-Bus type.
888
898
        dbus_name: D-Bus name of the variable
889
899
        type_func: Function that transform the value before sending it
890
900
                   to the D-Bus.  Default: no transform
891
901
        variant_level: D-Bus variant level.  Default: 1
892
902
        """
893
 
        real_value = [None,]
 
903
        attrname = "_{0}".format(dbus_name)
894
904
        def setter(self, value):
895
 
            old_value = real_value[0]
896
 
            real_value[0] = value
897
905
            if hasattr(self, "dbus_object_path"):
898
 
                if type_func(old_value) != type_func(real_value[0]):
899
 
                    dbus_value = transform_func(type_func(real_value[0]),
900
 
                                                variant_level)
 
906
                if (not hasattr(self, attrname) or
 
907
                    type_func(getattr(self, attrname, None))
 
908
                    != type_func(value)):
 
909
                    dbus_value = transform_func(type_func(value),
 
910
                                                variant_level
 
911
                                                =variant_level)
901
912
                    self.PropertyChanged(dbus.String(dbus_name),
902
913
                                         dbus_value)
 
914
            setattr(self, attrname, value)
903
915
        
904
 
        return property(lambda self: real_value[0], setter)
 
916
        return property(lambda self: getattr(self, attrname), setter)
905
917
    
906
918
    
907
919
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
912
924
    last_enabled = notifychangeproperty(datetime_to_dbus,
913
925
                                        "LastEnabled")
914
926
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
915
 
                                   type_func = lambda checker: checker is not None)
 
927
                                   type_func = lambda checker:
 
928
                                       checker is not None)
916
929
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
917
930
                                           "LastCheckedOK")
918
 
    last_approval_request = notifychangeproperty(datetime_to_dbus,
919
 
                                                 "LastApprovalRequest")
 
931
    last_approval_request = notifychangeproperty(
 
932
        datetime_to_dbus, "LastApprovalRequest")
920
933
    approved_by_default = notifychangeproperty(dbus.Boolean,
921
934
                                               "ApprovedByDefault")
922
 
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
923
 
                                          type_func = _timedelta_to_milliseconds)
924
 
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
925
 
                                             type_func = _timedelta_to_milliseconds)
 
935
    approval_delay = notifychangeproperty(dbus.UInt16,
 
936
                                          "ApprovalDelay",
 
937
                                          type_func =
 
938
                                          _timedelta_to_milliseconds)
 
939
    approval_duration = notifychangeproperty(
 
940
        dbus.UInt16, "ApprovalDuration",
 
941
        type_func = _timedelta_to_milliseconds)
926
942
    host = notifychangeproperty(dbus.String, "Host")
927
943
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
928
 
                                   type_func = _timedelta_to_milliseconds)
929
 
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
930
 
                                            type_func = _timedelta_to_milliseconds)
931
 
    interval = notifychangeproperty(dbus.UInt16, "Interval",
932
 
                                    type_func = _timedelta_to_milliseconds)
 
944
                                   type_func =
 
945
                                   _timedelta_to_milliseconds)
 
946
    extended_timeout = notifychangeproperty(
 
947
        dbus.UInt16, "ExtendedTimeout",
 
948
        type_func = _timedelta_to_milliseconds)
 
949
    interval = notifychangeproperty(dbus.UInt16,
 
950
                                    "Interval",
 
951
                                    type_func =
 
952
                                    _timedelta_to_milliseconds)
933
953
    checker_command = notifychangeproperty(dbus.String, "Checker")
934
954
    
935
955
    del notifychangeproperty
1170
1190
        gobject.source_remove(self.disable_initiator_tag)
1171
1191
        self.disable_initiator_tag = None
1172
1192
        self.expires = None
1173
 
        time_to_die = (self.
1174
 
                       _timedelta_to_milliseconds((self
1175
 
                                                   .last_checked_ok
1176
 
                                                   + self.timeout)
1177
 
                                                  - datetime.datetime
1178
 
                                                  .utcnow()))
 
1193
        time_to_die = _timedelta_to_milliseconds((self
 
1194
                                                  .last_checked_ok
 
1195
                                                  + self.timeout)
 
1196
                                                 - datetime.datetime
 
1197
                                                 .utcnow())
1179
1198
        if time_to_die <= 0:
1180
1199
            # The timeout has passed
1181
1200
            self.disable()
1182
1201
        else:
1183
1202
            self.expires = (datetime.datetime.utcnow()
1184
 
                            + datetime.timedelta(milliseconds = time_to_die))
 
1203
                            + datetime.timedelta(milliseconds =
 
1204
                                                 time_to_die))
1185
1205
            self.disable_initiator_tag = (gobject.timeout_add
1186
1206
                                          (time_to_die, self.disable))
1187
1207
    
1353
1373
                                       client.name)
1354
1374
                        if self.server.use_dbus:
1355
1375
                            # Emit D-Bus signal
1356
 
                            client.Rejected("Disabled")                    
 
1376
                            client.Rejected("Disabled")
1357
1377
                        return
1358
1378
                    
1359
1379
                    if client._approved or not client.approval_delay:
1376
1396
                        return
1377
1397
                    
1378
1398
                    #wait until timeout or approved
1379
 
                    #x = float(client._timedelta_to_milliseconds(delay))
1380
1399
                    time = datetime.datetime.now()
1381
1400
                    client.changedstate.acquire()
1382
 
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1401
                    (client.changedstate.wait
 
1402
                     (float(client._timedelta_to_milliseconds(delay)
 
1403
                            / 1000)))
1383
1404
                    client.changedstate.release()
1384
1405
                    time2 = datetime.datetime.now()
1385
1406
                    if (time2 - time) >= delay:
1409
1430
                    sent_size += sent
1410
1431
                
1411
1432
                logger.info("Sending secret to %s", client.name)
1412
 
                # bump the timeout as if seen
 
1433
                # bump the timeout using extended_timeout
1413
1434
                client.checked_ok(client.extended_timeout)
1414
1435
                if self.server.use_dbus:
1415
1436
                    # Emit D-Bus signal
1495
1516
        except:
1496
1517
            self.handle_error(request, address)
1497
1518
        self.close_request(request)
1498
 
            
 
1519
    
1499
1520
    def process_request(self, request, address):
1500
1521
        """Start a new process to process the request."""
1501
 
        multiprocessing.Process(target = self.sub_process_main,
1502
 
                                args = (request, address)).start()
 
1522
        proc = multiprocessing.Process(target = self.sub_process_main,
 
1523
                                       args = (request,
 
1524
                                               address))
 
1525
        proc.start()
 
1526
        return proc
1503
1527
 
1504
1528
 
1505
1529
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1511
1535
        """
1512
1536
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1513
1537
        
1514
 
        super(MultiprocessingMixInWithPipe,
1515
 
              self).process_request(request, client_address)
 
1538
        proc = MultiprocessingMixIn.process_request(self, request,
 
1539
                                                    client_address)
1516
1540
        self.child_pipe.close()
1517
 
        self.add_pipe(parent_pipe)
 
1541
        self.add_pipe(parent_pipe, proc)
1518
1542
    
1519
 
    def add_pipe(self, parent_pipe):
 
1543
    def add_pipe(self, parent_pipe, proc):
1520
1544
        """Dummy function; override as necessary"""
1521
1545
        raise NotImplementedError
1522
1546
 
1610
1634
    def server_activate(self):
1611
1635
        if self.enabled:
1612
1636
            return socketserver.TCPServer.server_activate(self)
 
1637
    
1613
1638
    def enable(self):
1614
1639
        self.enabled = True
1615
 
    def add_pipe(self, parent_pipe):
 
1640
    
 
1641
    def add_pipe(self, parent_pipe, proc):
1616
1642
        # Call "handle_ipc" for both data and EOF events
1617
1643
        gobject.io_add_watch(parent_pipe.fileno(),
1618
1644
                             gobject.IO_IN | gobject.IO_HUP,
1619
1645
                             functools.partial(self.handle_ipc,
1620
 
                                               parent_pipe = parent_pipe))
1621
 
        
 
1646
                                               parent_pipe =
 
1647
                                               parent_pipe,
 
1648
                                               proc = proc))
 
1649
    
1622
1650
    def handle_ipc(self, source, condition, parent_pipe=None,
1623
 
                   client_object=None):
 
1651
                   proc = None, client_object=None):
1624
1652
        condition_names = {
1625
1653
            gobject.IO_IN: "IN",   # There is data to read.
1626
1654
            gobject.IO_OUT: "OUT", # Data can be written (without
1635
1663
                                       for cond, name in
1636
1664
                                       condition_names.iteritems()
1637
1665
                                       if cond & condition)
1638
 
        # error or the other end of multiprocessing.Pipe has closed
 
1666
        # error, or the other end of multiprocessing.Pipe has closed
1639
1667
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1668
            # Wait for other process to exit
 
1669
            proc.join()
1640
1670
            return False
1641
1671
        
1642
1672
        # Read a request from the child
1656
1686
                            "dress: %s", fpr, address)
1657
1687
                if self.use_dbus:
1658
1688
                    # Emit D-Bus signal
1659
 
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
 
1689
                    mandos_dbus_service.ClientNotFound(fpr,
 
1690
                                                       address[0])
1660
1691
                parent_pipe.send(False)
1661
1692
                return False
1662
1693
            
1663
1694
            gobject.io_add_watch(parent_pipe.fileno(),
1664
1695
                                 gobject.IO_IN | gobject.IO_HUP,
1665
1696
                                 functools.partial(self.handle_ipc,
1666
 
                                                   parent_pipe = parent_pipe,
1667
 
                                                   client_object = client))
 
1697
                                                   parent_pipe =
 
1698
                                                   parent_pipe,
 
1699
                                                   proc = proc,
 
1700
                                                   client_object =
 
1701
                                                   client))
1668
1702
            parent_pipe.send(True)
1669
 
            # remove the old hook in favor of the new above hook on same fileno
 
1703
            # remove the old hook in favor of the new above hook on
 
1704
            # same fileno
1670
1705
            return False
1671
1706
        if command == 'funcall':
1672
1707
            funcname = request[1]
1673
1708
            args = request[2]
1674
1709
            kwargs = request[3]
1675
1710
            
1676
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1711
            parent_pipe.send(('data', getattr(client_object,
 
1712
                                              funcname)(*args,
 
1713
                                                         **kwargs)))
1677
1714
        
1678
1715
        if command == 'getattr':
1679
1716
            attrname = request[1]
1680
1717
            if callable(client_object.__getattribute__(attrname)):
1681
1718
                parent_pipe.send(('function',))
1682
1719
            else:
1683
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1720
                parent_pipe.send(('data', client_object
 
1721
                                  .__getattribute__(attrname)))
1684
1722
        
1685
1723
        if command == 'setattr':
1686
1724
            attrname = request[1]
1977
2015
        try:
1978
2016
            bus_name = dbus.service.BusName("se.recompile.Mandos",
1979
2017
                                            bus, do_not_queue=True)
1980
 
            old_bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
1981
 
                                                bus, do_not_queue=True)
 
2018
            old_bus_name = (dbus.service.BusName
 
2019
                            ("se.bsnet.fukt.Mandos", bus,
 
2020
                             do_not_queue=True))
1982
2021
        except dbus.exceptions.NameExistsException as e:
1983
2022
            logger.error(unicode(e) + ", disabling D-Bus")
1984
2023
            use_dbus = False
1997
2036
    
1998
2037
    client_class = Client
1999
2038
    if use_dbus:
2000
 
        client_class = functools.partial(ClientDBusTransitional, bus = bus)        
 
2039
        client_class = functools.partial(ClientDBusTransitional,
 
2040
                                         bus = bus)
2001
2041
    def client_config_items(config, section):
2002
2042
        special_settings = {
2003
2043
            "approved_by_default":
2098
2138
        "Cleanup function; run on exit"
2099
2139
        service.cleanup()
2100
2140
        
 
2141
        multiprocessing.active_children()
2101
2142
        while tcp_server.clients:
2102
2143
            client = tcp_server.clients.pop()
2103
2144
            if use_dbus:
2107
2148
            client.disable(quiet=True)
2108
2149
            if use_dbus:
2109
2150
                # Emit D-Bus signal
2110
 
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
2151
                mandos_dbus_service.ClientRemoved(client
 
2152
                                                  .dbus_object_path,
2111
2153
                                                  client.name)
2112
2154
    
2113
2155
    atexit.register(cleanup)