/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 server.py

  • Committer: Teddy Hogeborn
  • Date: 2008-06-30 01:43:39 UTC
  • Revision ID: teddy@fukt.bsnet.se-20080630014339-rsuztydpl2w5ml83
* server.py: Rewritten to use Zeroconf (mDNS/DNS-SD) in place of the
             old broadcast-UDP-to-port-49001 method.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
#!/usr/bin/python
2
 
# -*- mode: python; coding: utf-8 -*-
3
2
 
4
3
from __future__ import division
5
4
 
12
11
import gnutls.crypto
13
12
import gnutls.connection
14
13
import gnutls.errors
15
 
import gnutls.library.functions
16
 
import gnutls.library.constants
17
 
import gnutls.library.types
18
14
import ConfigParser
19
15
import sys
20
16
import re
22
18
import signal
23
19
from sets import Set
24
20
import subprocess
25
 
import atexit
26
 
import stat
27
21
 
28
22
import dbus
29
23
import gobject
30
24
import avahi
31
25
from dbus.mainloop.glib import DBusGMainLoop
32
 
import ctypes
33
 
 
34
 
import logging
35
 
import logging.handlers
36
 
 
37
 
logger = logging.Logger('mandos')
38
 
syslogger = logging.handlers.SysLogHandler\
39
 
            (facility = logging.handlers.SysLogHandler.LOG_DAEMON)
40
 
syslogger.setFormatter(logging.Formatter\
41
 
                        ('%(levelname)s: %(message)s'))
42
 
logger.addHandler(syslogger)
43
 
del syslogger
44
 
 
45
 
# This variable is used to optionally bind to a specified interface.
46
 
# It is a global variable to fit in with the other variables from the
47
 
# Avahi server example code.
 
26
 
 
27
# This variable is used to optionally bind to a specified
 
28
# interface.
48
29
serviceInterface = avahi.IF_UNSPEC
49
 
# From the Avahi server example code:
 
30
# It is a global variable to fit in with the rest of the
 
31
# variables from the Avahi server example code:
50
32
serviceName = "Mandos"
51
33
serviceType = "_mandos._tcp" # http://www.dns-sd.org/ServiceTypes.html
52
34
servicePort = None                      # Not known at startup
62
44
class Client(object):
63
45
    """A representation of a client host served by this server.
64
46
    Attributes:
65
 
    name:      string; from the config file, used in log messages
66
 
    fingerprint: string (40 or 32 hexadecimal digits); used to
67
 
                 uniquely identify the client
68
 
    secret:    bytestring; sent verbatim (over TLS) to client
69
 
    fqdn:      string (FQDN); available for use by the checker command
 
47
    password:  string
 
48
    fqdn:      string, FQDN (used by the checker)
70
49
    created:   datetime.datetime()
71
50
    last_seen: datetime.datetime() or None if not yet seen
72
51
    timeout:   datetime.timedelta(); How long from last_seen until
73
52
                                     this client is invalid
74
53
    interval:  datetime.timedelta(); How often to start a new checker
 
54
    timeout_milliseconds: Used by gobject.timeout_add()
 
55
    interval_milliseconds: - '' -
75
56
    stop_hook: If set, called by stop() as stop_hook(self)
76
57
    checker:   subprocess.Popen(); a running checker process used
77
58
                                   to see if the client lives.
79
60
    checker_initiator_tag: a gobject event source tag, or None
80
61
    stop_initiator_tag:    - '' -
81
62
    checker_callback_tag:  - '' -
82
 
    checker_command: string; External command which is run to check if
83
 
                     client lives.  %()s expansions are done at
84
 
                     runtime with vars(self) as dict, so that for
85
 
                     instance %(name)s can be used in the command.
86
 
    Private attibutes:
87
 
    _timeout: Real variable for 'timeout'
88
 
    _interval: Real variable for 'interval'
89
 
    _timeout_milliseconds: Used by gobject.timeout_add()
90
 
    _interval_milliseconds: - '' -
91
63
    """
92
 
    def _set_timeout(self, timeout):
93
 
        "Setter function for 'timeout' attribute"
94
 
        self._timeout = timeout
95
 
        self._timeout_milliseconds = ((self.timeout.days
96
 
                                       * 24 * 60 * 60 * 1000)
97
 
                                      + (self.timeout.seconds * 1000)
98
 
                                      + (self.timeout.microseconds
99
 
                                         // 1000))
100
 
    timeout = property(lambda self: self._timeout,
101
 
                       _set_timeout)
102
 
    del _set_timeout
103
 
    def _set_interval(self, interval):
104
 
        "Setter function for 'interval' attribute"
105
 
        self._interval = interval
106
 
        self._interval_milliseconds = ((self.interval.days
107
 
                                        * 24 * 60 * 60 * 1000)
108
 
                                       + (self.interval.seconds
109
 
                                          * 1000)
110
 
                                       + (self.interval.microseconds
111
 
                                          // 1000))
112
 
    interval = property(lambda self: self._interval,
113
 
                        _set_interval)
114
 
    del _set_interval
115
64
    def __init__(self, name=None, options=None, stop_hook=None,
116
 
                 fingerprint=None, secret=None, secfile=None,
117
 
                 fqdn=None, timeout=None, interval=-1, checker=None):
 
65
                 dn=None, password=None, passfile=None, fqdn=None,
 
66
                 timeout=None, interval=-1):
118
67
        self.name = name
119
 
        # Uppercase and remove spaces from fingerprint
120
 
        # for later comparison purposes with return value of
121
 
        # the fingerprint() function
122
 
        self.fingerprint = fingerprint.upper().replace(u" ", u"")
123
 
        if secret:
124
 
            self.secret = secret.decode(u"base64")
125
 
        elif secfile:
126
 
            sf = open(secfile)
127
 
            self.secret = sf.read()
128
 
            sf.close()
 
68
        self.dn = dn
 
69
        if password:
 
70
            self.password = password
 
71
        elif passfile:
 
72
            self.password = open(passfile).readall()
129
73
        else:
130
 
            raise RuntimeError(u"No secret or secfile for client %s"
 
74
            raise RuntimeError(u"No Password or Passfile for client %s"
131
75
                               % self.name)
132
76
        self.fqdn = fqdn                # string
133
77
        self.created = datetime.datetime.now()
134
78
        self.last_seen = None
135
79
        if timeout is None:
136
 
            self.timeout = options.timeout
137
 
        else:
138
 
            self.timeout = string_to_delta(timeout)
 
80
            timeout = options.timeout
 
81
        self.timeout = timeout
 
82
        self.timeout_milliseconds = ((self.timeout.days
 
83
                                      * 24 * 60 * 60 * 1000)
 
84
                                     + (self.timeout.seconds * 1000)
 
85
                                     + (self.timeout.microseconds
 
86
                                        // 1000))
139
87
        if interval == -1:
140
 
            self.interval = options.interval
 
88
            interval = options.interval
141
89
        else:
142
 
            self.interval = string_to_delta(interval)
 
90
            interval = string_to_delta(interval)
 
91
        self.interval = interval
 
92
        self.interval_milliseconds = ((self.interval.days
 
93
                                       * 24 * 60 * 60 * 1000)
 
94
                                      + (self.interval.seconds * 1000)
 
95
                                      + (self.interval.microseconds
 
96
                                         // 1000))
143
97
        self.stop_hook = stop_hook
144
98
        self.checker = None
145
99
        self.checker_initiator_tag = None
146
100
        self.stop_initiator_tag = None
147
101
        self.checker_callback_tag = None
148
 
        self.check_command = checker
149
102
    def start(self):
150
103
        """Start this clients checker and timeout hooks"""
151
104
        # Schedule a new checker to be started an 'interval' from now,
152
105
        # and every interval from then on.
153
 
        self.checker_initiator_tag = gobject.timeout_add\
154
 
                                     (self._interval_milliseconds,
155
 
                                      self.start_checker)
 
106
        self.checker_initiator_tag = gobject.\
 
107
                                     timeout_add(self.interval_milliseconds,
 
108
                                                 self.start_checker)
156
109
        # Also start a new checker *right now*.
157
110
        self.start_checker()
158
111
        # Schedule a stop() when 'timeout' has passed
159
 
        self.stop_initiator_tag = gobject.timeout_add\
160
 
                                  (self._timeout_milliseconds,
161
 
                                   self.stop)
 
112
        self.stop_initiator_tag = gobject.\
 
113
                                     timeout_add(self.timeout_milliseconds,
 
114
                                                 self.stop)
162
115
    def stop(self):
163
116
        """Stop this client.
164
117
        The possibility that this client might be restarted is left
165
118
        open, but not currently used."""
166
 
        logger.debug(u"Stopping client %s", self.name)
167
 
        self.secret = None
 
119
        # print "Stopping client", self.name
 
120
        self.password = None
168
121
        if self.stop_initiator_tag:
169
122
            gobject.source_remove(self.stop_initiator_tag)
170
123
            self.stop_initiator_tag = None
192
145
        now = datetime.datetime.now()
193
146
        if os.WIFEXITED(condition) \
194
147
               and (os.WEXITSTATUS(condition) == 0):
195
 
            logger.debug(u"Checker for %(name)s succeeded",
196
 
                         vars(self))
 
148
            #print "Checker for %(name)s succeeded" % vars(self)
197
149
            self.last_seen = now
198
150
            gobject.source_remove(self.stop_initiator_tag)
199
 
            self.stop_initiator_tag = gobject.timeout_add\
200
 
                                      (self._timeout_milliseconds,
201
 
                                       self.stop)
202
 
        elif not os.WIFEXITED(condition):
203
 
            logger.warning(u"Checker for %(name)s crashed?",
204
 
                           vars(self))
205
 
        else:
206
 
            logger.debug(u"Checker for %(name)s failed",
207
 
                         vars(self))
208
 
            self.checker = None
 
151
            self.stop_initiator_tag = gobject.\
 
152
                                      timeout_add(self.timeout_milliseconds,
 
153
                                                  self.stop)
 
154
        #else:
 
155
        #    if not os.WIFEXITED(condition):
 
156
        #        print "Checker for %(name)s crashed?" % vars(self)
 
157
        #    else:
 
158
        #        print "Checker for %(name)s failed" % vars(self)
 
159
        self.checker = None
209
160
        self.checker_callback_tag = None
210
161
    def start_checker(self):
211
162
        """Start a new checker subprocess if one is not running.
212
163
        If a checker already exists, leave it running and do
213
164
        nothing."""
214
165
        if self.checker is None:
215
 
            try:
216
 
                command = self.check_command % self.fqdn
217
 
            except TypeError:
218
 
                escaped_attrs = dict((key, re.escape(str(val)))
219
 
                                     for key, val in
220
 
                                     vars(self).iteritems())
221
 
                try:
222
 
                    command = self.check_command % escaped_attrs
223
 
                except TypeError, error:
224
 
                    logger.critical(u'Could not format string "%s":'
225
 
                                    u' %s', self.check_command, error)
226
 
                    return True # Try again later
227
 
            try:
228
 
                logger.debug(u"Starting checker %r for %s",
229
 
                             command, self.name)
 
166
            #print "Starting checker for", self.name
 
167
            try:
230
168
                self.checker = subprocess.\
231
 
                               Popen(command,
 
169
                               Popen("sleep 1; fping -q -- %s"
 
170
                                     % re.escape(self.fqdn),
 
171
                                     stdout=subprocess.PIPE,
232
172
                                     close_fds=True, shell=True,
233
173
                                     cwd="/")
234
 
                self.checker_callback_tag = gobject.child_watch_add\
235
 
                                            (self.checker.pid,
236
 
                                             self.checker_callback)
 
174
                self.checker_callback_tag = gobject.\
 
175
                                            child_watch_add(self.checker.pid,
 
176
                                                            self.\
 
177
                                                            checker_callback)
237
178
            except subprocess.OSError, error:
238
 
                logger.error(u"Failed to start subprocess: %s",
239
 
                             error)
 
179
                sys.stderr.write(u"Failed to start subprocess: %s\n"
 
180
                                 % error)
240
181
        # Re-run this periodically if run by gobject.timeout_add
241
182
        return True
242
183
    def stop_checker(self):
259
200
            return now < (self.last_seen + self.timeout)
260
201
 
261
202
 
262
 
def peer_certificate(session):
263
 
    "Return an OpenPGP data packet string for the peer's certificate"
264
 
    # If not an OpenPGP certificate...
265
 
    if gnutls.library.functions.gnutls_certificate_type_get\
266
 
            (session._c_object) \
267
 
           != gnutls.library.constants.GNUTLS_CRT_OPENPGP:
268
 
        # ...do the normal thing
269
 
        return session.peer_certificate
270
 
    list_size = ctypes.c_uint()
271
 
    cert_list = gnutls.library.functions.gnutls_certificate_get_peers\
272
 
        (session._c_object, ctypes.byref(list_size))
273
 
    if list_size.value == 0:
274
 
        return None
275
 
    cert = cert_list[0]
276
 
    return ctypes.string_at(cert.data, cert.size)
277
 
 
278
 
 
279
 
def fingerprint(openpgp):
280
 
    "Convert an OpenPGP data string to a hexdigit fingerprint string"
281
 
    # New empty GnuTLS certificate
282
 
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
283
 
    gnutls.library.functions.gnutls_openpgp_crt_init\
284
 
        (ctypes.byref(crt))
285
 
    # New GnuTLS "datum" with the OpenPGP public key
286
 
    datum = gnutls.library.types.gnutls_datum_t\
287
 
        (ctypes.cast(ctypes.c_char_p(openpgp),
288
 
                     ctypes.POINTER(ctypes.c_ubyte)),
289
 
         ctypes.c_uint(len(openpgp)))
290
 
    # Import the OpenPGP public key into the certificate
291
 
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
292
 
        (crt,
293
 
         ctypes.byref(datum),
294
 
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
295
 
    # New buffer for the fingerprint
296
 
    buffer = ctypes.create_string_buffer(20)
297
 
    buffer_length = ctypes.c_size_t()
298
 
    # Get the fingerprint from the certificate into the buffer
299
 
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
300
 
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
301
 
    # Deinit the certificate
302
 
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
303
 
    # Convert the buffer to a Python bytestring
304
 
    fpr = ctypes.string_at(buffer, buffer_length.value)
305
 
    # Convert the bytestring to hexadecimal notation
306
 
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
307
 
    return hex_fpr
308
 
 
309
 
 
310
203
class tcp_handler(SocketServer.BaseRequestHandler, object):
311
204
    """A TCP request handler class.
312
205
    Instantiated by IPv6_TCPServer for each request to handle it.
313
206
    Note: This will run in its own forked process."""
314
 
    
315
207
    def handle(self):
316
 
        logger.debug(u"TCP connection from: %s",
317
 
                     unicode(self.client_address))
318
 
        session = gnutls.connection.ClientSession(self.request,
319
 
                                                  gnutls.connection.\
320
 
                                                  X509Credentials())
321
 
        
322
 
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
323
 
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
324
 
        #                "+DHE-DSS"))
325
 
        priority = "SECURE256"
326
 
        
327
 
        gnutls.library.functions.gnutls_priority_set_direct\
328
 
            (session._c_object, priority, None);
329
 
        
 
208
        #print u"TCP request came"
 
209
        #print u"Request:", self.request
 
210
        #print u"Client Address:", self.client_address
 
211
        #print u"Server:", self.server
 
212
        session = gnutls.connection.ServerSession(self.request,
 
213
                                                  self.server\
 
214
                                                  .credentials)
330
215
        try:
331
216
            session.handshake()
332
217
        except gnutls.errors.GNUTLSError, error:
333
 
            logger.debug(u"Handshake failed: %s", error)
 
218
            #sys.stderr.write(u"Handshake failed: %s\n" % error)
334
219
            # Do not run session.bye() here: the session is not
335
220
            # established.  Just abandon the request.
336
221
            return
 
222
        #if session.peer_certificate:
 
223
        #    print "DN:", session.peer_certificate.subject
337
224
        try:
338
 
            fpr = fingerprint(peer_certificate(session))
339
 
        except (TypeError, gnutls.errors.GNUTLSError), error:
340
 
            logger.debug(u"Bad certificate: %s", error)
 
225
            session.verify_peer()
 
226
        except gnutls.errors.CertificateError, error:
 
227
            #sys.stderr.write(u"Verify failed: %s\n" % error)
341
228
            session.bye()
342
229
            return
343
 
        logger.debug(u"Fingerprint: %s", fpr)
344
230
        client = None
345
231
        for c in clients:
346
 
            if c.fingerprint == fpr:
 
232
            if c.dn == session.peer_certificate.subject:
347
233
                client = c
348
234
                break
349
235
        # Have to check if client.still_valid(), since it is possible
350
236
        # that the client timed out while establishing the GnuTLS
351
237
        # session.
352
 
        if (not client) or (not client.still_valid()):
353
 
            if client:
354
 
                logger.debug(u"Client %(name)s is invalid",
355
 
                             vars(client))
356
 
            else:
357
 
                logger.debug(u"Client not found for fingerprint: %s",
358
 
                             fpr)
359
 
            session.bye()
360
 
            return
361
 
        sent_size = 0
362
 
        while sent_size < len(client.secret):
363
 
            sent = session.send(client.secret[sent_size:])
364
 
            logger.debug(u"Sent: %d, remaining: %d",
365
 
                         sent, len(client.secret)
366
 
                         - (sent_size + sent))
367
 
            sent_size += sent
 
238
        if client and client.still_valid():
 
239
            session.send(client.password)
 
240
        else:
 
241
            #if client:
 
242
            #    sys.stderr.write(u"Client %(name)s is invalid\n"
 
243
            #                     % vars(client))
 
244
            #else:
 
245
            #    sys.stderr.write(u"Client not found for DN: %s\n"
 
246
            #                     % session.peer_certificate.subject)
 
247
            #session.send("gazonk")
 
248
            pass
368
249
        session.bye()
369
250
 
370
251
 
373
254
    Attributes:
374
255
        options:        Command line options
375
256
        clients:        Set() of Client objects
 
257
        credentials:    GnuTLS X.509 credentials
376
258
    """
377
259
    address_family = socket.AF_INET6
378
260
    def __init__(self, *args, **kwargs):
382
264
        if "clients" in kwargs:
383
265
            self.clients = kwargs["clients"]
384
266
            del kwargs["clients"]
 
267
        if "credentials" in kwargs:
 
268
            self.credentials = kwargs["credentials"]
 
269
            del kwargs["credentials"]
385
270
        return super(type(self), self).__init__(*args, **kwargs)
386
271
    def server_bind(self):
387
272
        """This overrides the normal server_bind() function
397
282
                                       self.options.interface)
398
283
            except socket.error, error:
399
284
                if error[0] == errno.EPERM:
400
 
                    logger.warning(u"No permission to"
401
 
                                   u" bind to interface %s",
402
 
                                   self.options.interface)
 
285
                    sys.stderr.write(u"Warning: No permission to bind to interface %s\n"
 
286
                                     % self.options.interface)
403
287
                else:
404
288
                    raise error
405
289
        # Only bind(2) the socket if we really need to.
459
343
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
460
344
        group.connect_to_signal('StateChanged',
461
345
                                entry_group_state_changed)
462
 
    logger.debug(u"Adding service '%s' of type '%s' ...",
463
 
                 serviceName, serviceType)
 
346
    
 
347
    # print "Adding service '%s' of type '%s' ..." % (serviceName,
 
348
    #                                                 serviceType)
464
349
    
465
350
    group.AddService(
466
351
            serviceInterface,           # interface
484
369
def server_state_changed(state):
485
370
    """From the Avahi server example code"""
486
371
    if state == avahi.SERVER_COLLISION:
487
 
        logger.warning(u"Server name collision")
 
372
        print "WARNING: Server name collision"
488
373
        remove_service()
489
374
    elif state == avahi.SERVER_RUNNING:
490
375
        add_service()
494
379
    """From the Avahi server example code"""
495
380
    global serviceName, server, rename_count
496
381
    
497
 
    logger.debug(u"state change: %i", state)
 
382
    # print "state change: %i" % state
498
383
    
499
384
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
500
 
        logger.debug(u"Service established.")
 
385
        pass
 
386
        # print "Service established."
501
387
    elif state == avahi.ENTRY_GROUP_COLLISION:
502
388
        
503
389
        rename_count = rename_count - 1
504
390
        if rename_count > 0:
505
391
            name = server.GetAlternativeServiceName(name)
506
 
            logger.warning(u"Service name collision, "
507
 
                           u"changing name to '%s' ...", name)
 
392
            print "WARNING: Service name collision, changing name to '%s' ..." % name
508
393
            remove_service()
509
394
            add_service()
510
395
            
511
396
        else:
512
 
            logger.error(u"No suitable service name found after %i"
513
 
                         u" retries, exiting.", n_rename)
514
 
            killme(1)
 
397
            print "ERROR: No suitable service name found after %i retries, exiting." % n_rename
 
398
            main_loop.quit()
515
399
    elif state == avahi.ENTRY_GROUP_FAILURE:
516
 
        logger.error(u"Error in group state changed %s",
517
 
                     unicode(error))
518
 
        killme(1)
 
400
        print "Error in group state changed", error
 
401
        main_loop.quit()
 
402
        return
519
403
 
520
404
 
521
405
def if_nametoindex(interface):
522
406
    """Call the C function if_nametoindex()"""
523
407
    try:
 
408
        if "ctypes" not in sys.modules:
 
409
            import ctypes
524
410
        libc = ctypes.cdll.LoadLibrary("libc.so.6")
525
411
        return libc.if_nametoindex(interface)
526
 
    except (OSError, AttributeError):
 
412
    except (ImportError, OSError, AttributeError):
527
413
        if "struct" not in sys.modules:
528
414
            import struct
529
415
        if "fcntl" not in sys.modules:
537
423
        return interface_index
538
424
 
539
425
 
540
 
def daemon(nochdir, noclose):
541
 
    """See daemon(3).  Standard BSD Unix function.
542
 
    This should really exist as os.daemon, but it doesn't (yet)."""
543
 
    if os.fork():
544
 
        sys.exit()
545
 
    os.setsid()
546
 
    if not nochdir:
547
 
        os.chdir("/")
548
 
    if not noclose:
549
 
        # Close all standard open file descriptors
550
 
        null = os.open("/dev/null", os.O_NOCTTY | os.O_RDWR)
551
 
        if not stat.S_ISCHR(os.fstat(null).st_mode):
552
 
            raise OSError(errno.ENODEV,
553
 
                          "/dev/null not a character device")
554
 
        os.dup2(null, sys.stdin.fileno())
555
 
        os.dup2(null, sys.stdout.fileno())
556
 
        os.dup2(null, sys.stderr.fileno())
557
 
        if null > 2:
558
 
            os.close(null)
559
 
 
560
 
 
561
 
def killme(status = 0):
562
 
    logger.debug("Stopping server with exit status %d", status)
563
 
    exitstatus = status
564
 
    if main_loop_started:
565
 
        main_loop.quit()
566
 
    else:
567
 
        sys.exit(status)
568
 
 
569
 
 
570
426
if __name__ == '__main__':
571
 
    exitstatus = 0
572
 
    main_loop_started = False
573
427
    parser = OptionParser()
574
428
    parser.add_option("-i", "--interface", type="string",
575
429
                      default=None, metavar="IF",
576
430
                      help="Bind to interface IF")
 
431
    parser.add_option("--cert", type="string", default="cert.pem",
 
432
                      metavar="FILE",
 
433
                      help="Public key certificate PEM file to use")
 
434
    parser.add_option("--key", type="string", default="key.pem",
 
435
                      metavar="FILE",
 
436
                      help="Private key PEM file to use")
 
437
    parser.add_option("--ca", type="string", default="ca.pem",
 
438
                      metavar="FILE",
 
439
                      help="Certificate Authority certificate PEM file to use")
 
440
    parser.add_option("--crl", type="string", default="crl.pem",
 
441
                      metavar="FILE",
 
442
                      help="Certificate Revokation List PEM file to use")
577
443
    parser.add_option("-p", "--port", type="int", default=None,
578
444
                      help="Port number to receive requests on")
579
445
    parser.add_option("--timeout", type="string", # Parsed later
584
450
                      help="How often to check that a client is up")
585
451
    parser.add_option("--check", action="store_true", default=False,
586
452
                      help="Run self-test")
587
 
    parser.add_option("--debug", action="store_true", default=False,
588
 
                      help="Debug mode")
589
453
    (options, args) = parser.parse_args()
590
454
    
591
455
    if options.check:
603
467
    except ValueError:
604
468
        parser.error("option --interval: Unparseable time")
605
469
    
 
470
    cert = gnutls.crypto.X509Certificate(open(options.cert).read())
 
471
    key = gnutls.crypto.X509PrivateKey(open(options.key).read())
 
472
    ca = gnutls.crypto.X509Certificate(open(options.ca).read())
 
473
    crl = gnutls.crypto.X509CRL(open(options.crl).read())
 
474
    cred = gnutls.connection.X509Credentials(cert, key, [ca], [crl])
 
475
    
606
476
    # Parse config file
607
 
    defaults = { "checker": "fping -q -- %%(fqdn)s" }
 
477
    defaults = {}
608
478
    client_config = ConfigParser.SafeConfigParser(defaults)
609
479
    #client_config.readfp(open("secrets.conf"), "secrets.conf")
610
480
    client_config.read("mandos-clients.conf")
618
488
            avahi.DBUS_INTERFACE_SERVER )
619
489
    # End of Avahi example code
620
490
    
621
 
    debug = options.debug
622
 
    
623
 
    if debug:
624
 
        console = logging.StreamHandler()
625
 
        # console.setLevel(logging.DEBUG)
626
 
        console.setFormatter(logging.Formatter\
627
 
                             ('%(levelname)s: %(message)s'))
628
 
        logger.addHandler(console)
629
 
        del console
630
 
    
631
491
    clients = Set()
632
492
    def remove_from_clients(client):
633
493
        clients.remove(client)
634
494
        if not clients:
635
 
            logger.debug(u"No clients left, exiting")
636
 
            killme()
 
495
            print "No clients left, exiting"
 
496
            main_loop.quit()
637
497
    
638
498
    clients.update(Set(Client(name=section, options=options,
639
499
                              stop_hook = remove_from_clients,
640
500
                              **(dict(client_config\
641
501
                                      .items(section))))
642
502
                       for section in client_config.sections()))
643
 
    
644
 
    if not debug:
645
 
        daemon(False, False)
646
 
    
647
 
    def cleanup():
648
 
        "Cleanup function; run on exit"
649
 
        global group
650
 
        # From the Avahi server example code
651
 
        if not group is None:
652
 
            group.Free()
653
 
            group = None
654
 
        # End of Avahi example code
655
 
        
656
 
        for client in clients:
657
 
            client.stop_hook = None
658
 
            client.stop()
659
 
    
660
 
    atexit.register(cleanup)
661
 
    
662
 
    if not debug:
663
 
        signal.signal(signal.SIGINT, signal.SIG_IGN)
664
 
    signal.signal(signal.SIGHUP, lambda signum, frame: killme())
665
 
    signal.signal(signal.SIGTERM, lambda signum, frame: killme())
666
 
    
667
503
    for client in clients:
668
504
        client.start()
669
505
    
670
506
    tcp_server = IPv6_TCPServer((None, options.port),
671
507
                                tcp_handler,
672
508
                                options=options,
673
 
                                clients=clients)
 
509
                                clients=clients,
 
510
                                credentials=cred)
674
511
    # Find out what random port we got
675
512
    servicePort = tcp_server.socket.getsockname()[1]
676
 
    logger.debug(u"Now listening on port %d", servicePort)
 
513
    #sys.stderr.write("Now listening on port %d\n" % servicePort)
677
514
    
678
515
    if options.interface is not None:
679
516
        serviceInterface = if_nametoindex(options.interface)
680
517
    
681
518
    # From the Avahi server example code
682
519
    server.connect_to_signal("StateChanged", server_state_changed)
683
 
    try:
684
 
        server_state_changed(server.GetState())
685
 
    except dbus.exceptions.DBusException, error:
686
 
        logger.critical(u"DBusException: %s", error)
687
 
        killme(1)
 
520
    server_state_changed(server.GetState())
688
521
    # End of Avahi example code
689
522
    
690
523
    gobject.io_add_watch(tcp_server.fileno(), gobject.IO_IN,
692
525
                         tcp_server.handle_request(*args[2:],
693
526
                                                   **kwargs) or True)
694
527
    try:
695
 
        main_loop_started = True
696
528
        main_loop.run()
697
529
    except KeyboardInterrupt:
698
 
        if debug:
699
 
            print
700
 
    
701
 
    sys.exit(exitstatus)
 
530
        print
 
531
    
 
532
    # Cleanup here
 
533
 
 
534
    # From the Avahi server example code
 
535
    if not group is None:
 
536
        group.Free()
 
537
    # End of Avahi example code
 
538
    
 
539
    for client in clients:
 
540
        client.stop_hook = None
 
541
        client.stop()