/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

Overflows are not detected by sscanf(), so stop using it:

* plugin-runner.c (main/parse_opt): Change from using "sscanf()" to
                                    "strtoimax()".
* plugins.d/mandos-client.c (main/parse_opt, main): Change from using
                                                    "sscanf()" to
                                                    "strtoimax()" and
                                                    "strtof()".
* splashy.c (main): Change from using "sscanf()" to "strtoimax()".
* usplash.c (main): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.4"
 
69
version = "1.0.5"
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: %(levelname)s: %(message)s'))
 
76
                       ('Mandos [%(process)d]: %(levelname)s:'
 
77
                        ' %(message)s'))
77
78
logger.addHandler(syslogger)
78
79
 
79
80
console = logging.StreamHandler()
80
 
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
81
 
                                       ' %(message)s'))
 
81
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
 
82
                                       ' %(levelname)s: %(message)s'))
82
83
logger.addHandler(console)
83
84
 
84
85
class AvahiError(Exception):
178
179
class Client(dbus.service.Object):
179
180
    """A representation of a client host served by this server.
180
181
    Attributes:
181
 
    name:       string; from the config file, used in log messages
 
182
    name:       string; from the config file, used in log messages and
 
183
                        D-Bus identifiers
182
184
    fingerprint: string (40 or 32 hexadecimal digits); used to
183
185
                 uniquely identify the client
184
186
    secret:     bytestring; sent verbatim (over TLS) to client
225
227
        if config is None:
226
228
            config = {}
227
229
        logger.debug(u"Creating client %r", self.name)
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
        self.use_dbus = False   # During __init__
235
231
        # Uppercase and remove spaces from fingerprint for later
236
232
        # comparison purposes with return value from the fingerprint()
237
233
        # function
261
257
        self.disable_initiator_tag = None
262
258
        self.checker_callback_tag = None
263
259
        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)
264
270
    
265
271
    def enable(self):
266
272
        """Start this client's checker and timeout hooks"""
319
325
            # Emit D-Bus signal
320
326
            self.PropertyChanged(dbus.String(u"checker_running"),
321
327
                                 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",
325
 
                        vars(self))
 
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))
326
337
            if self.use_dbus:
327
338
                # Emit D-Bus signal
328
 
                self.CheckerCompleted(dbus.Boolean(True),
329
 
                                      dbus.UInt16(condition),
 
339
                self.CheckerCompleted(dbus.Int16(exitstatus),
 
340
                                      dbus.Int64(condition),
330
341
                                      dbus.String(command))
331
 
            self.bump_timeout()
332
 
        elif not os.WIFEXITED(condition):
 
342
        else:
333
343
            logger.warning(u"Checker for %(name)s crashed?",
334
344
                           vars(self))
335
345
            if self.use_dbus:
336
346
                # Emit D-Bus signal
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),
 
347
                self.CheckerCompleted(dbus.Int16(-1),
 
348
                                      dbus.Int64(condition),
347
349
                                      dbus.String(command))
348
350
    
349
 
    def bump_timeout(self):
 
351
    def checked_ok(self):
350
352
        """Bump up the timeout for this client.
351
353
        This should only be called when the client has been seen,
352
354
        alive and well.
410
412
                                             (self.checker.pid,
411
413
                                              self.checker_callback,
412
414
                                              data=command))
 
415
                # The checker may have completed before the gobject
 
416
                # watch was added.  Check for this.
 
417
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
 
418
                if pid:
 
419
                    gobject.source_remove(self.checker_callback_tag)
 
420
                    self.checker_callback(pid, status, command)
413
421
            except OSError, error:
414
422
                logger.error(u"Failed to start subprocess: %s",
415
423
                             error)
448
456
            return now < (self.last_checked_ok + self.timeout)
449
457
    
450
458
    ## D-Bus methods & signals
451
 
    _interface = u"org.mandos_system.Mandos.Client"
 
459
    _interface = u"se.bsnet.fukt.Mandos.Client"
452
460
    
453
 
    # BumpTimeout - method
454
 
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
455
 
    BumpTimeout.__name__ = "BumpTimeout"
 
461
    # CheckedOK - method
 
462
    CheckedOK = dbus.service.method(_interface)(checked_ok)
 
463
    CheckedOK.__name__ = "CheckedOK"
456
464
    
457
465
    # CheckerCompleted - signal
458
 
    @dbus.service.signal(_interface, signature="bqs")
459
 
    def CheckerCompleted(self, success, condition, command):
 
466
    @dbus.service.signal(_interface, signature="nxs")
 
467
    def CheckerCompleted(self, exitcode, waitstatus, command):
460
468
        "D-Bus signal"
461
469
        pass
462
470
    
503
511
                dbus.String("checker_running"):
504
512
                    dbus.Boolean(self.checker is not None,
505
513
                                 variant_level=1),
 
514
                dbus.String("object_path"):
 
515
                    dbus.ObjectPath(self.dbus_object_path,
 
516
                                    variant_level=1)
506
517
                }, signature="sv")
507
518
    
508
519
    # IsStillValid - method
591
602
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
592
603
        # ...do the normal thing
593
604
        return session.peer_certificate
594
 
    list_size = ctypes.c_uint()
 
605
    list_size = ctypes.c_uint(1)
595
606
    cert_list = (gnutls.library.functions
596
607
                 .gnutls_certificate_get_peers
597
608
                 (session._c_object, ctypes.byref(list_size)))
 
609
    if not bool(cert_list) and list_size.value != 0:
 
610
        raise gnutls.errors.GNUTLSError("error getting peer"
 
611
                                        " certificate")
598
612
    if list_size.value == 0:
599
613
        return None
600
614
    cert = cert_list[0]
669
683
        # using OpenPGP certificates.
670
684
        
671
685
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
672
 
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
673
 
        #                "+DHE-DSS"))
 
686
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
687
        #                     "+DHE-DSS"))
674
688
        # Use a fallback default, since this MUST be set.
675
689
        priority = self.server.settings.get("priority", "NORMAL")
676
690
        (gnutls.library.functions
684
698
            # Do not run session.bye() here: the session is not
685
699
            # established.  Just abandon the request.
686
700
            return
 
701
        logger.debug(u"Handshake succeeded")
687
702
        try:
688
703
            fpr = fingerprint(peer_certificate(session))
689
704
        except (TypeError, gnutls.errors.GNUTLSError), error:
691
706
            session.bye()
692
707
            return
693
708
        logger.debug(u"Fingerprint: %s", fpr)
 
709
        
694
710
        for c in self.server.clients:
695
711
            if c.fingerprint == fpr:
696
712
                client = c
709
725
            session.bye()
710
726
            return
711
727
        ## This won't work here, since we're in a fork.
712
 
        # client.bump_timeout()
 
728
        # client.checked_ok()
713
729
        sent_size = 0
714
730
        while sent_size < len(client.secret):
715
731
            sent = session.send(client.secret[sent_size:])
755
771
                                 u" bind to interface %s",
756
772
                                 self.settings["interface"])
757
773
                else:
758
 
                    raise error
 
774
                    raise
759
775
        # Only bind(2) the socket if we really need to.
760
776
        if self.server_address[0] or self.server_address[1]:
761
777
            if not self.server_address[0]:
782
798
 
783
799
def string_to_delta(interval):
784
800
    """Parse a string and return a datetime.timedelta
785
 
 
 
801
    
786
802
    >>> string_to_delta('7d')
787
803
    datetime.timedelta(7)
788
804
    >>> string_to_delta('60s')
937
953
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
938
954
    # Convert the SafeConfigParser object to a dict
939
955
    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"))
 
956
    # Use the appropriate methods on the non-string config options
 
957
    server_settings["debug"] = server_config.getboolean("DEFAULT",
 
958
                                                        "debug")
 
959
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
 
960
                                                           "use_dbus")
 
961
    if server_settings["port"]:
 
962
        server_settings["port"] = server_config.getint("DEFAULT",
 
963
                                                       "port")
945
964
    del server_config
946
965
    
947
966
    # Override the settings from the config file with command line
988
1007
    pidfilename = "/var/run/mandos.pid"
989
1008
    try:
990
1009
        pidfile = open(pidfilename, "w")
991
 
    except IOError, error:
 
1010
    except IOError:
992
1011
        logger.error("Could not open file %r", pidfilename)
993
1012
    
994
1013
    try:
1006
1025
                uid = 65534
1007
1026
                gid = 65534
1008
1027
    try:
 
1028
        os.setgid(gid)
1009
1029
        os.setuid(uid)
1010
 
        os.setgid(gid)
1011
1030
    except OSError, error:
1012
1031
        if error[0] != errno.EPERM:
1013
1032
            raise error
1014
1033
    
 
1034
    # Enable all possible GnuTLS debugging
 
1035
    if debug:
 
1036
        # "Use a log level over 10 to enable all debugging options."
 
1037
        # - GnuTLS manual
 
1038
        gnutls.library.functions.gnutls_global_set_log_level(11)
 
1039
        
 
1040
        @gnutls.library.types.gnutls_log_func
 
1041
        def debug_gnutls(level, string):
 
1042
            logger.debug("GnuTLS: %s", string[:-1])
 
1043
        
 
1044
        (gnutls.library.functions
 
1045
         .gnutls_global_set_log_function(debug_gnutls))
 
1046
    
1015
1047
    global service
1016
1048
    service = AvahiService(name = server_settings["servicename"],
1017
1049
                           servicetype = "_mandos._tcp", )
1031
1063
                            avahi.DBUS_INTERFACE_SERVER)
1032
1064
    # End of Avahi example code
1033
1065
    if use_dbus:
1034
 
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
1035
 
                                        bus)
 
1066
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1036
1067
    
1037
1068
    clients.update(Set(Client(name = section,
1038
1069
                              config
1092
1123
        class MandosServer(dbus.service.Object):
1093
1124
            """A D-Bus proxy object"""
1094
1125
            def __init__(self):
1095
 
                dbus.service.Object.__init__(self, bus,
1096
 
                                             "/Mandos")
1097
 
            _interface = u"org.mandos_system.Mandos"
1098
 
 
 
1126
                dbus.service.Object.__init__(self, bus, "/")
 
1127
            _interface = u"se.bsnet.fukt.Mandos"
 
1128
            
1099
1129
            @dbus.service.signal(_interface, signature="oa{sv}")
1100
1130
            def ClientAdded(self, objpath, properties):
1101
1131
                "D-Bus signal"
1102
1132
                pass
1103
 
 
1104
 
            @dbus.service.signal(_interface, signature="o")
1105
 
            def ClientRemoved(self, objpath):
 
1133
            
 
1134
            @dbus.service.signal(_interface, signature="os")
 
1135
            def ClientRemoved(self, objpath, name):
1106
1136
                "D-Bus signal"
1107
1137
                pass
1108
 
 
 
1138
            
1109
1139
            @dbus.service.method(_interface, out_signature="ao")
1110
1140
            def GetAllClients(self):
 
1141
                "D-Bus method"
1111
1142
                return dbus.Array(c.dbus_object_path for c in clients)
1112
 
 
 
1143
            
1113
1144
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1114
1145
            def GetAllClientsWithProperties(self):
 
1146
                "D-Bus method"
1115
1147
                return dbus.Dictionary(
1116
1148
                    ((c.dbus_object_path, c.GetAllProperties())
1117
1149
                     for c in clients),
1118
1150
                    signature="oa{sv}")
1119
 
 
 
1151
            
1120
1152
            @dbus.service.method(_interface, in_signature="o")
1121
1153
            def RemoveClient(self, object_path):
 
1154
                "D-Bus method"
1122
1155
                for c in clients:
1123
1156
                    if c.dbus_object_path == object_path:
1124
1157
                        clients.remove(c)
1126
1159
                        c.use_dbus = False
1127
1160
                        c.disable()
1128
1161
                        # Emit D-Bus signal
1129
 
                        self.ClientRemoved(object_path)
 
1162
                        self.ClientRemoved(object_path, c.name)
1130
1163
                        return
1131
1164
                raise KeyError
1132
 
            @dbus.service.method(_interface)
1133
 
            def Quit(self):
1134
 
                main_loop.quit()
1135
 
 
 
1165
            
1136
1166
            del _interface
1137
 
    
 
1167
        
1138
1168
        mandos_server = MandosServer()
1139
1169
    
1140
1170
    for client in clients:
1176
1206
        sys.exit(1)
1177
1207
    except KeyboardInterrupt:
1178
1208
        if debug:
1179
 
            print
 
1209
            print >> sys.stderr
 
1210
        logger.debug("Server received KeyboardInterrupt")
 
1211
    logger.debug("Server exiting")
1180
1212
 
1181
1213
if __name__ == '__main__':
1182
1214
    main()