/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos

* README: Update copyright year; add "2009".
* debian/copyright: - '' -
* mandos: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.xml: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
 
36
36
import SocketServer
37
37
import socket
38
 
import optparse
 
38
from optparse import OptionParser
39
39
import datetime
40
40
import errno
41
41
import gnutls.crypto
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.5"
 
69
version = "1.0.2"
70
70
 
71
71
logger = logging.Logger('mandos')
72
72
syslogger = (logging.handlers.SysLogHandler
178
178
class Client(dbus.service.Object):
179
179
    """A representation of a client host served by this server.
180
180
    Attributes:
181
 
    name:       string; from the config file, used in log messages and
182
 
                        D-Bus identifiers
 
181
    name:       string; from the config file, used in log messages
183
182
    fingerprint: string (40 or 32 hexadecimal digits); used to
184
183
                 uniquely identify the client
185
184
    secret:     bytestring; sent verbatim (over TLS) to client
226
225
        if config is None:
227
226
            config = {}
228
227
        logger.debug(u"Creating client %r", self.name)
229
 
        self.use_dbus = False   # During __init__
 
228
        self.use_dbus = use_dbus
 
229
        if self.use_dbus:
 
230
            self.dbus_object_path = (dbus.ObjectPath
 
231
                                     ("/Mandos/clients/"
 
232
                                      + self.name.replace(".", "_")))
 
233
            dbus.service.Object.__init__(self, bus,
 
234
                                         self.dbus_object_path)
230
235
        # Uppercase and remove spaces from fingerprint for later
231
236
        # comparison purposes with return value from the fingerprint()
232
237
        # function
256
261
        self.disable_initiator_tag = None
257
262
        self.checker_callback_tag = None
258
263
        self.checker_command = config["checker"]
259
 
        self.last_connect = None
260
 
        # Only now, when this client is initialized, can it show up on
261
 
        # the D-Bus
262
 
        self.use_dbus = use_dbus
263
 
        if self.use_dbus:
264
 
            self.dbus_object_path = (dbus.ObjectPath
265
 
                                     ("/clients/"
266
 
                                      + self.name.replace(".", "_")))
267
 
            dbus.service.Object.__init__(self, bus,
268
 
                                         self.dbus_object_path)
269
264
    
270
265
    def enable(self):
271
266
        """Start this client's checker and timeout hooks"""
324
319
            # Emit D-Bus signal
325
320
            self.PropertyChanged(dbus.String(u"checker_running"),
326
321
                                 dbus.Boolean(False, variant_level=1))
327
 
        if os.WIFEXITED(condition):
328
 
            exitstatus = os.WEXITSTATUS(condition)
329
 
            if exitstatus == 0:
330
 
                logger.info(u"Checker for %(name)s succeeded",
331
 
                            vars(self))
332
 
                self.checked_ok()
333
 
            else:
334
 
                logger.info(u"Checker for %(name)s failed",
335
 
                            vars(self))
 
322
        if (os.WIFEXITED(condition)
 
323
            and (os.WEXITSTATUS(condition) == 0)):
 
324
            logger.info(u"Checker for %(name)s succeeded",
 
325
                        vars(self))
336
326
            if self.use_dbus:
337
327
                # Emit D-Bus signal
338
 
                self.CheckerCompleted(dbus.Int16(exitstatus),
339
 
                                      dbus.Int64(condition),
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
340
330
                                      dbus.String(command))
341
 
        else:
 
331
            self.bump_timeout()
 
332
        elif not os.WIFEXITED(condition):
342
333
            logger.warning(u"Checker for %(name)s crashed?",
343
334
                           vars(self))
344
335
            if self.use_dbus:
345
336
                # Emit D-Bus signal
346
 
                self.CheckerCompleted(dbus.Int16(-1),
347
 
                                      dbus.Int64(condition),
 
337
                self.CheckerCompleted(dbus.Boolean(False),
 
338
                                      dbus.UInt16(condition),
 
339
                                      dbus.String(command))
 
340
        else:
 
341
            logger.info(u"Checker for %(name)s failed",
 
342
                        vars(self))
 
343
            if self.use_dbus:
 
344
                # Emit D-Bus signal
 
345
                self.CheckerCompleted(dbus.Boolean(False),
 
346
                                      dbus.UInt16(condition),
348
347
                                      dbus.String(command))
349
348
    
350
 
    def checked_ok(self):
 
349
    def bump_timeout(self):
351
350
        """Bump up the timeout for this client.
352
351
        This should only be called when the client has been seen,
353
352
        alive and well.
449
448
            return now < (self.last_checked_ok + self.timeout)
450
449
    
451
450
    ## D-Bus methods & signals
452
 
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
451
    _interface = u"org.mandos_system.Mandos.Client"
453
452
    
454
 
    # CheckedOK - method
455
 
    CheckedOK = dbus.service.method(_interface)(checked_ok)
456
 
    CheckedOK.__name__ = "CheckedOK"
 
453
    # BumpTimeout - method
 
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
    BumpTimeout.__name__ = "BumpTimeout"
457
456
    
458
457
    # CheckerCompleted - signal
459
 
    @dbus.service.signal(_interface, signature="nxs")
460
 
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
461
460
        "D-Bus signal"
462
461
        pass
463
462
    
504
503
                dbus.String("checker_running"):
505
504
                    dbus.Boolean(self.checker is not None,
506
505
                                 variant_level=1),
507
 
                dbus.String("object_path"):
508
 
                    dbus.ObjectPath(self.dbus_object_path,
509
 
                                    variant_level=1)
510
506
                }, signature="sv")
511
507
    
512
508
    # IsStillValid - method
595
591
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
596
592
        # ...do the normal thing
597
593
        return session.peer_certificate
598
 
    list_size = ctypes.c_uint(1)
 
594
    list_size = ctypes.c_uint()
599
595
    cert_list = (gnutls.library.functions
600
596
                 .gnutls_certificate_get_peers
601
597
                 (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"
604
 
                                        " certificate")
605
598
    if list_size.value == 0:
606
599
        return None
607
600
    cert = cert_list[0]
691
684
            # Do not run session.bye() here: the session is not
692
685
            # established.  Just abandon the request.
693
686
            return
694
 
        logger.debug(u"Handshake succeeded")
695
687
        try:
696
688
            fpr = fingerprint(peer_certificate(session))
697
689
        except (TypeError, gnutls.errors.GNUTLSError), error:
699
691
            session.bye()
700
692
            return
701
693
        logger.debug(u"Fingerprint: %s", fpr)
702
 
        
703
694
        for c in self.server.clients:
704
695
            if c.fingerprint == fpr:
705
696
                client = c
718
709
            session.bye()
719
710
            return
720
711
        ## This won't work here, since we're in a fork.
721
 
        # client.checked_ok()
 
712
        # client.bump_timeout()
722
713
        sent_size = 0
723
714
        while sent_size < len(client.secret):
724
715
            sent = session.send(client.secret[sent_size:])
791
782
 
792
783
def string_to_delta(interval):
793
784
    """Parse a string and return a datetime.timedelta
794
 
    
 
785
 
795
786
    >>> string_to_delta('7d')
796
787
    datetime.timedelta(7)
797
788
    >>> string_to_delta('60s')
898
889
 
899
890
 
900
891
def main():
901
 
    parser = optparse.OptionParser(version = "%%prog %s" % version)
 
892
    parser = OptionParser(version = "%%prog %s" % version)
902
893
    parser.add_option("-i", "--interface", type="string",
903
894
                      metavar="IF", help="Bind to interface IF")
904
895
    parser.add_option("-a", "--address", type="string",
946
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
947
938
    # Convert the SafeConfigParser object to a dict
948
939
    server_settings = server_config.defaults()
949
 
    # Use the appropriate methods on the non-string config options
950
 
    server_settings["debug"] = server_config.getboolean("DEFAULT",
951
 
                                                        "debug")
952
 
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
953
 
                                                           "use_dbus")
954
 
    if server_settings["port"]:
955
 
        server_settings["port"] = server_config.getint("DEFAULT",
956
 
                                                       "port")
 
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"))
957
945
    del server_config
958
946
    
959
947
    # Override the settings from the config file with command line
970
958
    # For convenience
971
959
    debug = server_settings["debug"]
972
960
    use_dbus = server_settings["use_dbus"]
973
 
 
974
 
    def sigsegvhandler(signum, frame):
975
 
        raise RuntimeError('Segmentation fault')
976
961
    
977
962
    if not debug:
978
963
        syslogger.setLevel(logging.WARNING)
979
964
        console.setLevel(logging.WARNING)
980
 
    else:
981
 
        signal.signal(signal.SIGSEGV, sigsegvhandler)
982
965
    
983
966
    if server_settings["servicename"] != "Mandos":
984
967
        syslogger.setFormatter(logging.Formatter
989
972
    # Parse config file with clients
990
973
    client_defaults = { "timeout": "1h",
991
974
                        "interval": "5m",
992
 
                        "checker": "fping -q -- %%(host)s",
 
975
                        "checker": "fping -q -- %(host)s",
993
976
                        "host": "",
994
977
                        }
995
978
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1010
993
    
1011
994
    try:
1012
995
        uid = pwd.getpwnam("_mandos").pw_uid
 
996
    except KeyError:
 
997
        try:
 
998
            uid = pwd.getpwnam("mandos").pw_uid
 
999
        except KeyError:
 
1000
            try:
 
1001
                uid = pwd.getpwnam("nobody").pw_uid
 
1002
            except KeyError:
 
1003
                uid = 65534
 
1004
    try:
1013
1005
        gid = pwd.getpwnam("_mandos").pw_gid
1014
1006
    except KeyError:
1015
1007
        try:
1016
 
            uid = pwd.getpwnam("mandos").pw_uid
1017
1008
            gid = pwd.getpwnam("mandos").pw_gid
1018
1009
        except KeyError:
1019
1010
            try:
1020
 
                uid = pwd.getpwnam("nobody").pw_uid
1021
1011
                gid = pwd.getpwnam("nogroup").pw_gid
1022
1012
            except KeyError:
1023
 
                uid = 65534
1024
1013
                gid = 65534
1025
1014
    try:
 
1015
        os.setuid(uid)
1026
1016
        os.setgid(gid)
1027
 
        os.setuid(uid)
1028
1017
    except OSError, error:
1029
1018
        if error[0] != errno.EPERM:
1030
1019
            raise error
1031
1020
    
1032
 
    # Enable all possible GnuTLS debugging
1033
 
    if debug:
1034
 
        # "Use a log level over 10 to enable all debugging options."
1035
 
        # - GnuTLS manual
1036
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
1037
 
        
1038
 
        @gnutls.library.types.gnutls_log_func
1039
 
        def debug_gnutls(level, string):
1040
 
            logger.debug("GnuTLS: %s", string[:-1])
1041
 
        
1042
 
        (gnutls.library.functions
1043
 
         .gnutls_global_set_log_function(debug_gnutls))
1044
 
    
1045
1021
    global service
1046
1022
    service = AvahiService(name = server_settings["servicename"],
1047
1023
                           servicetype = "_mandos._tcp", )
1061
1037
                            avahi.DBUS_INTERFACE_SERVER)
1062
1038
    # End of Avahi example code
1063
1039
    if use_dbus:
1064
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1040
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1041
                                        bus)
1065
1042
    
1066
1043
    clients.update(Set(Client(name = section,
1067
1044
                              config
1121
1098
        class MandosServer(dbus.service.Object):
1122
1099
            """A D-Bus proxy object"""
1123
1100
            def __init__(self):
1124
 
                dbus.service.Object.__init__(self, bus, "/")
1125
 
            _interface = u"se.bsnet.fukt.Mandos"
1126
 
            
 
1101
                dbus.service.Object.__init__(self, bus,
 
1102
                                             "/Mandos")
 
1103
            _interface = u"org.mandos_system.Mandos"
 
1104
 
1127
1105
            @dbus.service.signal(_interface, signature="oa{sv}")
1128
1106
            def ClientAdded(self, objpath, properties):
1129
1107
                "D-Bus signal"
1130
1108
                pass
1131
 
            
1132
 
            @dbus.service.signal(_interface, signature="os")
1133
 
            def ClientRemoved(self, objpath, name):
 
1109
 
 
1110
            @dbus.service.signal(_interface, signature="o")
 
1111
            def ClientRemoved(self, objpath):
1134
1112
                "D-Bus signal"
1135
1113
                pass
1136
 
            
 
1114
 
1137
1115
            @dbus.service.method(_interface, out_signature="ao")
1138
1116
            def GetAllClients(self):
1139
 
                "D-Bus method"
1140
1117
                return dbus.Array(c.dbus_object_path for c in clients)
1141
 
            
 
1118
 
1142
1119
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1143
1120
            def GetAllClientsWithProperties(self):
1144
 
                "D-Bus method"
1145
1121
                return dbus.Dictionary(
1146
1122
                    ((c.dbus_object_path, c.GetAllProperties())
1147
1123
                     for c in clients),
1148
1124
                    signature="oa{sv}")
1149
 
            
 
1125
 
1150
1126
            @dbus.service.method(_interface, in_signature="o")
1151
1127
            def RemoveClient(self, object_path):
1152
 
                "D-Bus method"
1153
1128
                for c in clients:
1154
1129
                    if c.dbus_object_path == object_path:
1155
1130
                        clients.remove(c)
1157
1132
                        c.use_dbus = False
1158
1133
                        c.disable()
1159
1134
                        # Emit D-Bus signal
1160
 
                        self.ClientRemoved(object_path, c.name)
 
1135
                        self.ClientRemoved(object_path)
1161
1136
                        return
1162
1137
                raise KeyError
1163
 
            
 
1138
            @dbus.service.method(_interface)
 
1139
            def Quit(self):
 
1140
                main_loop.quit()
 
1141
 
1164
1142
            del _interface
1165
 
        
 
1143
    
1166
1144
        mandos_server = MandosServer()
1167
1145
    
1168
1146
    for client in clients: