/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

Merge from Teddy:

* New upstream release.
* debian/rules: Build with BROKEN_PIE set on mips and mipsel
  architectures - fixes FTBFS there.
* New upstream release.
* Do not copy unnecessary files to initrd (Closes: #551907)

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
 
from contextlib import closing
 
58
import contextlib
59
59
import struct
60
60
import fcntl
61
61
import functools
 
62
import cPickle as pickle
62
63
 
63
64
import dbus
64
65
import dbus.service
79
80
        SO_BINDTODEVICE = None
80
81
 
81
82
 
82
 
version = "1.0.12"
 
83
version = "1.0.14"
83
84
 
84
85
logger = logging.Logger(u'mandos')
85
86
syslogger = (logging.handlers.SysLogHandler
242
243
    enabled:    bool()
243
244
    last_checked_ok: datetime.datetime(); (UTC) or None
244
245
    timeout:    datetime.timedelta(); How long from last_checked_ok
245
 
                                      until this client is invalid
 
246
                                      until this client is disabled
246
247
    interval:   datetime.timedelta(); How often to start a new checker
247
248
    disable_hook:  If set, called by disable() as disable_hook(self)
248
249
    checker:    subprocess.Popen(); a running checker process used
290
291
        if u"secret" in config:
291
292
            self.secret = config[u"secret"].decode(u"base64")
292
293
        elif u"secfile" in config:
293
 
            with closing(open(os.path.expanduser
294
 
                              (os.path.expandvars
295
 
                               (config[u"secfile"])))) as secfile:
 
294
            with open(os.path.expanduser(os.path.expandvars
 
295
                                         (config[u"secfile"])),
 
296
                      "rb") as secfile:
296
297
                self.secret = secfile.read()
297
298
        else:
298
299
            raise TypeError(u"No secret or secfile for client %s"
324
325
        self.checker_initiator_tag = (gobject.timeout_add
325
326
                                      (self.interval_milliseconds(),
326
327
                                       self.start_checker))
327
 
        # Also start a new checker *right now*.
328
 
        self.start_checker()
329
328
        # Schedule a disable() when 'timeout' has passed
330
329
        self.disable_initiator_tag = (gobject.timeout_add
331
330
                                   (self.timeout_milliseconds(),
332
331
                                    self.disable))
333
332
        self.enabled = True
 
333
        # Also start a new checker *right now*.
 
334
        self.start_checker()
334
335
    
335
 
    def disable(self):
 
336
    def disable(self, quiet=True):
336
337
        """Disable this client."""
337
338
        if not getattr(self, "enabled", False):
338
339
            return False
339
 
        logger.info(u"Disabling client %s", self.name)
 
340
        if not quiet:
 
341
            logger.info(u"Disabling client %s", self.name)
340
342
        if getattr(self, u"disable_initiator_tag", False):
341
343
            gobject.source_remove(self.disable_initiator_tag)
342
344
            self.disable_initiator_tag = None
394
396
        # client would inevitably timeout, since no checker would get
395
397
        # a chance to run to completion.  If we instead leave running
396
398
        # checkers alone, the checker would have to take more time
397
 
        # than 'timeout' for the client to be declared invalid, which
398
 
        # is as it should be.
 
399
        # than 'timeout' for the client to be disabled, which is as it
 
400
        # should be.
399
401
        
400
402
        # If a checker exists, make sure it is not a zombie
401
 
        if self.checker is not None:
 
403
        try:
402
404
            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:
403
410
            if pid:
404
411
                logger.warning(u"Checker was a zombie")
405
412
                gobject.source_remove(self.checker_callback_tag)
461
468
        logger.debug(u"Stopping checker for %(name)s", vars(self))
462
469
        try:
463
470
            os.kill(self.checker.pid, signal.SIGTERM)
464
 
            #os.sleep(0.5)
 
471
            #time.sleep(0.5)
465
472
            #if self.checker.poll() is None:
466
473
            #    os.kill(self.checker.pid, signal.SIGKILL)
467
474
        except OSError, error:
468
475
            if error.errno != errno.ESRCH: # No such process
469
476
                raise
470
477
        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)
481
478
 
482
479
 
483
480
def dbus_service_property(dbus_interface, signature=u"v",
492
489
    dbus.service.method, except there is only "signature", since the
493
490
    type from Get() and the type sent to Set() is the same.
494
491
    """
 
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)
495
497
    def decorator(func):
496
498
        func._dbus_is_property = True
497
499
        func._dbus_interface = dbus_interface
583
585
        if prop._dbus_access == u"read":
584
586
            raise DBusPropertyAccessException(property_name)
585
587
        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
586
592
            value = dbus.ByteArray(''.join(unichr(byte)
587
593
                                           for byte in value))
588
594
        prop(value)
620
626
        """Standard D-Bus method, overloaded to insert property tags.
621
627
        """
622
628
        xmlstring = dbus.service.Object.Introspect(self, object_path,
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()
 
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)
641
666
        return xmlstring
642
667
 
643
668
 
680
705
                                       variant_level=1))
681
706
        return r
682
707
    
683
 
    def disable(self, signal = True):
 
708
    def disable(self, quiet = False):
684
709
        oldstate = getattr(self, u"enabled", False)
685
 
        r = Client.disable(self)
686
 
        if signal and oldstate != self.enabled:
 
710
        r = Client.disable(self, quiet=quiet)
 
711
        if not quiet and oldstate != self.enabled:
687
712
            # Emit D-Bus signal
688
713
            self.PropertyChanged(dbus.String(u"enabled"),
689
714
                                 dbus.Boolean(False, variant_level=1))
758
783
    ## D-Bus methods & signals
759
784
    _interface = u"se.bsnet.fukt.Mandos.Client"
760
785
    
 
786
    # CheckedOK - method
 
787
    @dbus.service.method(_interface)
 
788
    def CheckedOK(self):
 
789
        return self.checked_ok()
 
790
    
761
791
    # CheckerCompleted - signal
762
792
    @dbus.service.signal(_interface, signature=u"nxs")
763
793
    def CheckerCompleted(self, exitcode, waitstatus, command):
776
806
        "D-Bus signal"
777
807
        pass
778
808
    
779
 
    # ReceivedSecret - signal
 
809
    # GotSecret - signal
780
810
    @dbus.service.signal(_interface)
781
 
    def ReceivedSecret(self):
 
811
    def GotSecret(self):
782
812
        "D-Bus signal"
783
813
        pass
784
814
    
788
818
        "D-Bus signal"
789
819
        pass
790
820
    
 
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
    
791
844
    # name - property
792
845
    @dbus_service_property(_interface, signature=u"s", access=u"read")
793
846
    def name_dbus_property(self):
833
886
            self.disable()
834
887
    
835
888
    # last_checked_ok - property
836
 
    @dbus_service_property(_interface, signature=u"s", access=u"read")
837
 
    def last_checked_ok_dbus_property(self):
 
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
838
895
        if self.last_checked_ok is None:
839
896
            return dbus.String(u"")
840
897
        return dbus.String(self._datetime_to_dbus(self
914
971
    def object_path_dbus_property(self):
915
972
        return self.dbus_object_path # is already a dbus.ObjectPath
916
973
    
917
 
    # secret = property xxx
 
974
    # secret = property
918
975
    @dbus_service_property(_interface, signature=u"ay",
919
976
                           access=u"write", byte_arrays=True)
920
977
    def secret_dbus_property(self, value):
932
989
    def handle(self):
933
990
        logger.info(u"TCP connection from: %s",
934
991
                    unicode(self.client_address))
935
 
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
 
992
        logger.debug(u"IPC Pipe FD: %d", self.server.child_pipe[1])
936
993
        # Open IPC pipe to parent process
937
 
        with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
 
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):
938
999
            session = (gnutls.connection
939
1000
                       .ClientSession(self.request,
940
1001
                                      gnutls.connection
975
1036
                return
976
1037
            logger.debug(u"Handshake succeeded")
977
1038
            try:
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()
 
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()
1010
1073
    
1011
1074
    @staticmethod
1012
1075
    def peer_certificate(session):
1072
1135
        return hex_fpr
1073
1136
 
1074
1137
 
1075
 
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1076
 
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
 
1138
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
 
1139
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1077
1140
    def process_request(self, request, client_address):
1078
1141
        """Overrides and wraps the original process_request().
1079
1142
        
1080
1143
        This function creates a new pipe in self.pipe
1081
1144
        """
1082
 
        self.pipe = os.pipe()
1083
 
        super(ForkingMixInWithPipe,
 
1145
        self.child_pipe = os.pipe() # Child writes here
 
1146
        self.parent_pipe = os.pipe() # Parent writes here
 
1147
        super(ForkingMixInWithPipes,
1084
1148
              self).process_request(request, client_address)
1085
 
        os.close(self.pipe[1])  # close write end
1086
 
        self.add_pipe(self.pipe[0])
1087
 
    def add_pipe(self, pipe):
 
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):
1088
1154
        """Dummy function; override as necessary"""
1089
 
        os.close(pipe)
1090
 
 
1091
 
 
1092
 
class IPv6_TCPServer(ForkingMixInWithPipe,
 
1155
        os.close(child_pipe_fd)
 
1156
        os.close(parent_pipe_fd)
 
1157
 
 
1158
 
 
1159
class IPv6_TCPServer(ForkingMixInWithPipes,
1093
1160
                     socketserver.TCPServer, object):
1094
1161
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1095
1162
    
1180
1247
            return socketserver.TCPServer.server_activate(self)
1181
1248
    def enable(self):
1182
1249
        self.enabled = True
1183
 
    def add_pipe(self, pipe):
 
1250
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1184
1251
        # Call "handle_ipc" for both data and EOF events
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={}):
 
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={}):
1188
1259
        condition_names = {
1189
1260
            gobject.IO_IN: u"IN",   # There is data to read.
1190
1261
            gobject.IO_OUT: u"OUT", # Data can be written (without
1202
1273
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1203
1274
                     conditions_string)
1204
1275
        
1205
 
        # Turn the pipe file descriptor into a Python file object
 
1276
        # Turn the pipe file descriptors into Python file objects
1206
1277
        if source not in file_objects:
1207
1278
            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)
1208
1281
        
1209
1282
        # Read a line from the file object
1210
1283
        cmdline = file_objects[source].readline()
1211
1284
        if not cmdline:             # Empty line means end of file
1212
 
            # close the IPC pipe
 
1285
            # close the IPC pipes
1213
1286
            file_objects[source].close()
1214
1287
            del file_objects[source]
 
1288
            file_objects[reply_fd].close()
 
1289
            del file_objects[reply_fd]
1215
1290
            
1216
1291
            # Stop calling this function
1217
1292
            return False
1222
1297
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1223
1298
        
1224
1299
        if cmd == u"NOTFOUND":
1225
 
            logger.warning(u"Client not found for fingerprint: %s",
1226
 
                           args)
 
1300
            fpr, address = args.split(None, 1)
 
1301
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1302
                           u"dress: %s", fpr, address)
1227
1303
            if self.use_dbus:
1228
1304
                # Emit D-Bus signal
1229
 
                mandos_dbus_service.ClientNotFound(args)
1230
 
        elif cmd == u"INVALID":
 
1305
                mandos_dbus_service.ClientNotFound(fpr, address)
 
1306
        elif cmd == u"DISABLED":
1231
1307
            for client in self.clients:
1232
1308
                if client.name == args:
1233
 
                    logger.warning(u"Client %s is invalid", args)
 
1309
                    logger.warning(u"Client %s is disabled", args)
1234
1310
                    if self.use_dbus:
1235
1311
                        # Emit D-Bus signal
1236
1312
                        client.Rejected()
1237
1313
                    break
1238
1314
            else:
1239
 
                logger.error(u"Unknown client %s is invalid", args)
 
1315
                logger.error(u"Unknown client %s is disabled", args)
1240
1316
        elif cmd == u"SENDING":
1241
1317
            for client in self.clients:
1242
1318
                if client.name == args:
1244
1320
                    client.checked_ok()
1245
1321
                    if self.use_dbus:
1246
1322
                        # Emit D-Bus signal
1247
 
                        client.ReceivedSecret()
 
1323
                        client.GotSecret()
1248
1324
                    break
1249
1325
            else:
1250
1326
                logger.error(u"Sending secret to unknown client %s",
1251
1327
                             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])
1252
1341
        else:
1253
1342
            logger.error(u"Unknown IPC command: %r", cmdline)
1254
1343
        
1288
1377
            elif suffix == u"w":
1289
1378
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1290
1379
            else:
1291
 
                raise ValueError
1292
 
        except (ValueError, IndexError):
1293
 
            raise ValueError
 
1380
                raise ValueError(u"Unknown suffix %r" % suffix)
 
1381
        except (ValueError, IndexError), e:
 
1382
            raise ValueError(e.message)
1294
1383
        timevalue += delta
1295
1384
    return timevalue
1296
1385
 
1309
1398
        def if_nametoindex(interface):
1310
1399
            "Get an interface index the hard way, i.e. using fcntl()"
1311
1400
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
1312
 
            with closing(socket.socket()) as s:
 
1401
            with contextlib.closing(socket.socket()) as s:
1313
1402
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1314
1403
                                    struct.pack(str(u"16s16x"),
1315
1404
                                                interface))
1335
1424
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1336
1425
        if not stat.S_ISCHR(os.fstat(null).st_mode):
1337
1426
            raise OSError(errno.ENODEV,
1338
 
                          u"/dev/null not a character device")
 
1427
                          u"%s not a character device"
 
1428
                          % os.path.devnull)
1339
1429
        os.dup2(null, sys.stdin.fileno())
1340
1430
        os.dup2(null, sys.stdout.fileno())
1341
1431
        os.dup2(null, sys.stderr.fileno())
1508
1598
    bus = dbus.SystemBus()
1509
1599
    # End of Avahi example code
1510
1600
    if use_dbus:
1511
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
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
1512
1609
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1513
1610
    service = AvahiService(name = server_settings[u"servicename"],
1514
1611
                           servicetype = u"_mandos._tcp",
1540
1637
        daemon()
1541
1638
    
1542
1639
    try:
1543
 
        with closing(pidfile):
 
1640
        with pidfile:
1544
1641
            pid = os.getpid()
1545
1642
            pidfile.write(str(pid) + "\n")
1546
1643
        del pidfile
1552
1649
        pass
1553
1650
    del pidfilename
1554
1651
    
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
 
    
1566
1652
    if not debug:
1567
1653
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1568
1654
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1575
1661
                dbus.service.Object.__init__(self, bus, u"/")
1576
1662
            _interface = u"se.bsnet.fukt.Mandos"
1577
1663
            
1578
 
            @dbus.service.signal(_interface, signature=u"oa{sv}")
1579
 
            def ClientAdded(self, objpath, properties):
 
1664
            @dbus.service.signal(_interface, signature=u"o")
 
1665
            def ClientAdded(self, objpath):
1580
1666
                "D-Bus signal"
1581
1667
                pass
1582
1668
            
1583
 
            @dbus.service.signal(_interface, signature=u"s")
1584
 
            def ClientNotFound(self, fingerprint):
 
1669
            @dbus.service.signal(_interface, signature=u"ss")
 
1670
            def ClientNotFound(self, fingerprint, address):
1585
1671
                "D-Bus signal"
1586
1672
                pass
1587
1673
            
1613
1699
                        tcp_server.clients.remove(c)
1614
1700
                        c.remove_from_connection()
1615
1701
                        # Don't signal anything except ClientRemoved
1616
 
                        c.disable(signal=False)
 
1702
                        c.disable(quiet=True)
1617
1703
                        # Emit D-Bus signal
1618
1704
                        self.ClientRemoved(object_path, c.name)
1619
1705
                        return
1620
 
                raise KeyError
 
1706
                raise KeyError(object_path)
1621
1707
            
1622
1708
            del _interface
1623
1709
        
1624
1710
        mandos_dbus_service = MandosDBusService()
1625
1711
    
 
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
    
1626
1730
    for client in tcp_server.clients:
1627
1731
        if use_dbus:
1628
1732
            # Emit D-Bus signal
1629
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1630
 
                                            client.GetAll(u""))
 
1733
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
1631
1734
        client.enable()
1632
1735
    
1633
1736
    tcp_server.enable()
1651
1754
            service.activate()
1652
1755
        except dbus.exceptions.DBusException, error:
1653
1756
            logger.critical(u"DBusException: %s", error)
 
1757
            cleanup()
1654
1758
            sys.exit(1)
1655
1759
        # End of Avahi example code
1656
1760
        
1663
1767
        main_loop.run()
1664
1768
    except AvahiError, error:
1665
1769
        logger.critical(u"AvahiError: %s", error)
 
1770
        cleanup()
1666
1771
        sys.exit(1)
1667
1772
    except KeyboardInterrupt:
1668
1773
        if debug:
1669
1774
            print >> sys.stderr
1670
1775
        logger.debug(u"Server received KeyboardInterrupt")
1671
1776
    logger.debug(u"Server exiting")
 
1777
    # Must run before the D-Bus bus name gets deregistered
 
1778
    cleanup()
1672
1779
 
1673
1780
if __name__ == '__main__':
1674
1781
    main()