/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:
11
11
# and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008 Teddy Hogeborn
15
 
# Copyright © 2008 Björn Påhlsson
 
14
# Copyright © 2008,2009 Teddy Hogeborn
 
15
# Copyright © 2008,2009 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
82
82
logger.addHandler(console)
83
83
 
84
84
class AvahiError(Exception):
85
 
    def __init__(self, value):
 
85
    def __init__(self, value, *args, **kwargs):
86
86
        self.value = value
87
 
        super(AvahiError, self).__init__()
88
 
    def __str__(self):
89
 
        return repr(self.value)
 
87
        super(AvahiError, self).__init__(value, *args, **kwargs)
 
88
    def __unicode__(self):
 
89
        return unicode(repr(self.value))
90
90
 
91
91
class AvahiServiceError(AvahiError):
92
92
    pass
129
129
            logger.critical(u"No suitable Zeroconf service name found"
130
130
                            u" after %i retries, exiting.",
131
131
                            self.rename_count)
132
 
            raise AvahiServiceError("Too many renames")
 
132
            raise AvahiServiceError(u"Too many renames")
133
133
        self.name = server.GetAlternativeServiceName(self.name)
134
134
        logger.info(u"Changing Zeroconf service name to %r ...",
135
135
                    str(self.name))
201
201
                     client lives.  %() expansions are done at
202
202
                     runtime with vars(self) as dict, so that for
203
203
                     instance %(name)s can be used in the command.
204
 
    dbus_object_path: dbus.ObjectPath
205
 
    Private attibutes:
206
 
    _timeout: Real variable for 'timeout'
207
 
    _interval: Real variable for 'interval'
208
 
    _timeout_milliseconds: Used when calling gobject.timeout_add()
209
 
    _interval_milliseconds: - '' -
 
204
    use_dbus: bool(); Whether to provide D-Bus interface and signals
 
205
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
210
206
    """
211
 
    def _set_timeout(self, timeout):
212
 
        "Setter function for the 'timeout' attribute"
213
 
        self._timeout = timeout
214
 
        self._timeout_milliseconds = ((self.timeout.days
215
 
                                       * 24 * 60 * 60 * 1000)
216
 
                                      + (self.timeout.seconds * 1000)
217
 
                                      + (self.timeout.microseconds
218
 
                                         // 1000))
219
 
        # Emit D-Bus signal
220
 
        self.PropertyChanged(dbus.String(u"timeout"),
221
 
                             (dbus.UInt64(self._timeout_milliseconds,
222
 
                                          variant_level=1)))
223
 
    timeout = property(lambda self: self._timeout, _set_timeout)
224
 
    del _set_timeout
225
 
    
226
 
    def _set_interval(self, interval):
227
 
        "Setter function for the 'interval' attribute"
228
 
        self._interval = interval
229
 
        self._interval_milliseconds = ((self.interval.days
230
 
                                        * 24 * 60 * 60 * 1000)
231
 
                                       + (self.interval.seconds
232
 
                                          * 1000)
233
 
                                       + (self.interval.microseconds
234
 
                                          // 1000))
235
 
        # Emit D-Bus signal
236
 
        self.PropertyChanged(dbus.String(u"interval"),
237
 
                             (dbus.UInt64(self._interval_milliseconds,
238
 
                                          variant_level=1)))
239
 
    interval = property(lambda self: self._interval, _set_interval)
240
 
    del _set_interval
241
 
    
242
 
    def __init__(self, name = None, disable_hook=None, config=None):
 
207
    def timeout_milliseconds(self):
 
208
        "Return the 'timeout' attribute in milliseconds"
 
209
        return ((self.timeout.days * 24 * 60 * 60 * 1000)
 
210
                + (self.timeout.seconds * 1000)
 
211
                + (self.timeout.microseconds // 1000))
 
212
    
 
213
    def interval_milliseconds(self):
 
214
        "Return the 'interval' attribute in milliseconds"
 
215
        return ((self.interval.days * 24 * 60 * 60 * 1000)
 
216
                + (self.interval.seconds * 1000)
 
217
                + (self.interval.microseconds // 1000))
 
218
    
 
219
    def __init__(self, name = None, disable_hook=None, config=None,
 
220
                 use_dbus=True):
243
221
        """Note: the 'checker' key in 'config' sets the
244
222
        'checker_command' attribute and *not* the 'checker'
245
223
        attribute."""
246
 
        self.dbus_object_path = (dbus.ObjectPath
247
 
                                 ("/Mandos/clients/"
248
 
                                  + name.replace(".", "_")))
249
 
        dbus.service.Object.__init__(self, bus,
250
 
                                     self.dbus_object_path)
 
224
        self.name = name
251
225
        if config is None:
252
226
            config = {}
253
 
        self.name = name
254
227
        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)
255
235
        # Uppercase and remove spaces from fingerprint for later
256
236
        # comparison purposes with return value from the fingerprint()
257
237
        # function
288
268
        # Schedule a new checker to be started an 'interval' from now,
289
269
        # and every interval from then on.
290
270
        self.checker_initiator_tag = (gobject.timeout_add
291
 
                                      (self._interval_milliseconds,
 
271
                                      (self.interval_milliseconds(),
292
272
                                       self.start_checker))
293
273
        # Also start a new checker *right now*.
294
274
        self.start_checker()
295
275
        # Schedule a disable() when 'timeout' has passed
296
276
        self.disable_initiator_tag = (gobject.timeout_add
297
 
                                   (self._timeout_milliseconds,
 
277
                                   (self.timeout_milliseconds(),
298
278
                                    self.disable))
299
279
        self.enabled = True
300
 
        # Emit D-Bus signal
301
 
        self.PropertyChanged(dbus.String(u"enabled"),
302
 
                             dbus.Boolean(True, variant_level=1))
303
 
        self.PropertyChanged(dbus.String(u"last_enabled"),
304
 
                             (_datetime_to_dbus(self.last_enabled,
305
 
                                                variant_level=1)))
 
280
        if self.use_dbus:
 
281
            # Emit D-Bus signals
 
282
            self.PropertyChanged(dbus.String(u"enabled"),
 
283
                                 dbus.Boolean(True, variant_level=1))
 
284
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
285
                                 (_datetime_to_dbus(self.last_enabled,
 
286
                                                    variant_level=1)))
306
287
    
307
288
    def disable(self):
308
289
        """Disable this client."""
319
300
        if self.disable_hook:
320
301
            self.disable_hook(self)
321
302
        self.enabled = False
322
 
        # Emit D-Bus signal
323
 
        self.PropertyChanged(dbus.String(u"enabled"),
324
 
                             dbus.Boolean(False, variant_level=1))
 
303
        if self.use_dbus:
 
304
            # Emit D-Bus signal
 
305
            self.PropertyChanged(dbus.String(u"enabled"),
 
306
                                 dbus.Boolean(False, variant_level=1))
325
307
        # Do not run this again if called by a gobject.timeout_add
326
308
        return False
327
309
    
333
315
        """The checker has completed, so take appropriate actions."""
334
316
        self.checker_callback_tag = None
335
317
        self.checker = None
336
 
        # Emit D-Bus signal
337
 
        self.PropertyChanged(dbus.String(u"checker_running"),
338
 
                             dbus.Boolean(False, variant_level=1))
 
318
        if self.use_dbus:
 
319
            # Emit D-Bus signal
 
320
            self.PropertyChanged(dbus.String(u"checker_running"),
 
321
                                 dbus.Boolean(False, variant_level=1))
339
322
        if (os.WIFEXITED(condition)
340
323
            and (os.WEXITSTATUS(condition) == 0)):
341
324
            logger.info(u"Checker for %(name)s succeeded",
342
325
                        vars(self))
343
 
            # Emit D-Bus signal
344
 
            self.CheckerCompleted(dbus.Boolean(True),
345
 
                                  dbus.UInt16(condition),
346
 
                                  dbus.String(command))
 
326
            if self.use_dbus:
 
327
                # Emit D-Bus signal
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
 
330
                                      dbus.String(command))
347
331
            self.bump_timeout()
348
332
        elif not os.WIFEXITED(condition):
349
333
            logger.warning(u"Checker for %(name)s crashed?",
350
334
                           vars(self))
351
 
            # Emit D-Bus signal
352
 
            self.CheckerCompleted(dbus.Boolean(False),
353
 
                                  dbus.UInt16(condition),
354
 
                                  dbus.String(command))
 
335
            if self.use_dbus:
 
336
                # Emit D-Bus signal
 
337
                self.CheckerCompleted(dbus.Boolean(False),
 
338
                                      dbus.UInt16(condition),
 
339
                                      dbus.String(command))
355
340
        else:
356
341
            logger.info(u"Checker for %(name)s failed",
357
342
                        vars(self))
358
 
            # Emit D-Bus signal
359
 
            self.CheckerCompleted(dbus.Boolean(False),
360
 
                                  dbus.UInt16(condition),
361
 
                                  dbus.String(command))
 
343
            if self.use_dbus:
 
344
                # Emit D-Bus signal
 
345
                self.CheckerCompleted(dbus.Boolean(False),
 
346
                                      dbus.UInt16(condition),
 
347
                                      dbus.String(command))
362
348
    
363
349
    def bump_timeout(self):
364
350
        """Bump up the timeout for this client.
368
354
        self.last_checked_ok = datetime.datetime.utcnow()
369
355
        gobject.source_remove(self.disable_initiator_tag)
370
356
        self.disable_initiator_tag = (gobject.timeout_add
371
 
                                      (self._timeout_milliseconds,
 
357
                                      (self.timeout_milliseconds(),
372
358
                                       self.disable))
373
 
        self.PropertyChanged(dbus.String(u"last_checked_ok"),
374
 
                             (_datetime_to_dbus(self.last_checked_ok,
375
 
                                                variant_level=1)))
 
359
        if self.use_dbus:
 
360
            # Emit D-Bus signal
 
361
            self.PropertyChanged(
 
362
                dbus.String(u"last_checked_ok"),
 
363
                (_datetime_to_dbus(self.last_checked_ok,
 
364
                                   variant_level=1)))
376
365
    
377
366
    def start_checker(self):
378
367
        """Start a new checker subprocess if one is not running.
411
400
                self.checker = subprocess.Popen(command,
412
401
                                                close_fds=True,
413
402
                                                shell=True, cwd="/")
414
 
                # Emit D-Bus signal
415
 
                self.CheckerStarted(command)
416
 
                self.PropertyChanged(dbus.String("checker_running"),
417
 
                                     dbus.Boolean(True, variant_level=1))
 
403
                if self.use_dbus:
 
404
                    # Emit D-Bus signal
 
405
                    self.CheckerStarted(command)
 
406
                    self.PropertyChanged(
 
407
                        dbus.String("checker_running"),
 
408
                        dbus.Boolean(True, variant_level=1))
418
409
                self.checker_callback_tag = (gobject.child_watch_add
419
410
                                             (self.checker.pid,
420
411
                                              self.checker_callback,
442
433
            if error.errno != errno.ESRCH: # No such process
443
434
                raise
444
435
        self.checker = None
445
 
        self.PropertyChanged(dbus.String(u"checker_running"),
446
 
                             dbus.Boolean(False, variant_level=1))
 
436
        if self.use_dbus:
 
437
            self.PropertyChanged(dbus.String(u"checker_running"),
 
438
                                 dbus.Boolean(False, variant_level=1))
447
439
    
448
440
    def still_valid(self):
449
441
        """Has the timeout not yet passed for this client?"""
500
492
                     if self.last_checked_ok is not None
501
493
                     else dbus.Boolean (False, variant_level=1)),
502
494
                dbus.String("timeout"):
503
 
                    dbus.UInt64(self._timeout_milliseconds,
 
495
                    dbus.UInt64(self.timeout_milliseconds(),
504
496
                                variant_level=1),
505
497
                dbus.String("interval"):
506
 
                    dbus.UInt64(self._interval_milliseconds,
 
498
                    dbus.UInt64(self.interval_milliseconds(),
507
499
                                variant_level=1),
508
500
                dbus.String("checker"):
509
501
                    dbus.String(self.checker_command,
529
521
    def SetChecker(self, checker):
530
522
        "D-Bus setter method"
531
523
        self.checker_command = checker
 
524
        # Emit D-Bus signal
 
525
        self.PropertyChanged(dbus.String(u"checker"),
 
526
                             dbus.String(self.checker_command,
 
527
                                         variant_level=1))
532
528
    
533
529
    # SetHost - method
534
530
    @dbus.service.method(_interface, in_signature="s")
535
531
    def SetHost(self, host):
536
532
        "D-Bus setter method"
537
533
        self.host = host
 
534
        # Emit D-Bus signal
 
535
        self.PropertyChanged(dbus.String(u"host"),
 
536
                             dbus.String(self.host, variant_level=1))
538
537
    
539
538
    # SetInterval - method
540
539
    @dbus.service.method(_interface, in_signature="t")
541
540
    def SetInterval(self, milliseconds):
542
 
        self.interval = datetime.timdeelta(0, 0, 0, milliseconds)
 
541
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
 
542
        # Emit D-Bus signal
 
543
        self.PropertyChanged(dbus.String(u"interval"),
 
544
                             (dbus.UInt64(self.interval_milliseconds(),
 
545
                                          variant_level=1)))
543
546
    
544
547
    # SetSecret - method
545
548
    @dbus.service.method(_interface, in_signature="ay",
552
555
    @dbus.service.method(_interface, in_signature="t")
553
556
    def SetTimeout(self, milliseconds):
554
557
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
 
558
        # Emit D-Bus signal
 
559
        self.PropertyChanged(dbus.String(u"timeout"),
 
560
                             (dbus.UInt64(self.timeout_milliseconds(),
 
561
                                          variant_level=1)))
555
562
    
556
563
    # Enable - method
557
564
    Enable = dbus.service.method(_interface)(enable)
833
840
    elif state == avahi.ENTRY_GROUP_FAILURE:
834
841
        logger.critical(u"Avahi: Error in group state changed %s",
835
842
                        unicode(error))
836
 
        raise AvahiGroupError("State changed: %s", str(error))
 
843
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
837
844
 
838
845
def if_nametoindex(interface):
839
846
    """Call the C function if_nametoindex(), or equivalent"""
889
896
                      help="Address to listen for requests on")
890
897
    parser.add_option("-p", "--port", type="int",
891
898
                      help="Port number to receive requests on")
892
 
    parser.add_option("--check", action="store_true", default=False,
 
899
    parser.add_option("--check", action="store_true",
893
900
                      help="Run self-test")
894
901
    parser.add_option("--debug", action="store_true",
895
902
                      help="Debug mode; run in foreground and log to"
902
909
                      default="/etc/mandos", metavar="DIR",
903
910
                      help="Directory to search for configuration"
904
911
                      " files")
 
912
    parser.add_option("--no-dbus", action="store_false",
 
913
                      dest="use_dbus",
 
914
                      help="Do not provide D-Bus system bus"
 
915
                      " interface")
905
916
    options = parser.parse_args()[0]
906
917
    
907
918
    if options.check:
917
928
                        "priority":
918
929
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
919
930
                        "servicename": "Mandos",
 
931
                        "use_dbus": "True",
920
932
                        }
921
933
    
922
934
    # Parse config file for server-global settings
925
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
926
938
    # Convert the SafeConfigParser object to a dict
927
939
    server_settings = server_config.defaults()
928
 
    # Use getboolean on the boolean config option
 
940
    # Use getboolean on the boolean config options
929
941
    server_settings["debug"] = (server_config.getboolean
930
942
                                ("DEFAULT", "debug"))
 
943
    server_settings["use_dbus"] = (server_config.getboolean
 
944
                                   ("DEFAULT", "use_dbus"))
931
945
    del server_config
932
946
    
933
947
    # Override the settings from the config file with command line
934
948
    # options, if set.
935
949
    for option in ("interface", "address", "port", "debug",
936
 
                   "priority", "servicename", "configdir"):
 
950
                   "priority", "servicename", "configdir",
 
951
                   "use_dbus"):
937
952
        value = getattr(options, option)
938
953
        if value is not None:
939
954
            server_settings[option] = value
940
955
    del options
941
956
    # Now we have our good server settings in "server_settings"
942
957
    
 
958
    # For convenience
943
959
    debug = server_settings["debug"]
 
960
    use_dbus = server_settings["use_dbus"]
944
961
    
945
962
    if not debug:
946
963
        syslogger.setLevel(logging.WARNING)
1019
1036
                                           avahi.DBUS_PATH_SERVER),
1020
1037
                            avahi.DBUS_INTERFACE_SERVER)
1021
1038
    # End of Avahi example code
1022
 
    bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus)
 
1039
    if use_dbus:
 
1040
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1041
                                        bus)
1023
1042
    
1024
1043
    clients.update(Set(Client(name = section,
1025
1044
                              config
1026
 
                              = dict(client_config.items(section)))
 
1045
                              = dict(client_config.items(section)),
 
1046
                              use_dbus = use_dbus)
1027
1047
                       for section in client_config.sections()))
1028
1048
    if not clients:
1029
 
        logger.critical(u"No clients defined")
1030
 
        sys.exit(1)
 
1049
        logger.warning(u"No clients defined")
1031
1050
    
1032
1051
    if debug:
1033
1052
        # Redirect stdin so all checkers get /dev/null
1075
1094
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1076
1095
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1077
1096
    
1078
 
    class MandosServer(dbus.service.Object):
1079
 
        """A D-Bus proxy object"""
1080
 
        def __init__(self):
1081
 
            dbus.service.Object.__init__(self, bus,
1082
 
                                         "/Mandos")
1083
 
        _interface = u"org.mandos_system.Mandos"
1084
 
        
1085
 
        @dbus.service.signal(_interface, signature="oa{sv}")
1086
 
        def ClientAdded(self, objpath, properties):
1087
 
            "D-Bus signal"
1088
 
            pass
1089
 
        
1090
 
        @dbus.service.signal(_interface, signature="o")
1091
 
        def ClientRemoved(self, objpath):
1092
 
            "D-Bus signal"
1093
 
            pass
1094
 
        
1095
 
        @dbus.service.method(_interface, out_signature="ao")
1096
 
        def GetAllClients(self):
1097
 
            return dbus.Array(c.dbus_object_path for c in clients)
1098
 
        
1099
 
        @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1100
 
        def GetAllClientsWithProperties(self):
1101
 
            return dbus.Dictionary(
1102
 
                ((c.dbus_object_path, c.GetAllProperties())
1103
 
                 for c in clients),
1104
 
                signature="oa{sv}")
1105
 
        
1106
 
        @dbus.service.method(_interface, in_signature="o")
1107
 
        def RemoveClient(self, object_path):
1108
 
            for c in clients:
1109
 
                if c.dbus_object_path == object_path:
1110
 
                    c.disable()
1111
 
                    clients.remove(c)
1112
 
                    return
1113
 
            raise KeyError
1114
 
        
1115
 
        del _interface
 
1097
    if use_dbus:
 
1098
        class MandosServer(dbus.service.Object):
 
1099
            """A D-Bus proxy object"""
 
1100
            def __init__(self):
 
1101
                dbus.service.Object.__init__(self, bus,
 
1102
                                             "/Mandos")
 
1103
            _interface = u"org.mandos_system.Mandos"
 
1104
 
 
1105
            @dbus.service.signal(_interface, signature="oa{sv}")
 
1106
            def ClientAdded(self, objpath, properties):
 
1107
                "D-Bus signal"
 
1108
                pass
 
1109
 
 
1110
            @dbus.service.signal(_interface, signature="o")
 
1111
            def ClientRemoved(self, objpath):
 
1112
                "D-Bus signal"
 
1113
                pass
 
1114
 
 
1115
            @dbus.service.method(_interface, out_signature="ao")
 
1116
            def GetAllClients(self):
 
1117
                return dbus.Array(c.dbus_object_path for c in clients)
 
1118
 
 
1119
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
 
1120
            def GetAllClientsWithProperties(self):
 
1121
                return dbus.Dictionary(
 
1122
                    ((c.dbus_object_path, c.GetAllProperties())
 
1123
                     for c in clients),
 
1124
                    signature="oa{sv}")
 
1125
 
 
1126
            @dbus.service.method(_interface, in_signature="o")
 
1127
            def RemoveClient(self, object_path):
 
1128
                for c in clients:
 
1129
                    if c.dbus_object_path == object_path:
 
1130
                        clients.remove(c)
 
1131
                        # Don't signal anything except ClientRemoved
 
1132
                        c.use_dbus = False
 
1133
                        c.disable()
 
1134
                        # Emit D-Bus signal
 
1135
                        self.ClientRemoved(object_path)
 
1136
                        return
 
1137
                raise KeyError
 
1138
            @dbus.service.method(_interface)
 
1139
            def Quit(self):
 
1140
                main_loop.quit()
 
1141
 
 
1142
            del _interface
1116
1143
    
1117
 
    mandos_server = MandosServer()
 
1144
        mandos_server = MandosServer()
1118
1145
    
1119
1146
    for client in clients:
1120
 
        # Emit D-Bus signal
1121
 
        mandos_server.ClientAdded(client.dbus_object_path,
1122
 
                                  client.GetAllProperties())
 
1147
        if use_dbus:
 
1148
            # Emit D-Bus signal
 
1149
            mandos_server.ClientAdded(client.dbus_object_path,
 
1150
                                      client.GetAllProperties())
1123
1151
        client.enable()
1124
1152
    
1125
1153
    tcp_server.enable()
1150
1178
        logger.debug(u"Starting main loop")
1151
1179
        main_loop.run()
1152
1180
    except AvahiError, error:
1153
 
        logger.critical(u"AvahiError: %s" + unicode(error))
 
1181
        logger.critical(u"AvahiError: %s", error)
1154
1182
        sys.exit(1)
1155
1183
    except KeyboardInterrupt:
1156
1184
        if debug: