/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

todo

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
import logging
56
56
import logging.handlers
57
57
import pwd
58
 
import contextlib
 
58
from contextlib import closing
59
59
import struct
60
60
import fcntl
61
61
import functools
62
 
import cPickle as pickle
63
62
 
64
63
import dbus
65
64
import dbus.service
80
79
        SO_BINDTODEVICE = None
81
80
 
82
81
 
83
 
version = "1.0.14"
 
82
version = "1.0.12"
84
83
 
85
84
logger = logging.Logger(u'mandos')
86
85
syslogger = (logging.handlers.SysLogHandler
243
242
    enabled:    bool()
244
243
    last_checked_ok: datetime.datetime(); (UTC) or None
245
244
    timeout:    datetime.timedelta(); How long from last_checked_ok
246
 
                                      until this client is disabled
 
245
                                      until this client is invalid
247
246
    interval:   datetime.timedelta(); How often to start a new checker
248
247
    disable_hook:  If set, called by disable() as disable_hook(self)
249
248
    checker:    subprocess.Popen(); a running checker process used
291
290
        if u"secret" in config:
292
291
            self.secret = config[u"secret"].decode(u"base64")
293
292
        elif u"secfile" in config:
294
 
            with open(os.path.expanduser(os.path.expandvars
295
 
                                         (config[u"secfile"])),
296
 
                      "rb") as secfile:
 
293
            with closing(open(os.path.expanduser
 
294
                              (os.path.expandvars
 
295
                               (config[u"secfile"])))) as secfile:
297
296
                self.secret = secfile.read()
298
297
        else:
299
298
            raise TypeError(u"No secret or secfile for client %s"
325
324
        self.checker_initiator_tag = (gobject.timeout_add
326
325
                                      (self.interval_milliseconds(),
327
326
                                       self.start_checker))
 
327
        # Also start a new checker *right now*.
 
328
        self.start_checker()
328
329
        # Schedule a disable() when 'timeout' has passed
329
330
        self.disable_initiator_tag = (gobject.timeout_add
330
331
                                   (self.timeout_milliseconds(),
331
332
                                    self.disable))
332
333
        self.enabled = True
333
 
        # Also start a new checker *right now*.
334
 
        self.start_checker()
335
334
    
336
 
    def disable(self, quiet=True):
 
335
    def disable(self):
337
336
        """Disable this client."""
338
337
        if not getattr(self, "enabled", False):
339
338
            return False
340
 
        if not quiet:
341
 
            logger.info(u"Disabling client %s", self.name)
 
339
        logger.info(u"Disabling client %s", self.name)
342
340
        if getattr(self, u"disable_initiator_tag", False):
343
341
            gobject.source_remove(self.disable_initiator_tag)
344
342
            self.disable_initiator_tag = None
396
394
        # client would inevitably timeout, since no checker would get
397
395
        # a chance to run to completion.  If we instead leave running
398
396
        # checkers alone, the checker would have to take more time
399
 
        # than 'timeout' for the client to be disabled, which is as it
400
 
        # should be.
 
397
        # than 'timeout' for the client to be declared invalid, which
 
398
        # is as it should be.
401
399
        
402
400
        # If a checker exists, make sure it is not a zombie
403
 
        try:
 
401
        if self.checker is not None:
404
402
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
405
 
        except (AttributeError, OSError), error:
406
 
            if (isinstance(error, OSError)
407
 
                and error.errno != errno.ECHILD):
408
 
                raise error
409
 
        else:
410
403
            if pid:
411
404
                logger.warning(u"Checker was a zombie")
412
405
                gobject.source_remove(self.checker_callback_tag)
468
461
        logger.debug(u"Stopping checker for %(name)s", vars(self))
469
462
        try:
470
463
            os.kill(self.checker.pid, signal.SIGTERM)
471
 
            #time.sleep(0.5)
 
464
            #os.sleep(0.5)
472
465
            #if self.checker.poll() is None:
473
466
            #    os.kill(self.checker.pid, signal.SIGKILL)
474
467
        except OSError, error:
475
468
            if error.errno != errno.ESRCH: # No such process
476
469
                raise
477
470
        self.checker = None
 
471
    
 
472
    def still_valid(self):
 
473
        """Has the timeout not yet passed for this client?"""
 
474
        if not getattr(self, u"enabled", False):
 
475
            return False
 
476
        now = datetime.datetime.utcnow()
 
477
        if self.last_checked_ok is None:
 
478
            return now < (self.created + self.timeout)
 
479
        else:
 
480
            return now < (self.last_checked_ok + self.timeout)
478
481
 
479
482
 
480
483
def dbus_service_property(dbus_interface, signature=u"v",
489
492
    dbus.service.method, except there is only "signature", since the
490
493
    type from Get() and the type sent to Set() is the same.
491
494
    """
492
 
    # Encoding deeply encoded byte arrays is not supported yet by the
493
 
    # "Set" method, so we fail early here:
494
 
    if byte_arrays and signature != u"ay":
495
 
        raise ValueError(u"Byte arrays not supported for non-'ay'"
496
 
                         u" signature %r" % signature)
497
495
    def decorator(func):
498
496
        func._dbus_is_property = True
499
497
        func._dbus_interface = dbus_interface
585
583
        if prop._dbus_access == u"read":
586
584
            raise DBusPropertyAccessException(property_name)
587
585
        if prop._dbus_get_args_options[u"byte_arrays"]:
588
 
            # The byte_arrays option is not supported yet on
589
 
            # signatures other than "ay".
590
 
            if prop._dbus_signature != u"ay":
591
 
                raise ValueError
592
586
            value = dbus.ByteArray(''.join(unichr(byte)
593
587
                                           for byte in value))
594
588
        prop(value)
626
620
        """Standard D-Bus method, overloaded to insert property tags.
627
621
        """
628
622
        xmlstring = dbus.service.Object.Introspect(self, object_path,
629
 
                                                   connection)
630
 
        try:
631
 
            document = xml.dom.minidom.parseString(xmlstring)
632
 
            def make_tag(document, name, prop):
633
 
                e = document.createElement(u"property")
634
 
                e.setAttribute(u"name", name)
635
 
                e.setAttribute(u"type", prop._dbus_signature)
636
 
                e.setAttribute(u"access", prop._dbus_access)
637
 
                return e
638
 
            for if_tag in document.getElementsByTagName(u"interface"):
639
 
                for tag in (make_tag(document, name, prop)
640
 
                            for name, prop
641
 
                            in self._get_all_dbus_properties()
642
 
                            if prop._dbus_interface
643
 
                            == if_tag.getAttribute(u"name")):
644
 
                    if_tag.appendChild(tag)
645
 
                # Add the names to the return values for the
646
 
                # "org.freedesktop.DBus.Properties" methods
647
 
                if (if_tag.getAttribute(u"name")
648
 
                    == u"org.freedesktop.DBus.Properties"):
649
 
                    for cn in if_tag.getElementsByTagName(u"method"):
650
 
                        if cn.getAttribute(u"name") == u"Get":
651
 
                            for arg in cn.getElementsByTagName(u"arg"):
652
 
                                if (arg.getAttribute(u"direction")
653
 
                                    == u"out"):
654
 
                                    arg.setAttribute(u"name", u"value")
655
 
                        elif cn.getAttribute(u"name") == u"GetAll":
656
 
                            for arg in cn.getElementsByTagName(u"arg"):
657
 
                                if (arg.getAttribute(u"direction")
658
 
                                    == u"out"):
659
 
                                    arg.setAttribute(u"name", u"props")
660
 
            xmlstring = document.toxml(u"utf-8")
661
 
            document.unlink()
662
 
        except (AttributeError, xml.dom.DOMException,
663
 
                xml.parsers.expat.ExpatError), error:
664
 
            logger.error(u"Failed to override Introspection method",
665
 
                         error)
 
623
                                           connection)
 
624
        document = xml.dom.minidom.parseString(xmlstring)
 
625
        del xmlstring
 
626
        def make_tag(document, name, prop):
 
627
            e = document.createElement(u"property")
 
628
            e.setAttribute(u"name", name)
 
629
            e.setAttribute(u"type", prop._dbus_signature)
 
630
            e.setAttribute(u"access", prop._dbus_access)
 
631
            return e
 
632
        for if_tag in document.getElementsByTagName(u"interface"):
 
633
            for tag in (make_tag(document, name, prop)
 
634
                        for name, prop
 
635
                        in self._get_all_dbus_properties()
 
636
                        if prop._dbus_interface
 
637
                        == if_tag.getAttribute(u"name")):
 
638
                if_tag.appendChild(tag)
 
639
        xmlstring = document.toxml(u"utf-8")
 
640
        document.unlink()
666
641
        return xmlstring
667
642
 
668
643
 
705
680
                                       variant_level=1))
706
681
        return r
707
682
    
708
 
    def disable(self, quiet = False):
 
683
    def disable(self, signal = True):
709
684
        oldstate = getattr(self, u"enabled", False)
710
 
        r = Client.disable(self, quiet=quiet)
711
 
        if not quiet and oldstate != self.enabled:
 
685
        r = Client.disable(self)
 
686
        if signal and oldstate != self.enabled:
712
687
            # Emit D-Bus signal
713
688
            self.PropertyChanged(dbus.String(u"enabled"),
714
689
                                 dbus.Boolean(False, variant_level=1))
783
758
    ## D-Bus methods & signals
784
759
    _interface = u"se.bsnet.fukt.Mandos.Client"
785
760
    
786
 
    # CheckedOK - method
787
 
    @dbus.service.method(_interface)
788
 
    def CheckedOK(self):
789
 
        return self.checked_ok()
790
 
    
791
761
    # CheckerCompleted - signal
792
762
    @dbus.service.signal(_interface, signature=u"nxs")
793
763
    def CheckerCompleted(self, exitcode, waitstatus, command):
806
776
        "D-Bus signal"
807
777
        pass
808
778
    
809
 
    # GotSecret - signal
 
779
    # ReceivedSecret - signal
810
780
    @dbus.service.signal(_interface)
811
 
    def GotSecret(self):
 
781
    def ReceivedSecret(self):
812
782
        "D-Bus signal"
813
783
        pass
814
784
    
818
788
        "D-Bus signal"
819
789
        pass
820
790
    
821
 
    # Enable - method
822
 
    @dbus.service.method(_interface)
823
 
    def Enable(self):
824
 
        "D-Bus method"
825
 
        self.enable()
826
 
    
827
 
    # StartChecker - method
828
 
    @dbus.service.method(_interface)
829
 
    def StartChecker(self):
830
 
        "D-Bus method"
831
 
        self.start_checker()
832
 
    
833
 
    # Disable - method
834
 
    @dbus.service.method(_interface)
835
 
    def Disable(self):
836
 
        "D-Bus method"
837
 
        self.disable()
838
 
    
839
 
    # StopChecker - method
840
 
    @dbus.service.method(_interface)
841
 
    def StopChecker(self):
842
 
        self.stop_checker()
843
 
    
844
791
    # name - property
845
792
    @dbus_service_property(_interface, signature=u"s", access=u"read")
846
793
    def name_dbus_property(self):
886
833
            self.disable()
887
834
    
888
835
    # last_checked_ok - property
889
 
    @dbus_service_property(_interface, signature=u"s",
890
 
                           access=u"readwrite")
891
 
    def last_checked_ok_dbus_property(self, value=None):
892
 
        if value is not None:
893
 
            self.checked_ok()
894
 
            return
 
836
    @dbus_service_property(_interface, signature=u"s", access=u"read")
 
837
    def last_checked_ok_dbus_property(self):
895
838
        if self.last_checked_ok is None:
896
839
            return dbus.String(u"")
897
840
        return dbus.String(self._datetime_to_dbus(self
971
914
    def object_path_dbus_property(self):
972
915
        return self.dbus_object_path # is already a dbus.ObjectPath
973
916
    
974
 
    # secret = property
 
917
    # secret = property xxx
975
918
    @dbus_service_property(_interface, signature=u"ay",
976
919
                           access=u"write", byte_arrays=True)
977
920
    def secret_dbus_property(self, value):
989
932
    def handle(self):
990
933
        logger.info(u"TCP connection from: %s",
991
934
                    unicode(self.client_address))
992
 
        logger.debug(u"IPC Pipe FD: %d", self.server.child_pipe[1])
 
935
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
993
936
        # Open IPC pipe to parent process
994
 
        with contextlib.nested(os.fdopen(self.server.child_pipe[1],
995
 
                                         u"w", 1),
996
 
                               os.fdopen(self.server.parent_pipe[0],
997
 
                                         u"r", 0)) as (ipc,
998
 
                                                       ipc_return):
 
937
        with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
999
938
            session = (gnutls.connection
1000
939
                       .ClientSession(self.request,
1001
940
                                      gnutls.connection
1036
975
                return
1037
976
            logger.debug(u"Handshake succeeded")
1038
977
            try:
1039
 
                try:
1040
 
                    fpr = self.fingerprint(self.peer_certificate
1041
 
                                           (session))
1042
 
                except (TypeError, gnutls.errors.GNUTLSError), error:
1043
 
                    logger.warning(u"Bad certificate: %s", error)
1044
 
                    return
1045
 
                logger.debug(u"Fingerprint: %s", fpr)
1046
 
 
1047
 
                for c in self.server.clients:
1048
 
                    if c.fingerprint == fpr:
1049
 
                        client = c
1050
 
                        break
1051
 
                else:
1052
 
                    ipc.write(u"NOTFOUND %s %s\n"
1053
 
                              % (fpr, unicode(self.client_address)))
1054
 
                    return
1055
 
                # Have to check if client.enabled, since it is
1056
 
                # possible that the client was disabled since the
1057
 
                # GnuTLS session was established.
1058
 
                ipc.write(u"GETATTR enabled %s\n" % fpr)
1059
 
                enabled = pickle.load(ipc_return)
1060
 
                if not enabled:
1061
 
                    ipc.write(u"DISABLED %s\n" % client.name)
1062
 
                    return
1063
 
                ipc.write(u"SENDING %s\n" % client.name)
1064
 
                sent_size = 0
1065
 
                while sent_size < len(client.secret):
1066
 
                    sent = session.send(client.secret[sent_size:])
1067
 
                    logger.debug(u"Sent: %d, remaining: %d",
1068
 
                                 sent, len(client.secret)
1069
 
                                 - (sent_size + sent))
1070
 
                    sent_size += sent
1071
 
            finally:
1072
 
                session.bye()
 
978
                fpr = self.fingerprint(self.peer_certificate(session))
 
979
            except (TypeError, gnutls.errors.GNUTLSError), error:
 
980
                logger.warning(u"Bad certificate: %s", error)
 
981
                session.bye()
 
982
                return
 
983
            logger.debug(u"Fingerprint: %s", fpr)
 
984
            
 
985
            for c in self.server.clients:
 
986
                if c.fingerprint == fpr:
 
987
                    client = c
 
988
                    break
 
989
            else:
 
990
                ipc.write(u"NOTFOUND %s %s\n"
 
991
                          % (fpr, unicode(self.client_address)))
 
992
                session.bye()
 
993
                return
 
994
            # Have to check if client.still_valid(), since it is
 
995
            # possible that the client timed out while establishing
 
996
            # the GnuTLS session.
 
997
            if not client.still_valid():
 
998
                ipc.write(u"INVALID %s\n" % client.name)
 
999
                session.bye()
 
1000
                return
 
1001
            ipc.write(u"SENDING %s\n" % client.name)
 
1002
            sent_size = 0
 
1003
            while sent_size < len(client.secret):
 
1004
                sent = session.send(client.secret[sent_size:])
 
1005
                logger.debug(u"Sent: %d, remaining: %d",
 
1006
                             sent, len(client.secret)
 
1007
                             - (sent_size + sent))
 
1008
                sent_size += sent
 
1009
            session.bye()
1073
1010
    
1074
1011
    @staticmethod
1075
1012
    def peer_certificate(session):
1135
1072
        return hex_fpr
1136
1073
 
1137
1074
 
1138
 
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1139
 
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
 
1075
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
 
1076
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
1140
1077
    def process_request(self, request, client_address):
1141
1078
        """Overrides and wraps the original process_request().
1142
1079
        
1143
1080
        This function creates a new pipe in self.pipe
1144
1081
        """
1145
 
        self.child_pipe = os.pipe() # Child writes here
1146
 
        self.parent_pipe = os.pipe() # Parent writes here
1147
 
        super(ForkingMixInWithPipes,
 
1082
        self.pipe = os.pipe()
 
1083
        super(ForkingMixInWithPipe,
1148
1084
              self).process_request(request, client_address)
1149
 
        # Close unused ends for parent
1150
 
        os.close(self.parent_pipe[0]) # close read end
1151
 
        os.close(self.child_pipe[1])  # close write end
1152
 
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1153
 
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
 
1085
        os.close(self.pipe[1])  # close write end
 
1086
        self.add_pipe(self.pipe[0])
 
1087
    def add_pipe(self, pipe):
1154
1088
        """Dummy function; override as necessary"""
1155
 
        os.close(child_pipe_fd)
1156
 
        os.close(parent_pipe_fd)
1157
 
 
1158
 
 
1159
 
class IPv6_TCPServer(ForkingMixInWithPipes,
 
1089
        os.close(pipe)
 
1090
 
 
1091
 
 
1092
class IPv6_TCPServer(ForkingMixInWithPipe,
1160
1093
                     socketserver.TCPServer, object):
1161
1094
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1162
1095
    
1247
1180
            return socketserver.TCPServer.server_activate(self)
1248
1181
    def enable(self):
1249
1182
        self.enabled = True
1250
 
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
 
1183
    def add_pipe(self, pipe):
1251
1184
        # Call "handle_ipc" for both data and EOF events
1252
 
        gobject.io_add_watch(child_pipe_fd,
1253
 
                             gobject.IO_IN | gobject.IO_HUP,
1254
 
                             functools.partial(self.handle_ipc,
1255
 
                                               reply_fd
1256
 
                                               =parent_pipe_fd))
1257
 
    def handle_ipc(self, source, condition, reply_fd=None,
1258
 
                   file_objects={}):
 
1185
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
 
1186
                             self.handle_ipc)
 
1187
    def handle_ipc(self, source, condition, file_objects={}):
1259
1188
        condition_names = {
1260
1189
            gobject.IO_IN: u"IN",   # There is data to read.
1261
1190
            gobject.IO_OUT: u"OUT", # Data can be written (without
1273
1202
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1274
1203
                     conditions_string)
1275
1204
        
1276
 
        # Turn the pipe file descriptors into Python file objects
 
1205
        # Turn the pipe file descriptor into a Python file object
1277
1206
        if source not in file_objects:
1278
1207
            file_objects[source] = os.fdopen(source, u"r", 1)
1279
 
        if reply_fd not in file_objects:
1280
 
            file_objects[reply_fd] = os.fdopen(reply_fd, u"w", 0)
1281
1208
        
1282
1209
        # Read a line from the file object
1283
1210
        cmdline = file_objects[source].readline()
1284
1211
        if not cmdline:             # Empty line means end of file
1285
 
            # close the IPC pipes
 
1212
            # close the IPC pipe
1286
1213
            file_objects[source].close()
1287
1214
            del file_objects[source]
1288
 
            file_objects[reply_fd].close()
1289
 
            del file_objects[reply_fd]
1290
1215
            
1291
1216
            # Stop calling this function
1292
1217
            return False
1297
1222
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1298
1223
        
1299
1224
        if cmd == u"NOTFOUND":
1300
 
            fpr, address = args.split(None, 1)
1301
 
            logger.warning(u"Client not found for fingerprint: %s, ad"
1302
 
                           u"dress: %s", fpr, address)
 
1225
            logger.warning(u"Client not found for fingerprint: %s",
 
1226
                           args)
1303
1227
            if self.use_dbus:
1304
1228
                # Emit D-Bus signal
1305
 
                mandos_dbus_service.ClientNotFound(fpr, address)
1306
 
        elif cmd == u"DISABLED":
 
1229
                mandos_dbus_service.ClientNotFound(args)
 
1230
        elif cmd == u"INVALID":
1307
1231
            for client in self.clients:
1308
1232
                if client.name == args:
1309
 
                    logger.warning(u"Client %s is disabled", args)
 
1233
                    logger.warning(u"Client %s is invalid", args)
1310
1234
                    if self.use_dbus:
1311
1235
                        # Emit D-Bus signal
1312
1236
                        client.Rejected()
1313
1237
                    break
1314
1238
            else:
1315
 
                logger.error(u"Unknown client %s is disabled", args)
 
1239
                logger.error(u"Unknown client %s is invalid", args)
1316
1240
        elif cmd == u"SENDING":
1317
1241
            for client in self.clients:
1318
1242
                if client.name == args:
1320
1244
                    client.checked_ok()
1321
1245
                    if self.use_dbus:
1322
1246
                        # Emit D-Bus signal
1323
 
                        client.GotSecret()
 
1247
                        client.ReceivedSecret()
1324
1248
                    break
1325
1249
            else:
1326
1250
                logger.error(u"Sending secret to unknown client %s",
1327
1251
                             args)
1328
 
        elif cmd == u"GETATTR":
1329
 
            attr_name, fpr = args.split(None, 1)
1330
 
            for client in self.clients:
1331
 
                if client.fingerprint == fpr:
1332
 
                    attr_value = getattr(client, attr_name, None)
1333
 
                    logger.debug("IPC reply: %r", attr_value)
1334
 
                    pickle.dump(attr_value, file_objects[reply_fd])
1335
 
                    break
1336
 
            else:
1337
 
                logger.error(u"Client %s on address %s requesting "
1338
 
                             u"attribute %s not found", fpr, address,
1339
 
                             attr_name)
1340
 
                pickle.dump(None, file_objects[reply_fd])
1341
1252
        else:
1342
1253
            logger.error(u"Unknown IPC command: %r", cmdline)
1343
1254
        
1377
1288
            elif suffix == u"w":
1378
1289
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1379
1290
            else:
1380
 
                raise ValueError(u"Unknown suffix %r" % suffix)
1381
 
        except (ValueError, IndexError), e:
1382
 
            raise ValueError(e.message)
 
1291
                raise ValueError
 
1292
        except (ValueError, IndexError):
 
1293
            raise ValueError
1383
1294
        timevalue += delta
1384
1295
    return timevalue
1385
1296
 
1398
1309
        def if_nametoindex(interface):
1399
1310
            "Get an interface index the hard way, i.e. using fcntl()"
1400
1311
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
1401
 
            with contextlib.closing(socket.socket()) as s:
 
1312
            with closing(socket.socket()) as s:
1402
1313
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1403
1314
                                    struct.pack(str(u"16s16x"),
1404
1315
                                                interface))
1424
1335
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1425
1336
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1426
1337
            raise OSError(errno.ENODEV,
1427
 
                          u"%s not a character device"
1428
 
                          % os.path.devnull)
 
1338
                          u"/dev/null not a character device")
1429
1339
        os.dup2(null, sys.stdin.fileno())
1430
1340
        os.dup2(null, sys.stdout.fileno())
1431
1341
        os.dup2(null, sys.stderr.fileno())
1598
1508
    bus = dbus.SystemBus()
1599
1509
    # End of Avahi example code
1600
1510
    if use_dbus:
1601
 
        try:
1602
 
            bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos",
1603
 
                                            bus, do_not_queue=True)
1604
 
        except dbus.exceptions.NameExistsException, e:
1605
 
            logger.error(unicode(e) + u", disabling D-Bus")
1606
 
            use_dbus = False
1607
 
            server_settings[u"use_dbus"] = False
1608
 
            tcp_server.use_dbus = False
 
1511
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1609
1512
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1610
1513
    service = AvahiService(name = server_settings[u"servicename"],
1611
1514
                           servicetype = u"_mandos._tcp",
1637
1540
        daemon()
1638
1541
    
1639
1542
    try:
1640
 
        with pidfile:
 
1543
        with closing(pidfile):
1641
1544
            pid = os.getpid()
1642
1545
            pidfile.write(str(pid) + "\n")
1643
1546
        del pidfile
1649
1552
        pass
1650
1553
    del pidfilename
1651
1554
    
 
1555
    def cleanup():
 
1556
        "Cleanup function; run on exit"
 
1557
        service.cleanup()
 
1558
        
 
1559
        while tcp_server.clients:
 
1560
            client = tcp_server.clients.pop()
 
1561
            client.disable_hook = None
 
1562
            client.disable()
 
1563
    
 
1564
    atexit.register(cleanup)
 
1565
    
1652
1566
    if not debug:
1653
1567
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1654
1568
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1661
1575
                dbus.service.Object.__init__(self, bus, u"/")
1662
1576
            _interface = u"se.bsnet.fukt.Mandos"
1663
1577
            
1664
 
            @dbus.service.signal(_interface, signature=u"o")
1665
 
            def ClientAdded(self, objpath):
 
1578
            @dbus.service.signal(_interface, signature=u"oa{sv}")
 
1579
            def ClientAdded(self, objpath, properties):
1666
1580
                "D-Bus signal"
1667
1581
                pass
1668
1582
            
1669
 
            @dbus.service.signal(_interface, signature=u"ss")
1670
 
            def ClientNotFound(self, fingerprint, address):
 
1583
            @dbus.service.signal(_interface, signature=u"s")
 
1584
            def ClientNotFound(self, fingerprint):
1671
1585
                "D-Bus signal"
1672
1586
                pass
1673
1587
            
1699
1613
                        tcp_server.clients.remove(c)
1700
1614
                        c.remove_from_connection()
1701
1615
                        # Don't signal anything except ClientRemoved
1702
 
                        c.disable(quiet=True)
 
1616
                        c.disable(signal=False)
1703
1617
                        # Emit D-Bus signal
1704
1618
                        self.ClientRemoved(object_path, c.name)
1705
1619
                        return
1706
 
                raise KeyError(object_path)
 
1620
                raise KeyError
1707
1621
            
1708
1622
            del _interface
1709
1623
        
1710
1624
        mandos_dbus_service = MandosDBusService()
1711
1625
    
1712
 
    def cleanup():
1713
 
        "Cleanup function; run on exit"
1714
 
        service.cleanup()
1715
 
        
1716
 
        while tcp_server.clients:
1717
 
            client = tcp_server.clients.pop()
1718
 
            if use_dbus:
1719
 
                client.remove_from_connection()
1720
 
            client.disable_hook = None
1721
 
            # Don't signal anything except ClientRemoved
1722
 
            client.disable(quiet=True)
1723
 
            if use_dbus:
1724
 
                # Emit D-Bus signal
1725
 
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
1726
 
                                                  client.name)
1727
 
    
1728
 
    atexit.register(cleanup)
1729
 
    
1730
1626
    for client in tcp_server.clients:
1731
1627
        if use_dbus:
1732
1628
            # Emit D-Bus signal
1733
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
 
1629
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
 
1630
                                            client.GetAll(u""))
1734
1631
        client.enable()
1735
1632
    
1736
1633
    tcp_server.enable()
1754
1651
            service.activate()
1755
1652
        except dbus.exceptions.DBusException, error:
1756
1653
            logger.critical(u"DBusException: %s", error)
1757
 
            cleanup()
1758
1654
            sys.exit(1)
1759
1655
        # End of Avahi example code
1760
1656
        
1767
1663
        main_loop.run()
1768
1664
    except AvahiError, error:
1769
1665
        logger.critical(u"AvahiError: %s", error)
1770
 
        cleanup()
1771
1666
        sys.exit(1)
1772
1667
    except KeyboardInterrupt:
1773
1668
        if debug:
1774
1669
            print >> sys.stderr
1775
1670
        logger.debug(u"Server received KeyboardInterrupt")
1776
1671
    logger.debug(u"Server exiting")
1777
 
    # Must run before the D-Bus bus name gets deregistered
1778
 
    cleanup()
1779
1672
 
1780
1673
if __name__ == '__main__':
1781
1674
    main()