178
178
class Client(dbus.service.Object):
 
179
179
    """A representation of a client host served by this server.
 
181
 
    name:       string; from the config file, used in log messages
 
 
181
    name:       string; from the config file, used in log messages and
 
182
183
    fingerprint: string (40 or 32 hexadecimal digits); used to
 
183
184
                 uniquely identify the client
 
184
185
    secret:     bytestring; sent verbatim (over TLS) to client
 
 
225
226
        if config is None:
 
227
228
        logger.debug(u"Creating client %r", self.name)
 
228
 
        self.use_dbus = use_dbus
 
230
 
            self.dbus_object_path = (dbus.ObjectPath
 
232
 
                                      + self.name.replace(".", "_")))
 
233
 
            dbus.service.Object.__init__(self, bus,
 
234
 
                                         self.dbus_object_path)
 
 
229
        self.use_dbus = False   # During __init__
 
235
230
        # Uppercase and remove spaces from fingerprint for later
 
236
231
        # comparison purposes with return value from the fingerprint()
 
 
261
256
        self.disable_initiator_tag = None
 
262
257
        self.checker_callback_tag = None
 
263
258
        self.checker_command = config["checker"]
 
 
259
        self.last_connect = None
 
 
260
        # Only now, when this client is initialized, can it show up on
 
 
262
        self.use_dbus = use_dbus
 
 
264
            self.dbus_object_path = (dbus.ObjectPath
 
 
266
                                      + self.name.replace(".", "_")))
 
 
267
            dbus.service.Object.__init__(self, bus,
 
 
268
                                         self.dbus_object_path)
 
265
270
    def enable(self):
 
266
271
        """Start this client's checker and timeout hooks"""
 
 
319
324
            # Emit D-Bus signal
 
320
325
            self.PropertyChanged(dbus.String(u"checker_running"),
 
321
326
                                 dbus.Boolean(False, variant_level=1))
 
322
 
        if (os.WIFEXITED(condition)
 
323
 
            and (os.WEXITSTATUS(condition) == 0)):
 
324
 
            logger.info(u"Checker for %(name)s succeeded",
 
 
327
        if os.WIFEXITED(condition):
 
 
328
            exitstatus = os.WEXITSTATUS(condition)
 
 
330
                logger.info(u"Checker for %(name)s succeeded",
 
 
334
                logger.info(u"Checker for %(name)s failed",
 
326
336
            if self.use_dbus:
 
327
337
                # Emit D-Bus signal
 
328
 
                self.CheckerCompleted(dbus.Boolean(True),
 
329
 
                                      dbus.UInt16(condition),
 
 
338
                self.CheckerCompleted(dbus.Int16(exitstatus),
 
 
339
                                      dbus.Int64(condition),
 
330
340
                                      dbus.String(command))
 
332
 
        elif not os.WIFEXITED(condition):
 
333
342
            logger.warning(u"Checker for %(name)s crashed?",
 
335
344
            if self.use_dbus:
 
336
345
                # Emit D-Bus signal
 
337
 
                self.CheckerCompleted(dbus.Boolean(False),
 
338
 
                                      dbus.UInt16(condition),
 
339
 
                                      dbus.String(command))
 
341
 
            logger.info(u"Checker for %(name)s failed",
 
345
 
                self.CheckerCompleted(dbus.Boolean(False),
 
346
 
                                      dbus.UInt16(condition),
 
 
346
                self.CheckerCompleted(dbus.Int16(-1),
 
 
347
                                      dbus.Int64(condition),
 
347
348
                                      dbus.String(command))
 
349
 
    def bump_timeout(self):
 
 
350
    def checked_ok(self):
 
350
351
        """Bump up the timeout for this client.
 
351
352
        This should only be called when the client has been seen,
 
 
448
449
            return now < (self.last_checked_ok + self.timeout)
 
450
451
    ## D-Bus methods & signals
 
451
 
    _interface = u"org.mandos_system.Mandos.Client"
 
 
452
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
453
 
    # BumpTimeout - method
 
454
 
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
 
    BumpTimeout.__name__ = "BumpTimeout"
 
 
455
    CheckedOK = dbus.service.method(_interface)(checked_ok)
 
 
456
    CheckedOK.__name__ = "CheckedOK"
 
457
458
    # CheckerCompleted - signal
 
458
 
    @dbus.service.signal(_interface, signature="bqs")
 
459
 
    def CheckerCompleted(self, success, condition, command):
 
 
459
    @dbus.service.signal(_interface, signature="nxs")
 
 
460
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
 
503
504
                dbus.String("checker_running"):
 
504
505
                    dbus.Boolean(self.checker is not None,
 
505
506
                                 variant_level=1),
 
 
507
                dbus.String("object_path"):
 
 
508
                    dbus.ObjectPath(self.dbus_object_path,
 
506
510
                }, signature="sv")
 
508
512
    # IsStillValid - method
 
 
591
595
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
 
592
596
        # ...do the normal thing
 
593
597
        return session.peer_certificate
 
594
 
    list_size = ctypes.c_uint()
 
 
598
    list_size = ctypes.c_uint(1)
 
595
599
    cert_list = (gnutls.library.functions
 
596
600
                 .gnutls_certificate_get_peers
 
597
601
                 (session._c_object, ctypes.byref(list_size)))
 
 
602
    if not bool(cert_list) and list_size.value != 0:
 
 
603
        raise gnutls.errors.GNUTLSError("error getting peer"
 
598
605
    if list_size.value == 0:
 
600
607
    cert = cert_list[0]
 
 
684
691
            # Do not run session.bye() here: the session is not
 
685
692
            # established.  Just abandon the request.
 
 
694
        logger.debug(u"Handshake succeeded")
 
688
696
            fpr = fingerprint(peer_certificate(session))
 
689
697
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
 
892
 
    parser = OptionParser(version = "%%prog %s" % version)
 
 
901
    parser = optparse.OptionParser(version = "%%prog %s" % version)
 
893
902
    parser.add_option("-i", "--interface", type="string",
 
894
903
                      metavar="IF", help="Bind to interface IF")
 
895
904
    parser.add_option("-a", "--address", type="string",
 
 
937
946
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
 
938
947
    # Convert the SafeConfigParser object to a dict
 
939
948
    server_settings = server_config.defaults()
 
940
 
    # Use getboolean on the boolean config options
 
941
 
    server_settings["debug"] = (server_config.getboolean
 
942
 
                                ("DEFAULT", "debug"))
 
943
 
    server_settings["use_dbus"] = (server_config.getboolean
 
944
 
                                   ("DEFAULT", "use_dbus"))
 
 
949
    # Use the appropriate methods on the non-string config options
 
 
950
    server_settings["debug"] = server_config.getboolean("DEFAULT",
 
 
952
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
 
 
954
    if server_settings["port"]:
 
 
955
        server_settings["port"] = server_config.getint("DEFAULT",
 
945
957
    del server_config
 
947
959
    # Override the settings from the config file with command line
 
 
958
970
    # For convenience
 
959
971
    debug = server_settings["debug"]
 
960
972
    use_dbus = server_settings["use_dbus"]
 
 
974
    def sigsegvhandler(signum, frame):
 
 
975
        raise RuntimeError('Segmentation fault')
 
963
978
        syslogger.setLevel(logging.WARNING)
 
964
979
        console.setLevel(logging.WARNING)
 
 
981
        signal.signal(signal.SIGSEGV, sigsegvhandler)
 
966
983
    if server_settings["servicename"] != "Mandos":
 
967
984
        syslogger.setFormatter(logging.Formatter
 
 
972
989
    # Parse config file with clients
 
973
990
    client_defaults = { "timeout": "1h",
 
974
991
                        "interval": "5m",
 
975
 
                        "checker": "fping -q -- %(host)s",
 
 
992
                        "checker": "fping -q -- %%(host)s",
 
978
995
    client_config = ConfigParser.SafeConfigParser(client_defaults)
 
 
995
1012
        uid = pwd.getpwnam("_mandos").pw_uid
 
 
1013
        gid = pwd.getpwnam("_mandos").pw_gid
 
996
1014
    except KeyError:
 
998
1016
            uid = pwd.getpwnam("mandos").pw_uid
 
 
1017
            gid = pwd.getpwnam("mandos").pw_gid
 
999
1018
        except KeyError:
 
1001
1020
                uid = pwd.getpwnam("nobody").pw_uid
 
 
1021
                gid = pwd.getpwnam("nogroup").pw_gid
 
1002
1022
            except KeyError:
 
1005
 
        gid = pwd.getpwnam("_mandos").pw_gid
 
1008
 
            gid = pwd.getpwnam("mandos").pw_gid
 
1011
 
                gid = pwd.getpwnam("nogroup").pw_gid
 
1017
1028
    except OSError, error:
 
1018
1029
        if error[0] != errno.EPERM:
 
 
1032
    # Enable all possible GnuTLS debugging
 
 
1034
        # "Use a log level over 10 to enable all debugging options."
 
 
1036
        gnutls.library.functions.gnutls_global_set_log_level(11)
 
 
1038
        @gnutls.library.types.gnutls_log_func
 
 
1039
        def debug_gnutls(level, string):
 
 
1040
            logger.debug("GnuTLS: %s", string[:-1])
 
 
1042
        (gnutls.library.functions
 
 
1043
         .gnutls_global_set_log_function(debug_gnutls))
 
1022
1046
    service = AvahiService(name = server_settings["servicename"],
 
1023
1047
                           servicetype = "_mandos._tcp", )
 
 
1037
1061
                            avahi.DBUS_INTERFACE_SERVER)
 
1038
1062
    # End of Avahi example code
 
1040
 
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
 
1064
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1043
1066
    clients.update(Set(Client(name = section,
 
 
1098
1121
        class MandosServer(dbus.service.Object):
 
1099
1122
            """A D-Bus proxy object"""
 
1100
1123
            def __init__(self):
 
1101
 
                dbus.service.Object.__init__(self, bus,
 
1103
 
            _interface = u"org.mandos_system.Mandos"
 
 
1124
                dbus.service.Object.__init__(self, bus, "/")
 
 
1125
            _interface = u"se.bsnet.fukt.Mandos"
 
1105
1127
            @dbus.service.signal(_interface, signature="oa{sv}")
 
1106
1128
            def ClientAdded(self, objpath, properties):
 
1110
 
            @dbus.service.signal(_interface, signature="o")
 
1111
 
            def ClientRemoved(self, objpath):
 
 
1132
            @dbus.service.signal(_interface, signature="os")
 
 
1133
            def ClientRemoved(self, objpath, name):
 
1115
1137
            @dbus.service.method(_interface, out_signature="ao")
 
1116
1138
            def GetAllClients(self):
 
1117
1140
                return dbus.Array(c.dbus_object_path for c in clients)
 
1119
1142
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
 
1120
1143
            def GetAllClientsWithProperties(self):
 
1121
1145
                return dbus.Dictionary(
 
1122
1146
                    ((c.dbus_object_path, c.GetAllProperties())
 
1123
1147
                     for c in clients),
 
1124
1148
                    signature="oa{sv}")
 
1126
1150
            @dbus.service.method(_interface, in_signature="o")
 
1127
1151
            def RemoveClient(self, object_path):
 
1128
1153
                for c in clients:
 
1129
1154
                    if c.dbus_object_path == object_path:
 
1130
1155
                        clients.remove(c)
 
 
1132
1157
                        c.use_dbus = False
 
1134
1159
                        # Emit D-Bus signal
 
1135
 
                        self.ClientRemoved(object_path)
 
 
1160
                        self.ClientRemoved(object_path, c.name)
 
1138
 
            @dbus.service.method(_interface)
 
1144
1166
        mandos_server = MandosServer()
 
1146
1168
    for client in clients: