/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

Tags: version-1.0.5-1
* Makefile (version): Changed to "1.0.5".
* NEWS (1.0.5): New entry.
* debian/changelog (1.0.5-1): - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
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.
412
410
                                             (self.checker.pid,
413
411
                                              self.checker_callback,
414
412
                                              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)
421
413
            except OSError, error:
422
414
                logger.error(u"Failed to start subprocess: %s",
423
415
                             error)
456
448
            return now < (self.last_checked_ok + self.timeout)
457
449
    
458
450
    ## D-Bus methods & signals
459
 
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
451
    _interface = u"org.mandos_system.Mandos.Client"
460
452
    
461
 
    # CheckedOK - method
462
 
    CheckedOK = dbus.service.method(_interface)(checked_ok)
463
 
    CheckedOK.__name__ = "CheckedOK"
 
453
    # BumpTimeout - method
 
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
    BumpTimeout.__name__ = "BumpTimeout"
464
456
    
465
457
    # CheckerCompleted - signal
466
 
    @dbus.service.signal(_interface, signature="nxs")
467
 
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
468
460
        "D-Bus signal"
469
461
        pass
470
462
    
511
503
                dbus.String("checker_running"):
512
504
                    dbus.Boolean(self.checker is not None,
513
505
                                 variant_level=1),
514
 
                dbus.String("object_path"):
515
 
                    dbus.ObjectPath(self.dbus_object_path,
516
 
                                    variant_level=1)
517
506
                }, signature="sv")
518
507
    
519
508
    # IsStillValid - method
602
591
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
603
592
        # ...do the normal thing
604
593
        return session.peer_certificate
605
 
    list_size = ctypes.c_uint(1)
 
594
    list_size = ctypes.c_uint()
606
595
    cert_list = (gnutls.library.functions
607
596
                 .gnutls_certificate_get_peers
608
597
                 (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")
612
598
    if list_size.value == 0:
613
599
        return None
614
600
    cert = cert_list[0]
683
669
        # using OpenPGP certificates.
684
670
        
685
671
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
686
 
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
687
 
        #                     "+DHE-DSS"))
 
672
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
673
        #                "+DHE-DSS"))
688
674
        # Use a fallback default, since this MUST be set.
689
675
        priority = self.server.settings.get("priority", "NORMAL")
690
676
        (gnutls.library.functions
698
684
            # Do not run session.bye() here: the session is not
699
685
            # established.  Just abandon the request.
700
686
            return
701
 
        logger.debug(u"Handshake succeeded")
702
687
        try:
703
688
            fpr = fingerprint(peer_certificate(session))
704
689
        except (TypeError, gnutls.errors.GNUTLSError), error:
706
691
            session.bye()
707
692
            return
708
693
        logger.debug(u"Fingerprint: %s", fpr)
709
 
        
710
694
        for c in self.server.clients:
711
695
            if c.fingerprint == fpr:
712
696
                client = c
725
709
            session.bye()
726
710
            return
727
711
        ## This won't work here, since we're in a fork.
728
 
        # client.checked_ok()
 
712
        # client.bump_timeout()
729
713
        sent_size = 0
730
714
        while sent_size < len(client.secret):
731
715
            sent = session.send(client.secret[sent_size:])
771
755
                                 u" bind to interface %s",
772
756
                                 self.settings["interface"])
773
757
                else:
774
 
                    raise
 
758
                    raise error
775
759
        # Only bind(2) the socket if we really need to.
776
760
        if self.server_address[0] or self.server_address[1]:
777
761
            if not self.server_address[0]:
798
782
 
799
783
def string_to_delta(interval):
800
784
    """Parse a string and return a datetime.timedelta
801
 
    
 
785
 
802
786
    >>> string_to_delta('7d')
803
787
    datetime.timedelta(7)
804
788
    >>> string_to_delta('60s')
927
911
                      " files")
928
912
    parser.add_option("--no-dbus", action="store_false",
929
913
                      dest="use_dbus",
930
 
                      help="Do not provide D-Bus system bus"
931
 
                      " interface")
 
914
                      help=optparse.SUPPRESS_HELP) # XXX: Not done yet
932
915
    options = parser.parse_args()[0]
933
916
    
934
917
    if options.check:
953
936
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
954
937
    # Convert the SafeConfigParser object to a dict
955
938
    server_settings = server_config.defaults()
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")
 
939
    # Use getboolean on the boolean config options
 
940
    server_settings["debug"] = (server_config.getboolean
 
941
                                ("DEFAULT", "debug"))
 
942
    server_settings["use_dbus"] = (server_config.getboolean
 
943
                                   ("DEFAULT", "use_dbus"))
964
944
    del server_config
965
945
    
966
946
    # Override the settings from the config file with command line
977
957
    # For convenience
978
958
    debug = server_settings["debug"]
979
959
    use_dbus = server_settings["use_dbus"]
 
960
    use_dbus = False            # XXX: Not done yet
980
961
    
981
962
    if not debug:
982
963
        syslogger.setLevel(logging.WARNING)
1007
988
    pidfilename = "/var/run/mandos.pid"
1008
989
    try:
1009
990
        pidfile = open(pidfilename, "w")
1010
 
    except IOError:
 
991
    except IOError, error:
1011
992
        logger.error("Could not open file %r", pidfilename)
1012
993
    
1013
994
    try:
1025
1006
                uid = 65534
1026
1007
                gid = 65534
1027
1008
    try:
 
1009
        os.setuid(uid)
1028
1010
        os.setgid(gid)
1029
 
        os.setuid(uid)
1030
1011
    except OSError, error:
1031
1012
        if error[0] != errno.EPERM:
1032
1013
            raise error
1033
1014
    
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
 
    
1047
1015
    global service
1048
1016
    service = AvahiService(name = server_settings["servicename"],
1049
1017
                           servicetype = "_mandos._tcp", )
1063
1031
                            avahi.DBUS_INTERFACE_SERVER)
1064
1032
    # End of Avahi example code
1065
1033
    if use_dbus:
1066
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1034
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1035
                                        bus)
1067
1036
    
1068
1037
    clients.update(Set(Client(name = section,
1069
1038
                              config
1123
1092
        class MandosServer(dbus.service.Object):
1124
1093
            """A D-Bus proxy object"""
1125
1094
            def __init__(self):
1126
 
                dbus.service.Object.__init__(self, bus, "/")
1127
 
            _interface = u"se.bsnet.fukt.Mandos"
1128
 
            
 
1095
                dbus.service.Object.__init__(self, bus,
 
1096
                                             "/Mandos")
 
1097
            _interface = u"org.mandos_system.Mandos"
 
1098
 
1129
1099
            @dbus.service.signal(_interface, signature="oa{sv}")
1130
1100
            def ClientAdded(self, objpath, properties):
1131
1101
                "D-Bus signal"
1132
1102
                pass
1133
 
            
1134
 
            @dbus.service.signal(_interface, signature="os")
1135
 
            def ClientRemoved(self, objpath, name):
 
1103
 
 
1104
            @dbus.service.signal(_interface, signature="o")
 
1105
            def ClientRemoved(self, objpath):
1136
1106
                "D-Bus signal"
1137
1107
                pass
1138
 
            
 
1108
 
1139
1109
            @dbus.service.method(_interface, out_signature="ao")
1140
1110
            def GetAllClients(self):
1141
 
                "D-Bus method"
1142
1111
                return dbus.Array(c.dbus_object_path for c in clients)
1143
 
            
 
1112
 
1144
1113
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1145
1114
            def GetAllClientsWithProperties(self):
1146
 
                "D-Bus method"
1147
1115
                return dbus.Dictionary(
1148
1116
                    ((c.dbus_object_path, c.GetAllProperties())
1149
1117
                     for c in clients),
1150
1118
                    signature="oa{sv}")
1151
 
            
 
1119
 
1152
1120
            @dbus.service.method(_interface, in_signature="o")
1153
1121
            def RemoveClient(self, object_path):
1154
 
                "D-Bus method"
1155
1122
                for c in clients:
1156
1123
                    if c.dbus_object_path == object_path:
1157
1124
                        clients.remove(c)
1159
1126
                        c.use_dbus = False
1160
1127
                        c.disable()
1161
1128
                        # Emit D-Bus signal
1162
 
                        self.ClientRemoved(object_path, c.name)
 
1129
                        self.ClientRemoved(object_path)
1163
1130
                        return
1164
1131
                raise KeyError
1165
 
            
 
1132
            @dbus.service.method(_interface)
 
1133
            def Quit(self):
 
1134
                main_loop.quit()
 
1135
 
1166
1136
            del _interface
1167
 
        
 
1137
    
1168
1138
        mandos_server = MandosServer()
1169
1139
    
1170
1140
    for client in clients:
1206
1176
        sys.exit(1)
1207
1177
    except KeyboardInterrupt:
1208
1178
        if debug:
1209
 
            print >> sys.stderr
1210
 
        logger.debug("Server received KeyboardInterrupt")
1211
 
    logger.debug("Server exiting")
 
1179
            print
1212
1180
 
1213
1181
if __name__ == '__main__':
1214
1182
    main()