/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-02-09 19:26:19 UTC
  • Revision ID: teddy@fukt.bsnet.se-20090209192619-7rse82x9pypq4ihk
* plugin-runner.c: Comment change.

* plugins.d/mandos-client.c: Comment changes.
  (quit_now): New global flag.
  (handle_sigterm): Only call avahi_simple_poll_quit once.
  (main): Also handle SIGINT and SIGHUP.

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
35
35
 
36
36
import SocketServer
37
37
import socket
38
 
from optparse import OptionParser
 
38
import optparse
39
39
import datetime
40
40
import errno
41
41
import gnutls.crypto
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
version = "1.0.2"
 
69
version = "1.0.5"
70
70
 
71
71
logger = logging.Logger('mandos')
72
72
syslogger = (logging.handlers.SysLogHandler
73
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
74
74
              address = "/dev/log"))
75
75
syslogger.setFormatter(logging.Formatter
76
 
                       ('Mandos: %(levelname)s: %(message)s'))
 
76
                       ('Mandos [%(process)d]: %(levelname)s:'
 
77
                        ' %(message)s'))
77
78
logger.addHandler(syslogger)
78
79
 
79
80
console = logging.StreamHandler()
80
 
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
81
 
                                       ' %(message)s'))
 
81
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
 
82
                                       ' %(levelname)s: %(message)s'))
82
83
logger.addHandler(console)
83
84
 
84
85
class AvahiError(Exception):
85
 
    def __init__(self, value):
 
86
    def __init__(self, value, *args, **kwargs):
86
87
        self.value = value
87
 
        super(AvahiError, self).__init__()
88
 
    def __str__(self):
89
 
        return repr(self.value)
 
88
        super(AvahiError, self).__init__(value, *args, **kwargs)
 
89
    def __unicode__(self):
 
90
        return unicode(repr(self.value))
90
91
 
91
92
class AvahiServiceError(AvahiError):
92
93
    pass
129
130
            logger.critical(u"No suitable Zeroconf service name found"
130
131
                            u" after %i retries, exiting.",
131
132
                            self.rename_count)
132
 
            raise AvahiServiceError("Too many renames")
 
133
            raise AvahiServiceError(u"Too many renames")
133
134
        self.name = server.GetAlternativeServiceName(self.name)
134
135
        logger.info(u"Changing Zeroconf service name to %r ...",
135
136
                    str(self.name))
178
179
class Client(dbus.service.Object):
179
180
    """A representation of a client host served by this server.
180
181
    Attributes:
181
 
    name:       string; from the config file, used in log messages
 
182
    name:       string; from the config file, used in log messages and
 
183
                        D-Bus identifiers
182
184
    fingerprint: string (40 or 32 hexadecimal digits); used to
183
185
                 uniquely identify the client
184
186
    secret:     bytestring; sent verbatim (over TLS) to client
201
203
                     client lives.  %() expansions are done at
202
204
                     runtime with vars(self) as dict, so that for
203
205
                     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: - '' -
 
206
    use_dbus: bool(); Whether to provide D-Bus interface and signals
 
207
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
210
208
    """
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):
 
209
    def timeout_milliseconds(self):
 
210
        "Return the 'timeout' attribute in milliseconds"
 
211
        return ((self.timeout.days * 24 * 60 * 60 * 1000)
 
212
                + (self.timeout.seconds * 1000)
 
213
                + (self.timeout.microseconds // 1000))
 
214
    
 
215
    def interval_milliseconds(self):
 
216
        "Return the 'interval' attribute in milliseconds"
 
217
        return ((self.interval.days * 24 * 60 * 60 * 1000)
 
218
                + (self.interval.seconds * 1000)
 
219
                + (self.interval.microseconds // 1000))
 
220
    
 
221
    def __init__(self, name = None, disable_hook=None, config=None,
 
222
                 use_dbus=True):
243
223
        """Note: the 'checker' key in 'config' sets the
244
224
        'checker_command' attribute and *not* the 'checker'
245
225
        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)
 
226
        self.name = name
251
227
        if config is None:
252
228
            config = {}
253
 
        self.name = name
254
229
        logger.debug(u"Creating client %r", self.name)
 
230
        self.use_dbus = False   # During __init__
255
231
        # Uppercase and remove spaces from fingerprint for later
256
232
        # comparison purposes with return value from the fingerprint()
257
233
        # function
281
257
        self.disable_initiator_tag = None
282
258
        self.checker_callback_tag = None
283
259
        self.checker_command = config["checker"]
 
260
        self.last_connect = None
 
261
        # Only now, when this client is initialized, can it show up on
 
262
        # the D-Bus
 
263
        self.use_dbus = use_dbus
 
264
        if self.use_dbus:
 
265
            self.dbus_object_path = (dbus.ObjectPath
 
266
                                     ("/clients/"
 
267
                                      + self.name.replace(".", "_")))
 
268
            dbus.service.Object.__init__(self, bus,
 
269
                                         self.dbus_object_path)
284
270
    
285
271
    def enable(self):
286
272
        """Start this client's checker and timeout hooks"""
288
274
        # Schedule a new checker to be started an 'interval' from now,
289
275
        # and every interval from then on.
290
276
        self.checker_initiator_tag = (gobject.timeout_add
291
 
                                      (self._interval_milliseconds,
 
277
                                      (self.interval_milliseconds(),
292
278
                                       self.start_checker))
293
279
        # Also start a new checker *right now*.
294
280
        self.start_checker()
295
281
        # Schedule a disable() when 'timeout' has passed
296
282
        self.disable_initiator_tag = (gobject.timeout_add
297
 
                                   (self._timeout_milliseconds,
 
283
                                   (self.timeout_milliseconds(),
298
284
                                    self.disable))
299
285
        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)))
 
286
        if self.use_dbus:
 
287
            # Emit D-Bus signals
 
288
            self.PropertyChanged(dbus.String(u"enabled"),
 
289
                                 dbus.Boolean(True, variant_level=1))
 
290
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
291
                                 (_datetime_to_dbus(self.last_enabled,
 
292
                                                    variant_level=1)))
306
293
    
307
294
    def disable(self):
308
295
        """Disable this client."""
319
306
        if self.disable_hook:
320
307
            self.disable_hook(self)
321
308
        self.enabled = False
322
 
        # Emit D-Bus signal
323
 
        self.PropertyChanged(dbus.String(u"enabled"),
324
 
                             dbus.Boolean(False, variant_level=1))
 
309
        if self.use_dbus:
 
310
            # Emit D-Bus signal
 
311
            self.PropertyChanged(dbus.String(u"enabled"),
 
312
                                 dbus.Boolean(False, variant_level=1))
325
313
        # Do not run this again if called by a gobject.timeout_add
326
314
        return False
327
315
    
333
321
        """The checker has completed, so take appropriate actions."""
334
322
        self.checker_callback_tag = None
335
323
        self.checker = None
336
 
        # Emit D-Bus signal
337
 
        self.PropertyChanged(dbus.String(u"checker_running"),
338
 
                             dbus.Boolean(False, variant_level=1))
339
 
        if (os.WIFEXITED(condition)
340
 
            and (os.WEXITSTATUS(condition) == 0)):
341
 
            logger.info(u"Checker for %(name)s succeeded",
342
 
                        vars(self))
 
324
        if self.use_dbus:
343
325
            # Emit D-Bus signal
344
 
            self.CheckerCompleted(dbus.Boolean(True),
345
 
                                  dbus.UInt16(condition),
346
 
                                  dbus.String(command))
347
 
            self.bump_timeout()
348
 
        elif not os.WIFEXITED(condition):
 
326
            self.PropertyChanged(dbus.String(u"checker_running"),
 
327
                                 dbus.Boolean(False, variant_level=1))
 
328
        if os.WIFEXITED(condition):
 
329
            exitstatus = os.WEXITSTATUS(condition)
 
330
            if exitstatus == 0:
 
331
                logger.info(u"Checker for %(name)s succeeded",
 
332
                            vars(self))
 
333
                self.checked_ok()
 
334
            else:
 
335
                logger.info(u"Checker for %(name)s failed",
 
336
                            vars(self))
 
337
            if self.use_dbus:
 
338
                # Emit D-Bus signal
 
339
                self.CheckerCompleted(dbus.Int16(exitstatus),
 
340
                                      dbus.Int64(condition),
 
341
                                      dbus.String(command))
 
342
        else:
349
343
            logger.warning(u"Checker for %(name)s crashed?",
350
344
                           vars(self))
351
 
            # Emit D-Bus signal
352
 
            self.CheckerCompleted(dbus.Boolean(False),
353
 
                                  dbus.UInt16(condition),
354
 
                                  dbus.String(command))
355
 
        else:
356
 
            logger.info(u"Checker for %(name)s failed",
357
 
                        vars(self))
358
 
            # Emit D-Bus signal
359
 
            self.CheckerCompleted(dbus.Boolean(False),
360
 
                                  dbus.UInt16(condition),
361
 
                                  dbus.String(command))
 
345
            if self.use_dbus:
 
346
                # Emit D-Bus signal
 
347
                self.CheckerCompleted(dbus.Int16(-1),
 
348
                                      dbus.Int64(condition),
 
349
                                      dbus.String(command))
362
350
    
363
 
    def bump_timeout(self):
 
351
    def checked_ok(self):
364
352
        """Bump up the timeout for this client.
365
353
        This should only be called when the client has been seen,
366
354
        alive and well.
368
356
        self.last_checked_ok = datetime.datetime.utcnow()
369
357
        gobject.source_remove(self.disable_initiator_tag)
370
358
        self.disable_initiator_tag = (gobject.timeout_add
371
 
                                      (self._timeout_milliseconds,
 
359
                                      (self.timeout_milliseconds(),
372
360
                                       self.disable))
373
 
        self.PropertyChanged(dbus.String(u"last_checked_ok"),
374
 
                             (_datetime_to_dbus(self.last_checked_ok,
375
 
                                                variant_level=1)))
 
361
        if self.use_dbus:
 
362
            # Emit D-Bus signal
 
363
            self.PropertyChanged(
 
364
                dbus.String(u"last_checked_ok"),
 
365
                (_datetime_to_dbus(self.last_checked_ok,
 
366
                                   variant_level=1)))
376
367
    
377
368
    def start_checker(self):
378
369
        """Start a new checker subprocess if one is not running.
411
402
                self.checker = subprocess.Popen(command,
412
403
                                                close_fds=True,
413
404
                                                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))
 
405
                if self.use_dbus:
 
406
                    # Emit D-Bus signal
 
407
                    self.CheckerStarted(command)
 
408
                    self.PropertyChanged(
 
409
                        dbus.String("checker_running"),
 
410
                        dbus.Boolean(True, variant_level=1))
418
411
                self.checker_callback_tag = (gobject.child_watch_add
419
412
                                             (self.checker.pid,
420
413
                                              self.checker_callback,
442
435
            if error.errno != errno.ESRCH: # No such process
443
436
                raise
444
437
        self.checker = None
445
 
        self.PropertyChanged(dbus.String(u"checker_running"),
446
 
                             dbus.Boolean(False, variant_level=1))
 
438
        if self.use_dbus:
 
439
            self.PropertyChanged(dbus.String(u"checker_running"),
 
440
                                 dbus.Boolean(False, variant_level=1))
447
441
    
448
442
    def still_valid(self):
449
443
        """Has the timeout not yet passed for this client?"""
456
450
            return now < (self.last_checked_ok + self.timeout)
457
451
    
458
452
    ## D-Bus methods & signals
459
 
    _interface = u"org.mandos_system.Mandos.Client"
 
453
    _interface = u"se.bsnet.fukt.Mandos.Client"
460
454
    
461
 
    # BumpTimeout - method
462
 
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
463
 
    BumpTimeout.__name__ = "BumpTimeout"
 
455
    # CheckedOK - method
 
456
    CheckedOK = dbus.service.method(_interface)(checked_ok)
 
457
    CheckedOK.__name__ = "CheckedOK"
464
458
    
465
459
    # CheckerCompleted - signal
466
 
    @dbus.service.signal(_interface, signature="bqs")
467
 
    def CheckerCompleted(self, success, condition, command):
 
460
    @dbus.service.signal(_interface, signature="nxs")
 
461
    def CheckerCompleted(self, exitcode, waitstatus, command):
468
462
        "D-Bus signal"
469
463
        pass
470
464
    
500
494
                     if self.last_checked_ok is not None
501
495
                     else dbus.Boolean (False, variant_level=1)),
502
496
                dbus.String("timeout"):
503
 
                    dbus.UInt64(self._timeout_milliseconds,
 
497
                    dbus.UInt64(self.timeout_milliseconds(),
504
498
                                variant_level=1),
505
499
                dbus.String("interval"):
506
 
                    dbus.UInt64(self._interval_milliseconds,
 
500
                    dbus.UInt64(self.interval_milliseconds(),
507
501
                                variant_level=1),
508
502
                dbus.String("checker"):
509
503
                    dbus.String(self.checker_command,
511
505
                dbus.String("checker_running"):
512
506
                    dbus.Boolean(self.checker is not None,
513
507
                                 variant_level=1),
 
508
                dbus.String("object_path"):
 
509
                    dbus.ObjectPath(self.dbus_object_path,
 
510
                                    variant_level=1)
514
511
                }, signature="sv")
515
512
    
516
513
    # IsStillValid - method
529
526
    def SetChecker(self, checker):
530
527
        "D-Bus setter method"
531
528
        self.checker_command = checker
 
529
        # Emit D-Bus signal
 
530
        self.PropertyChanged(dbus.String(u"checker"),
 
531
                             dbus.String(self.checker_command,
 
532
                                         variant_level=1))
532
533
    
533
534
    # SetHost - method
534
535
    @dbus.service.method(_interface, in_signature="s")
535
536
    def SetHost(self, host):
536
537
        "D-Bus setter method"
537
538
        self.host = host
 
539
        # Emit D-Bus signal
 
540
        self.PropertyChanged(dbus.String(u"host"),
 
541
                             dbus.String(self.host, variant_level=1))
538
542
    
539
543
    # SetInterval - method
540
544
    @dbus.service.method(_interface, in_signature="t")
541
545
    def SetInterval(self, milliseconds):
542
 
        self.interval = datetime.timdeelta(0, 0, 0, milliseconds)
 
546
        self.interval = datetime.timedelta(0, 0, 0, milliseconds)
 
547
        # Emit D-Bus signal
 
548
        self.PropertyChanged(dbus.String(u"interval"),
 
549
                             (dbus.UInt64(self.interval_milliseconds(),
 
550
                                          variant_level=1)))
543
551
    
544
552
    # SetSecret - method
545
553
    @dbus.service.method(_interface, in_signature="ay",
552
560
    @dbus.service.method(_interface, in_signature="t")
553
561
    def SetTimeout(self, milliseconds):
554
562
        self.timeout = datetime.timedelta(0, 0, 0, milliseconds)
 
563
        # Emit D-Bus signal
 
564
        self.PropertyChanged(dbus.String(u"timeout"),
 
565
                             (dbus.UInt64(self.timeout_milliseconds(),
 
566
                                          variant_level=1)))
555
567
    
556
568
    # Enable - method
557
569
    Enable = dbus.service.method(_interface)(enable)
584
596
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
585
597
        # ...do the normal thing
586
598
        return session.peer_certificate
587
 
    list_size = ctypes.c_uint()
 
599
    list_size = ctypes.c_uint(1)
588
600
    cert_list = (gnutls.library.functions
589
601
                 .gnutls_certificate_get_peers
590
602
                 (session._c_object, ctypes.byref(list_size)))
 
603
    if not bool(cert_list) and list_size.value != 0:
 
604
        raise gnutls.errors.GNUTLSError("error getting peer"
 
605
                                        " certificate")
591
606
    if list_size.value == 0:
592
607
        return None
593
608
    cert = cert_list[0]
662
677
        # using OpenPGP certificates.
663
678
        
664
679
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
665
 
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
666
 
        #                "+DHE-DSS"))
 
680
        #                     "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
681
        #                     "+DHE-DSS"))
667
682
        # Use a fallback default, since this MUST be set.
668
683
        priority = self.server.settings.get("priority", "NORMAL")
669
684
        (gnutls.library.functions
677
692
            # Do not run session.bye() here: the session is not
678
693
            # established.  Just abandon the request.
679
694
            return
 
695
        logger.debug(u"Handshake succeeded")
680
696
        try:
681
697
            fpr = fingerprint(peer_certificate(session))
682
698
        except (TypeError, gnutls.errors.GNUTLSError), error:
684
700
            session.bye()
685
701
            return
686
702
        logger.debug(u"Fingerprint: %s", fpr)
 
703
        
687
704
        for c in self.server.clients:
688
705
            if c.fingerprint == fpr:
689
706
                client = c
702
719
            session.bye()
703
720
            return
704
721
        ## This won't work here, since we're in a fork.
705
 
        # client.bump_timeout()
 
722
        # client.checked_ok()
706
723
        sent_size = 0
707
724
        while sent_size < len(client.secret):
708
725
            sent = session.send(client.secret[sent_size:])
748
765
                                 u" bind to interface %s",
749
766
                                 self.settings["interface"])
750
767
                else:
751
 
                    raise error
 
768
                    raise
752
769
        # Only bind(2) the socket if we really need to.
753
770
        if self.server_address[0] or self.server_address[1]:
754
771
            if not self.server_address[0]:
775
792
 
776
793
def string_to_delta(interval):
777
794
    """Parse a string and return a datetime.timedelta
778
 
 
 
795
    
779
796
    >>> string_to_delta('7d')
780
797
    datetime.timedelta(7)
781
798
    >>> string_to_delta('60s')
833
850
    elif state == avahi.ENTRY_GROUP_FAILURE:
834
851
        logger.critical(u"Avahi: Error in group state changed %s",
835
852
                        unicode(error))
836
 
        raise AvahiGroupError("State changed: %s", str(error))
 
853
        raise AvahiGroupError(u"State changed: %s" % unicode(error))
837
854
 
838
855
def if_nametoindex(interface):
839
856
    """Call the C function if_nametoindex(), or equivalent"""
882
899
 
883
900
 
884
901
def main():
885
 
    parser = OptionParser(version = "%%prog %s" % version)
 
902
    parser = optparse.OptionParser(version = "%%prog %s" % version)
886
903
    parser.add_option("-i", "--interface", type="string",
887
904
                      metavar="IF", help="Bind to interface IF")
888
905
    parser.add_option("-a", "--address", type="string",
889
906
                      help="Address to listen for requests on")
890
907
    parser.add_option("-p", "--port", type="int",
891
908
                      help="Port number to receive requests on")
892
 
    parser.add_option("--check", action="store_true", default=False,
 
909
    parser.add_option("--check", action="store_true",
893
910
                      help="Run self-test")
894
911
    parser.add_option("--debug", action="store_true",
895
912
                      help="Debug mode; run in foreground and log to"
902
919
                      default="/etc/mandos", metavar="DIR",
903
920
                      help="Directory to search for configuration"
904
921
                      " files")
 
922
    parser.add_option("--no-dbus", action="store_false",
 
923
                      dest="use_dbus",
 
924
                      help="Do not provide D-Bus system bus"
 
925
                      " interface")
905
926
    options = parser.parse_args()[0]
906
927
    
907
928
    if options.check:
917
938
                        "priority":
918
939
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
919
940
                        "servicename": "Mandos",
 
941
                        "use_dbus": "True",
920
942
                        }
921
943
    
922
944
    # Parse config file for server-global settings
925
947
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
926
948
    # Convert the SafeConfigParser object to a dict
927
949
    server_settings = server_config.defaults()
928
 
    # Use getboolean on the boolean config option
929
 
    server_settings["debug"] = (server_config.getboolean
930
 
                                ("DEFAULT", "debug"))
 
950
    # Use the appropriate methods on the non-string config options
 
951
    server_settings["debug"] = server_config.getboolean("DEFAULT",
 
952
                                                        "debug")
 
953
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
 
954
                                                           "use_dbus")
 
955
    if server_settings["port"]:
 
956
        server_settings["port"] = server_config.getint("DEFAULT",
 
957
                                                       "port")
931
958
    del server_config
932
959
    
933
960
    # Override the settings from the config file with command line
934
961
    # options, if set.
935
962
    for option in ("interface", "address", "port", "debug",
936
 
                   "priority", "servicename", "configdir"):
 
963
                   "priority", "servicename", "configdir",
 
964
                   "use_dbus"):
937
965
        value = getattr(options, option)
938
966
        if value is not None:
939
967
            server_settings[option] = value
940
968
    del options
941
969
    # Now we have our good server settings in "server_settings"
942
970
    
 
971
    # For convenience
943
972
    debug = server_settings["debug"]
 
973
    use_dbus = server_settings["use_dbus"]
944
974
    
945
975
    if not debug:
946
976
        syslogger.setLevel(logging.WARNING)
955
985
    # Parse config file with clients
956
986
    client_defaults = { "timeout": "1h",
957
987
                        "interval": "5m",
958
 
                        "checker": "fping -q -- %(host)s",
 
988
                        "checker": "fping -q -- %%(host)s",
959
989
                        "host": "",
960
990
                        }
961
991
    client_config = ConfigParser.SafeConfigParser(client_defaults)
971
1001
    pidfilename = "/var/run/mandos.pid"
972
1002
    try:
973
1003
        pidfile = open(pidfilename, "w")
974
 
    except IOError, error:
 
1004
    except IOError:
975
1005
        logger.error("Could not open file %r", pidfilename)
976
1006
    
977
1007
    try:
978
1008
        uid = pwd.getpwnam("_mandos").pw_uid
 
1009
        gid = pwd.getpwnam("_mandos").pw_gid
979
1010
    except KeyError:
980
1011
        try:
981
1012
            uid = pwd.getpwnam("mandos").pw_uid
 
1013
            gid = pwd.getpwnam("mandos").pw_gid
982
1014
        except KeyError:
983
1015
            try:
984
1016
                uid = pwd.getpwnam("nobody").pw_uid
 
1017
                gid = pwd.getpwnam("nogroup").pw_gid
985
1018
            except KeyError:
986
1019
                uid = 65534
987
 
    try:
988
 
        gid = pwd.getpwnam("_mandos").pw_gid
989
 
    except KeyError:
990
 
        try:
991
 
            gid = pwd.getpwnam("mandos").pw_gid
992
 
        except KeyError:
993
 
            try:
994
 
                gid = pwd.getpwnam("nogroup").pw_gid
995
 
            except KeyError:
996
1020
                gid = 65534
997
1021
    try:
 
1022
        os.setgid(gid)
998
1023
        os.setuid(uid)
999
 
        os.setgid(gid)
1000
1024
    except OSError, error:
1001
1025
        if error[0] != errno.EPERM:
1002
1026
            raise error
1003
1027
    
 
1028
    # Enable all possible GnuTLS debugging
 
1029
    if debug:
 
1030
        # "Use a log level over 10 to enable all debugging options."
 
1031
        # - GnuTLS manual
 
1032
        gnutls.library.functions.gnutls_global_set_log_level(11)
 
1033
        
 
1034
        @gnutls.library.types.gnutls_log_func
 
1035
        def debug_gnutls(level, string):
 
1036
            logger.debug("GnuTLS: %s", string[:-1])
 
1037
        
 
1038
        (gnutls.library.functions
 
1039
         .gnutls_global_set_log_function(debug_gnutls))
 
1040
    
1004
1041
    global service
1005
1042
    service = AvahiService(name = server_settings["servicename"],
1006
1043
                           servicetype = "_mandos._tcp", )
1019
1056
                                           avahi.DBUS_PATH_SERVER),
1020
1057
                            avahi.DBUS_INTERFACE_SERVER)
1021
1058
    # End of Avahi example code
1022
 
    bus_name = dbus.service.BusName(u"org.mandos-system.Mandos", bus)
 
1059
    if use_dbus:
 
1060
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
1023
1061
    
1024
1062
    clients.update(Set(Client(name = section,
1025
1063
                              config
1026
 
                              = dict(client_config.items(section)))
 
1064
                              = dict(client_config.items(section)),
 
1065
                              use_dbus = use_dbus)
1027
1066
                       for section in client_config.sections()))
1028
1067
    if not clients:
1029
 
        logger.critical(u"No clients defined")
1030
 
        sys.exit(1)
 
1068
        logger.warning(u"No clients defined")
1031
1069
    
1032
1070
    if debug:
1033
1071
        # Redirect stdin so all checkers get /dev/null
1075
1113
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1076
1114
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1077
1115
    
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
1116
 
    
1117
 
    mandos_server = MandosServer()
 
1116
    if use_dbus:
 
1117
        class MandosServer(dbus.service.Object):
 
1118
            """A D-Bus proxy object"""
 
1119
            def __init__(self):
 
1120
                dbus.service.Object.__init__(self, bus, "/")
 
1121
            _interface = u"se.bsnet.fukt.Mandos"
 
1122
            
 
1123
            @dbus.service.signal(_interface, signature="oa{sv}")
 
1124
            def ClientAdded(self, objpath, properties):
 
1125
                "D-Bus signal"
 
1126
                pass
 
1127
            
 
1128
            @dbus.service.signal(_interface, signature="os")
 
1129
            def ClientRemoved(self, objpath, name):
 
1130
                "D-Bus signal"
 
1131
                pass
 
1132
            
 
1133
            @dbus.service.method(_interface, out_signature="ao")
 
1134
            def GetAllClients(self):
 
1135
                "D-Bus method"
 
1136
                return dbus.Array(c.dbus_object_path for c in clients)
 
1137
            
 
1138
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
 
1139
            def GetAllClientsWithProperties(self):
 
1140
                "D-Bus method"
 
1141
                return dbus.Dictionary(
 
1142
                    ((c.dbus_object_path, c.GetAllProperties())
 
1143
                     for c in clients),
 
1144
                    signature="oa{sv}")
 
1145
            
 
1146
            @dbus.service.method(_interface, in_signature="o")
 
1147
            def RemoveClient(self, object_path):
 
1148
                "D-Bus method"
 
1149
                for c in clients:
 
1150
                    if c.dbus_object_path == object_path:
 
1151
                        clients.remove(c)
 
1152
                        # Don't signal anything except ClientRemoved
 
1153
                        c.use_dbus = False
 
1154
                        c.disable()
 
1155
                        # Emit D-Bus signal
 
1156
                        self.ClientRemoved(object_path, c.name)
 
1157
                        return
 
1158
                raise KeyError
 
1159
            
 
1160
            del _interface
 
1161
        
 
1162
        mandos_server = MandosServer()
1118
1163
    
1119
1164
    for client in clients:
1120
 
        # Emit D-Bus signal
1121
 
        mandos_server.ClientAdded(client.dbus_object_path,
1122
 
                                  client.GetAllProperties())
 
1165
        if use_dbus:
 
1166
            # Emit D-Bus signal
 
1167
            mandos_server.ClientAdded(client.dbus_object_path,
 
1168
                                      client.GetAllProperties())
1123
1169
        client.enable()
1124
1170
    
1125
1171
    tcp_server.enable()
1150
1196
        logger.debug(u"Starting main loop")
1151
1197
        main_loop.run()
1152
1198
    except AvahiError, error:
1153
 
        logger.critical(u"AvahiError: %s" + unicode(error))
 
1199
        logger.critical(u"AvahiError: %s", error)
1154
1200
        sys.exit(1)
1155
1201
    except KeyboardInterrupt:
1156
1202
        if debug:
1157
 
            print
 
1203
            print >> sys.stderr
 
1204
        logger.debug("Server received KeyboardInterrupt")
 
1205
    logger.debug("Server exiting")
1158
1206
 
1159
1207
if __name__ == '__main__':
1160
1208
    main()