/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

merge

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,2009 Teddy Hogeborn
15
 
# Copyright © 2008,2009 Björn Påhlsson
 
14
# Copyright © 2008 Teddy Hogeborn
 
15
# Copyright © 2008 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.3"
 
69
version = "1.0.2"
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, *args, **kwargs):
 
85
    def __init__(self, value):
86
86
        self.value = value
87
 
        super(AvahiError, self).__init__(value, *args, **kwargs)
88
 
    def __unicode__(self):
89
 
        return unicode(repr(self.value))
 
87
        super(AvahiError, self).__init__()
 
88
    def __str__(self):
 
89
        return 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(u"Too many renames")
 
132
            raise AvahiServiceError("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
 
 
178
173
class Client(dbus.service.Object):
179
174
    """A representation of a client host served by this server.
180
175
    Attributes:
181
 
    name:       string; from the config file, used in log messages
 
176
    name:      string; from the config file, used in log messages
182
177
    fingerprint: string (40 or 32 hexadecimal digits); used to
183
178
                 uniquely identify the client
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()
 
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
189
183
    last_checked_ok: datetime.datetime(); (UTC) or None
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.
 
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.
197
191
    checker_initiator_tag: a gobject event source tag, or None
198
 
    disable_initiator_tag:    - '' -
 
192
    stop_initiator_tag:    - '' -
199
193
    checker_callback_tag:  - '' -
200
194
    checker_command: string; External command which is run to check if
201
195
                     client lives.  %() expansions are done at
202
196
                     runtime with vars(self) as dict, so that for
203
197
                     instance %(name)s can be used in the command.
204
 
    use_dbus: bool(); Whether to provide D-Bus interface and signals
205
 
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
 
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: - '' -
206
203
    """
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):
 
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):
221
232
        """Note: the 'checker' key in 'config' sets the
222
233
        'checker_command' attribute and *not* the 'checker'
223
234
        attribute."""
224
 
        self.name = name
 
235
        dbus.service.Object.__init__(self, bus,
 
236
                                     "/Mandos/clients/%s"
 
237
                                     % name.replace(".", "_"))
225
238
        if config is None:
226
239
            config = {}
 
240
        self.name = name
227
241
        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)
235
242
        # Uppercase and remove spaces from fingerprint for later
236
243
        # comparison purposes with return value from the fingerprint()
237
244
        # function
250
257
                            % self.name)
251
258
        self.host = config.get("host", "")
252
259
        self.created = datetime.datetime.utcnow()
253
 
        self.enabled = False
254
 
        self.last_enabled = None
 
260
        self.started = None
255
261
        self.last_checked_ok = None
256
262
        self.timeout = string_to_delta(config["timeout"])
257
263
        self.interval = string_to_delta(config["interval"])
258
 
        self.disable_hook = disable_hook
 
264
        self.stop_hook = stop_hook
259
265
        self.checker = None
260
266
        self.checker_initiator_tag = None
261
 
        self.disable_initiator_tag = None
 
267
        self.stop_initiator_tag = None
262
268
        self.checker_callback_tag = None
263
 
        self.checker_command = config["checker"]
 
269
        self.check_command = config["checker"]
264
270
    
265
 
    def enable(self):
 
271
    def start(self):
266
272
        """Start this client's checker and timeout hooks"""
267
 
        self.last_enabled = datetime.datetime.utcnow()
 
273
        self.started = datetime.datetime.utcnow()
268
274
        # Schedule a new checker to be started an 'interval' from now,
269
275
        # and every interval from then on.
270
276
        self.checker_initiator_tag = (gobject.timeout_add
271
 
                                      (self.interval_milliseconds(),
 
277
                                      (self._interval_milliseconds,
272
278
                                       self.start_checker))
273
279
        # Also start a new checker *right now*.
274
280
        self.start_checker()
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)))
 
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)
287
287
    
288
 
    def disable(self):
289
 
        """Disable this client."""
290
 
        if not getattr(self, "enabled", False):
 
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:
291
293
            return False
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
 
294
        if getattr(self, "stop_initiator_tag", False):
 
295
            gobject.source_remove(self.stop_initiator_tag)
 
296
            self.stop_initiator_tag = None
296
297
        if getattr(self, "checker_initiator_tag", False):
297
298
            gobject.source_remove(self.checker_initiator_tag)
298
299
            self.checker_initiator_tag = None
299
300
        self.stop_checker()
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))
 
301
        if self.stop_hook:
 
302
            self.stop_hook(self)
 
303
        self.started = None
 
304
        # Emit D-Bus signal
 
305
        self.StateChanged(False)
307
306
        # Do not run this again if called by a gobject.timeout_add
308
307
        return False
309
308
    
310
309
    def __del__(self):
311
 
        self.disable_hook = None
312
 
        self.disable()
 
310
        self.stop_hook = None
 
311
        self.stop()
313
312
    
314
 
    def checker_callback(self, pid, condition, command):
 
313
    def checker_callback(self, pid, condition):
315
314
        """The checker has completed, so take appropriate actions."""
316
315
        self.checker_callback_tag = None
317
316
        self.checker = None
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)
 
317
        if (os.WIFEXITED(condition) 
323
318
            and (os.WEXITSTATUS(condition) == 0)):
324
319
            logger.info(u"Checker for %(name)s succeeded",
325
320
                        vars(self))
326
 
            if self.use_dbus:
327
 
                # Emit D-Bus signal
328
 
                self.CheckerCompleted(dbus.Boolean(True),
329
 
                                      dbus.UInt16(condition),
330
 
                                      dbus.String(command))
 
321
            # Emit D-Bus signal
 
322
            self.CheckerCompleted(True)
331
323
            self.bump_timeout()
332
324
        elif not os.WIFEXITED(condition):
333
325
            logger.warning(u"Checker for %(name)s crashed?",
334
326
                           vars(self))
335
 
            if self.use_dbus:
336
 
                # Emit D-Bus signal
337
 
                self.CheckerCompleted(dbus.Boolean(False),
338
 
                                      dbus.UInt16(condition),
339
 
                                      dbus.String(command))
 
327
            # Emit D-Bus signal
 
328
            self.CheckerCompleted(False)
340
329
        else:
341
330
            logger.info(u"Checker for %(name)s failed",
342
331
                        vars(self))
343
 
            if self.use_dbus:
344
 
                # Emit D-Bus signal
345
 
                self.CheckerCompleted(dbus.Boolean(False),
346
 
                                      dbus.UInt16(condition),
347
 
                                      dbus.String(command))
 
332
            # Emit D-Bus signal
 
333
            self.CheckerCompleted(False)
348
334
    
349
335
    def bump_timeout(self):
350
336
        """Bump up the timeout for this client.
352
338
        alive and well.
353
339
        """
354
340
        self.last_checked_ok = datetime.datetime.utcnow()
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)))
 
341
        gobject.source_remove(self.stop_initiator_tag)
 
342
        self.stop_initiator_tag = (gobject.timeout_add
 
343
                                   (self._timeout_milliseconds,
 
344
                                    self.stop))
365
345
    
366
346
    def start_checker(self):
367
347
        """Start a new checker subprocess if one is not running.
377
357
        # is as it should be.
378
358
        if self.checker is None:
379
359
            try:
380
 
                # In case checker_command has exactly one % operator
381
 
                command = self.checker_command % self.host
 
360
                # In case check_command has exactly one % operator
 
361
                command = self.check_command % self.host
382
362
            except TypeError:
383
363
                # Escape attributes for the shell
384
364
                escaped_attrs = dict((key, re.escape(str(val)))
385
365
                                     for key, val in
386
366
                                     vars(self).iteritems())
387
367
                try:
388
 
                    command = self.checker_command % escaped_attrs
 
368
                    command = self.check_command % escaped_attrs
389
369
                except TypeError, error:
390
370
                    logger.error(u'Could not format string "%s":'
391
 
                                 u' %s', self.checker_command, error)
 
371
                                 u' %s', self.check_command, error)
392
372
                    return True # Try again later
393
373
            try:
394
374
                logger.info(u"Starting checker %r for %s",
400
380
                self.checker = subprocess.Popen(command,
401
381
                                                close_fds=True,
402
382
                                                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))
409
383
                self.checker_callback_tag = (gobject.child_watch_add
410
384
                                             (self.checker.pid,
411
 
                                              self.checker_callback,
412
 
                                              data=command))
 
385
                                              self.checker_callback))
 
386
                # Emit D-Bus signal
 
387
                self.CheckerStarted(command)
413
388
            except OSError, error:
414
389
                logger.error(u"Failed to start subprocess: %s",
415
390
                             error)
433
408
            if error.errno != errno.ESRCH: # No such process
434
409
                raise
435
410
        self.checker = None
436
 
        if self.use_dbus:
437
 
            self.PropertyChanged(dbus.String(u"checker_running"),
438
 
                                 dbus.Boolean(False, variant_level=1))
439
411
    
440
412
    def still_valid(self):
441
413
        """Has the timeout not yet passed for this client?"""
442
 
        if not getattr(self, "enabled", False):
 
414
        if not self.started:
443
415
            return False
444
416
        now = datetime.datetime.utcnow()
445
417
        if self.last_checked_ok is None:
450
422
    ## D-Bus methods & signals
451
423
    _interface = u"org.mandos_system.Mandos.Client"
452
424
    
 
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
    
453
430
    # BumpTimeout - method
454
431
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
455
432
    BumpTimeout.__name__ = "BumpTimeout"
456
433
    
 
434
    # IntervalChanged - signal
 
435
    @dbus.service.signal(_interface, signature="t")
 
436
    def IntervalChanged(self, t):
 
437
        "D-Bus signal"
 
438
        pass
 
439
    
457
440
    # CheckerCompleted - signal
458
 
    @dbus.service.signal(_interface, signature="bqs")
459
 
    def CheckerCompleted(self, success, condition, command):
 
441
    @dbus.service.signal(_interface, signature="b")
 
442
    def CheckerCompleted(self, success):
460
443
        "D-Bus signal"
461
444
        pass
462
445
    
 
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
    
463
452
    # CheckerStarted - signal
464
453
    @dbus.service.signal(_interface, signature="s")
465
454
    def CheckerStarted(self, command):
466
455
        "D-Bus signal"
467
456
        pass
468
457
    
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
 
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
518
509
    
519
510
    # SetChecker - method
520
511
    @dbus.service.method(_interface, in_signature="s")
521
512
    def SetChecker(self, checker):
522
513
        "D-Bus setter method"
523
514
        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))
528
515
    
529
516
    # SetHost - method
530
517
    @dbus.service.method(_interface, in_signature="s")
531
518
    def SetHost(self, host):
532
519
        "D-Bus setter method"
533
520
        self.host = host
534
 
        # Emit D-Bus signal
535
 
        self.PropertyChanged(dbus.String(u"host"),
536
 
                             dbus.String(self.host, variant_level=1))
537
521
    
538
522
    # SetInterval - method
539
523
    @dbus.service.method(_interface, in_signature="t")
540
524
    def SetInterval(self, 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)))
 
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)
546
531
    
547
532
    # SetSecret - method
548
533
    @dbus.service.method(_interface, in_signature="ay",
551
536
        "D-Bus setter method"
552
537
        self.secret = str(secret)
553
538
    
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"
 
539
    # Start - method
 
540
    Start = dbus.service.method(_interface)(start)
 
541
    Start.__name__ = "Start"
566
542
    
567
543
    # StartChecker - method
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()
 
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"
578
561
    
579
562
    # StopChecker - method
580
563
    StopChecker = dbus.service.method(_interface)(stop_checker)
581
564
    StopChecker.__name__ = "StopChecker"
582
565
    
 
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
583
573
    del _interface
584
574
 
585
575
 
840
830
    elif state == avahi.ENTRY_GROUP_FAILURE:
841
831
        logger.critical(u"Avahi: Error in group state changed %s",
842
832
                        unicode(error))
843
 
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
 
833
        raise AvahiGroupError("State changed: %s", str(error))
844
834
 
845
835
def if_nametoindex(interface):
846
836
    """Call the C function if_nametoindex(), or equivalent"""
896
886
                      help="Address to listen for requests on")
897
887
    parser.add_option("-p", "--port", type="int",
898
888
                      help="Port number to receive requests on")
899
 
    parser.add_option("--check", action="store_true",
 
889
    parser.add_option("--check", action="store_true", default=False,
900
890
                      help="Run self-test")
901
891
    parser.add_option("--debug", action="store_true",
902
892
                      help="Debug mode; run in foreground and log to"
909
899
                      default="/etc/mandos", metavar="DIR",
910
900
                      help="Directory to search for configuration"
911
901
                      " 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")
916
902
    options = parser.parse_args()[0]
917
903
    
918
904
    if options.check:
928
914
                        "priority":
929
915
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
930
916
                        "servicename": "Mandos",
931
 
                        "use_dbus": "True",
932
917
                        }
933
918
    
934
919
    # Parse config file for server-global settings
937
922
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
938
923
    # Convert the SafeConfigParser object to a dict
939
924
    server_settings = server_config.defaults()
940
 
    # Use getboolean on the boolean config options
 
925
    # Use getboolean on the boolean config option
941
926
    server_settings["debug"] = (server_config.getboolean
942
927
                                ("DEFAULT", "debug"))
943
 
    server_settings["use_dbus"] = (server_config.getboolean
944
 
                                   ("DEFAULT", "use_dbus"))
945
928
    del server_config
946
929
    
947
930
    # Override the settings from the config file with command line
948
931
    # options, if set.
949
932
    for option in ("interface", "address", "port", "debug",
950
 
                   "priority", "servicename", "configdir",
951
 
                   "use_dbus"):
 
933
                   "priority", "servicename", "configdir"):
952
934
        value = getattr(options, option)
953
935
        if value is not None:
954
936
            server_settings[option] = value
955
937
    del options
956
938
    # Now we have our good server settings in "server_settings"
957
939
    
958
 
    # For convenience
959
940
    debug = server_settings["debug"]
960
 
    use_dbus = server_settings["use_dbus"]
961
941
    
962
942
    if not debug:
963
943
        syslogger.setLevel(logging.WARNING)
972
952
    # Parse config file with clients
973
953
    client_defaults = { "timeout": "1h",
974
954
                        "interval": "5m",
975
 
                        "checker": "fping -q -- %%(host)s",
 
955
                        "checker": "fping -q -- %(host)s",
976
956
                        "host": "",
977
957
                        }
978
958
    client_config = ConfigParser.SafeConfigParser(client_defaults)
991
971
    except IOError, error:
992
972
        logger.error("Could not open file %r", pidfilename)
993
973
    
994
 
    try:
995
 
        uid = pwd.getpwnam("_mandos").pw_uid
996
 
        gid = pwd.getpwnam("_mandos").pw_gid
997
 
    except KeyError:
998
 
        try:
999
 
            uid = pwd.getpwnam("mandos").pw_uid
1000
 
            gid = pwd.getpwnam("mandos").pw_gid
1001
 
        except KeyError:
1002
 
            try:
1003
 
                uid = pwd.getpwnam("nobody").pw_uid
1004
 
                gid = pwd.getpwnam("nogroup").pw_gid
1005
 
            except KeyError:
1006
 
                uid = 65534
1007
 
                gid = 65534
 
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
1008
990
    try:
1009
991
        os.setuid(uid)
1010
992
        os.setgid(gid)
1030
1012
                                           avahi.DBUS_PATH_SERVER),
1031
1013
                            avahi.DBUS_INTERFACE_SERVER)
1032
1014
    # End of Avahi example code
1033
 
    if use_dbus:
1034
 
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
1035
 
                                        bus)
 
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()
1036
1022
    
1037
1023
    clients.update(Set(Client(name = section,
 
1024
                              stop_hook = remove_from_clients,
1038
1025
                              config
1039
 
                              = dict(client_config.items(section)),
1040
 
                              use_dbus = use_dbus)
 
1026
                              = dict(client_config.items(section)))
1041
1027
                       for section in client_config.sections()))
1042
1028
    if not clients:
1043
 
        logger.warning(u"No clients defined")
 
1029
        logger.critical(u"No clients defined")
 
1030
        sys.exit(1)
1044
1031
    
1045
1032
    if debug:
1046
1033
        # Redirect stdin so all checkers get /dev/null
1078
1065
        
1079
1066
        while clients:
1080
1067
            client = clients.pop()
1081
 
            client.disable_hook = None
1082
 
            client.disable()
 
1068
            client.stop_hook = None
 
1069
            client.stop()
1083
1070
    
1084
1071
    atexit.register(cleanup)
1085
1072
    
1088
1075
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1089
1076
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1090
1077
    
1091
 
    if use_dbus:
1092
 
        class MandosServer(dbus.service.Object):
1093
 
            """A D-Bus proxy object"""
1094
 
            def __init__(self):
1095
 
                dbus.service.Object.__init__(self, bus,
1096
 
                                             "/Mandos")
1097
 
            _interface = u"org.mandos_system.Mandos"
1098
 
 
1099
 
            @dbus.service.signal(_interface, signature="oa{sv}")
1100
 
            def ClientAdded(self, objpath, properties):
1101
 
                "D-Bus signal"
1102
 
                pass
1103
 
 
1104
 
            @dbus.service.signal(_interface, signature="o")
1105
 
            def ClientRemoved(self, objpath):
1106
 
                "D-Bus signal"
1107
 
                pass
1108
 
 
1109
 
            @dbus.service.method(_interface, out_signature="ao")
1110
 
            def GetAllClients(self):
1111
 
                return dbus.Array(c.dbus_object_path for c in clients)
1112
 
 
1113
 
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1114
 
            def GetAllClientsWithProperties(self):
1115
 
                return dbus.Dictionary(
1116
 
                    ((c.dbus_object_path, c.GetAllProperties())
1117
 
                     for c in clients),
1118
 
                    signature="oa{sv}")
1119
 
 
1120
 
            @dbus.service.method(_interface, in_signature="o")
1121
 
            def RemoveClient(self, object_path):
1122
 
                for c in clients:
1123
 
                    if c.dbus_object_path == object_path:
1124
 
                        clients.remove(c)
1125
 
                        # Don't signal anything except ClientRemoved
1126
 
                        c.use_dbus = False
1127
 
                        c.disable()
1128
 
                        # Emit D-Bus signal
1129
 
                        self.ClientRemoved(object_path)
1130
 
                        return
1131
 
                raise KeyError
1132
 
            @dbus.service.method(_interface)
1133
 
            def Quit(self):
1134
 
                main_loop.quit()
1135
 
 
1136
 
            del _interface
1137
 
    
1138
 
        mandos_server = MandosServer()
1139
 
    
1140
1078
    for client in clients:
1141
 
        if use_dbus:
1142
 
            # Emit D-Bus signal
1143
 
            mandos_server.ClientAdded(client.dbus_object_path,
1144
 
                                      client.GetAllProperties())
1145
 
        client.enable()
 
1079
        client.start()
1146
1080
    
1147
1081
    tcp_server.enable()
1148
1082
    tcp_server.server_activate()
1172
1106
        logger.debug(u"Starting main loop")
1173
1107
        main_loop.run()
1174
1108
    except AvahiError, error:
1175
 
        logger.critical(u"AvahiError: %s", error)
 
1109
        logger.critical(u"AvahiError: %s" + unicode(error))
1176
1110
        sys.exit(1)
1177
1111
    except KeyboardInterrupt:
1178
1112
        if debug: