9
11
import gnutls.crypto
 
10
12
import gnutls.connection
 
11
13
import gnutls.errors
 
 
14
import gnutls.library.functions
 
 
15
import gnutls.library.constants
 
 
16
import gnutls.library.types
 
12
17
import ConfigParser
 
 
28
from dbus.mainloop.glib import DBusGMainLoop
 
 
32
import logging.handlers
 
 
34
# logghandler.setFormatter(logging.Formatter('%(levelname)s %(message)s')
 
 
36
logger = logging.Logger('mandos')
 
 
37
logger.addHandler(logging.handlers.SysLogHandler(facility = logging.handlers.SysLogHandler.LOG_DAEMON))
 
 
39
# This variable is used to optionally bind to a specified interface.
 
 
40
# It is a global variable to fit in with the other variables from the
 
 
41
# Avahi server example code.
 
 
42
serviceInterface = avahi.IF_UNSPEC
 
 
43
# From the Avahi server example code:
 
 
44
serviceName = "Mandos"
 
 
45
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
 
 
46
servicePort = None                      # Not known at startup
 
 
47
serviceTXT = []                         # TXT record for the service
 
 
48
domain = ""                  # Domain to publish on, default to .local
 
 
49
host = ""          # Host to publish records for, default to localhost
 
 
50
group = None #our entry group
 
 
51
rename_count = 12       # Counter so we only rename after collisions a
 
 
52
                        # sensible number of times
 
 
53
# End of Avahi example code
 
14
56
class Client(object):
 
15
 
    def __init__(self, name=None, dn=None, password=None,
 
16
 
                 passfile=None, fqdn=None, timeout=None,
 
 
57
    """A representation of a client host served by this server.
 
 
59
    name:      string; from the config file, used in log messages
 
 
60
    fingerprint: string (40 or 32 hexadecimal digits); used to
 
 
61
                 uniquely identify the client
 
 
62
    secret:    bytestring; sent verbatim (over TLS) to client
 
 
63
    fqdn:      string (FQDN); available for use by the checker command
 
 
64
    created:   datetime.datetime()
 
 
65
    last_seen: datetime.datetime() or None if not yet seen
 
 
66
    timeout:   datetime.timedelta(); How long from last_seen until
 
 
67
                                     this client is invalid
 
 
68
    interval:  datetime.timedelta(); How often to start a new checker
 
 
69
    stop_hook: If set, called by stop() as stop_hook(self)
 
 
70
    checker:   subprocess.Popen(); a running checker process used
 
 
71
                                   to see if the client lives.
 
 
72
                                   Is None if no process is running.
 
 
73
    checker_initiator_tag: a gobject event source tag, or None
 
 
74
    stop_initiator_tag:    - '' -
 
 
75
    checker_callback_tag:  - '' -
 
 
76
    checker_command: string; External command which is run to check if
 
 
77
                     client lives.  %()s expansions are done at
 
 
78
                     runtime with vars(self) as dict, so that for
 
 
79
                     instance %(name)s can be used in the command.
 
 
81
    _timeout: Real variable for 'timeout'
 
 
82
    _interval: Real variable for 'interval'
 
 
83
    _timeout_milliseconds: Used by gobject.timeout_add()
 
 
84
    _interval_milliseconds: - '' -
 
 
86
    def _set_timeout(self, timeout):
 
 
87
        "Setter function for 'timeout' attribute"
 
 
88
        self._timeout = timeout
 
 
89
        self._timeout_milliseconds = ((self.timeout.days
 
 
90
                                       * 24 * 60 * 60 * 1000)
 
 
91
                                      + (self.timeout.seconds * 1000)
 
 
92
                                      + (self.timeout.microseconds
 
 
94
    timeout = property(lambda self: self._timeout,
 
 
97
    def _set_interval(self, interval):
 
 
98
        "Setter function for 'interval' attribute"
 
 
99
        self._interval = interval
 
 
100
        self._interval_milliseconds = ((self.interval.days
 
 
101
                                        * 24 * 60 * 60 * 1000)
 
 
102
                                       + (self.interval.seconds
 
 
104
                                       + (self.interval.microseconds
 
 
106
    interval = property(lambda self: self._interval,
 
 
109
    def __init__(self, name=None, options=None, stop_hook=None,
 
 
110
                 fingerprint=None, secret=None, secfile=None, fqdn=None,
 
 
111
                 timeout=None, interval=-1, checker=None):
 
21
 
            self.password = password
 
23
 
            self.password = open(passfile).readall()
 
 
113
        # Uppercase and remove spaces from fingerprint
 
 
114
        # for later comparison purposes with return value of
 
 
115
        # the fingerprint() function
 
 
116
        self.fingerprint = fingerprint.upper().replace(u" ", u"")
 
 
118
            self.secret = secret.decode(u"base64")
 
 
121
            self.secret = sf.read()
 
25
 
            print "No Password or Passfile in client config file"
 
26
 
            # raise RuntimeError XXX
 
27
 
            self.password = "gazonk"
 
 
124
            raise RuntimeError(u"No secret or secfile for client %s"
 
 
126
        self.fqdn = fqdn                # string
 
 
127
        self.created = datetime.datetime.now()
 
30
128
        self.last_seen = None
 
31
129
        if timeout is None:
 
32
 
            timeout = self.server.options.timeout
 
 
130
            timeout = options.timeout
 
33
131
        self.timeout = timeout
 
34
132
        if interval == -1:
 
35
 
            interval = self.server.options.interval
 
 
133
            interval = options.interval
 
 
135
            interval = string_to_delta(interval)
 
36
136
        self.interval = interval
 
38
 
def server_bind(self):
 
39
 
    if self.options.interface:
 
40
 
        if not hasattr(socket, "SO_BINDTODEVICE"):
 
41
 
            # From /usr/include/asm-i486/socket.h
 
42
 
            socket.SO_BINDTODEVICE = 25
 
44
 
            self.socket.setsockopt(socket.SOL_SOCKET,
 
45
 
                                   socket.SO_BINDTODEVICE,
 
46
 
                                   self.options.interface)
 
47
 
        except socket.error, error:
 
48
 
            if error[0] == errno.EPERM:
 
49
 
                print "Warning: Denied permission to bind to interface", \
 
50
 
                      self.options.interface
 
 
137
        self.stop_hook = stop_hook
 
 
139
        self.checker_initiator_tag = None
 
 
140
        self.stop_initiator_tag = None
 
 
141
        self.checker_callback_tag = None
 
 
142
        self.check_command = checker
 
 
144
        """Start this clients checker and timeout hooks"""
 
 
145
        # Schedule a new checker to be started an 'interval' from now,
 
 
146
        # and every interval from then on.
 
 
147
        self.checker_initiator_tag = gobject.timeout_add\
 
 
148
                                     (self._interval_milliseconds,
 
 
150
        # Also start a new checker *right now*.
 
 
152
        # Schedule a stop() when 'timeout' has passed
 
 
153
        self.stop_initiator_tag = gobject.timeout_add\
 
 
154
                                  (self._timeout_milliseconds,
 
 
158
        The possibility that this client might be restarted is left
 
 
159
        open, but not currently used."""
 
 
160
        logger.debug(u"Stopping client %s", self.name)
 
 
162
        if self.stop_initiator_tag:
 
 
163
            gobject.source_remove(self.stop_initiator_tag)
 
 
164
            self.stop_initiator_tag = None
 
 
165
        if self.checker_initiator_tag:
 
 
166
            gobject.source_remove(self.checker_initiator_tag)
 
 
167
            self.checker_initiator_tag = None
 
 
171
        # Do not run this again if called by a gobject.timeout_add
 
 
174
        # Some code duplication here and in stop()
 
 
175
        if hasattr(self, "stop_initiator_tag") \
 
 
176
               and self.stop_initiator_tag:
 
 
177
            gobject.source_remove(self.stop_initiator_tag)
 
 
178
            self.stop_initiator_tag = None
 
 
179
        if hasattr(self, "checker_initiator_tag") \
 
 
180
               and self.checker_initiator_tag:
 
 
181
            gobject.source_remove(self.checker_initiator_tag)
 
 
182
            self.checker_initiator_tag = None
 
 
184
    def checker_callback(self, pid, condition):
 
 
185
        """The checker has completed, so take appropriate actions."""
 
 
186
        now = datetime.datetime.now()
 
 
187
        if os.WIFEXITED(condition) \
 
 
188
               and (os.WEXITSTATUS(condition) == 0):
 
 
189
            logger.debug(u"Checker for %(name)s succeeded",
 
 
192
            gobject.source_remove(self.stop_initiator_tag)
 
 
193
            self.stop_initiator_tag = gobject.timeout_add\
 
 
194
                                      (self._timeout_milliseconds,
 
 
196
        if not os.WIFEXITED(condition):
 
 
197
            logger.warning(u"Checker for %(name)s crashed?",
 
 
200
            logger.debug(u"Checker for %(name)s failed",
 
 
203
        self.checker_callback_tag = None
 
 
204
    def start_checker(self):
 
 
205
        """Start a new checker subprocess if one is not running.
 
 
206
        If a checker already exists, leave it running and do
 
 
208
        if self.checker is None:
 
 
209
            logger.debug(u"Starting checker for %s",
 
 
212
                command = self.check_command % self.fqdn
 
 
214
                escaped_attrs = dict((key, re.escape(str(val)))
 
 
216
                                     vars(self).iteritems())
 
 
218
                    command = self.check_command % escaped_attrs
 
 
219
                except TypeError, error:
 
 
220
                    logger.critical(u'Could not format string "%s": %s',
 
 
221
                                    self.check_command, error)
 
 
222
                    return True # Try again later
 
 
224
                self.checker = subprocess.\
 
 
226
                                     stdout=subprocess.PIPE,
 
 
227
                                     close_fds=True, shell=True,
 
 
229
                self.checker_callback_tag = gobject.\
 
 
230
                                            child_watch_add(self.checker.pid,
 
 
233
            except subprocess.OSError, error:
 
 
234
                logger.error(u"Failed to start subprocess: %s",
 
 
236
        # Re-run this periodically if run by gobject.timeout_add
 
 
238
    def stop_checker(self):
 
 
239
        """Force the checker process, if any, to stop."""
 
 
240
        if not hasattr(self, "checker") or self.checker is None:
 
 
242
        gobject.source_remove(self.checker_callback_tag)
 
 
243
        self.checker_callback_tag = None
 
 
244
        os.kill(self.checker.pid, signal.SIGTERM)
 
 
245
        if self.checker.poll() is None:
 
 
246
            os.kill(self.checker.pid, signal.SIGKILL)
 
 
248
    def still_valid(self, now=None):
 
 
249
        """Has the timeout not yet passed for this client?"""
 
 
251
            now = datetime.datetime.now()
 
 
252
        if self.last_seen is None:
 
 
253
            return now < (self.created + self.timeout)
 
 
255
            return now < (self.last_seen + self.timeout)
 
 
258
def peer_certificate(session):
 
 
259
    # If not an OpenPGP certificate...
 
 
260
    if gnutls.library.functions.gnutls_certificate_type_get\
 
 
261
            (session._c_object) \
 
 
262
           != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
 
 
263
        # ...do the normal thing
 
 
264
        return session.peer_certificate
 
 
265
    list_size = ctypes.c_uint()
 
 
266
    cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
 
 
267
        (session._c_object, ctypes.byref(list_size))
 
 
268
    if list_size.value == 0:
 
 
271
    return ctypes.string_at(cert.data, cert.size)
 
 
274
def fingerprint(openpgp):
 
 
275
    # New empty GnuTLS certificate
 
 
276
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
 
277
    gnutls.library.functions.gnutls_openpgp_crt_init\
 
 
279
    # New GnuTLS "datum" with the OpenPGP public key
 
 
280
    datum = gnutls.library.types.gnutls_datum_t\
 
 
281
        (ctypes.cast(ctypes.c_char_p(openpgp),
 
 
282
                     ctypes.POINTER(ctypes.c_ubyte)),
 
 
283
         ctypes.c_uint(len(openpgp)))
 
 
284
    # Import the OpenPGP public key into the certificate
 
 
285
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
 
 
288
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
 
289
    # New buffer for the fingerprint
 
 
290
    buffer = ctypes.create_string_buffer(20)
 
 
291
    buffer_length = ctypes.c_size_t()
 
 
292
    # Get the fingerprint from the certificate into the buffer
 
 
293
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
 
 
294
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
 
 
295
    # Deinit the certificate
 
 
296
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
 
297
    # Convert the buffer to a Python bytestring
 
 
298
    fpr = ctypes.string_at(buffer, buffer_length.value)
 
 
299
    # Convert the bytestring to hexadecimal notation
 
 
300
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
 
 
304
class tcp_handler(SocketServer.BaseRequestHandler, object):
 
 
305
    """A TCP request handler class.
 
 
306
    Instantiated by IPv6_TCPServer for each request to handle it.
 
 
307
    Note: This will run in its own forked process."""
 
 
310
        logger.debug(u"TCP connection from: %s",
 
 
311
                     unicode(self.client_address))
 
 
312
        session = gnutls.connection.ClientSession(self.request,
 
 
316
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
 
 
317
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
 
319
        priority = "SECURE256"
 
 
321
        gnutls.library.functions.gnutls_priority_set_direct\
 
 
322
            (session._c_object, priority, None);
 
 
326
        except gnutls.errors.GNUTLSError, error:
 
 
327
            logger.debug(u"Handshake failed: %s", error)
 
 
328
            # Do not run session.bye() here: the session is not
 
 
329
            # established.  Just abandon the request.
 
 
332
            fpr = fingerprint(peer_certificate(session))
 
 
333
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
 
334
            logger.debug(u"Bad certificate: %s", error)
 
 
337
        logger.debug(u"Fingerprint: %s", fpr)
 
 
340
            if c.fingerprint == fpr:
 
 
343
        # Have to check if client.still_valid(), since it is possible
 
 
344
        # that the client timed out while establishing the GnuTLS
 
 
346
        if (not client) or (not client.still_valid()):
 
 
348
                logger.debug(u"Client %(name)s is invalid",
 
53
 
    return super(type(self), self).server_bind()
 
56
 
def init_with_options(self, *args, **kwargs):
 
57
 
    if "options" in kwargs:
 
58
 
        self.options = kwargs["options"]
 
60
 
    if "clients" in kwargs:
 
61
 
        self.clients = kwargs["clients"]
 
63
 
    if "credentials" in kwargs:
 
64
 
        self.credentials = kwargs["credentials"]
 
65
 
        del kwargs["credentials"]
 
66
 
    return super(type(self), self).__init__(*args, **kwargs)
 
69
 
class udp_handler(SocketServer.DatagramRequestHandler, object):
 
71
 
        self.wfile.write("Polo")
 
72
 
        print "UDP request answered"
 
75
 
class IPv6_UDPServer(SocketServer.UDPServer, object):
 
76
 
    __init__ = init_with_options
 
77
 
    address_family = socket.AF_INET6
 
78
 
    allow_reuse_address = True
 
79
 
    server_bind = server_bind
 
80
 
    def verify_request(self, request, client_address):
 
81
 
        print "UDP request came"
 
82
 
        return request[0] == "Marco"
 
85
 
class tcp_handler(SocketServer.BaseRequestHandler, object):
 
87
 
        print "TCP request came"
 
88
 
        print "Request:", self.request
 
89
 
        print "Client Address:", self.client_address
 
90
 
        print "Server:", self.server
 
91
 
        session = gnutls.connection.ServerSession(self.request,
 
92
 
                                                  self.server.credentials)
 
94
 
        if session.peer_certificate:
 
95
 
            print "DN:", session.peer_certificate.subject
 
98
 
        except gnutls.errors.CertificateError, error:
 
99
 
            print "Verify failed", error
 
 
351
                logger.debug(u"Client not found for fingerprint: %s",
 
103
 
            session.send(dict((client.dn, client.password)
 
104
 
                              for client in self.server.clients)
 
105
 
                         [session.peer_certificate.subject])
 
107
 
            session.send("gazonk")
 
 
356
        while sent_size < len(client.secret):
 
 
357
            sent = session.send(client.secret[sent_size:])
 
 
358
            logger.debug(u"Sent: %d, remaining: %d",
 
 
359
                         sent, len(client.secret)
 
 
360
                         - (sent_size + sent))
 
111
365
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
 
112
 
    __init__ = init_with_options
 
 
366
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
 
 
368
        options:        Command line options
 
 
369
        clients:        Set() of Client objects
 
113
371
    address_family = socket.AF_INET6
 
114
 
    allow_reuse_address = True
 
115
 
    request_queue_size = 1024
 
116
 
    server_bind = server_bind
 
 
372
    def __init__(self, *args, **kwargs):
 
 
373
        if "options" in kwargs:
 
 
374
            self.options = kwargs["options"]
 
 
375
            del kwargs["options"]
 
 
376
        if "clients" in kwargs:
 
 
377
            self.clients = kwargs["clients"]
 
 
378
            del kwargs["clients"]
 
 
379
        return super(type(self), self).__init__(*args, **kwargs)
 
 
380
    def server_bind(self):
 
 
381
        """This overrides the normal server_bind() function
 
 
382
        to bind to an interface if one was specified, and also NOT to
 
 
383
        bind to an address or port if they were not specified."""
 
 
384
        if self.options.interface:
 
 
385
            if not hasattr(socket, "SO_BINDTODEVICE"):
 
 
386
                # From /usr/include/asm-i486/socket.h
 
 
387
                socket.SO_BINDTODEVICE = 25
 
 
389
                self.socket.setsockopt(socket.SOL_SOCKET,
 
 
390
                                       socket.SO_BINDTODEVICE,
 
 
391
                                       self.options.interface)
 
 
392
            except socket.error, error:
 
 
393
                if error[0] == errno.EPERM:
 
 
394
                    logger.warning(u"No permission to"
 
 
395
                                   u" bind to interface %s",
 
 
396
                                   self.options.interface)
 
 
399
        # Only bind(2) the socket if we really need to.
 
 
400
        if self.server_address[0] or self.server_address[1]:
 
 
401
            if not self.server_address[0]:
 
 
403
                self.server_address = (in6addr_any,
 
 
404
                                       self.server_address[1])
 
 
405
            elif self.server_address[1] is None:
 
 
406
                self.server_address = (self.server_address[0],
 
 
408
            return super(type(self), self).server_bind()
 
 
411
def string_to_delta(interval):
 
 
412
    """Parse a string and return a datetime.timedelta
 
 
414
    >>> string_to_delta('7d')
 
 
415
    datetime.timedelta(7)
 
 
416
    >>> string_to_delta('60s')
 
 
417
    datetime.timedelta(0, 60)
 
 
418
    >>> string_to_delta('60m')
 
 
419
    datetime.timedelta(0, 3600)
 
 
420
    >>> string_to_delta('24h')
 
 
421
    datetime.timedelta(1)
 
 
422
    >>> string_to_delta(u'1w')
 
 
423
    datetime.timedelta(7)
 
 
426
        suffix=unicode(interval[-1])
 
 
427
        value=int(interval[:-1])
 
 
429
            delta = datetime.timedelta(value)
 
 
431
            delta = datetime.timedelta(0, value)
 
 
433
            delta = datetime.timedelta(0, 0, 0, 0, value)
 
 
435
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
 
 
437
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
 
 
440
    except (ValueError, IndexError):
 
 
446
    """From the Avahi server example code"""
 
 
447
    global group, serviceName, serviceType, servicePort, serviceTXT, \
 
 
450
        group = dbus.Interface(
 
 
451
                bus.get_object( avahi.DBUS_NAME,
 
 
452
                                server.EntryGroupNew()),
 
 
453
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
 
454
        group.connect_to_signal('StateChanged',
 
 
455
                                entry_group_state_changed)
 
 
456
    logger.debug(u"Adding service '%s' of type '%s' ...",
 
 
457
                 serviceName, serviceType)
 
 
460
            serviceInterface,           # interface
 
 
461
            avahi.PROTO_INET6,          # protocol
 
 
462
            dbus.UInt32(0),             # flags
 
 
463
            serviceName, serviceType,
 
 
465
            dbus.UInt16(servicePort),
 
 
466
            avahi.string_array_to_txt_array(serviceTXT))
 
 
470
def remove_service():
 
 
471
    """From the Avahi server example code"""
 
 
474
    if not group is None:
 
 
478
def server_state_changed(state):
 
 
479
    """From the Avahi server example code"""
 
 
480
    if state == avahi.SERVER_COLLISION:
 
 
481
        logger.warning(u"Server name collision")
 
 
483
    elif state == avahi.SERVER_RUNNING:
 
 
487
def entry_group_state_changed(state, error):
 
 
488
    """From the Avahi server example code"""
 
 
489
    global serviceName, server, rename_count
 
 
491
    logger.debug(u"state change: %i", state)
 
 
493
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
 
 
494
        logger.debug(u"Service established.")
 
 
495
    elif state == avahi.ENTRY_GROUP_COLLISION:
 
 
497
        rename_count = rename_count - 1
 
 
499
            name = server.GetAlternativeServiceName(name)
 
 
500
            logger.warning(u"Service name collision, "
 
 
501
                           u"changing name to '%s' ...", name)
 
 
506
            logger.error(u"No suitable service name found "
 
 
507
                         u"after %i retries, exiting.",
 
 
510
    elif state == avahi.ENTRY_GROUP_FAILURE:
 
 
511
        logger.error(u"Error in group state changed %s",
 
 
517
def if_nametoindex(interface):
 
 
518
    """Call the C function if_nametoindex()"""
 
 
520
        libc = ctypes.cdll.LoadLibrary("libc.so.6")
 
 
521
        return libc.if_nametoindex(interface)
 
 
522
    except (OSError, AttributeError):
 
 
523
        if "struct" not in sys.modules:
 
 
525
        if "fcntl" not in sys.modules:
 
 
527
        SIOCGIFINDEX = 0x8933      # From /usr/include/linux/sockios.h
 
 
529
        ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
 
530
                            struct.pack("16s16x", interface))
 
 
532
        interface_index = struct.unpack("I", ifreq[16:20])[0]
 
 
533
        return interface_index
 
 
536
if __name__ == '__main__':
 
124
537
    parser = OptionParser()
 
125
538
    parser.add_option("-i", "--interface", type="string",
 
126
 
                      default="eth0", metavar="IF",
 
127
 
                      help="Interface to bind to")
 
 
539
                      default=None, metavar="IF",
 
 
540
                      help="Bind to interface IF")
 
128
541
    parser.add_option("--cert", type="string", default="cert.pem",
 
130
 
                      help="Public key certificate to use")
 
 
543
                      help="Public key certificate PEM file to use")
 
131
544
    parser.add_option("--key", type="string", default="key.pem",
 
133
 
                      help="Private key to use")
 
 
546
                      help="Private key PEM file to use")
 
134
547
    parser.add_option("--ca", type="string", default="ca.pem",
 
136
 
                      help="Certificate Authority certificate to use")
 
 
549
                      help="Certificate Authority certificate PEM file to use")
 
137
550
    parser.add_option("--crl", type="string", default="crl.pem",
 
139
 
                      help="Certificate Revokation List to use")
 
140
 
    parser.add_option("-p", "--port", type="int", default=49001,
 
 
552
                      help="Certificate Revokation List PEM file to use")
 
 
553
    parser.add_option("-p", "--port", type="int", default=None,
 
141
554
                      help="Port number to receive requests on")
 
142
 
    parser.add_option("--dh", type="int", metavar="BITS",
 
143
 
                      help="DH group to use")
 
144
 
    parser.add_option("-t", "--timeout", type="string", # Parsed later
 
 
555
    parser.add_option("--timeout", type="string", # Parsed later
 
146
557
                      help="Amount of downtime allowed for clients")
 
 
558
    parser.add_option("--interval", type="string", # Parsed later
 
 
560
                      help="How often to check that a client is up")
 
 
561
    parser.add_option("--check", action="store_true", default=False,
 
 
562
                      help="Run self-test")
 
 
563
    parser.add_option("--debug", action="store_true", default=False,
 
147
565
    (options, args) = parser.parse_args()
 
149
 
    # Parse the time argument
 
 
572
    # Parse the time arguments
 
151
 
        suffix=options.timeout[-1]
 
152
 
        value=int(options.timeout[:-1])
 
154
 
            options.timeout = datetime.timedelta(value)
 
156
 
            options.timeout = datetime.timedelta(0, value)
 
158
 
            options.timeout = datetime.timedelta(0, 0, 0, 0, value)
 
160
 
            options.timeout = datetime.timedelta(0, 0, 0, 0, 0, value)
 
162
 
            options.timeout = datetime.timedelta(0, 0, 0, 0, 0, 0,
 
166
 
    except (ValueError, IndexError):
 
 
574
        options.timeout = string_to_delta(options.timeout)
 
167
576
        parser.error("option --timeout: Unparseable time")
 
169
 
    cert = gnutls.crypto.X509Certificate(open(options.cert).read())
 
170
 
    key = gnutls.crypto.X509PrivateKey(open(options.key).read())
 
171
 
    ca = gnutls.crypto.X509Certificate(open(options.ca).read())
 
172
 
    crl = gnutls.crypto.X509CRL(open(options.crl).read())
 
173
 
    cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
 
 
578
        options.interval = string_to_delta(options.interval)
 
 
580
        parser.error("option --interval: Unparseable time")
 
175
582
    # Parse config file
 
177
 
    client_config_object = ConfigParser.SafeConfigParser(defaults)
 
178
 
    client_config_object.read("mandos-clients.conf")
 
179
 
    clients = [Client(name=section,
 
180
 
                      **(dict(client_config_object.items(section))))
 
181
 
               for section in client_config_object.sections()]
 
183
 
    udp_server = IPv6_UDPServer((in6addr_any, options.port),
 
187
 
    tcp_server = IPv6_TCPServer((in6addr_any, options.port),
 
 
583
    defaults = { "checker": "sleep 1; fping -q -- %%(fqdn)s" }
 
 
584
    client_config = ConfigParser.SafeConfigParser(defaults)
 
 
585
    #client_config.readfp(open("secrets.conf"), "secrets.conf")
 
 
586
    client_config.read("mandos-clients.conf")
 
 
588
    # From the Avahi server example code
 
 
589
    DBusGMainLoop(set_as_default=True )
 
 
590
    main_loop = gobject.MainLoop()
 
 
591
    bus = dbus.SystemBus()
 
 
592
    server = dbus.Interface(
 
 
593
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
 
 
594
            avahi.DBUS_INTERFACE_SERVER )
 
 
595
    # End of Avahi example code
 
 
597
    debug = options.debug
 
 
600
    def remove_from_clients(client):
 
 
601
        clients.remove(client)
 
 
603
            logger.debug(u"No clients left, exiting")
 
 
606
    clients.update(Set(Client(name=section, options=options,
 
 
607
                              stop_hook = remove_from_clients,
 
 
608
                              **(dict(client_config\
 
 
610
                       for section in client_config.sections()))
 
 
611
    for client in clients:
 
 
614
    tcp_server = IPv6_TCPServer((None, options.port),
 
194
 
        in_, out, err = select.select((udp_server,
 
197
 
            server.handle_request()
 
200
 
if __name__ == "__main__":
 
 
618
    # Find out what random port we got
 
 
619
    servicePort = tcp_server.socket.getsockname()[1]
 
 
620
    logger.debug(u"Now listening on port %d", servicePort)
 
 
622
    if options.interface is not None:
 
 
623
        serviceInterface = if_nametoindex(options.interface)
 
 
625
    # From the Avahi server example code
 
 
626
    server.connect_to_signal("StateChanged", server_state_changed)
 
 
627
    server_state_changed(server.GetState())
 
 
628
    # End of Avahi example code
 
 
630
    gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
 
 
631
                         lambda *args, **kwargs:
 
 
632
                         tcp_server.handle_request(*args[2:],
 
 
636
    except KeyboardInterrupt:
 
 
641
    # From the Avahi server example code
 
 
642
    if not group is None:
 
 
644
    # End of Avahi example code
 
 
646
    for client in clients:
 
 
647
        client.stop_hook = None