6
6
# This program is partly derived from an example program for an Avahi
 
7
7
# service publisher, downloaded from
 
8
8
# <http://avahi.org/wiki/PythonPublishExample>.  This includes the
 
9
 
# methods "add" and "remove" in the "AvahiService" class, the
 
10
 
# "server_state_changed" and "entry_group_state_changed" functions,
 
11
 
# and some lines in "main".
 
 
9
# following functions: "AvahiService.add", "AvahiService.remove",
 
 
10
# "server_state_changed", "entry_group_state_changed", and some lines
 
13
13
# Everything else is
 
14
14
# Copyright © 2007-2008 Teddy Hogeborn & Björn Påhlsson
 
 
24
24
#     GNU General Public License for more details.
 
26
26
# You should have received a copy of the GNU General Public License
 
27
 
# along with this program.  If not, see
 
28
 
# <http://www.gnu.org/licenses/>.
 
 
27
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
30
29
# Contact the authors at <mandos@fukt.bsnet.se>.
 
 
63
61
from dbus.mainloop.glib import DBusGMainLoop
 
 
64
# Brief description of the operation of this program:
 
 
66
# This server announces itself as a Zeroconf service.  Connecting
 
 
67
# clients use the TLS protocol, with the unusual quirk that this
 
 
68
# server program acts as a TLS "client" while a connecting client acts
 
 
69
# as a TLS "server".  The client (acting as a TLS "server") must
 
 
70
# supply an OpenPGP certificate, and the fingerprint of this
 
 
71
# certificate is used by this server to look up (in a list read from a
 
 
72
# file at start time) which binary blob to give the client.  No other
 
 
73
# authentication or authorization is done by this server.
 
68
76
logger = logging.Logger('mandos')
 
69
77
syslogger = logging.handlers.SysLogHandler\
 
70
 
            (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
 
 
78
            (facility = logging.handlers.SysLogHandler.LOG_DAEMON)
 
72
79
syslogger.setFormatter(logging.Formatter\
 
73
 
                        ('Mandos: %(levelname)s: %(message)s'))
 
 
80
                        ('%(levelname)s: %(message)s'))
 
74
81
logger.addHandler(syslogger)
 
76
 
console = logging.StreamHandler()
 
77
 
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
 
79
 
logger.addHandler(console)
 
81
85
class AvahiError(Exception):
 
82
86
    def __init__(self, value):
 
 
94
98
class AvahiService(object):
 
95
 
    """An Avahi (Zeroconf) service.
 
97
100
    interface: integer; avahi.IF_UNSPEC or an interface index.
 
98
101
               Used to optionally bind to the specified interface.
 
99
 
    name: string; Example: 'Mandos'
 
100
 
    type: string; Example: '_mandos._tcp'.
 
101
 
                  See <http://www.dns-sd.org/ServiceTypes.html>
 
102
 
    port: integer; what port to announce
 
103
 
    TXT: list of strings; TXT record for the service
 
104
 
    domain: string; Domain to publish on, default to .local if empty.
 
105
 
    host: string; Host to publish records for, default is localhost
 
106
 
    max_renames: integer; maximum number of renames
 
107
 
    rename_count: integer; counter so we only rename after collisions
 
108
 
                  a sensible number of times
 
 
102
    name = string; Example: "Mandos"
 
 
103
    type = string; Example: "_mandos._tcp".
 
 
104
                   See <http://www.dns-sd.org/ServiceTypes.html>
 
 
105
    port = integer; what port to announce
 
 
106
    TXT = list of strings; TXT record for the service
 
 
107
    domain = string; Domain to publish on, default to .local if empty.
 
 
108
    host = string; Host to publish records for, default to localhost
 
 
110
    max_renames = integer; maximum number of renames
 
 
111
    rename_count = integer; counter so we only rename after collisions
 
 
112
                   a sensible number of times
 
110
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
 
111
115
                 type = None, port = None, TXT = None, domain = "",
 
112
 
                 host = "", max_renames = 32768):
 
 
116
                 host = "", max_renames = 12):
 
 
117
        """An Avahi (Zeroconf) service. """
 
113
118
        self.interface = interface
 
 
121
126
        self.domain = domain
 
123
128
        self.rename_count = 0
 
124
 
        self.max_renames = max_renames
 
125
129
    def rename(self):
 
126
130
        """Derived from the Avahi example code"""
 
127
131
        if self.rename_count >= self.max_renames:
 
128
 
            logger.critical(u"No suitable Zeroconf service name found"
 
129
 
                            u" after %i retries, exiting.",
 
 
132
            logger.critical(u"No suitable service name found after %i"
 
 
133
                            u" retries, exiting.", rename_count)
 
131
134
            raise AvahiServiceError("Too many renames")
 
132
 
        self.name = server.GetAlternativeServiceName(self.name)
 
133
 
        logger.info(u"Changing Zeroconf service name to %r ...",
 
135
 
        syslogger.setFormatter(logging.Formatter\
 
136
 
                               ('Mandos (%s): %%(levelname)s:'
 
137
 
                               ' %%(message)s' % self.name))
 
 
135
        name = server.GetAlternativeServiceName(name)
 
 
136
        logger.notice(u"Changing name to %r ...", name)
 
140
139
        self.rename_count += 1
 
 
152
151
                     avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
153
152
            group.connect_to_signal('StateChanged',
 
154
153
                                    entry_group_state_changed)
 
155
 
        logger.debug(u"Adding Zeroconf service '%s' of type '%s' ...",
 
 
154
        logger.debug(u"Adding service '%s' of type '%s' ...",
 
156
155
                     service.name, service.type)
 
157
156
        group.AddService(
 
158
157
                self.interface,         # interface
 
 
176
175
    fingerprint: string (40 or 32 hexadecimal digits); used to
 
177
176
                 uniquely identify the client
 
178
177
    secret:    bytestring; sent verbatim (over TLS) to client
 
179
 
    host:      string; available for use by the checker command
 
 
178
    fqdn:      string (FQDN); available for use by the checker command
 
180
179
    created:   datetime.datetime(); object creation, not client host
 
181
180
    last_checked_ok: datetime.datetime() or None if not yet checked OK
 
182
181
    timeout:   datetime.timedelta(); How long from last_checked_ok
 
 
222
221
    interval = property(lambda self: self._interval,
 
224
223
    del _set_interval
 
225
 
    def __init__(self, name = None, stop_hook=None, config={}):
 
226
 
        """Note: the 'checker' key in 'config' sets the
 
227
 
        'checker_command' attribute and *not* the 'checker'
 
 
224
    def __init__(self, name=None, stop_hook=None, fingerprint=None,
 
 
225
                 secret=None, secfile=None, fqdn=None, timeout=None,
 
 
226
                 interval=-1, checker=None):
 
 
227
        """Note: the 'checker' argument sets the 'checker_command'
 
 
228
        attribute and not the 'checker' attribute.."""
 
230
230
        logger.debug(u"Creating client %r", self.name)
 
231
 
        # Uppercase and remove spaces from fingerprint for later
 
232
 
        # comparison purposes with return value from the fingerprint()
 
234
 
        self.fingerprint = config["fingerprint"].upper()\
 
 
231
        # Uppercase and remove spaces from fingerprint
 
 
232
        # for later comparison purposes with return value of
 
 
233
        # the fingerprint() function
 
 
234
        self.fingerprint = fingerprint.upper().replace(u" ", u"")
 
236
235
        logger.debug(u"  Fingerprint: %s", self.fingerprint)
 
237
 
        if "secret" in config:
 
238
 
            self.secret = config["secret"].decode(u"base64")
 
239
 
        elif "secfile" in config:
 
240
 
            sf = open(config["secfile"])
 
 
237
            self.secret = secret.decode(u"base64")
 
241
240
            self.secret = sf.read()
 
244
243
            raise TypeError(u"No secret or secfile for client %s"
 
246
 
        self.host = config.get("host", "")
 
247
246
        self.created = datetime.datetime.now()
 
248
247
        self.last_checked_ok = None
 
249
 
        self.timeout = string_to_delta(config["timeout"])
 
250
 
        self.interval = string_to_delta(config["interval"])
 
 
248
        self.timeout = string_to_delta(timeout)
 
 
249
        self.interval = string_to_delta(interval)
 
251
250
        self.stop_hook = stop_hook
 
252
251
        self.checker = None
 
253
252
        self.checker_initiator_tag = None
 
254
253
        self.stop_initiator_tag = None
 
255
254
        self.checker_callback_tag = None
 
256
 
        self.check_command = config["checker"]
 
 
255
        self.check_command = checker
 
258
257
        """Start this client's checker and timeout hooks"""
 
259
258
        # Schedule a new checker to be started an 'interval' from now,
 
 
272
271
        The possibility that a client might be restarted is left open,
 
273
272
        but not currently used."""
 
274
273
        # If this client doesn't have a secret, it is already stopped.
 
275
 
        if hasattr(self, "secret") and self.secret:
 
276
 
            logger.info(u"Stopping client %s", self.name)
 
 
275
            logger.debug(u"Stopping client %s", self.name)
 
277
276
            self.secret = None
 
 
298
297
        self.checker = None
 
299
298
        if os.WIFEXITED(condition) \
 
300
299
               and (os.WEXITSTATUS(condition) == 0):
 
301
 
            logger.info(u"Checker for %(name)s succeeded",
 
 
300
            logger.debug(u"Checker for %(name)s succeeded",
 
303
302
            self.last_checked_ok = now
 
304
303
            gobject.source_remove(self.stop_initiator_tag)
 
305
304
            self.stop_initiator_tag = gobject.timeout_add\
 
 
309
308
            logger.warning(u"Checker for %(name)s crashed?",
 
312
 
            logger.info(u"Checker for %(name)s failed",
 
 
311
            logger.debug(u"Checker for %(name)s failed",
 
314
313
    def start_checker(self):
 
315
314
        """Start a new checker subprocess if one is not running.
 
316
315
        If a checker already exists, leave it running and do
 
 
339
338
                                 u' %s', self.check_command, error)
 
340
339
                    return True # Try again later
 
342
 
                logger.info(u"Starting checker %r for %s",
 
344
 
                # We don't need to redirect stdout and stderr, since
 
345
 
                # in normal mode, that is already done by daemon(),
 
346
 
                # and in debug mode we don't want to.  (Stdin is
 
347
 
                # always replaced by /dev/null.)
 
 
341
                logger.debug(u"Starting checker %r for %s",
 
348
343
                self.checker = subprocess.Popen(command,
 
350
345
                                                shell=True, cwd="/")
 
351
346
                self.checker_callback_tag = gobject.child_watch_add\
 
352
347
                                            (self.checker.pid,
 
353
348
                                             self.checker_callback)
 
354
 
            except OSError, error:
 
 
349
            except subprocess.OSError, error:
 
355
350
                logger.error(u"Failed to start subprocess: %s",
 
357
352
        # Re-run this periodically if run by gobject.timeout_add
 
 
363
358
            self.checker_callback_tag = None
 
364
359
        if getattr(self, "checker", None) is None:
 
366
 
        logger.debug(u"Stopping checker for %(name)s", vars(self))
 
 
361
        logger.debug("Stopping checker for %(name)s", vars(self))
 
368
363
            os.kill(self.checker.pid, signal.SIGTERM)
 
 
402
397
def fingerprint(openpgp):
 
403
398
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
 
 
399
    # New empty GnuTLS certificate
 
 
400
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
 
401
    gnutls.library.functions.gnutls_openpgp_crt_init\
 
404
403
    # New GnuTLS "datum" with the OpenPGP public key
 
405
404
    datum = gnutls.library.types.gnutls_datum_t\
 
406
405
        (ctypes.cast(ctypes.c_char_p(openpgp),
 
407
406
                     ctypes.POINTER(ctypes.c_ubyte)),
 
408
407
         ctypes.c_uint(len(openpgp)))
 
409
 
    # New empty GnuTLS certificate
 
410
 
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
411
 
    gnutls.library.functions.gnutls_openpgp_crt_init\
 
413
408
    # Import the OpenPGP public key into the certificate
 
414
 
    gnutls.library.functions.gnutls_openpgp_crt_import\
 
415
 
                    (crt, ctypes.byref(datum),
 
416
 
                     gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
417
 
    # Verify the self signature in the key
 
418
 
    crtverify = ctypes.c_uint();
 
419
 
    gnutls.library.functions.gnutls_openpgp_crt_verify_self\
 
420
 
        (crt, 0, ctypes.byref(crtverify))
 
421
 
    if crtverify.value != 0:
 
422
 
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
423
 
        raise gnutls.errors.CertificateSecurityError("Verify failed")
 
 
409
    ret = gnutls.library.functions.gnutls_openpgp_crt_import\
 
 
412
         gnutls.library.constants.GNUTLS_OPENPGP_FMT_RAW)
 
424
413
    # New buffer for the fingerprint
 
425
414
    buffer = ctypes.create_string_buffer(20)
 
426
415
    buffer_length = ctypes.c_size_t()
 
 
442
431
    Note: This will run in its own forked process."""
 
444
433
    def handle(self):
 
445
 
        logger.info(u"TCP connection from: %s",
 
 
434
        logger.debug(u"TCP connection from: %s",
 
446
435
                     unicode(self.client_address))
 
447
436
        session = gnutls.connection.ClientSession\
 
448
437
                  (self.request, gnutls.connection.X509Credentials())
 
450
 
        line = self.request.makefile().readline()
 
451
 
        logger.debug(u"Protocol version: %r", line)
 
453
 
            if int(line.strip().split()[0]) > 1:
 
455
 
        except (ValueError, IndexError, RuntimeError), error:
 
456
 
            logger.error(u"Unknown protocol version: %s", error)
 
459
438
        # Note: gnutls.connection.X509Credentials is really a generic
 
460
439
        # GnuTLS certificate credentials object so long as no X.509
 
461
440
        # keys are added to it.  Therefore, we can use it here despite
 
 
475
454
            session.handshake()
 
476
455
        except gnutls.errors.GNUTLSError, error:
 
477
 
            logger.warning(u"Handshake failed: %s", error)
 
 
456
            logger.debug(u"Handshake failed: %s", error)
 
478
457
            # Do not run session.bye() here: the session is not
 
479
458
            # established.  Just abandon the request.
 
482
461
            fpr = fingerprint(peer_certificate(session))
 
483
462
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
484
 
            logger.warning(u"Bad certificate: %s", error)
 
 
463
            logger.debug(u"Bad certificate: %s", error)
 
487
466
        logger.debug(u"Fingerprint: %s", fpr)
 
 
494
 
            logger.warning(u"Client not found for fingerprint: %s",
 
 
473
            logger.debug(u"Client not found for fingerprint: %s", fpr)
 
498
476
        # Have to check if client.still_valid(), since it is possible
 
499
477
        # that the client timed out while establishing the GnuTLS
 
501
479
        if not client.still_valid():
 
502
 
            logger.warning(u"Client %(name)s is invalid",
 
 
480
            logger.debug(u"Client %(name)s is invalid", vars(client))
 
 
528
504
        if "clients" in kwargs:
 
529
505
            self.clients = kwargs["clients"]
 
530
506
            del kwargs["clients"]
 
532
507
        return super(type(self), self).__init__(*args, **kwargs)
 
533
508
    def server_bind(self):
 
534
509
        """This overrides the normal server_bind() function
 
535
510
        to bind to an interface if one was specified, and also NOT to
 
536
511
        bind to an address or port if they were not specified."""
 
537
 
        if self.settings["interface"]:
 
 
512
        if self.settings["interface"] != avahi.IF_UNSPEC:
 
538
513
            # 25 is from /usr/include/asm-i486/socket.h
 
539
514
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
 
 
543
518
                                       self.settings["interface"])
 
544
519
            except socket.error, error:
 
545
520
                if error[0] == errno.EPERM:
 
546
 
                    logger.error(u"No permission to"
 
547
 
                                 u" bind to interface %s",
 
548
 
                                 self.settings["interface"])
 
 
521
                    logger.warning(u"No permission to"
 
 
522
                                   u" bind to interface %s",
 
 
523
                                   self.settings["interface"])
 
551
526
        # Only bind(2) the socket if we really need to.
 
 
554
529
                in6addr_any = "::"
 
555
530
                self.server_address = (in6addr_any,
 
556
531
                                       self.server_address[1])
 
557
 
            elif not self.server_address[1]:
 
 
532
            elif self.server_address[1] is None:
 
558
533
                self.server_address = (self.server_address[0],
 
560
 
#                 if self.settings["interface"]:
 
561
 
#                     self.server_address = (self.server_address[0],
 
567
535
            return super(type(self), self).server_bind()
 
568
 
    def server_activate(self):
 
570
 
            return super(type(self), self).server_activate()
 
575
538
def string_to_delta(interval):
 
 
585
548
    datetime.timedelta(1)
 
586
549
    >>> string_to_delta(u'1w')
 
587
550
    datetime.timedelta(7)
 
588
 
    >>> string_to_delta('5m 30s')
 
589
 
    datetime.timedelta(0, 330)
 
591
 
    timevalue = datetime.timedelta(0)
 
592
 
    for s in interval.split():
 
594
 
            suffix=unicode(s[-1])
 
597
 
                delta = datetime.timedelta(value)
 
599
 
                delta = datetime.timedelta(0, value)
 
601
 
                delta = datetime.timedelta(0, 0, 0, 0, value)
 
603
 
                delta = datetime.timedelta(0, 0, 0, 0, 0, value)
 
605
 
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
 
608
 
        except (ValueError, IndexError):
 
 
553
        suffix=unicode(interval[-1])
 
 
554
        value=int(interval[:-1])
 
 
556
            delta = datetime.timedelta(value)
 
 
558
            delta = datetime.timedelta(0, value)
 
 
560
            delta = datetime.timedelta(0, 0, 0, 0, value)
 
 
562
            delta = datetime.timedelta(0, 0, 0, 0, 0, value)
 
 
564
            delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
 
 
567
    except (ValueError, IndexError):
 
614
572
def server_state_changed(state):
 
615
573
    """Derived from the Avahi example code"""
 
616
574
    if state == avahi.SERVER_COLLISION:
 
617
 
        logger.error(u"Zeroconf server name collision")
 
 
575
        logger.warning(u"Server name collision")
 
619
577
    elif state == avahi.SERVER_RUNNING:
 
 
623
581
def entry_group_state_changed(state, error):
 
624
582
    """Derived from the Avahi example code"""
 
625
 
    logger.debug(u"Avahi state change: %i", state)
 
 
583
    logger.debug(u"state change: %i", state)
 
627
585
    if state == avahi.ENTRY_GROUP_ESTABLISHED:
 
628
 
        logger.debug(u"Zeroconf service established.")
 
 
586
        logger.debug(u"Service established.")
 
629
587
    elif state == avahi.ENTRY_GROUP_COLLISION:
 
630
 
        logger.warning(u"Zeroconf service name collision.")
 
 
588
        logger.warning(u"Service name collision.")
 
632
590
    elif state == avahi.ENTRY_GROUP_FAILURE:
 
633
 
        logger.critical(u"Avahi: Error in group state changed %s",
 
 
591
        logger.critical(u"Error in group state changed %s",
 
635
593
        raise AvahiGroupError("State changed: %s", str(error))
 
637
 
def if_nametoindex(interface):
 
 
595
def if_nametoindex(interface, _func=[None]):
 
638
596
    """Call the C function if_nametoindex(), or equivalent"""
 
639
 
    global if_nametoindex
 
 
597
    if _func[0] is not None:
 
 
598
        return _func[0](interface)
 
641
600
        if "ctypes.util" not in sys.modules:
 
642
601
            import ctypes.util
 
643
 
        if_nametoindex = ctypes.cdll.LoadLibrary\
 
644
 
            (ctypes.util.find_library("c")).if_nametoindex
 
 
604
                libc = ctypes.cdll.LoadLibrary\
 
 
605
                       (ctypes.util.find_library("c"))
 
 
606
                func[0] = libc.if_nametoindex
 
 
607
                return _func[0](interface)
 
645
611
    except (OSError, AttributeError):
 
646
612
        if "struct" not in sys.modules:
 
648
614
        if "fcntl" not in sys.modules:
 
650
 
        def if_nametoindex(interface):
 
 
616
        def the_hard_way(interface):
 
651
617
            "Get an interface index the hard way, i.e. using fcntl()"
 
652
618
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
 
653
619
            s = socket.socket()
 
 
657
623
            interface_index = struct.unpack("I", ifreq[16:20])[0]
 
658
624
            return interface_index
 
659
 
    return if_nametoindex(interface)
 
662
 
def daemon(nochdir = False, noclose = False):
 
 
625
        _func[0] = the_hard_way
 
 
626
        return _func[0](interface)
 
 
629
def daemon(nochdir, noclose):
 
663
630
    """See daemon(3).  Standard BSD Unix function.
 
664
631
    This should really exist as os.daemon, but it doesn't (yet)."""
 
 
686
651
    global main_loop_started
 
687
652
    main_loop_started = False
 
689
 
    parser = OptionParser(version = "%%prog %s" % version)
 
 
654
    parser = OptionParser()
 
690
655
    parser.add_option("-i", "--interface", type="string",
 
691
656
                      metavar="IF", help="Bind to interface IF")
 
692
657
    parser.add_option("-a", "--address", type="string",
 
 
695
660
                      help="Port number to receive requests on")
 
696
661
    parser.add_option("--check", action="store_true", default=False,
 
697
662
                      help="Run self-test")
 
698
 
    parser.add_option("--debug", action="store_true",
 
 
663
    parser.add_option("--debug", action="store_true", default=False,
 
699
664
                      help="Debug mode; run in foreground and log to"
 
701
666
    parser.add_option("--priority", type="string", help="GnuTLS"
 
 
726
691
    # Parse config file for server-global settings
 
727
692
    server_config = ConfigParser.SafeConfigParser(server_defaults)
 
728
693
    del server_defaults
 
729
 
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
 
 
694
    server_config.read(os.path.join(options.configdir, "server.conf"))
 
 
695
    server_section = "server"
 
730
696
    # Convert the SafeConfigParser object to a dict
 
731
 
    server_settings = server_config.defaults()
 
 
697
    server_settings = dict(server_config.items(server_section))
 
732
698
    # Use getboolean on the boolean config option
 
733
699
    server_settings["debug"] = server_config.getboolean\
 
 
700
                               (server_section, "debug")
 
735
701
    del server_config
 
 
702
    if not server_settings["interface"]:
 
 
703
        server_settings["interface"] = avahi.IF_UNSPEC
 
737
705
    # Override the settings from the config file with command line
 
738
706
    # options, if set.
 
 
745
713
    # Now we have our good server settings in "server_settings"
 
747
 
    debug = server_settings["debug"]
 
750
 
        syslogger.setLevel(logging.WARNING)
 
751
 
        console.setLevel(logging.WARNING)
 
753
 
    if server_settings["servicename"] != "Mandos":
 
754
 
        syslogger.setFormatter(logging.Formatter\
 
755
 
                               ('Mandos (%s): %%(levelname)s:'
 
757
 
                                % server_settings["servicename"]))
 
759
715
    # Parse config file with clients
 
760
716
    client_defaults = { "timeout": "1h",
 
761
717
                        "interval": "5m",
 
762
 
                        "checker": "fping -q -- %(host)s",
 
 
718
                        "checker": "fping -q -- %%(fqdn)s",
 
765
720
    client_config = ConfigParser.SafeConfigParser(client_defaults)
 
766
721
    client_config.read(os.path.join(server_settings["configdir"],
 
770
 
    tcp_server = IPv6_TCPServer((server_settings["address"],
 
771
 
                                 server_settings["port"]),
 
773
 
                                settings=server_settings,
 
775
 
    pidfilename = "/var/run/mandos.pid"
 
777
 
        pidfile = open(pidfilename, "w")
 
778
 
    except IOError, error:
 
779
 
        logger.error("Could not open file %r", pidfilename)
 
784
 
        uid = pwd.getpwnam("mandos").pw_uid
 
787
 
            uid = pwd.getpwnam("nobody").pw_uid
 
791
 
        gid = pwd.getpwnam("mandos").pw_gid
 
794
 
            gid = pwd.getpwnam("nogroup").pw_gid
 
800
 
    except OSError, error:
 
801
 
        if error[0] != errno.EPERM:
 
805
725
    service = AvahiService(name = server_settings["servicename"],
 
806
726
                           type = "_mandos._tcp", );
 
807
 
    if server_settings["interface"]:
 
808
 
        service.interface = if_nametoindex\
 
809
 
                            (server_settings["interface"])
 
 
815
732
    DBusGMainLoop(set_as_default=True )
 
816
733
    main_loop = gobject.MainLoop()
 
817
734
    bus = dbus.SystemBus()
 
818
 
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME,
 
819
 
                                           avahi.DBUS_PATH_SERVER),
 
820
 
                            avahi.DBUS_INTERFACE_SERVER)
 
 
735
    server = dbus.Interface(
 
 
736
            bus.get_object( avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER ),
 
 
737
            avahi.DBUS_INTERFACE_SERVER )
 
821
738
    # End of Avahi example code
 
 
740
    debug = server_settings["debug"]
 
 
743
        console = logging.StreamHandler()
 
 
744
        # console.setLevel(logging.DEBUG)
 
 
745
        console.setFormatter(logging.Formatter\
 
 
746
                             ('%(levelname)s: %(message)s'))
 
 
747
        logger.addHandler(console)
 
823
751
    def remove_from_clients(client):
 
824
752
        clients.remove(client)
 
826
 
            logger.critical(u"No clients left, exiting")
 
 
754
            logger.debug(u"No clients left, exiting")
 
829
 
    clients.update(Set(Client(name = section,
 
 
757
    clients.update(Set(Client(name=section,
 
830
758
                              stop_hook = remove_from_clients,
 
832
 
                              = dict(client_config.items(section)))
 
 
759
                              **(dict(client_config\
 
833
761
                       for section in client_config.sections()))
 
835
 
        logger.critical(u"No clients defined")
 
839
 
        # Redirect stdin so all checkers get /dev/null
 
840
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
841
 
        os.dup2(null, sys.stdin.fileno())
 
846
 
        logger.removeHandler(console)
 
847
 
        # Close all input and output, do double fork, etc.
 
852
 
        pidfile.write(str(pid) + "\n")
 
856
 
        logger.error(u"Could not write to file %r with PID %d",
 
859
 
        # "pidfile" was never created
 
864
767
        "Cleanup function; run on exit"
 
 
884
787
    for client in clients:
 
888
 
    tcp_server.server_activate()
 
 
790
    tcp_server = IPv6_TCPServer((server_settings["address"],
 
 
791
                                 server_settings["port"]),
 
 
793
                                settings=server_settings,
 
890
795
    # Find out what port we got
 
891
796
    service.port = tcp_server.socket.getsockname()[1]
 
892
 
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
 
893
 
                u" scope_id %d" % tcp_server.socket.getsockname())
 
 
797
    logger.debug(u"Now listening on port %d", service.port)
 
895
 
    #service.interface = tcp_server.socket.getsockname()[3]
 
 
799
    if not server_settings["interface"]:
 
 
800
        service.interface = if_nametoindex\
 
 
801
                            (server_settings["interface"])
 
898
804
        # From the Avahi example code