6
6
# This program is partly derived from an example program for an Avahi
 
7
7
# service publisher, downloaded from
 
8
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
 
9
 
# methods "add" and "remove" in the "AvahiService" class, the
 
10
 
# "server_state_changed" and "entry_group_state_changed" functions,
 
11
 
# and some lines in "main".
 
 
9
# following functions: "add_service", "remove_service",
 
 
10
# "server_state_changed", "entry_group_state_changed", and some lines
 
14
 
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
 
13
# Everything else is Copyright © 2007-2008 Teddy Hogeborn and Björn
 
16
16
# This program is free software: you can redistribute it and/or modify
 
17
17
# it under the terms of the GNU General Public License as published by
 
 
61
62
from dbus.mainloop.glib import DBusGMainLoop
 
 
65
# Brief description of the operation of this program:
 
 
67
# This server announces itself as a Zeroconf service.  Connecting
 
 
68
# clients use the TLS protocol, with the unusual quirk that this
 
 
69
# server program acts as a TLS "client" while the connecting clients
 
 
70
# acts as a TLS "server".  The clients (acting as a TLS "server") must
 
 
71
# supply an OpenPGP certificate, and the fingerprint of this
 
 
72
# certificate is used by this server to look up (in a list read from a
 
 
73
# file at start time) which binary blob to give the client.  No other
 
 
74
# authentication or authorization is done by this server.
 
65
77
logger = logging.Logger('mandos')
 
66
78
syslogger = logging.handlers.SysLogHandler\
 
 
70
82
logger.addHandler(syslogger)
 
74
 
class AvahiError(Exception):
 
75
 
    def __init__(self, value):
 
78
 
        return repr(self.value)
 
80
 
class AvahiServiceError(AvahiError):
 
83
 
class AvahiGroupError(AvahiError):
 
87
 
class AvahiService(object):
 
88
 
    """An Avahi (Zeroconf) service.
 
90
 
    interface: integer; avahi.IF_UNSPEC or an interface index.
 
91
 
               Used to optionally bind to the specified interface.
 
92
 
    name: string; Example: 'Mandos'
 
93
 
    type: string; Example: '_mandos._tcp'.
 
94
 
                  See <http://www.dns-sd.org/ServiceTypes.html>
 
95
 
    port: integer; what port to announce
 
96
 
    TXT: list of strings; TXT record for the service
 
97
 
    domain: string; Domain to publish on, default to .local if empty.
 
98
 
    host: string; Host to publish records for, default is localhost
 
99
 
    max_renames: integer; maximum number of renames
 
100
 
    rename_count: integer; counter so we only rename after collisions
 
101
 
                  a sensible number of times
 
103
 
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
 
104
 
                 type = None, port = None, TXT = None, domain = "",
 
105
 
                 host = "", max_renames = 12):
 
106
 
        self.interface = interface
 
116
 
        self.rename_count = 0
 
118
 
        """Derived from the Avahi example code"""
 
119
 
        if self.rename_count >= self.max_renames:
 
120
 
            logger.critical(u"No suitable service name found after %i"
 
121
 
                            u" retries, exiting.", rename_count)
 
122
 
            raise AvahiServiceError("Too many renames")
 
123
 
        name = server.GetAlternativeServiceName(name)
 
124
 
        logger.error(u"Changing name to %r ...", name)
 
127
 
        self.rename_count += 1
 
129
 
        """Derived from the Avahi example code"""
 
130
 
        if group is not None:
 
133
 
        """Derived from the Avahi example code"""
 
136
 
            group = dbus.Interface\
 
137
 
                    (bus.get_object(avahi.DBUS_NAME,
 
138
 
                                    server.EntryGroupNew()),
 
139
 
                     avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
140
 
            group.connect_to_signal('StateChanged',
 
141
 
                                    entry_group_state_changed)
 
142
 
        logger.debug(u"Adding service '%s' of type '%s' ...",
 
143
 
                     service.name, service.type)
 
145
 
                self.interface,         # interface
 
146
 
                avahi.PROTO_INET6,      # protocol
 
147
 
                dbus.UInt32(0),         # flags
 
148
 
                self.name, self.type,
 
149
 
                self.domain, self.host,
 
150
 
                dbus.UInt16(self.port),
 
151
 
                avahi.string_array_to_txt_array(self.TXT))
 
 
85
# This variable is used to optionally bind to a specified interface.
 
 
86
# It is a global variable to fit in with the other variables from the
 
 
88
serviceInterface = avahi.IF_UNSPEC
 
154
89
# From the Avahi example code:
 
155
 
group = None                            # our entry group
 
 
90
serviceName = "Mandos"
 
 
91
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
 
 
92
servicePort = None                      # Not known at startup
 
 
93
serviceTXT = []                         # TXT record for the service
 
 
94
domain = ""                  # Domain to publish on, default to .local
 
 
95
host = ""          # Host to publish records for, default to localhost
 
 
96
group = None #our entry group
 
 
97
rename_count = 12       # Counter so we only rename after collisions a
 
 
98
                        # sensible number of times
 
156
99
# End of Avahi example code
 
 
164
107
                 uniquely identify the client
 
165
108
    secret:    bytestring; sent verbatim (over TLS) to client
 
166
109
    fqdn:      string (FQDN); available for use by the checker command
 
167
 
    created:   datetime.datetime(); object creation, not client host
 
168
 
    last_checked_ok: datetime.datetime() or None if not yet checked OK
 
169
 
    timeout:   datetime.timedelta(); How long from last_checked_ok
 
170
 
                                     until this client is invalid
 
 
110
    created:   datetime.datetime()
 
 
111
    last_seen: datetime.datetime() or None if not yet seen
 
 
112
    timeout:   datetime.timedelta(); How long from last_seen until
 
 
113
                                     this client is invalid
 
171
114
    interval:  datetime.timedelta(); How often to start a new checker
 
172
115
    stop_hook: If set, called by stop() as stop_hook(self)
 
173
116
    checker:   subprocess.Popen(); a running checker process used
 
174
117
                                   to see if the client lives.
 
175
 
                                   'None' if no process is running.
 
 
118
                                   Is None if no process is running.
 
176
119
    checker_initiator_tag: a gobject event source tag, or None
 
177
120
    stop_initiator_tag:    - '' -
 
178
121
    checker_callback_tag:  - '' -
 
179
122
    checker_command: string; External command which is run to check if
 
180
 
                     client lives.  %() expansions are done at
 
 
123
                     client lives.  %()s expansions are done at
 
181
124
                     runtime with vars(self) as dict, so that for
 
182
125
                     instance %(name)s can be used in the command.
 
183
126
    Private attibutes:
 
184
127
    _timeout: Real variable for 'timeout'
 
185
128
    _interval: Real variable for 'interval'
 
186
 
    _timeout_milliseconds: Used when calling gobject.timeout_add()
 
 
129
    _timeout_milliseconds: Used by gobject.timeout_add()
 
187
130
    _interval_milliseconds: - '' -
 
189
132
    def _set_timeout(self, timeout):
 
 
209
152
    interval = property(lambda self: self._interval,
 
211
154
    del _set_interval
 
212
 
    def __init__(self, name = None, stop_hook=None, config={}):
 
213
 
        """Note: the 'checker' key in 'config' sets the
 
214
 
        'checker_command' attribute and *not* the 'checker'
 
 
155
    def __init__(self, name=None, options=None, stop_hook=None,
 
 
156
                 fingerprint=None, secret=None, secfile=None,
 
 
157
                 fqdn=None, timeout=None, interval=-1, checker=None):
 
 
158
        """Note: the 'checker' argument sets the 'checker_command'
 
 
159
        attribute and not the 'checker' attribute.."""
 
217
 
        logger.debug(u"Creating client %r", self.name)
 
218
 
        # Uppercase and remove spaces from fingerprint for later
 
219
 
        # comparison purposes with return value from the fingerprint()
 
221
 
        self.fingerprint = config["fingerprint"].upper()\
 
223
 
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
 
224
 
        if "secret" in config:
 
225
 
            self.secret = config["secret"].decode(u"base64")
 
226
 
        elif "secfile" in config:
 
227
 
            sf = open(config["secfile"])
 
 
161
        # Uppercase and remove spaces from fingerprint
 
 
162
        # for later comparison purposes with return value of
 
 
163
        # the fingerprint() function
 
 
164
        self.fingerprint = fingerprint.upper().replace(u" ", u"")
 
 
166
            self.secret = secret.decode(u"base64")
 
228
169
            self.secret = sf.read()
 
231
 
            raise TypeError(u"No secret or secfile for client %s"
 
233
 
        self.fqdn = config.get("fqdn", "")
 
 
172
            raise RuntimeError(u"No secret or secfile for client %s"
 
 
174
        self.fqdn = fqdn                # string
 
234
175
        self.created = datetime.datetime.now()
 
235
 
        self.last_checked_ok = None
 
236
 
        self.timeout = string_to_delta(config["timeout"])
 
237
 
        self.interval = string_to_delta(config["interval"])
 
 
176
        self.last_seen = None
 
 
178
            self.timeout = options.timeout
 
 
180
            self.timeout = string_to_delta(timeout)
 
 
182
            self.interval = options.interval
 
 
184
            self.interval = string_to_delta(interval)
 
238
185
        self.stop_hook = stop_hook
 
239
186
        self.checker = None
 
240
187
        self.checker_initiator_tag = None
 
241
188
        self.stop_initiator_tag = None
 
242
189
        self.checker_callback_tag = None
 
243
 
        self.check_command = config["checker"]
 
 
190
        self.check_command = checker
 
245
192
        """Start this client's checker and timeout hooks"""
 
246
193
        # Schedule a new checker to be started an 'interval' from now,
 
 
258
205
        """Stop this client.
 
259
 
        The possibility that a client might be restarted is left open,
 
260
 
        but not currently used."""
 
 
206
        The possibility that this client might be restarted is left
 
 
207
        open, but not currently used."""
 
261
208
        # If this client doesn't have a secret, it is already stopped.
 
263
 
            logger.info(u"Stopping client %s", self.name)
 
 
210
            logger.debug(u"Stopping client %s", self.name)
 
264
211
            self.secret = None
 
267
 
        if getattr(self, "stop_initiator_tag", False):
 
 
214
        if hasattr(self, "stop_initiator_tag") \
 
 
215
               and self.stop_initiator_tag:
 
268
216
            gobject.source_remove(self.stop_initiator_tag)
 
269
217
            self.stop_initiator_tag = None
 
270
 
        if getattr(self, "checker_initiator_tag", False):
 
 
218
        if hasattr(self, "checker_initiator_tag") \
 
 
219
               and self.checker_initiator_tag:
 
271
220
            gobject.source_remove(self.checker_initiator_tag)
 
272
221
            self.checker_initiator_tag = None
 
273
222
        self.stop_checker()
 
 
312
261
        # is as it should be.
 
313
262
        if self.checker is None:
 
315
 
                # In case check_command has exactly one % operator
 
316
264
                command = self.check_command % self.fqdn
 
317
265
            except TypeError:
 
318
 
                # Escape attributes for the shell
 
319
266
                escaped_attrs = dict((key, re.escape(str(val)))
 
321
268
                                     vars(self).iteritems())
 
323
270
                    command = self.check_command % escaped_attrs
 
324
271
                except TypeError, error:
 
325
 
                    logger.error(u'Could not format string "%s":'
 
326
 
                                 u' %s', self.check_command, error)
 
 
272
                    logger.critical(u'Could not format string "%s":'
 
 
273
                                    u' %s', self.check_command, error)
 
327
274
                    return True # Try again later
 
329
 
                logger.info(u"Starting checker %r for %s",
 
331
 
                self.checker = subprocess.Popen(command,
 
 
276
                logger.debug(u"Starting checker %r for %s",
 
 
278
                self.checker = subprocess.\
 
 
280
                                     close_fds=True, shell=True,
 
334
282
                self.checker_callback_tag = gobject.child_watch_add\
 
335
283
                                            (self.checker.pid,
 
336
284
                                             self.checker_callback)
 
 
353
301
            #if self.checker.poll() is None:
 
354
302
            #    os.kill(self.checker.pid, signal.SIGKILL)
 
355
303
        except OSError, error:
 
356
 
            if error.errno != errno.ESRCH: # No such process
 
 
304
            if error.errno != errno.ESRCH:
 
358
306
        self.checker = None
 
359
 
    def still_valid(self):
 
 
307
    def still_valid(self, now=None):
 
360
308
        """Has the timeout not yet passed for this client?"""
 
361
 
        now = datetime.datetime.now()
 
362
 
        if self.last_checked_ok is None:
 
 
310
            now = datetime.datetime.now()
 
 
311
        if self.last_seen is None:
 
363
312
            return now < (self.created + self.timeout)
 
365
 
            return now < (self.last_checked_ok + self.timeout)
 
 
314
            return now < (self.last_seen + self.timeout)
 
368
317
def peer_certificate(session):
 
 
385
334
def fingerprint(openpgp):
 
386
335
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
 
 
336
    # New empty GnuTLS certificate
 
 
337
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
 
338
    gnutls.library.functions.gnutls_openpgp_crt_init\
 
387
340
    # New GnuTLS "datum" with the OpenPGP public key
 
388
341
    datum = gnutls.library.types.gnutls_datum_t\
 
389
342
        (ctypes.cast(ctypes.c_char_p(openpgp),
 
390
343
                     ctypes.POINTER(ctypes.c_ubyte)),
 
391
344
         ctypes.c_uint(len(openpgp)))
 
392
 
    # New empty GnuTLS certificate
 
393
 
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
394
 
    gnutls.library.functions.gnutls_openpgp_crt_init\
 
396
345
    # Import the OpenPGP public key into the certificate
 
397
 
    gnutls.library.functions.gnutls_openpgp_crt_import\
 
398
 
                    (crt, ctypes.byref(datum),
 
399
 
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
 
346
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
 
 
349
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
400
350
    # New buffer for the fingerprint
 
401
351
    buffer = ctypes.create_string_buffer(20)
 
402
352
    buffer_length = ctypes.c_size_t()
 
 
418
368
    Note: This will run in its own forked process."""
 
420
370
    def handle(self):
 
421
 
        logger.info(u"TCP connection from: %s",
 
 
371
        logger.debug(u"TCP connection from: %s",
 
422
372
                     unicode(self.client_address))
 
423
 
        session = gnutls.connection.ClientSession\
 
424
 
                  (self.request, gnutls.connection.X509Credentials())
 
426
 
        line = self.request.makefile().readline()
 
427
 
        logger.debug(u"Protocol version: %r", line)
 
429
 
            if int(line.strip().split()[0]) > 1:
 
431
 
        except (ValueError, IndexError, RuntimeError), error:
 
432
 
            logger.error(u"Unknown protocol version: %s", error)
 
435
 
        # Note: gnutls.connection.X509Credentials is really a generic
 
436
 
        # GnuTLS certificate credentials object so long as no X.509
 
437
 
        # keys are added to it.  Therefore, we can use it here despite
 
438
 
        # using OpenPGP certificates.
 
 
373
        session = gnutls.connection.ClientSession(self.request,
 
440
377
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
 
441
378
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
443
 
        priority = "NORMAL"             # Fallback default, since this
 
445
 
        if self.server.settings["priority"]:
 
446
 
            priority = self.server.settings["priority"]
 
 
380
        priority = "SECURE256"
 
447
382
        gnutls.library.functions.gnutls_priority_set_direct\
 
448
383
            (session._c_object, priority, None);
 
451
386
            session.handshake()
 
452
387
        except gnutls.errors.GNUTLSError, error:
 
453
 
            logger.warning(u"Handshake failed: %s", error)
 
 
388
            logger.debug(u"Handshake failed: %s", error)
 
454
389
            # Do not run session.bye() here: the session is not
 
455
390
            # established.  Just abandon the request.
 
458
393
            fpr = fingerprint(peer_certificate(session))
 
459
394
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
460
 
            logger.warning(u"Bad certificate: %s", error)
 
 
395
            logger.debug(u"Bad certificate: %s", error)
 
463
398
        logger.debug(u"Fingerprint: %s", fpr)
 
 
508
442
        """This overrides the normal server_bind() function
 
509
443
        to bind to an interface if one was specified, and also NOT to
 
510
444
        bind to an address or port if they were not specified."""
 
511
 
        if self.settings["interface"]:
 
512
 
            # 25 is from /usr/include/asm-i486/socket.h
 
513
 
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
 
 
445
        if self.options.interface:
 
 
446
            if not hasattr(socket, "SO_BINDTODEVICE"):
 
 
447
                # From /usr/include/asm-i486/socket.h
 
 
448
                socket.SO_BINDTODEVICE = 25
 
515
450
                self.socket.setsockopt(socket.SOL_SOCKET,
 
517
 
                                       self.settings["interface"])
 
 
451
                                       socket.SO_BINDTODEVICE,
 
 
452
                                       self.options.interface)
 
518
453
            except socket.error, error:
 
519
454
                if error[0] == errno.EPERM:
 
520
 
                    logger.error(u"No permission to"
 
521
 
                                 u" bind to interface %s",
 
522
 
                                 self.settings["interface"])
 
 
455
                    logger.warning(u"No permission to"
 
 
456
                                   u" bind to interface %s",
 
 
457
                                   self.options.interface)
 
525
460
        # Only bind(2) the socket if we really need to.
 
 
 
507
    """Derived from the Avahi example code"""
 
 
508
    global group, serviceName, serviceType, servicePort, serviceTXT, \
 
 
511
        group = dbus.Interface(
 
 
512
                bus.get_object( avahi.DBUS_NAME,
 
 
513
                                server.EntryGroupNew()),
 
 
514
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
 
515
        group.connect_to_signal('StateChanged',
 
 
516
                                entry_group_state_changed)
 
 
517
    logger.debug(u"Adding service '%s' of type '%s' ...",
 
 
518
                 serviceName, serviceType)
 
 
521
            serviceInterface,           # interface
 
 
522
            avahi.PROTO_INET6,          # protocol
 
 
523
            dbus.UInt32(0),             # flags
 
 
524
            serviceName, serviceType,
 
 
526
            dbus.UInt16(servicePort),
 
 
527
            avahi.string_array_to_txt_array(serviceTXT))
 
 
531
def remove_service():
 
 
532
    """From the Avahi example code"""
 
 
535
    if not group is None:
 
571
539
def server_state_changed(state):
 
572
540
    """Derived from the Avahi example code"""
 
573
541
    if state == avahi.SERVER_COLLISION:
 
574
 
        logger.error(u"Server name collision")
 
 
542
        logger.warning(u"Server name collision")
 
576
544
    elif state == avahi.SERVER_RUNNING:
 
580
548
def entry_group_state_changed(state, error):
 
581
549
    """Derived from the Avahi example code"""
 
 
550
    global serviceName, server, rename_count
 
582
552
    logger.debug(u"state change: %i", state)
 
584
554
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
 
585
555
        logger.debug(u"Service established.")
 
586
556
    elif state == avahi.ENTRY_GROUP_COLLISION:
 
587
 
        logger.warning(u"Service name collision.")
 
 
558
        rename_count = rename_count - 1
 
 
560
            name = server.GetAlternativeServiceName(name)
 
 
561
            logger.warning(u"Service name collision, "
 
 
562
                           u"changing name to '%s' ...", name)
 
 
567
            logger.error(u"No suitable service name found after %i"
 
 
568
                         u" retries, exiting.", n_rename)
 
589
570
    elif state == avahi.ENTRY_GROUP_FAILURE:
 
590
 
        logger.critical(u"Error in group state changed %s",
 
592
 
        raise AvahiGroupError("State changed: %s", str(error))
 
 
571
        logger.error(u"Error in group state changed %s",
 
594
576
def if_nametoindex(interface):
 
595
 
    """Call the C function if_nametoindex(), or equivalent"""
 
596
 
    global if_nametoindex
 
 
577
    """Call the C function if_nametoindex()"""
 
598
 
        if "ctypes.util" not in sys.modules:
 
600
 
        if_nametoindex = ctypes.cdll.LoadLibrary\
 
601
 
            (ctypes.util.find_library("c")).if_nametoindex
 
 
579
        libc = ctypes.cdll.LoadLibrary("libc.so.6")
 
 
580
        return libc.if_nametoindex(interface)
 
602
581
    except (OSError, AttributeError):
 
603
582
        if "struct" not in sys.modules:
 
605
584
        if "fcntl" not in sys.modules:
 
607
 
        def if_nametoindex(interface):
 
608
 
            "Get an interface index the hard way, i.e. using fcntl()"
 
609
 
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
 
611
 
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
612
 
                                struct.pack("16s16x", interface))
 
614
 
            interface_index = struct.unpack("I", ifreq[16:20])[0]
 
615
 
            return interface_index
 
616
 
    return if_nametoindex(interface)
 
619
 
def daemon(nochdir = False, noclose = False):
 
 
586
        SIOCGIFINDEX = 0x8933      # From /usr/include/linux/sockios.h
 
 
588
        ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
 
589
                            struct.pack("16s16x", interface))
 
 
591
        interface_index = struct.unpack("I", ifreq[16:20])[0]
 
 
592
        return interface_index
 
 
595
def daemon(nochdir, noclose):
 
620
596
    """See daemon(3).  Standard BSD Unix function.
 
621
597
    This should really exist as os.daemon, but it doesn't (yet)."""
 
 
 
616
def killme(status = 0):
 
 
617
    logger.debug("Stopping server with exit status %d", status)
 
 
619
    if main_loop_started:
 
643
628
    global main_loop_started
 
644
629
    main_loop_started = False
 
646
631
    parser = OptionParser()
 
647
632
    parser.add_option("-i", "--interface", type="string",
 
648
 
                      metavar="IF", help="Bind to interface IF")
 
649
 
    parser.add_option("-a", "--address", type="string",
 
 
633
                      default=None, metavar="IF",
 
 
634
                      help="Bind to interface IF")
 
 
635
    parser.add_option("-a", "--address", type="string", default=None,
 
650
636
                      help="Address to listen for requests on")
 
651
 
    parser.add_option("-p", "--port", type="int",
 
 
637
    parser.add_option("-p", "--port", type="int", default=None,
 
652
638
                      help="Port number to receive requests on")
 
 
639
    parser.add_option("--timeout", type="string", # Parsed later
 
 
641
                      help="Amount of downtime allowed for clients")
 
 
642
    parser.add_option("--interval", type="string", # Parsed later
 
 
644
                      help="How often to check that a client is up")
 
653
645
    parser.add_option("--check", action="store_true", default=False,
 
654
646
                      help="Run self-test")
 
655
647
    parser.add_option("--debug", action="store_true", default=False,
 
656
 
                      help="Debug mode; run in foreground and log to"
 
658
 
    parser.add_option("--priority", type="string", help="GnuTLS"
 
659
 
                      " priority string (see GnuTLS documentation)")
 
660
 
    parser.add_option("--servicename", type="string", metavar="NAME",
 
661
 
                      help="Zeroconf service name")
 
662
 
    parser.add_option("--configdir", type="string",
 
663
 
                      default="/etc/mandos", metavar="DIR",
 
664
 
                      help="Directory to search for configuration"
 
666
649
    (options, args) = parser.parse_args()
 
668
651
    if options.check:
 
 
670
653
        doctest.testmod()
 
673
 
    # Default values for config file for server-global settings
 
674
 
    server_defaults = { "interface": "",
 
679
 
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
 
680
 
                        "servicename": "Mandos",
 
683
 
    # Parse config file for server-global settings
 
684
 
    server_config = ConfigParser.SafeConfigParser(server_defaults)
 
686
 
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
 
687
 
    server_section = "server"
 
688
 
    # Convert the SafeConfigParser object to a dict
 
689
 
    server_settings = dict(server_config.items(server_section))
 
690
 
    # Use getboolean on the boolean config option
 
691
 
    server_settings["debug"] = server_config.getboolean\
 
692
 
                               (server_section, "debug")
 
695
 
    # Override the settings from the config file with command line
 
697
 
    for option in ("interface", "address", "port", "debug",
 
698
 
                   "priority", "servicename", "configdir"):
 
699
 
        value = getattr(options, option)
 
700
 
        if value is not None:
 
701
 
            server_settings[option] = value
 
703
 
    # Now we have our good server settings in "server_settings"
 
705
 
    # Parse config file with clients
 
706
 
    client_defaults = { "timeout": "1h",
 
708
 
                        "checker": "fping -q -- %%(fqdn)s",
 
710
 
    client_config = ConfigParser.SafeConfigParser(client_defaults)
 
711
 
    client_config.read(os.path.join(server_settings["configdir"],
 
715
 
    service = AvahiService(name = server_settings["servicename"],
 
716
 
                           type = "_mandos._tcp", );
 
717
 
    if server_settings["interface"]:
 
718
 
        service.interface = if_nametoindex(server_settings["interface"])
 
 
656
    # Parse the time arguments
 
 
658
        options.timeout = string_to_delta(options.timeout)
 
 
660
        parser.error("option --timeout: Unparseable time")
 
 
662
        options.interval = string_to_delta(options.interval)
 
 
664
        parser.error("option --interval: Unparseable time")
 
 
667
    defaults = { "checker": "fping -q -- %%(fqdn)s" }
 
 
668
    client_config = ConfigParser.SafeConfigParser(defaults)
 
 
669
    #client_config.readfp(open("global.conf"), "global.conf")
 
 
670
    client_config.read("mandos-clients.conf")
 
 
775
727
        signal.signal(signal.SIGINT, signal.SIG_IGN)
 
776
 
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
 
777
 
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
 
 
728
    signal.signal(signal.SIGHUP, lambda signum, frame: killme())
 
 
729
    signal.signal(signal.SIGTERM, lambda signum, frame: killme())
 
779
731
    for client in clients:
 
782
 
    tcp_server = IPv6_TCPServer((server_settings["address"],
 
783
 
                                 server_settings["port"]),
 
 
734
    tcp_server = IPv6_TCPServer((options.address, options.port),
 
785
 
                                settings=server_settings,
 
787
 
    # Find out what port we got
 
788
 
    service.port = tcp_server.socket.getsockname()[1]
 
789
 
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
 
790
 
                u" scope_id %d" % tcp_server.socket.getsockname())
 
792
 
    #service.interface = tcp_server.socket.getsockname()[3]
 
795
 
        # From the Avahi example code
 
796
 
        server.connect_to_signal("StateChanged", server_state_changed)
 
798
 
            server_state_changed(server.GetState())
 
799
 
        except dbus.exceptions.DBusException, error:
 
800
 
            logger.critical(u"DBusException: %s", error)
 
802
 
        # End of Avahi example code
 
804
 
        gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
 
805
 
                             lambda *args, **kwargs:
 
806
 
                             tcp_server.handle_request\
 
807
 
                             (*args[2:], **kwargs) or True)
 
 
738
    # Find out what random port we got
 
 
740
    servicePort = tcp_server.socket.getsockname()[1]
 
 
741
    logger.debug(u"Now listening on port %d", servicePort)
 
 
743
    if options.interface is not None:
 
 
744
        global serviceInterface
 
 
745
        serviceInterface = if_nametoindex(options.interface)
 
 
747
    # From the Avahi example code
 
 
748
    server.connect_to_signal("StateChanged", server_state_changed)
 
 
750
        server_state_changed(server.GetState())
 
 
751
    except dbus.exceptions.DBusException, error:
 
 
752
        logger.critical(u"DBusException: %s", error)
 
 
754
    # End of Avahi example code
 
 
756
    gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
 
 
757
                         lambda *args, **kwargs:
 
 
758
                         tcp_server.handle_request(*args[2:],
 
809
761
        logger.debug("Starting main loop")
 
810
762
        main_loop_started = True
 
812
 
    except AvahiError, error:
 
813
 
        logger.critical(u"AvahiError: %s" + unicode(error))
 
815
764
    except KeyboardInterrupt:
 
819
770
if __name__ == '__main__':