/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-06 22:49:50 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090106224950-2tfsnbvyuqeulo17
Eliminate warning on 64-bit systems, thanks to Frans Pop
<elendil@planet.nl> for reporting it:

* plugin-runner.c (main): Move "ssize_t sret" to function-wide scope.
                          Use it instead of "int ret" to store return
                          value from "read()".

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
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.2"
 
69
version = "1.0.3"
70
70
 
71
71
logger = logging.Logger('mandos')
72
72
syslogger = (logging.handlers.SysLogHandler
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))
170
170
# End of Avahi example code
171
171
 
172
172
 
 
173
def _datetime_to_dbus(dt, variant_level=0):
 
174
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
175
    return dbus.String(dt.isoformat(), variant_level=variant_level)
 
176
 
 
177
 
173
178
class Client(dbus.service.Object):
174
179
    """A representation of a client host served by this server.
175
180
    Attributes:
176
 
    name:      string; from the config file, used in log messages
 
181
    name:       string; from the config file, used in log messages
177
182
    fingerprint: string (40 or 32 hexadecimal digits); used to
178
183
                 uniquely identify the client
179
 
    secret:    bytestring; sent verbatim (over TLS) to client
180
 
    host:      string; available for use by the checker command
181
 
    created:   datetime.datetime(); (UTC) object creation
182
 
    started:   datetime.datetime(); (UTC) last started
 
184
    secret:     bytestring; sent verbatim (over TLS) to client
 
185
    host:       string; available for use by the checker command
 
186
    created:    datetime.datetime(); (UTC) object creation
 
187
    last_enabled: datetime.datetime(); (UTC)
 
188
    enabled:    bool()
183
189
    last_checked_ok: datetime.datetime(); (UTC) or None
184
 
    timeout:   datetime.timedelta(); How long from last_checked_ok
185
 
                                     until this client is invalid
186
 
    interval:  datetime.timedelta(); How often to start a new checker
187
 
    stop_hook: If set, called by stop() as stop_hook(self)
188
 
    checker:   subprocess.Popen(); a running checker process used
189
 
                                   to see if the client lives.
190
 
                                   'None' if no process is running.
 
190
    timeout:    datetime.timedelta(); How long from last_checked_ok
 
191
                                      until this client is invalid
 
192
    interval:   datetime.timedelta(); How often to start a new checker
 
193
    disable_hook:  If set, called by disable() as disable_hook(self)
 
194
    checker:    subprocess.Popen(); a running checker process used
 
195
                                    to see if the client lives.
 
196
                                    'None' if no process is running.
191
197
    checker_initiator_tag: a gobject event source tag, or None
192
 
    stop_initiator_tag:    - '' -
 
198
    disable_initiator_tag:    - '' -
193
199
    checker_callback_tag:  - '' -
194
200
    checker_command: string; External command which is run to check if
195
201
                     client lives.  %() expansions are done at
196
202
                     runtime with vars(self) as dict, so that for
197
203
                     instance %(name)s can be used in the command.
198
 
    Private attibutes:
199
 
    _timeout: Real variable for 'timeout'
200
 
    _interval: Real variable for 'interval'
201
 
    _timeout_milliseconds: Used when calling gobject.timeout_add()
202
 
    _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
203
206
    """
204
 
    def _set_timeout(self, timeout):
205
 
        "Setter function for the 'timeout' attribute"
206
 
        self._timeout = timeout
207
 
        self._timeout_milliseconds = ((self.timeout.days
208
 
                                       * 24 * 60 * 60 * 1000)
209
 
                                      + (self.timeout.seconds * 1000)
210
 
                                      + (self.timeout.microseconds
211
 
                                         // 1000))
212
 
        # Emit D-Bus signal
213
 
        self.TimeoutChanged(self._timeout_milliseconds)
214
 
    timeout = property(lambda self: self._timeout, _set_timeout)
215
 
    del _set_timeout
216
 
    
217
 
    def _set_interval(self, interval):
218
 
        "Setter function for the 'interval' attribute"
219
 
        self._interval = interval
220
 
        self._interval_milliseconds = ((self.interval.days
221
 
                                        * 24 * 60 * 60 * 1000)
222
 
                                       + (self.interval.seconds
223
 
                                          * 1000)
224
 
                                       + (self.interval.microseconds
225
 
                                          // 1000))
226
 
        # Emit D-Bus signal
227
 
        self.IntervalChanged(self._interval_milliseconds)
228
 
    interval = property(lambda self: self._interval, _set_interval)
229
 
    del _set_interval
230
 
    
231
 
    def __init__(self, name = None, stop_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):
232
221
        """Note: the 'checker' key in 'config' sets the
233
222
        'checker_command' attribute and *not* the 'checker'
234
223
        attribute."""
235
 
        dbus.service.Object.__init__(self, bus,
236
 
                                     "/Mandos/clients/%s"
237
 
                                     % name.replace(".", "_"))
 
224
        self.name = name
238
225
        if config is None:
239
226
            config = {}
240
 
        self.name = name
241
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)
242
235
        # Uppercase and remove spaces from fingerprint for later
243
236
        # comparison purposes with return value from the fingerprint()
244
237
        # function
257
250
                            % self.name)
258
251
        self.host = config.get("host", "")
259
252
        self.created = datetime.datetime.utcnow()
260
 
        self.started = None
 
253
        self.enabled = False
 
254
        self.last_enabled = None
261
255
        self.last_checked_ok = None
262
256
        self.timeout = string_to_delta(config["timeout"])
263
257
        self.interval = string_to_delta(config["interval"])
264
 
        self.stop_hook = stop_hook
 
258
        self.disable_hook = disable_hook
265
259
        self.checker = None
266
260
        self.checker_initiator_tag = None
267
 
        self.stop_initiator_tag = None
 
261
        self.disable_initiator_tag = None
268
262
        self.checker_callback_tag = None
269
 
        self.check_command = config["checker"]
 
263
        self.checker_command = config["checker"]
270
264
    
271
 
    def start(self):
 
265
    def enable(self):
272
266
        """Start this client's checker and timeout hooks"""
273
 
        self.started = datetime.datetime.utcnow()
 
267
        self.last_enabled = datetime.datetime.utcnow()
274
268
        # Schedule a new checker to be started an 'interval' from now,
275
269
        # and every interval from then on.
276
270
        self.checker_initiator_tag = (gobject.timeout_add
277
 
                                      (self._interval_milliseconds,
 
271
                                      (self.interval_milliseconds(),
278
272
                                       self.start_checker))
279
273
        # Also start a new checker *right now*.
280
274
        self.start_checker()
281
 
        # Schedule a stop() when 'timeout' has passed
282
 
        self.stop_initiator_tag = (gobject.timeout_add
283
 
                                   (self._timeout_milliseconds,
284
 
                                    self.stop))
285
 
        # Emit D-Bus signal
286
 
        self.StateChanged(True)
 
275
        # Schedule a disable() when 'timeout' has passed
 
276
        self.disable_initiator_tag = (gobject.timeout_add
 
277
                                   (self.timeout_milliseconds(),
 
278
                                    self.disable))
 
279
        self.enabled = True
 
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)))
287
287
    
288
 
    def stop(self):
289
 
        """Stop this client."""
290
 
        if getattr(self, "started", None) is not None:
291
 
            logger.info(u"Stopping client %s", self.name)
292
 
        else:
 
288
    def disable(self):
 
289
        """Disable this client."""
 
290
        if not getattr(self, "enabled", False):
293
291
            return False
294
 
        if getattr(self, "stop_initiator_tag", False):
295
 
            gobject.source_remove(self.stop_initiator_tag)
296
 
            self.stop_initiator_tag = None
 
292
        logger.info(u"Disabling client %s", self.name)
 
293
        if getattr(self, "disable_initiator_tag", False):
 
294
            gobject.source_remove(self.disable_initiator_tag)
 
295
            self.disable_initiator_tag = None
297
296
        if getattr(self, "checker_initiator_tag", False):
298
297
            gobject.source_remove(self.checker_initiator_tag)
299
298
            self.checker_initiator_tag = None
300
299
        self.stop_checker()
301
 
        if self.stop_hook:
302
 
            self.stop_hook(self)
303
 
        self.started = None
304
 
        # Emit D-Bus signal
305
 
        self.StateChanged(False)
 
300
        if self.disable_hook:
 
301
            self.disable_hook(self)
 
302
        self.enabled = False
 
303
        if self.use_dbus:
 
304
            # Emit D-Bus signal
 
305
            self.PropertyChanged(dbus.String(u"enabled"),
 
306
                                 dbus.Boolean(False, variant_level=1))
306
307
        # Do not run this again if called by a gobject.timeout_add
307
308
        return False
308
309
    
309
310
    def __del__(self):
310
 
        self.stop_hook = None
311
 
        self.stop()
 
311
        self.disable_hook = None
 
312
        self.disable()
312
313
    
313
 
    def checker_callback(self, pid, condition):
 
314
    def checker_callback(self, pid, condition, command):
314
315
        """The checker has completed, so take appropriate actions."""
315
316
        self.checker_callback_tag = None
316
317
        self.checker = None
317
 
        if (os.WIFEXITED(condition) 
 
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))
 
322
        if (os.WIFEXITED(condition)
318
323
            and (os.WEXITSTATUS(condition) == 0)):
319
324
            logger.info(u"Checker for %(name)s succeeded",
320
325
                        vars(self))
321
 
            # Emit D-Bus signal
322
 
            self.CheckerCompleted(True)
 
326
            if self.use_dbus:
 
327
                # Emit D-Bus signal
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
 
330
                                      dbus.String(command))
323
331
            self.bump_timeout()
324
332
        elif not os.WIFEXITED(condition):
325
333
            logger.warning(u"Checker for %(name)s crashed?",
326
334
                           vars(self))
327
 
            # Emit D-Bus signal
328
 
            self.CheckerCompleted(False)
 
335
            if self.use_dbus:
 
336
                # Emit D-Bus signal
 
337
                self.CheckerCompleted(dbus.Boolean(False),
 
338
                                      dbus.UInt16(condition),
 
339
                                      dbus.String(command))
329
340
        else:
330
341
            logger.info(u"Checker for %(name)s failed",
331
342
                        vars(self))
332
 
            # Emit D-Bus signal
333
 
            self.CheckerCompleted(False)
 
343
            if self.use_dbus:
 
344
                # Emit D-Bus signal
 
345
                self.CheckerCompleted(dbus.Boolean(False),
 
346
                                      dbus.UInt16(condition),
 
347
                                      dbus.String(command))
334
348
    
335
349
    def bump_timeout(self):
336
350
        """Bump up the timeout for this client.
338
352
        alive and well.
339
353
        """
340
354
        self.last_checked_ok = datetime.datetime.utcnow()
341
 
        gobject.source_remove(self.stop_initiator_tag)
342
 
        self.stop_initiator_tag = (gobject.timeout_add
343
 
                                   (self._timeout_milliseconds,
344
 
                                    self.stop))
 
355
        gobject.source_remove(self.disable_initiator_tag)
 
356
        self.disable_initiator_tag = (gobject.timeout_add
 
357
                                      (self.timeout_milliseconds(),
 
358
                                       self.disable))
 
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)))
345
365
    
346
366
    def start_checker(self):
347
367
        """Start a new checker subprocess if one is not running.
357
377
        # is as it should be.
358
378
        if self.checker is None:
359
379
            try:
360
 
                # In case check_command has exactly one % operator
361
 
                command = self.check_command % self.host
 
380
                # In case checker_command has exactly one % operator
 
381
                command = self.checker_command % self.host
362
382
            except TypeError:
363
383
                # Escape attributes for the shell
364
384
                escaped_attrs = dict((key, re.escape(str(val)))
365
385
                                     for key, val in
366
386
                                     vars(self).iteritems())
367
387
                try:
368
 
                    command = self.check_command % escaped_attrs
 
388
                    command = self.checker_command % escaped_attrs
369
389
                except TypeError, error:
370
390
                    logger.error(u'Could not format string "%s":'
371
 
                                 u' %s', self.check_command, error)
 
391
                                 u' %s', self.checker_command, error)
372
392
                    return True # Try again later
373
393
            try:
374
394
                logger.info(u"Starting checker %r for %s",
380
400
                self.checker = subprocess.Popen(command,
381
401
                                                close_fds=True,
382
402
                                                shell=True, cwd="/")
 
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))
383
409
                self.checker_callback_tag = (gobject.child_watch_add
384
410
                                             (self.checker.pid,
385
 
                                              self.checker_callback))
386
 
                # Emit D-Bus signal
387
 
                self.CheckerStarted(command)
 
411
                                              self.checker_callback,
 
412
                                              data=command))
388
413
            except OSError, error:
389
414
                logger.error(u"Failed to start subprocess: %s",
390
415
                             error)
408
433
            if error.errno != errno.ESRCH: # No such process
409
434
                raise
410
435
        self.checker = None
 
436
        if self.use_dbus:
 
437
            self.PropertyChanged(dbus.String(u"checker_running"),
 
438
                                 dbus.Boolean(False, variant_level=1))
411
439
    
412
440
    def still_valid(self):
413
441
        """Has the timeout not yet passed for this client?"""
414
 
        if not self.started:
 
442
        if not getattr(self, "enabled", False):
415
443
            return False
416
444
        now = datetime.datetime.utcnow()
417
445
        if self.last_checked_ok is None:
422
450
    ## D-Bus methods & signals
423
451
    _interface = u"org.mandos_system.Mandos.Client"
424
452
    
425
 
    def _datetime_to_dbus_struct(dt):
426
 
        return dbus.Struct(dt.year, dt.month, dt.day, dt.hour,
427
 
                           dt.minute, dt.second, dt.microsecond,
428
 
                           signature="nyyyyyu")
429
 
    
430
453
    # BumpTimeout - method
431
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
432
455
    BumpTimeout.__name__ = "BumpTimeout"
433
456
    
434
 
    # IntervalChanged - signal
435
 
    @dbus.service.signal(_interface, signature="t")
436
 
    def IntervalChanged(self, t):
437
 
        "D-Bus signal"
438
 
        pass
439
 
    
440
457
    # CheckerCompleted - signal
441
 
    @dbus.service.signal(_interface, signature="b")
442
 
    def CheckerCompleted(self, success):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
443
460
        "D-Bus signal"
444
461
        pass
445
462
    
446
 
    # CheckerIsRunning - method
447
 
    @dbus.service.method(_interface, out_signature="b")
448
 
    def CheckerIsRunning(self):
449
 
        "D-Bus getter method"
450
 
        return self.checker is not None
451
 
    
452
463
    # CheckerStarted - signal
453
464
    @dbus.service.signal(_interface, signature="s")
454
465
    def CheckerStarted(self, command):
455
466
        "D-Bus signal"
456
467
        pass
457
468
    
458
 
    # GetChecker - method
459
 
    @dbus.service.method(_interface, out_signature="s")
460
 
    def GetChecker(self):
461
 
        "D-Bus getter method"
462
 
        return self.checker_command
463
 
    
464
 
    # GetCreated - method
465
 
    @dbus.service.method(_interface, out_signature="(nyyyyyu)")
466
 
    def GetCreated(self):
467
 
        "D-Bus getter method"
468
 
        return datetime_to_dbus_struct(self.created)
469
 
    
470
 
    # GetFingerprint - method
471
 
    @dbus.service.method(_interface, out_signature="s")
472
 
    def GetFingerprint(self):
473
 
        "D-Bus getter method"
474
 
        return self.fingerprint
475
 
    
476
 
    # GetHost - method
477
 
    @dbus.service.method(_interface, out_signature="s")
478
 
    def GetHost(self):
479
 
        "D-Bus getter method"
480
 
        return self.host
481
 
    
482
 
    # GetInterval - method
483
 
    @dbus.service.method(_interface, out_signature="t")
484
 
    def GetInterval(self):
485
 
        "D-Bus getter method"
486
 
        return self._interval_milliseconds
487
 
    
488
 
    # GetName - method
489
 
    @dbus.service.method(_interface, out_signature="s")
490
 
    def GetName(self):
491
 
        "D-Bus getter method"
492
 
        return self.name
493
 
    
494
 
    # GetStarted - method
495
 
    @dbus.service.method(_interface, out_signature="(nyyyyyu)")
496
 
    def GetStarted(self):
497
 
        "D-Bus getter method"
498
 
        if self.started is not None:
499
 
            return datetime_to_dbus_struct(self.started)
500
 
        else:
501
 
            return dbus.Struct(0, 0, 0, 0, 0, 0, 0,
502
 
                               signature="nyyyyyu")
503
 
    
504
 
    # GetTimeout - method
505
 
    @dbus.service.method(_interface, out_signature="t")
506
 
    def GetTimeout(self):
507
 
        "D-Bus getter method"
508
 
        return self._timeout_milliseconds
 
469
    # GetAllProperties - method
 
470
    @dbus.service.method(_interface, out_signature="a{sv}")
 
471
    def GetAllProperties(self):
 
472
        "D-Bus method"
 
473
        return dbus.Dictionary({
 
474
                dbus.String("name"):
 
475
                    dbus.String(self.name, variant_level=1),
 
476
                dbus.String("fingerprint"):
 
477
                    dbus.String(self.fingerprint, variant_level=1),
 
478
                dbus.String("host"):
 
479
                    dbus.String(self.host, variant_level=1),
 
480
                dbus.String("created"):
 
481
                    _datetime_to_dbus(self.created, variant_level=1),
 
482
                dbus.String("last_enabled"):
 
483
                    (_datetime_to_dbus(self.last_enabled,
 
484
                                       variant_level=1)
 
485
                     if self.last_enabled is not None
 
486
                     else dbus.Boolean(False, variant_level=1)),
 
487
                dbus.String("enabled"):
 
488
                    dbus.Boolean(self.enabled, variant_level=1),
 
489
                dbus.String("last_checked_ok"):
 
490
                    (_datetime_to_dbus(self.last_checked_ok,
 
491
                                       variant_level=1)
 
492
                     if self.last_checked_ok is not None
 
493
                     else dbus.Boolean (False, variant_level=1)),
 
494
                dbus.String("timeout"):
 
495
                    dbus.UInt64(self.timeout_milliseconds(),
 
496
                                variant_level=1),
 
497
                dbus.String("interval"):
 
498
                    dbus.UInt64(self.interval_milliseconds(),
 
499
                                variant_level=1),
 
500
                dbus.String("checker"):
 
501
                    dbus.String(self.checker_command,
 
502
                                variant_level=1),
 
503
                dbus.String("checker_running"):
 
504
                    dbus.Boolean(self.checker is not None,
 
505
                                 variant_level=1),
 
506
                }, signature="sv")
 
507
    
 
508
    # IsStillValid - method
 
509
    IsStillValid = (dbus.service.method(_interface, out_signature="b")
 
510
                    (still_valid))
 
511
    IsStillValid.__name__ = "IsStillValid"
 
512
    
 
513
    # PropertyChanged - signal
 
514
    @dbus.service.signal(_interface, signature="sv")
 
515
    def PropertyChanged(self, property, value):
 
516
        "D-Bus signal"
 
517
        pass
509
518
    
510
519
    # SetChecker - method
511
520
    @dbus.service.method(_interface, in_signature="s")
512
521
    def SetChecker(self, checker):
513
522
        "D-Bus setter method"
514
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))
515
528
    
516
529
    # SetHost - method
517
530
    @dbus.service.method(_interface, in_signature="s")
518
531
    def SetHost(self, host):
519
532
        "D-Bus setter method"
520
533
        self.host = host
 
534
        # Emit D-Bus signal
 
535
        self.PropertyChanged(dbus.String(u"host"),
 
536
                             dbus.String(self.host, variant_level=1))
521
537
    
522
538
    # SetInterval - method
523
539
    @dbus.service.method(_interface, in_signature="t")
524
540
    def SetInterval(self, milliseconds):
525
 
        self.interval = datetime.timdeelta(0, 0, 0, milliseconds)
526
 
    
527
 
    # SetTimeout - method
528
 
    @dbus.service.method(_interface, in_signature="t")
529
 
    def SetTimeout(self, milliseconds):
530
 
        self.timeout = datetime.timedelta(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)))
531
546
    
532
547
    # SetSecret - method
533
548
    @dbus.service.method(_interface, in_signature="ay",
536
551
        "D-Bus setter method"
537
552
        self.secret = str(secret)
538
553
    
539
 
    # Start - method
540
 
    Start = dbus.service.method(_interface)(start)
541
 
    Start.__name__ = "Start"
 
554
    # SetTimeout - method
 
555
    @dbus.service.method(_interface, in_signature="t")
 
556
    def SetTimeout(self, milliseconds):
 
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)))
 
562
    
 
563
    # Enable - method
 
564
    Enable = dbus.service.method(_interface)(enable)
 
565
    Enable.__name__ = "Enable"
542
566
    
543
567
    # StartChecker - method
544
 
    StartChecker = dbus.service.method(_interface)(start_checker)
545
 
    StartChecker.__name__ = "StartChecker"
546
 
    
547
 
    # StateChanged - signal
548
 
    @dbus.service.signal(_interface, signature="b")
549
 
    def StateChanged(self, started):
550
 
        "D-Bus signal"
551
 
        pass
552
 
    
553
 
    # StillValid - method
554
 
    StillValid = (dbus.service.method(_interface, out_signature="b")
555
 
                  (still_valid))
556
 
    StillValid.__name__ = "StillValid"
557
 
    
558
 
    # Stop - method
559
 
    Stop = dbus.service.method(_interface)(stop)
560
 
    Stop.__name__ = "Stop"
 
568
    @dbus.service.method(_interface)
 
569
    def StartChecker(self):
 
570
        "D-Bus method"
 
571
        self.start_checker()
 
572
    
 
573
    # Disable - method
 
574
    @dbus.service.method(_interface)
 
575
    def Disable(self):
 
576
        "D-Bus method"
 
577
        self.disable()
561
578
    
562
579
    # StopChecker - method
563
580
    StopChecker = dbus.service.method(_interface)(stop_checker)
564
581
    StopChecker.__name__ = "StopChecker"
565
582
    
566
 
    # TimeoutChanged - signal
567
 
    @dbus.service.signal(_interface, signature="t")
568
 
    def TimeoutChanged(self, t):
569
 
        "D-Bus signal"
570
 
        pass
571
 
    
572
 
    del _datetime_to_dbus_struct
573
583
    del _interface
574
584
 
575
585
 
830
840
    elif state == avahi.ENTRY_GROUP_FAILURE:
831
841
        logger.critical(u"Avahi: Error in group state changed %s",
832
842
                        unicode(error))
833
 
        raise AvahiGroupError("State changed: %s", str(error))
 
843
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
834
844
 
835
845
def if_nametoindex(interface):
836
846
    """Call the C function if_nametoindex(), or equivalent"""
886
896
                      help="Address to listen for requests on")
887
897
    parser.add_option("-p", "--port", type="int",
888
898
                      help="Port number to receive requests on")
889
 
    parser.add_option("--check", action="store_true", default=False,
 
899
    parser.add_option("--check", action="store_true",
890
900
                      help="Run self-test")
891
901
    parser.add_option("--debug", action="store_true",
892
902
                      help="Debug mode; run in foreground and log to"
899
909
                      default="/etc/mandos", metavar="DIR",
900
910
                      help="Directory to search for configuration"
901
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")
902
916
    options = parser.parse_args()[0]
903
917
    
904
918
    if options.check:
914
928
                        "priority":
915
929
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
916
930
                        "servicename": "Mandos",
 
931
                        "use_dbus": "True",
917
932
                        }
918
933
    
919
934
    # Parse config file for server-global settings
922
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
923
938
    # Convert the SafeConfigParser object to a dict
924
939
    server_settings = server_config.defaults()
925
 
    # Use getboolean on the boolean config option
 
940
    # Use getboolean on the boolean config options
926
941
    server_settings["debug"] = (server_config.getboolean
927
942
                                ("DEFAULT", "debug"))
 
943
    server_settings["use_dbus"] = (server_config.getboolean
 
944
                                   ("DEFAULT", "use_dbus"))
928
945
    del server_config
929
946
    
930
947
    # Override the settings from the config file with command line
931
948
    # options, if set.
932
949
    for option in ("interface", "address", "port", "debug",
933
 
                   "priority", "servicename", "configdir"):
 
950
                   "priority", "servicename", "configdir",
 
951
                   "use_dbus"):
934
952
        value = getattr(options, option)
935
953
        if value is not None:
936
954
            server_settings[option] = value
937
955
    del options
938
956
    # Now we have our good server settings in "server_settings"
939
957
    
 
958
    # For convenience
940
959
    debug = server_settings["debug"]
 
960
    use_dbus = server_settings["use_dbus"]
941
961
    
942
962
    if not debug:
943
963
        syslogger.setLevel(logging.WARNING)
971
991
    except IOError, error:
972
992
        logger.error("Could not open file %r", pidfilename)
973
993
    
974
 
    uid = 65534
975
 
    gid = 65534
976
 
    try:
977
 
        uid = pwd.getpwnam("mandos").pw_uid
978
 
    except KeyError:
979
 
        try:
980
 
            uid = pwd.getpwnam("nobody").pw_uid
981
 
        except KeyError:
982
 
            pass
983
 
    try:
984
 
        gid = pwd.getpwnam("mandos").pw_gid
985
 
    except KeyError:
986
 
        try:
987
 
            gid = pwd.getpwnam("nogroup").pw_gid
988
 
        except KeyError:
989
 
            pass
 
994
    try:
 
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:
 
1005
        gid = pwd.getpwnam("_mandos").pw_gid
 
1006
    except KeyError:
 
1007
        try:
 
1008
            gid = pwd.getpwnam("mandos").pw_gid
 
1009
        except KeyError:
 
1010
            try:
 
1011
                gid = pwd.getpwnam("nogroup").pw_gid
 
1012
            except KeyError:
 
1013
                gid = 65534
990
1014
    try:
991
1015
        os.setuid(uid)
992
1016
        os.setgid(gid)
1012
1036
                                           avahi.DBUS_PATH_SERVER),
1013
1037
                            avahi.DBUS_INTERFACE_SERVER)
1014
1038
    # End of Avahi example code
1015
 
    bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus)
1016
 
    
1017
 
    def remove_from_clients(client):
1018
 
        clients.remove(client)
1019
 
        if not clients:
1020
 
            logger.critical(u"No clients left, exiting")
1021
 
            sys.exit()
 
1039
    if use_dbus:
 
1040
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1041
                                        bus)
1022
1042
    
1023
1043
    clients.update(Set(Client(name = section,
1024
 
                              stop_hook = remove_from_clients,
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
1065
1084
        
1066
1085
        while clients:
1067
1086
            client = clients.pop()
1068
 
            client.stop_hook = None
1069
 
            client.stop()
 
1087
            client.disable_hook = None
 
1088
            client.disable()
1070
1089
    
1071
1090
    atexit.register(cleanup)
1072
1091
    
1075
1094
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1076
1095
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1077
1096
    
 
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
 
1143
    
 
1144
        mandos_server = MandosServer()
 
1145
    
1078
1146
    for client in clients:
1079
 
        client.start()
 
1147
        if use_dbus:
 
1148
            # Emit D-Bus signal
 
1149
            mandos_server.ClientAdded(client.dbus_object_path,
 
1150
                                      client.GetAllProperties())
 
1151
        client.enable()
1080
1152
    
1081
1153
    tcp_server.enable()
1082
1154
    tcp_server.server_activate()
1106
1178
        logger.debug(u"Starting main loop")
1107
1179
        main_loop.run()
1108
1180
    except AvahiError, error:
1109
 
        logger.critical(u"AvahiError: %s" + unicode(error))
 
1181
        logger.critical(u"AvahiError: %s", error)
1110
1182
        sys.exit(1)
1111
1183
    except KeyboardInterrupt:
1112
1184
        if debug: