/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2008-11-09 06:40:29 UTC
  • mto: (237.2.1 mandos) (24.1.113 mandos)
  • mto: This revision was merged to the branch mainline in revision 238.
  • Revision ID: teddy@fukt.bsnet.se-20081109064029-df71jpoce308cq3v
First steps of a D-Bus interface to the server.

* mandos: Also import "dbus.service".
  (Client): Inherit from "dbus.service.Object", which is a new-style
            class, so inheriting from "object" is no longer necessary.
  (Client.interface): New temporary variable which only exists during
                     class definition.

  (Client.getName, Client.getFingerprint): New D-Bus getter methods.
  (Client.setSecret): New D-Bus setter method.
  (Client._set_timeout): Emit D-Bus signal "TimeoutChanged".
  (Client.getTimeout): New D-Bus getter method.
  (Client.TimeoutChanged): New D-Bus signal.
  (Client._set_interval): Emit D-Bus signal "IntervalChanged".
  (Client.getInterval): New D-Bus getter method.
  (Client.intervalChanged): New D-Bus signal.
  (Client.__init__): Also call "dbus.service.Object.__init__".
  (Client.started): New boolean attribute.
  (Client.start, Client.stop): Update "self.started", and emit D-Bus
                               signal "StateChanged".
  (Client.StateChanged): New D-Bus signal.
  (Client.stop): Use "self.started" instead of misusing "self.secret".
                 Also simplify code by using "getattr" instead of
                 "hasattr".
  (Client.checker_callback): Emit D-Bus signal "CheckerCompleted".
  (Client.CheckerCompleted): New D-Bus signal.
  (Client.bumpTimeout): D-Bus method name for "bump_timeout".
  (Client.start_checker): Emit D-Bus signal "CheckerStarted".
  (Client.CheckerStarted): New D-Bus signal.
  (Client.checkerIsRunning): New D-Bus method.
  (Client.StopChecker): D-Bus method name for "stop_checker".
  (Client.still_valid): First check "self.started".
  (Client.stillValid): D-Bus method name for "still_valid".

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
14
# Copyright © 2008 Teddy Hogeborn & Björn Påhlsson
15
15
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
24
24
#     GNU General Public License for more details.
25
25
26
26
# You should have received a copy of the GNU General Public License
27
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
27
# along with this program.  If not, see
 
28
# <http://www.gnu.org/licenses/>.
28
29
29
30
# Contact the authors at <mandos@fukt.bsnet.se>.
30
31
31
32
 
32
 
from __future__ import division
 
33
from __future__ import division, with_statement, absolute_import
33
34
 
34
35
import SocketServer
35
36
import socket
36
 
import select
37
37
from optparse import OptionParser
38
38
import datetime
39
39
import errno
54
54
import stat
55
55
import logging
56
56
import logging.handlers
 
57
import pwd
 
58
from contextlib import closing
57
59
 
58
60
import dbus
 
61
import dbus.service
59
62
import gobject
60
63
import avahi
61
64
from dbus.mainloop.glib import DBusGMainLoop
62
65
import ctypes
 
66
import ctypes.util
63
67
 
64
 
version = "1.0"
 
68
version = "1.0.2"
65
69
 
66
70
logger = logging.Logger('mandos')
67
71
syslogger = logging.handlers.SysLogHandler\
79
83
class AvahiError(Exception):
80
84
    def __init__(self, value):
81
85
        self.value = value
 
86
        super(AvahiError, self).__init__()
82
87
    def __str__(self):
83
88
        return repr(self.value)
84
89
 
106
111
                  a sensible number of times
107
112
    """
108
113
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
109
 
                 type = None, port = None, TXT = None, domain = "",
 
114
                 servicetype = None, port = None, TXT = None, domain = "",
110
115
                 host = "", max_renames = 32768):
111
116
        self.interface = interface
112
117
        self.name = name
113
 
        self.type = type
 
118
        self.type = servicetype
114
119
        self.port = port
115
120
        if TXT is None:
116
121
            self.TXT = []
123
128
    def rename(self):
124
129
        """Derived from the Avahi example code"""
125
130
        if self.rename_count >= self.max_renames:
126
 
            logger.critical(u"No suitable service name found after %i"
127
 
                            u" retries, exiting.", rename_count)
 
131
            logger.critical(u"No suitable Zeroconf service name found"
 
132
                            u" after %i retries, exiting.",
 
133
                            self.rename_count)
128
134
            raise AvahiServiceError("Too many renames")
129
135
        self.name = server.GetAlternativeServiceName(self.name)
130
 
        logger.info(u"Changing name to %r ...", str(self.name))
 
136
        logger.info(u"Changing Zeroconf service name to %r ...",
 
137
                    str(self.name))
131
138
        syslogger.setFormatter(logging.Formatter\
132
139
                               ('Mandos (%s): %%(levelname)s:'
133
140
                               ' %%(message)s' % self.name))
148
155
                     avahi.DBUS_INTERFACE_ENTRY_GROUP)
149
156
            group.connect_to_signal('StateChanged',
150
157
                                    entry_group_state_changed)
151
 
        logger.debug(u"Adding service '%s' of type '%s' ...",
 
158
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
152
159
                     service.name, service.type)
153
160
        group.AddService(
154
161
                self.interface,         # interface
165
172
# End of Avahi example code
166
173
 
167
174
 
168
 
class Client(object):
 
175
class Client(dbus.service.Object):
169
176
    """A representation of a client host served by this server.
170
177
    Attributes:
171
178
    name:      string; from the config file, used in log messages
174
181
    secret:    bytestring; sent verbatim (over TLS) to client
175
182
    host:      string; available for use by the checker command
176
183
    created:   datetime.datetime(); object creation, not client host
 
184
    started:   bool()
177
185
    last_checked_ok: datetime.datetime() or None if not yet checked OK
178
186
    timeout:   datetime.timedelta(); How long from last_checked_ok
179
187
                                     until this client is invalid
195
203
    _timeout_milliseconds: Used when calling gobject.timeout_add()
196
204
    _interval_milliseconds: - '' -
197
205
    """
 
206
    interface = u"org.mandos_system.Mandos.Clients"
 
207
    
 
208
    @dbus.service.method(interface, out_signature="s")
 
209
    def getName(self):
 
210
        "D-Bus getter method"
 
211
        return self.name
 
212
    
 
213
    @dbus.service.method(interface, out_signature="s")
 
214
    def getFingerprint(self):
 
215
        "D-Bus getter method"
 
216
        return self.fingerprint
 
217
    
 
218
    @dbus.service.method(interface, in_signature="ay",
 
219
                         byte_arrays=True)
 
220
    def setSecret(self, secret):
 
221
        "D-Bus setter method"
 
222
        self.secret = secret
 
223
    
198
224
    def _set_timeout(self, timeout):
199
 
        "Setter function for 'timeout' attribute"
 
225
        "Setter function for the 'timeout' attribute"
200
226
        self._timeout = timeout
201
227
        self._timeout_milliseconds = ((self.timeout.days
202
228
                                       * 24 * 60 * 60 * 1000)
203
229
                                      + (self.timeout.seconds * 1000)
204
230
                                      + (self.timeout.microseconds
205
231
                                         // 1000))
206
 
    timeout = property(lambda self: self._timeout,
207
 
                       _set_timeout)
 
232
        # Emit D-Bus signal
 
233
        self.TimeoutChanged(self._timeout_milliseconds)
 
234
    timeout = property(lambda self: self._timeout, _set_timeout)
208
235
    del _set_timeout
 
236
    
 
237
    @dbus.service.method(interface, out_signature="t")
 
238
    def getTimeout(self):
 
239
        "D-Bus getter method"
 
240
        return self._timeout_milliseconds
 
241
    
 
242
    @dbus.service.signal(interface, signature="t")
 
243
    def TimeoutChanged(self, t):
 
244
        "D-Bus signal"
 
245
        pass
 
246
    
209
247
    def _set_interval(self, interval):
210
 
        "Setter function for 'interval' attribute"
 
248
        "Setter function for the 'interval' attribute"
211
249
        self._interval = interval
212
250
        self._interval_milliseconds = ((self.interval.days
213
251
                                        * 24 * 60 * 60 * 1000)
215
253
                                          * 1000)
216
254
                                       + (self.interval.microseconds
217
255
                                          // 1000))
218
 
    interval = property(lambda self: self._interval,
219
 
                        _set_interval)
 
256
        # Emit D-Bus signal
 
257
        self.IntervalChanged(self._interval_milliseconds)
 
258
    interval = property(lambda self: self._interval, _set_interval)
220
259
    del _set_interval
221
 
    def __init__(self, name = None, stop_hook=None, config={}):
 
260
    
 
261
    @dbus.service.method(interface, out_signature="t")
 
262
    def getInterval(self):
 
263
        "D-Bus getter method"
 
264
        return self._interval_milliseconds
 
265
    
 
266
    @dbus.service.signal(interface, signature="t")
 
267
    def IntervalChanged(self, t):
 
268
        "D-Bus signal"
 
269
        pass
 
270
    
 
271
    def __init__(self, name = None, stop_hook=None, config=None):
222
272
        """Note: the 'checker' key in 'config' sets the
223
273
        'checker_command' attribute and *not* the 'checker'
224
274
        attribute."""
 
275
        dbus.service.Object.__init__(self, bus,
 
276
                                     "/Mandos/Clients/%s"
 
277
                                     % name.replace(".", "_"))
 
278
        if config is None:
 
279
            config = {}
225
280
        self.name = name
226
281
        logger.debug(u"Creating client %r", self.name)
227
282
        # Uppercase and remove spaces from fingerprint for later
233
288
        if "secret" in config:
234
289
            self.secret = config["secret"].decode(u"base64")
235
290
        elif "secfile" in config:
236
 
            sf = open(config["secfile"])
237
 
            self.secret = sf.read()
238
 
            sf.close()
 
291
            with closing(open(os.path.expanduser
 
292
                              (os.path.expandvars
 
293
                               (config["secfile"])))) \
 
294
                               as secfile:
 
295
                self.secret = secfile.read()
239
296
        else:
240
297
            raise TypeError(u"No secret or secfile for client %s"
241
298
                            % self.name)
242
299
        self.host = config.get("host", "")
243
300
        self.created = datetime.datetime.now()
 
301
        self.started = False
244
302
        self.last_checked_ok = None
245
303
        self.timeout = string_to_delta(config["timeout"])
246
304
        self.interval = string_to_delta(config["interval"])
250
308
        self.stop_initiator_tag = None
251
309
        self.checker_callback_tag = None
252
310
        self.check_command = config["checker"]
 
311
    
253
312
    def start(self):
254
313
        """Start this client's checker and timeout hooks"""
 
314
        self.started = True
255
315
        # Schedule a new checker to be started an 'interval' from now,
256
316
        # and every interval from then on.
257
317
        self.checker_initiator_tag = gobject.timeout_add\
263
323
        self.stop_initiator_tag = gobject.timeout_add\
264
324
                                  (self._timeout_milliseconds,
265
325
                                   self.stop)
 
326
        # Emit D-Bus signal
 
327
        self.StateChanged(True)
 
328
    
 
329
    @dbus.service.signal(interface, signature="b")
 
330
    def StateChanged(self, started):
 
331
        "D-Bus signal"
 
332
        pass
 
333
    
266
334
    def stop(self):
267
 
        """Stop this client.
268
 
        The possibility that a client might be restarted is left open,
269
 
        but not currently used."""
270
 
        # If this client doesn't have a secret, it is already stopped.
271
 
        if hasattr(self, "secret") and self.secret:
 
335
        """Stop this client."""
 
336
        if getattr(self, "started", False):
272
337
            logger.info(u"Stopping client %s", self.name)
273
 
            self.secret = None
274
338
        else:
275
339
            return False
276
340
        if getattr(self, "stop_initiator_tag", False):
282
346
        self.stop_checker()
283
347
        if self.stop_hook:
284
348
            self.stop_hook(self)
 
349
        # Emit D-Bus signal
 
350
        self.StateChanged(False)
285
351
        # Do not run this again if called by a gobject.timeout_add
286
352
        return False
 
353
    # D-Bus variant
 
354
    Stop = dbus.service.method(interface)(stop)
 
355
    
287
356
    def __del__(self):
288
357
        self.stop_hook = None
289
358
        self.stop()
 
359
    
290
360
    def checker_callback(self, pid, condition):
291
361
        """The checker has completed, so take appropriate actions."""
292
 
        now = datetime.datetime.now()
293
362
        self.checker_callback_tag = None
294
363
        self.checker = None
295
364
        if os.WIFEXITED(condition) \
296
365
               and (os.WEXITSTATUS(condition) == 0):
297
366
            logger.info(u"Checker for %(name)s succeeded",
298
367
                        vars(self))
299
 
            self.last_checked_ok = now
300
 
            gobject.source_remove(self.stop_initiator_tag)
301
 
            self.stop_initiator_tag = gobject.timeout_add\
302
 
                                      (self._timeout_milliseconds,
303
 
                                       self.stop)
 
368
            # Emit D-Bus signal
 
369
            self.CheckerCompleted(True)
 
370
            self.bump_timeout()
304
371
        elif not os.WIFEXITED(condition):
305
372
            logger.warning(u"Checker for %(name)s crashed?",
306
373
                           vars(self))
 
374
            # Emit D-Bus signal
 
375
            self.CheckerCompleted(False)
307
376
        else:
308
377
            logger.info(u"Checker for %(name)s failed",
309
378
                        vars(self))
 
379
            # Emit D-Bus signal
 
380
            self.CheckerCompleted(False)
 
381
    
 
382
    @dbus.service.signal(interface, signature="b")
 
383
    def CheckerCompleted(self, success):
 
384
        "D-Bus signal"
 
385
        pass
 
386
    
 
387
    def bump_timeout(self):
 
388
        """Bump up the timeout for this client.
 
389
        This should only be called when the client has been seen,
 
390
        alive and well.
 
391
        """
 
392
        self.last_checked_ok = datetime.datetime.now()
 
393
        gobject.source_remove(self.stop_initiator_tag)
 
394
        self.stop_initiator_tag = gobject.timeout_add\
 
395
            (self._timeout_milliseconds, self.stop)
 
396
    # D-Bus variant
 
397
    bumpTimeout = dbus.service.method(interface)(bump_timeout)
 
398
    
310
399
    def start_checker(self):
311
400
        """Start a new checker subprocess if one is not running.
312
401
        If a checker already exists, leave it running and do
347
436
                self.checker_callback_tag = gobject.child_watch_add\
348
437
                                            (self.checker.pid,
349
438
                                             self.checker_callback)
 
439
                # Emit D-Bus signal
 
440
                self.CheckerStarted(command)
350
441
            except OSError, error:
351
442
                logger.error(u"Failed to start subprocess: %s",
352
443
                             error)
353
444
        # Re-run this periodically if run by gobject.timeout_add
354
445
        return True
 
446
    
 
447
    @dbus.service.signal(interface, signature="s")
 
448
    def CheckerStarted(self, command):
 
449
        pass
 
450
    
 
451
    @dbus.service.method(interface, out_signature="b")
 
452
    def checkerIsRunning(self):
 
453
        "D-Bus getter method"
 
454
        return self.checker is not None
 
455
    
355
456
    def stop_checker(self):
356
457
        """Force the checker process, if any, to stop."""
357
458
        if self.checker_callback_tag:
369
470
            if error.errno != errno.ESRCH: # No such process
370
471
                raise
371
472
        self.checker = None
 
473
    # D-Bus variant
 
474
    StopChecker = dbus.service.method(interface)(stop_checker)
 
475
    
372
476
    def still_valid(self):
373
477
        """Has the timeout not yet passed for this client?"""
 
478
        if not self.started:
 
479
            return False
374
480
        now = datetime.datetime.now()
375
481
        if self.last_checked_ok is None:
376
482
            return now < (self.created + self.timeout)
377
483
        else:
378
484
            return now < (self.last_checked_ok + self.timeout)
 
485
    # D-Bus variant
 
486
    stillValid = dbus.service.method(interface, out_signature="b")\
 
487
        (still_valid)
 
488
    
 
489
    del interface
379
490
 
380
491
 
381
492
def peer_certificate(session):
411
522
                    (crt, ctypes.byref(datum),
412
523
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
413
524
    # Verify the self signature in the key
414
 
    crtverify = ctypes.c_uint();
 
525
    crtverify = ctypes.c_uint()
415
526
    gnutls.library.functions.gnutls_openpgp_crt_verify_self\
416
 
        (crt, ctypes.c_uint(0), ctypes.byref(crtverify))
 
527
        (crt, 0, ctypes.byref(crtverify))
417
528
    if crtverify.value != 0:
418
529
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
419
530
        raise gnutls.errors.CertificateSecurityError("Verify failed")
420
531
    # New buffer for the fingerprint
421
 
    buffer = ctypes.create_string_buffer(20)
422
 
    buffer_length = ctypes.c_size_t()
 
532
    buf = ctypes.create_string_buffer(20)
 
533
    buf_len = ctypes.c_size_t()
423
534
    # Get the fingerprint from the certificate into the buffer
424
535
    gnutls.library.functions.gnutls_openpgp_crt_get_fingerprint\
425
 
        (crt, ctypes.byref(buffer), ctypes.byref(buffer_length))
 
536
        (crt, ctypes.byref(buf), ctypes.byref(buf_len))
426
537
    # Deinit the certificate
427
538
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
428
539
    # Convert the buffer to a Python bytestring
429
 
    fpr = ctypes.string_at(buffer, buffer_length.value)
 
540
    fpr = ctypes.string_at(buf, buf_len.value)
430
541
    # Convert the bytestring to hexadecimal notation
431
542
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
432
543
    return hex_fpr
433
544
 
434
545
 
435
 
class tcp_handler(SocketServer.BaseRequestHandler, object):
 
546
class TCP_handler(SocketServer.BaseRequestHandler, object):
436
547
    """A TCP request handler class.
437
548
    Instantiated by IPv6_TCPServer for each request to handle it.
438
549
    Note: This will run in its own forked process."""
439
550
    
440
551
    def handle(self):
441
552
        logger.info(u"TCP connection from: %s",
442
 
                     unicode(self.client_address))
 
553
                    unicode(self.client_address))
443
554
        session = gnutls.connection.ClientSession\
444
555
                  (self.request, gnutls.connection.X509Credentials())
445
556
        
460
571
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
461
572
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
462
573
        #                "+DHE-DSS"))
463
 
        priority = "NORMAL"             # Fallback default, since this
464
 
                                        # MUST be set.
465
 
        if self.server.settings["priority"]:
466
 
            priority = self.server.settings["priority"]
 
574
        # Use a fallback default, since this MUST be set.
 
575
        priority = self.server.settings.get("priority", "NORMAL")
467
576
        gnutls.library.functions.gnutls_priority_set_direct\
468
 
            (session._c_object, priority, None);
 
577
            (session._c_object, priority, None)
469
578
        
470
579
        try:
471
580
            session.handshake()
499
608
                           vars(client))
500
609
            session.bye()
501
610
            return
 
611
        ## This won't work here, since we're in a fork.
 
612
        # client.bump_timeout()
502
613
        sent_size = 0
503
614
        while sent_size < len(client.secret):
504
615
            sent = session.send(client.secret[sent_size:])
509
620
        session.bye()
510
621
 
511
622
 
512
 
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
 
623
class IPv6_TCPServer(SocketServer.ForkingMixIn,
 
624
                     SocketServer.TCPServer, object):
513
625
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
514
626
    Attributes:
515
627
        settings:       Server settings
516
628
        clients:        Set() of Client objects
 
629
        enabled:        Boolean; whether this server is activated yet
517
630
    """
518
631
    address_family = socket.AF_INET6
519
632
    def __init__(self, *args, **kwargs):
523
636
        if "clients" in kwargs:
524
637
            self.clients = kwargs["clients"]
525
638
            del kwargs["clients"]
526
 
        return super(type(self), self).__init__(*args, **kwargs)
 
639
        self.enabled = False
 
640
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
527
641
    def server_bind(self):
528
642
        """This overrides the normal server_bind() function
529
643
        to bind to an interface if one was specified, and also NOT to
558
672
#                                            if_nametoindex
559
673
#                                            (self.settings
560
674
#                                             ["interface"]))
561
 
            return super(type(self), self).server_bind()
 
675
            return super(IPv6_TCPServer, self).server_bind()
 
676
    def server_activate(self):
 
677
        if self.enabled:
 
678
            return super(IPv6_TCPServer, self).server_activate()
 
679
    def enable(self):
 
680
        self.enabled = True
562
681
 
563
682
 
564
683
def string_to_delta(interval):
580
699
    timevalue = datetime.timedelta(0)
581
700
    for s in interval.split():
582
701
        try:
583
 
            suffix=unicode(s[-1])
584
 
            value=int(s[:-1])
 
702
            suffix = unicode(s[-1])
 
703
            value = int(s[:-1])
585
704
            if suffix == u"d":
586
705
                delta = datetime.timedelta(value)
587
706
            elif suffix == u"s":
603
722
def server_state_changed(state):
604
723
    """Derived from the Avahi example code"""
605
724
    if state == avahi.SERVER_COLLISION:
606
 
        logger.error(u"Server name collision")
 
725
        logger.error(u"Zeroconf server name collision")
607
726
        service.remove()
608
727
    elif state == avahi.SERVER_RUNNING:
609
728
        service.add()
611
730
 
612
731
def entry_group_state_changed(state, error):
613
732
    """Derived from the Avahi example code"""
614
 
    logger.debug(u"state change: %i", state)
 
733
    logger.debug(u"Avahi state change: %i", state)
615
734
    
616
735
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
617
 
        logger.debug(u"Service established.")
 
736
        logger.debug(u"Zeroconf service established.")
618
737
    elif state == avahi.ENTRY_GROUP_COLLISION:
619
 
        logger.warning(u"Service name collision.")
 
738
        logger.warning(u"Zeroconf service name collision.")
620
739
        service.rename()
621
740
    elif state == avahi.ENTRY_GROUP_FAILURE:
622
 
        logger.critical(u"Error in group state changed %s",
 
741
        logger.critical(u"Avahi: Error in group state changed %s",
623
742
                        unicode(error))
624
743
        raise AvahiGroupError("State changed: %s", str(error))
625
744
 
627
746
    """Call the C function if_nametoindex(), or equivalent"""
628
747
    global if_nametoindex
629
748
    try:
630
 
        if "ctypes.util" not in sys.modules:
631
 
            import ctypes.util
632
749
        if_nametoindex = ctypes.cdll.LoadLibrary\
633
750
            (ctypes.util.find_library("c")).if_nametoindex
634
751
    except (OSError, AttributeError):
639
756
        def if_nametoindex(interface):
640
757
            "Get an interface index the hard way, i.e. using fcntl()"
641
758
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
642
 
            s = socket.socket()
643
 
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
644
 
                                struct.pack("16s16x", interface))
645
 
            s.close()
 
759
            with closing(socket.socket()) as s:
 
760
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
761
                                    struct.pack("16s16x", interface))
646
762
            interface_index = struct.unpack("I", ifreq[16:20])[0]
647
763
            return interface_index
648
764
    return if_nametoindex(interface)
672
788
 
673
789
 
674
790
def main():
675
 
    global main_loop_started
676
 
    main_loop_started = False
677
 
    
678
791
    parser = OptionParser(version = "%%prog %s" % version)
679
792
    parser.add_option("-i", "--interface", type="string",
680
793
                      metavar="IF", help="Bind to interface IF")
695
808
                      default="/etc/mandos", metavar="DIR",
696
809
                      help="Directory to search for configuration"
697
810
                      " files")
698
 
    (options, args) = parser.parse_args()
 
811
    options = parser.parse_args()[0]
699
812
    
700
813
    if options.check:
701
814
        import doctest
755
868
    client_config.read(os.path.join(server_settings["configdir"],
756
869
                                    "clients.conf"))
757
870
    
 
871
    clients = Set()
 
872
    tcp_server = IPv6_TCPServer((server_settings["address"],
 
873
                                 server_settings["port"]),
 
874
                                TCP_handler,
 
875
                                settings=server_settings,
 
876
                                clients=clients)
 
877
    pidfilename = "/var/run/mandos.pid"
 
878
    try:
 
879
        pidfile = open(pidfilename, "w")
 
880
    except IOError, error:
 
881
        logger.error("Could not open file %r", pidfilename)
 
882
    
 
883
    uid = 65534
 
884
    gid = 65534
 
885
    try:
 
886
        uid = pwd.getpwnam("mandos").pw_uid
 
887
    except KeyError:
 
888
        try:
 
889
            uid = pwd.getpwnam("nobody").pw_uid
 
890
        except KeyError:
 
891
            pass
 
892
    try:
 
893
        gid = pwd.getpwnam("mandos").pw_gid
 
894
    except KeyError:
 
895
        try:
 
896
            gid = pwd.getpwnam("nogroup").pw_gid
 
897
        except KeyError:
 
898
            pass
 
899
    try:
 
900
        os.setuid(uid)
 
901
        os.setgid(gid)
 
902
    except OSError, error:
 
903
        if error[0] != errno.EPERM:
 
904
            raise error
 
905
    
758
906
    global service
759
907
    service = AvahiService(name = server_settings["servicename"],
760
 
                           type = "_mandos._tcp", );
 
908
                           servicetype = "_mandos._tcp", )
761
909
    if server_settings["interface"]:
762
 
        service.interface = if_nametoindex(server_settings["interface"])
 
910
        service.interface = if_nametoindex\
 
911
                            (server_settings["interface"])
763
912
    
764
913
    global main_loop
765
914
    global bus
768
917
    DBusGMainLoop(set_as_default=True )
769
918
    main_loop = gobject.MainLoop()
770
919
    bus = dbus.SystemBus()
771
 
    server = dbus.Interface(
772
 
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
773
 
            avahi.DBUS_INTERFACE_SERVER )
 
920
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
 
921
                                           avahi.DBUS_PATH_SERVER),
 
922
                            avahi.DBUS_INTERFACE_SERVER)
774
923
    # End of Avahi example code
775
924
    
776
 
    clients = Set()
777
925
    def remove_from_clients(client):
778
926
        clients.remove(client)
779
927
        if not clients:
801
949
        # Close all input and output, do double fork, etc.
802
950
        daemon()
803
951
    
804
 
    pidfilename = "/var/run/mandos/mandos.pid"
805
 
    pid = os.getpid()
806
952
    try:
807
 
        pidfile = open(pidfilename, "w")
 
953
        pid = os.getpid()
808
954
        pidfile.write(str(pid) + "\n")
809
955
        pidfile.close()
810
956
        del pidfile
811
 
    except IOError, err:
812
 
        logger.error(u"Could not write %s file with PID %d",
813
 
                     pidfilename, os.getpid())
 
957
    except IOError:
 
958
        logger.error(u"Could not write to file %r with PID %d",
 
959
                     pidfilename, pid)
 
960
    except NameError:
 
961
        # "pidfile" was never created
 
962
        pass
 
963
    del pidfilename
814
964
    
815
965
    def cleanup():
816
966
        "Cleanup function; run on exit"
836
986
    for client in clients:
837
987
        client.start()
838
988
    
839
 
    tcp_server = IPv6_TCPServer((server_settings["address"],
840
 
                                 server_settings["port"]),
841
 
                                tcp_handler,
842
 
                                settings=server_settings,
843
 
                                clients=clients)
 
989
    tcp_server.enable()
 
990
    tcp_server.server_activate()
 
991
    
844
992
    # Find out what port we got
845
993
    service.port = tcp_server.socket.getsockname()[1]
846
994
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
864
1012
                             (*args[2:], **kwargs) or True)
865
1013
        
866
1014
        logger.debug(u"Starting main loop")
867
 
        main_loop_started = True
868
1015
        main_loop.run()
869
1016
    except AvahiError, error:
870
1017
        logger.critical(u"AvahiError: %s" + unicode(error))