/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

  • Committer: Teddy Hogeborn
  • Date: 2009-01-04 21:54:55 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090104215455-o5q1zdrlxzr1wmn1
* 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
73
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
74
74
              address = "/dev/log"))
75
75
syslogger.setFormatter(logging.Formatter
76
 
                       ('Mandos [%(process)d]: %(levelname)s:'
77
 
                        ' %(message)s'))
 
76
                       ('Mandos: %(levelname)s: %(message)s'))
78
77
logger.addHandler(syslogger)
79
78
 
80
79
console = logging.StreamHandler()
81
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
82
 
                                       ' %(levelname)s: %(message)s'))
 
80
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
 
81
                                       ' %(message)s'))
83
82
logger.addHandler(console)
84
83
 
85
84
class AvahiError(Exception):
179
178
class Client(dbus.service.Object):
180
179
    """A representation of a client host served by this server.
181
180
    Attributes:
182
 
    name:       string; from the config file, used in log messages and
183
 
                        D-Bus identifiers
 
181
    name:       string; from the config file, used in log messages
184
182
    fingerprint: string (40 or 32 hexadecimal digits); used to
185
183
                 uniquely identify the client
186
184
    secret:     bytestring; sent verbatim (over TLS) to client
227
225
        if config is None:
228
226
            config = {}
229
227
        logger.debug(u"Creating client %r", self.name)
230
 
        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)
231
235
        # Uppercase and remove spaces from fingerprint for later
232
236
        # comparison purposes with return value from the fingerprint()
233
237
        # function
257
261
        self.disable_initiator_tag = None
258
262
        self.checker_callback_tag = None
259
263
        self.checker_command = config["checker"]
260
 
        self.last_connect = None
261
 
        # Only now, when this client is initialized, can it show up on
262
 
        # the D-Bus
263
 
        self.use_dbus = use_dbus
264
 
        if self.use_dbus:
265
 
            self.dbus_object_path = (dbus.ObjectPath
266
 
                                     ("/clients/"
267
 
                                      + self.name.replace(".", "_")))
268
 
            dbus.service.Object.__init__(self, bus,
269
 
                                         self.dbus_object_path)
270
264
    
271
265
    def enable(self):
272
266
        """Start this client's checker and timeout hooks"""
325
319
            # Emit D-Bus signal
326
320
            self.PropertyChanged(dbus.String(u"checker_running"),
327
321
                                 dbus.Boolean(False, variant_level=1))
328
 
        if os.WIFEXITED(condition):
329
 
            exitstatus = os.WEXITSTATUS(condition)
330
 
            if exitstatus == 0:
331
 
                logger.info(u"Checker for %(name)s succeeded",
332
 
                            vars(self))
333
 
                self.checked_ok()
334
 
            else:
335
 
                logger.info(u"Checker for %(name)s failed",
336
 
                            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))
337
326
            if self.use_dbus:
338
327
                # Emit D-Bus signal
339
 
                self.CheckerCompleted(dbus.Int16(exitstatus),
340
 
                                      dbus.Int64(condition),
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
341
330
                                      dbus.String(command))
342
 
        else:
 
331
            self.bump_timeout()
 
332
        elif not os.WIFEXITED(condition):
343
333
            logger.warning(u"Checker for %(name)s crashed?",
344
334
                           vars(self))
345
335
            if self.use_dbus:
346
336
                # Emit D-Bus signal
347
 
                self.CheckerCompleted(dbus.Int16(-1),
348
 
                                      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),
349
347
                                      dbus.String(command))
350
348
    
351
 
    def checked_ok(self):
 
349
    def bump_timeout(self):
352
350
        """Bump up the timeout for this client.
353
351
        This should only be called when the client has been seen,
354
352
        alive and well.
450
448
            return now < (self.last_checked_ok + self.timeout)
451
449
    
452
450
    ## D-Bus methods & signals
453
 
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
451
    _interface = u"org.mandos_system.Mandos.Client"
454
452
    
455
 
    # CheckedOK - method
456
 
    CheckedOK = dbus.service.method(_interface)(checked_ok)
457
 
    CheckedOK.__name__ = "CheckedOK"
 
453
    # BumpTimeout - method
 
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
    BumpTimeout.__name__ = "BumpTimeout"
458
456
    
459
457
    # CheckerCompleted - signal
460
 
    @dbus.service.signal(_interface, signature="nxs")
461
 
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
462
460
        "D-Bus signal"
463
461
        pass
464
462
    
505
503
                dbus.String("checker_running"):
506
504
                    dbus.Boolean(self.checker is not None,
507
505
                                 variant_level=1),
508
 
                dbus.String("object_path"):
509
 
                    dbus.ObjectPath(self.dbus_object_path,
510
 
                                    variant_level=1)
511
506
                }, signature="sv")
512
507
    
513
508
    # IsStillValid - method
596
591
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
597
592
        # ...do the normal thing
598
593
        return session.peer_certificate
599
 
    list_size = ctypes.c_uint(1)
 
594
    list_size = ctypes.c_uint()
600
595
    cert_list = (gnutls.library.functions
601
596
                 .gnutls_certificate_get_peers
602
597
                 (session._c_object, ctypes.byref(list_size)))
603
 
    if not bool(cert_list) and list_size.value != 0:
604
 
        raise gnutls.errors.GNUTLSError("error getting peer"
605
 
                                        " certificate")
606
598
    if list_size.value == 0:
607
599
        return None
608
600
    cert = cert_list[0]
677
669
        # using OpenPGP certificates.
678
670
        
679
671
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
680
 
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
681
 
        #                     "+DHE-DSS"))
 
672
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
673
        #                "+DHE-DSS"))
682
674
        # Use a fallback default, since this MUST be set.
683
675
        priority = self.server.settings.get("priority", "NORMAL")
684
676
        (gnutls.library.functions
692
684
            # Do not run session.bye() here: the session is not
693
685
            # established.  Just abandon the request.
694
686
            return
695
 
        logger.debug(u"Handshake succeeded")
696
687
        try:
697
688
            fpr = fingerprint(peer_certificate(session))
698
689
        except (TypeError, gnutls.errors.GNUTLSError), error:
700
691
            session.bye()
701
692
            return
702
693
        logger.debug(u"Fingerprint: %s", fpr)
703
 
        
704
694
        for c in self.server.clients:
705
695
            if c.fingerprint == fpr:
706
696
                client = c
719
709
            session.bye()
720
710
            return
721
711
        ## This won't work here, since we're in a fork.
722
 
        # client.checked_ok()
 
712
        # client.bump_timeout()
723
713
        sent_size = 0
724
714
        while sent_size < len(client.secret):
725
715
            sent = session.send(client.secret[sent_size:])
765
755
                                 u" bind to interface %s",
766
756
                                 self.settings["interface"])
767
757
                else:
768
 
                    raise
 
758
                    raise error
769
759
        # Only bind(2) the socket if we really need to.
770
760
        if self.server_address[0] or self.server_address[1]:
771
761
            if not self.server_address[0]:
792
782
 
793
783
def string_to_delta(interval):
794
784
    """Parse a string and return a datetime.timedelta
795
 
    
 
785
 
796
786
    >>> string_to_delta('7d')
797
787
    datetime.timedelta(7)
798
788
    >>> string_to_delta('60s')
899
889
 
900
890
 
901
891
def main():
902
 
    parser = optparse.OptionParser(version = "%%prog %s" % version)
 
892
    parser = OptionParser(version = "%%prog %s" % version)
903
893
    parser.add_option("-i", "--interface", type="string",
904
894
                      metavar="IF", help="Bind to interface IF")
905
895
    parser.add_option("-a", "--address", type="string",
947
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
948
938
    # Convert the SafeConfigParser object to a dict
949
939
    server_settings = server_config.defaults()
950
 
    # Use the appropriate methods on the non-string config options
951
 
    server_settings["debug"] = server_config.getboolean("DEFAULT",
952
 
                                                        "debug")
953
 
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
954
 
                                                           "use_dbus")
955
 
    if server_settings["port"]:
956
 
        server_settings["port"] = server_config.getint("DEFAULT",
957
 
                                                       "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"))
958
945
    del server_config
959
946
    
960
947
    # Override the settings from the config file with command line
985
972
    # Parse config file with clients
986
973
    client_defaults = { "timeout": "1h",
987
974
                        "interval": "5m",
988
 
                        "checker": "fping -q -- %%(host)s",
 
975
                        "checker": "fping -q -- %(host)s",
989
976
                        "host": "",
990
977
                        }
991
978
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1001
988
    pidfilename = "/var/run/mandos.pid"
1002
989
    try:
1003
990
        pidfile = open(pidfilename, "w")
1004
 
    except IOError:
 
991
    except IOError, error:
1005
992
        logger.error("Could not open file %r", pidfilename)
1006
993
    
1007
994
    try:
1008
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:
1009
1005
        gid = pwd.getpwnam("_mandos").pw_gid
1010
1006
    except KeyError:
1011
1007
        try:
1012
 
            uid = pwd.getpwnam("mandos").pw_uid
1013
1008
            gid = pwd.getpwnam("mandos").pw_gid
1014
1009
        except KeyError:
1015
1010
            try:
1016
 
                uid = pwd.getpwnam("nobody").pw_uid
1017
1011
                gid = pwd.getpwnam("nogroup").pw_gid
1018
1012
            except KeyError:
1019
 
                uid = 65534
1020
1013
                gid = 65534
1021
1014
    try:
 
1015
        os.setuid(uid)
1022
1016
        os.setgid(gid)
1023
 
        os.setuid(uid)
1024
1017
    except OSError, error:
1025
1018
        if error[0] != errno.EPERM:
1026
1019
            raise error
1027
1020
    
1028
 
    # Enable all possible GnuTLS debugging
1029
 
    if debug:
1030
 
        # "Use a log level over 10 to enable all debugging options."
1031
 
        # - GnuTLS manual
1032
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
1033
 
        
1034
 
        @gnutls.library.types.gnutls_log_func
1035
 
        def debug_gnutls(level, string):
1036
 
            logger.debug("GnuTLS: %s", string[:-1])
1037
 
        
1038
 
        (gnutls.library.functions
1039
 
         .gnutls_global_set_log_function(debug_gnutls))
1040
 
    
1041
1021
    global service
1042
1022
    service = AvahiService(name = server_settings["servicename"],
1043
1023
                           servicetype = "_mandos._tcp", )
1057
1037
                            avahi.DBUS_INTERFACE_SERVER)
1058
1038
    # End of Avahi example code
1059
1039
    if use_dbus:
1060
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1040
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1041
                                        bus)
1061
1042
    
1062
1043
    clients.update(Set(Client(name = section,
1063
1044
                              config
1117
1098
        class MandosServer(dbus.service.Object):
1118
1099
            """A D-Bus proxy object"""
1119
1100
            def __init__(self):
1120
 
                dbus.service.Object.__init__(self, bus, "/")
1121
 
            _interface = u"se.bsnet.fukt.Mandos"
1122
 
            
 
1101
                dbus.service.Object.__init__(self, bus,
 
1102
                                             "/Mandos")
 
1103
            _interface = u"org.mandos_system.Mandos"
 
1104
 
1123
1105
            @dbus.service.signal(_interface, signature="oa{sv}")
1124
1106
            def ClientAdded(self, objpath, properties):
1125
1107
                "D-Bus signal"
1126
1108
                pass
1127
 
            
1128
 
            @dbus.service.signal(_interface, signature="os")
1129
 
            def ClientRemoved(self, objpath, name):
 
1109
 
 
1110
            @dbus.service.signal(_interface, signature="o")
 
1111
            def ClientRemoved(self, objpath):
1130
1112
                "D-Bus signal"
1131
1113
                pass
1132
 
            
 
1114
 
1133
1115
            @dbus.service.method(_interface, out_signature="ao")
1134
1116
            def GetAllClients(self):
1135
 
                "D-Bus method"
1136
1117
                return dbus.Array(c.dbus_object_path for c in clients)
1137
 
            
 
1118
 
1138
1119
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1139
1120
            def GetAllClientsWithProperties(self):
1140
 
                "D-Bus method"
1141
1121
                return dbus.Dictionary(
1142
1122
                    ((c.dbus_object_path, c.GetAllProperties())
1143
1123
                     for c in clients),
1144
1124
                    signature="oa{sv}")
1145
 
            
 
1125
 
1146
1126
            @dbus.service.method(_interface, in_signature="o")
1147
1127
            def RemoveClient(self, object_path):
1148
 
                "D-Bus method"
1149
1128
                for c in clients:
1150
1129
                    if c.dbus_object_path == object_path:
1151
1130
                        clients.remove(c)
1153
1132
                        c.use_dbus = False
1154
1133
                        c.disable()
1155
1134
                        # Emit D-Bus signal
1156
 
                        self.ClientRemoved(object_path, c.name)
 
1135
                        self.ClientRemoved(object_path)
1157
1136
                        return
1158
1137
                raise KeyError
1159
 
            
 
1138
            @dbus.service.method(_interface)
 
1139
            def Quit(self):
 
1140
                main_loop.quit()
 
1141
 
1160
1142
            del _interface
1161
 
        
 
1143
    
1162
1144
        mandos_server = MandosServer()
1163
1145
    
1164
1146
    for client in clients:
1200
1182
        sys.exit(1)
1201
1183
    except KeyboardInterrupt:
1202
1184
        if debug:
1203
 
            print >> sys.stderr
1204
 
        logger.debug("Server received KeyboardInterrupt")
1205
 
    logger.debug("Server exiting")
 
1185
            print
1206
1186
 
1207
1187
if __name__ == '__main__':
1208
1188
    main()