/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

* README: Update copyright year; add "2009".
* debian/copyright: - '' -
* mandos: - '' -
* mandos-clients.conf.xml: - '' -
* mandos-keygen: - '' -
* mandos-keygen.xml: - '' -
* mandos.conf.xml: - '' -
* mandos.xml: - '' -
* plugin-runner.c: - '' -
* plugin-runner.xml: - '' -
* plugins.d/askpass-fifo.c: - '' -
* plugins.d/askpass-fifo.xml: - '' -
* plugins.d/mandos-client.c: - '' -
* plugins.d/mandos-client.xml: - '' -
* plugins.d/password-prompt.c: - '' -
* plugins.d/password-prompt.xml: - '' -
* plugins.d/splashy.c: - '' -
* plugins.d/splashy.xml: - '' -
* plugins.d/usplash.c: - '' -
* plugins.d/usplash.xml: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
35
35
 
36
36
import SocketServer
37
37
import socket
38
 
import optparse
 
38
from optparse import OptionParser
39
39
import datetime
40
40
import errno
41
41
import gnutls.crypto
66
66
import ctypes
67
67
import ctypes.util
68
68
 
69
 
try:
70
 
    SO_BINDTODEVICE = socket.SO_BINDTODEVICE
71
 
except AttributeError:
72
 
    try:
73
 
        from IN import SO_BINDTODEVICE
74
 
    except ImportError:
75
 
        # From /usr/include/asm/socket.h
76
 
        SO_BINDTODEVICE = 25
77
 
 
78
 
 
79
 
version = "1.0.8"
 
69
version = "1.0.2"
80
70
 
81
71
logger = logging.Logger('mandos')
82
72
syslogger = (logging.handlers.SysLogHandler
83
73
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
84
74
              address = "/dev/log"))
85
75
syslogger.setFormatter(logging.Formatter
86
 
                       ('Mandos [%(process)d]: %(levelname)s:'
87
 
                        ' %(message)s'))
 
76
                       ('Mandos: %(levelname)s: %(message)s'))
88
77
logger.addHandler(syslogger)
89
78
 
90
79
console = logging.StreamHandler()
91
 
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
92
 
                                       ' %(levelname)s: %(message)s'))
 
80
console.setFormatter(logging.Formatter('%(name)s: %(levelname)s:'
 
81
                                       ' %(message)s'))
93
82
logger.addHandler(console)
94
83
 
95
84
class AvahiError(Exception):
108
97
 
109
98
class AvahiService(object):
110
99
    """An Avahi (Zeroconf) service.
111
 
    
112
100
    Attributes:
113
101
    interface: integer; avahi.IF_UNSPEC or an interface index.
114
102
               Used to optionally bind to the specified interface.
125
113
    """
126
114
    def __init__(self, interface = avahi.IF_UNSPEC, name = None,
127
115
                 servicetype = None, port = None, TXT = None,
128
 
                 domain = "", host = "", max_renames = 32768,
129
 
                 protocol = avahi.PROTO_UNSPEC):
 
116
                 domain = "", host = "", max_renames = 32768):
130
117
        self.interface = interface
131
118
        self.name = name
132
119
        self.type = servicetype
136
123
        self.host = host
137
124
        self.rename_count = 0
138
125
        self.max_renames = max_renames
139
 
        self.protocol = protocol
140
126
    def rename(self):
141
127
        """Derived from the Avahi example code"""
142
128
        if self.rename_count >= self.max_renames:
148
134
        logger.info(u"Changing Zeroconf service name to %r ...",
149
135
                    str(self.name))
150
136
        syslogger.setFormatter(logging.Formatter
151
 
                               ('Mandos (%s) [%%(process)d]:'
152
 
                                ' %%(levelname)s: %%(message)s'
153
 
                                % self.name))
 
137
                               ('Mandos (%s): %%(levelname)s:'
 
138
                                ' %%(message)s' % self.name))
154
139
        self.remove()
155
140
        self.add()
156
141
        self.rename_count += 1
172
157
                     service.name, service.type)
173
158
        group.AddService(
174
159
                self.interface,         # interface
175
 
                self.protocol,          # protocol
 
160
                avahi.PROTO_INET6,      # protocol
176
161
                dbus.UInt32(0),         # flags
177
162
                self.name, self.type,
178
163
                self.domain, self.host,
190
175
    return dbus.String(dt.isoformat(), variant_level=variant_level)
191
176
 
192
177
 
193
 
class Client(object):
 
178
class Client(dbus.service.Object):
194
179
    """A representation of a client host served by this server.
195
 
    
196
180
    Attributes:
197
 
    name:       string; from the config file, used in log messages and
198
 
                        D-Bus identifiers
 
181
    name:       string; from the config file, used in log messages
199
182
    fingerprint: string (40 or 32 hexadecimal digits); used to
200
183
                 uniquely identify the client
201
184
    secret:     bytestring; sent verbatim (over TLS) to client
218
201
                     client lives.  %() expansions are done at
219
202
                     runtime with vars(self) as dict, so that for
220
203
                     instance %(name)s can be used in the command.
221
 
    current_checker_command: string; current running checker_command
 
204
    use_dbus: bool(); Whether to provide D-Bus interface and signals
 
205
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
222
206
    """
223
207
    def timeout_milliseconds(self):
224
208
        "Return the 'timeout' attribute in milliseconds"
232
216
                + (self.interval.seconds * 1000)
233
217
                + (self.interval.microseconds // 1000))
234
218
    
235
 
    def __init__(self, name = None, disable_hook=None, config=None):
 
219
    def __init__(self, name = None, disable_hook=None, config=None,
 
220
                 use_dbus=True):
236
221
        """Note: the 'checker' key in 'config' sets the
237
222
        'checker_command' attribute and *not* the 'checker'
238
223
        attribute."""
240
225
        if config is None:
241
226
            config = {}
242
227
        logger.debug(u"Creating client %r", self.name)
 
228
        self.use_dbus = use_dbus
 
229
        if self.use_dbus:
 
230
            self.dbus_object_path = (dbus.ObjectPath
 
231
                                     ("/Mandos/clients/"
 
232
                                      + self.name.replace(".", "_")))
 
233
            dbus.service.Object.__init__(self, bus,
 
234
                                         self.dbus_object_path)
243
235
        # Uppercase and remove spaces from fingerprint for later
244
236
        # comparison purposes with return value from the fingerprint()
245
237
        # function
269
261
        self.disable_initiator_tag = None
270
262
        self.checker_callback_tag = None
271
263
        self.checker_command = config["checker"]
272
 
        self.current_checker_command = None
273
 
        self.last_connect = None
274
264
    
275
265
    def enable(self):
276
266
        """Start this client's checker and timeout hooks"""
287
277
                                   (self.timeout_milliseconds(),
288
278
                                    self.disable))
289
279
        self.enabled = True
 
280
        if self.use_dbus:
 
281
            # Emit D-Bus signals
 
282
            self.PropertyChanged(dbus.String(u"enabled"),
 
283
                                 dbus.Boolean(True, variant_level=1))
 
284
            self.PropertyChanged(dbus.String(u"last_enabled"),
 
285
                                 (_datetime_to_dbus(self.last_enabled,
 
286
                                                    variant_level=1)))
290
287
    
291
288
    def disable(self):
292
289
        """Disable this client."""
303
300
        if self.disable_hook:
304
301
            self.disable_hook(self)
305
302
        self.enabled = False
 
303
        if self.use_dbus:
 
304
            # Emit D-Bus signal
 
305
            self.PropertyChanged(dbus.String(u"enabled"),
 
306
                                 dbus.Boolean(False, variant_level=1))
306
307
        # Do not run this again if called by a gobject.timeout_add
307
308
        return False
308
309
    
314
315
        """The checker has completed, so take appropriate actions."""
315
316
        self.checker_callback_tag = None
316
317
        self.checker = None
317
 
        if os.WIFEXITED(condition):
318
 
            exitstatus = os.WEXITSTATUS(condition)
319
 
            if exitstatus == 0:
320
 
                logger.info(u"Checker for %(name)s succeeded",
321
 
                            vars(self))
322
 
                self.checked_ok()
323
 
            else:
324
 
                logger.info(u"Checker for %(name)s failed",
325
 
                            vars(self))
326
 
        else:
 
318
        if self.use_dbus:
 
319
            # Emit D-Bus signal
 
320
            self.PropertyChanged(dbus.String(u"checker_running"),
 
321
                                 dbus.Boolean(False, variant_level=1))
 
322
        if (os.WIFEXITED(condition)
 
323
            and (os.WEXITSTATUS(condition) == 0)):
 
324
            logger.info(u"Checker for %(name)s succeeded",
 
325
                        vars(self))
 
326
            if self.use_dbus:
 
327
                # Emit D-Bus signal
 
328
                self.CheckerCompleted(dbus.Boolean(True),
 
329
                                      dbus.UInt16(condition),
 
330
                                      dbus.String(command))
 
331
            self.bump_timeout()
 
332
        elif not os.WIFEXITED(condition):
327
333
            logger.warning(u"Checker for %(name)s crashed?",
328
334
                           vars(self))
 
335
            if self.use_dbus:
 
336
                # Emit D-Bus signal
 
337
                self.CheckerCompleted(dbus.Boolean(False),
 
338
                                      dbus.UInt16(condition),
 
339
                                      dbus.String(command))
 
340
        else:
 
341
            logger.info(u"Checker for %(name)s failed",
 
342
                        vars(self))
 
343
            if self.use_dbus:
 
344
                # Emit D-Bus signal
 
345
                self.CheckerCompleted(dbus.Boolean(False),
 
346
                                      dbus.UInt16(condition),
 
347
                                      dbus.String(command))
329
348
    
330
 
    def checked_ok(self):
 
349
    def bump_timeout(self):
331
350
        """Bump up the timeout for this client.
332
 
        
333
351
        This should only be called when the client has been seen,
334
352
        alive and well.
335
353
        """
338
356
        self.disable_initiator_tag = (gobject.timeout_add
339
357
                                      (self.timeout_milliseconds(),
340
358
                                       self.disable))
 
359
        if self.use_dbus:
 
360
            # Emit D-Bus signal
 
361
            self.PropertyChanged(
 
362
                dbus.String(u"last_checked_ok"),
 
363
                (_datetime_to_dbus(self.last_checked_ok,
 
364
                                   variant_level=1)))
341
365
    
342
366
    def start_checker(self):
343
367
        """Start a new checker subprocess if one is not running.
344
 
        
345
368
        If a checker already exists, leave it running and do
346
369
        nothing."""
347
370
        # The reason for not killing a running checker is that if we
352
375
        # checkers alone, the checker would have to take more time
353
376
        # than 'timeout' for the client to be declared invalid, which
354
377
        # is as it should be.
355
 
        
356
 
        # If a checker exists, make sure it is not a zombie
357
 
        if self.checker is not None:
358
 
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
359
 
            if pid:
360
 
                logger.warning("Checker was a zombie")
361
 
                gobject.source_remove(self.checker_callback_tag)
362
 
                self.checker_callback(pid, status,
363
 
                                      self.current_checker_command)
364
 
        # Start a new checker if needed
365
378
        if self.checker is None:
366
379
            try:
367
380
                # In case checker_command has exactly one % operator
377
390
                    logger.error(u'Could not format string "%s":'
378
391
                                 u' %s', self.checker_command, error)
379
392
                    return True # Try again later
380
 
            self.current_checker_command = command
381
393
            try:
382
394
                logger.info(u"Starting checker %r for %s",
383
395
                            command, self.name)
388
400
                self.checker = subprocess.Popen(command,
389
401
                                                close_fds=True,
390
402
                                                shell=True, cwd="/")
 
403
                if self.use_dbus:
 
404
                    # Emit D-Bus signal
 
405
                    self.CheckerStarted(command)
 
406
                    self.PropertyChanged(
 
407
                        dbus.String("checker_running"),
 
408
                        dbus.Boolean(True, variant_level=1))
391
409
                self.checker_callback_tag = (gobject.child_watch_add
392
410
                                             (self.checker.pid,
393
411
                                              self.checker_callback,
394
412
                                              data=command))
395
 
                # The checker may have completed before the gobject
396
 
                # watch was added.  Check for this.
397
 
                pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
398
 
                if pid:
399
 
                    gobject.source_remove(self.checker_callback_tag)
400
 
                    self.checker_callback(pid, status, command)
401
413
            except OSError, error:
402
414
                logger.error(u"Failed to start subprocess: %s",
403
415
                             error)
421
433
            if error.errno != errno.ESRCH: # No such process
422
434
                raise
423
435
        self.checker = None
 
436
        if self.use_dbus:
 
437
            self.PropertyChanged(dbus.String(u"checker_running"),
 
438
                                 dbus.Boolean(False, variant_level=1))
424
439
    
425
440
    def still_valid(self):
426
441
        """Has the timeout not yet passed for this client?"""
431
446
            return now < (self.created + self.timeout)
432
447
        else:
433
448
            return now < (self.last_checked_ok + self.timeout)
434
 
 
435
 
 
436
 
class ClientDBus(Client, dbus.service.Object):
437
 
    """A Client class using D-Bus
438
 
    
439
 
    Attributes:
440
 
    dbus_object_path: dbus.ObjectPath ; only set if self.use_dbus
441
 
    """
442
 
    # dbus.service.Object doesn't use super(), so we can't either.
443
 
    
444
 
    def __init__(self, *args, **kwargs):
445
 
        Client.__init__(self, *args, **kwargs)
446
 
        # Only now, when this client is initialized, can it show up on
447
 
        # the D-Bus
448
 
        self.dbus_object_path = (dbus.ObjectPath
449
 
                                 ("/clients/"
450
 
                                  + self.name.replace(".", "_")))
451
 
        dbus.service.Object.__init__(self, bus,
452
 
                                     self.dbus_object_path)
453
 
    def enable(self):
454
 
        oldstate = getattr(self, "enabled", False)
455
 
        r = Client.enable(self)
456
 
        if oldstate != self.enabled:
457
 
            # Emit D-Bus signals
458
 
            self.PropertyChanged(dbus.String(u"enabled"),
459
 
                                 dbus.Boolean(True, variant_level=1))
460
 
            self.PropertyChanged(dbus.String(u"last_enabled"),
461
 
                                 (_datetime_to_dbus(self.last_enabled,
462
 
                                                    variant_level=1)))
463
 
        return r
464
 
    
465
 
    def disable(self, signal = True):
466
 
        oldstate = getattr(self, "enabled", False)
467
 
        r = Client.disable(self)
468
 
        if signal and oldstate != self.enabled:
469
 
            # Emit D-Bus signal
470
 
            self.PropertyChanged(dbus.String(u"enabled"),
471
 
                                 dbus.Boolean(False, variant_level=1))
472
 
        return r
473
 
    
474
 
    def __del__(self, *args, **kwargs):
475
 
        try:
476
 
            self.remove_from_connection()
477
 
        except LookupError:
478
 
            pass
479
 
        if hasattr(dbus.service.Object, "__del__"):
480
 
            dbus.service.Object.__del__(self, *args, **kwargs)
481
 
        Client.__del__(self, *args, **kwargs)
482
 
    
483
 
    def checker_callback(self, pid, condition, command,
484
 
                         *args, **kwargs):
485
 
        self.checker_callback_tag = None
486
 
        self.checker = None
487
 
        # Emit D-Bus signal
488
 
        self.PropertyChanged(dbus.String(u"checker_running"),
489
 
                             dbus.Boolean(False, variant_level=1))
490
 
        if os.WIFEXITED(condition):
491
 
            exitstatus = os.WEXITSTATUS(condition)
492
 
            # Emit D-Bus signal
493
 
            self.CheckerCompleted(dbus.Int16(exitstatus),
494
 
                                  dbus.Int64(condition),
495
 
                                  dbus.String(command))
496
 
        else:
497
 
            # Emit D-Bus signal
498
 
            self.CheckerCompleted(dbus.Int16(-1),
499
 
                                  dbus.Int64(condition),
500
 
                                  dbus.String(command))
501
 
        
502
 
        return Client.checker_callback(self, pid, condition, command,
503
 
                                       *args, **kwargs)
504
 
    
505
 
    def checked_ok(self, *args, **kwargs):
506
 
        r = Client.checked_ok(self, *args, **kwargs)
507
 
        # Emit D-Bus signal
508
 
        self.PropertyChanged(
509
 
            dbus.String(u"last_checked_ok"),
510
 
            (_datetime_to_dbus(self.last_checked_ok,
511
 
                               variant_level=1)))
512
 
        return r
513
 
    
514
 
    def start_checker(self, *args, **kwargs):
515
 
        old_checker = self.checker
516
 
        if self.checker is not None:
517
 
            old_checker_pid = self.checker.pid
518
 
        else:
519
 
            old_checker_pid = None
520
 
        r = Client.start_checker(self, *args, **kwargs)
521
 
        # Only if new checker process was started
522
 
        if (self.checker is not None
523
 
            and old_checker_pid != self.checker.pid):
524
 
            # Emit D-Bus signal
525
 
            self.CheckerStarted(self.current_checker_command)
526
 
            self.PropertyChanged(
527
 
                dbus.String("checker_running"),
528
 
                dbus.Boolean(True, variant_level=1))
529
 
        return r
530
 
    
531
 
    def stop_checker(self, *args, **kwargs):
532
 
        old_checker = getattr(self, "checker", None)
533
 
        r = Client.stop_checker(self, *args, **kwargs)
534
 
        if (old_checker is not None
535
 
            and getattr(self, "checker", None) is None):
536
 
            self.PropertyChanged(dbus.String(u"checker_running"),
537
 
                                 dbus.Boolean(False, variant_level=1))
538
 
        return r
539
449
    
540
450
    ## D-Bus methods & signals
541
 
    _interface = u"se.bsnet.fukt.Mandos.Client"
 
451
    _interface = u"org.mandos_system.Mandos.Client"
542
452
    
543
 
    # CheckedOK - method
544
 
    CheckedOK = dbus.service.method(_interface)(checked_ok)
545
 
    CheckedOK.__name__ = "CheckedOK"
 
453
    # BumpTimeout - method
 
454
    BumpTimeout = dbus.service.method(_interface)(bump_timeout)
 
455
    BumpTimeout.__name__ = "BumpTimeout"
546
456
    
547
457
    # CheckerCompleted - signal
548
 
    @dbus.service.signal(_interface, signature="nxs")
549
 
    def CheckerCompleted(self, exitcode, waitstatus, command):
 
458
    @dbus.service.signal(_interface, signature="bqs")
 
459
    def CheckerCompleted(self, success, condition, command):
550
460
        "D-Bus signal"
551
461
        pass
552
462
    
593
503
                dbus.String("checker_running"):
594
504
                    dbus.Boolean(self.checker is not None,
595
505
                                 variant_level=1),
596
 
                dbus.String("object_path"):
597
 
                    dbus.ObjectPath(self.dbus_object_path,
598
 
                                    variant_level=1)
599
506
                }, signature="sv")
600
507
    
601
508
    # IsStillValid - method
602
 
    @dbus.service.method(_interface, out_signature="b")
603
 
    def IsStillValid(self):
604
 
        return self.still_valid()
 
509
    IsStillValid = (dbus.service.method(_interface, out_signature="b")
 
510
                    (still_valid))
 
511
    IsStillValid.__name__ = "IsStillValid"
605
512
    
606
513
    # PropertyChanged - signal
607
514
    @dbus.service.signal(_interface, signature="sv")
609
516
        "D-Bus signal"
610
517
        pass
611
518
    
612
 
    # ReceivedSecret - signal
613
 
    @dbus.service.signal(_interface)
614
 
    def ReceivedSecret(self):
615
 
        "D-Bus signal"
616
 
        pass
617
 
    
618
 
    # Rejected - signal
619
 
    @dbus.service.signal(_interface)
620
 
    def Rejected(self):
621
 
        "D-Bus signal"
622
 
        pass
623
 
    
624
519
    # SetChecker - method
625
520
    @dbus.service.method(_interface, in_signature="s")
626
521
    def SetChecker(self, checker):
688
583
    del _interface
689
584
 
690
585
 
691
 
class ClientHandler(SocketServer.BaseRequestHandler, object):
692
 
    """A class to handle client connections.
693
 
    
694
 
    Instantiated once for each connection to handle it.
 
586
def peer_certificate(session):
 
587
    "Return the peer's OpenPGP certificate as a bytestring"
 
588
    # If not an OpenPGP certificate...
 
589
    if (gnutls.library.functions
 
590
        .gnutls_certificate_type_get(session._c_object)
 
591
        != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
 
592
        # ...do the normal thing
 
593
        return session.peer_certificate
 
594
    list_size = ctypes.c_uint()
 
595
    cert_list = (gnutls.library.functions
 
596
                 .gnutls_certificate_get_peers
 
597
                 (session._c_object, ctypes.byref(list_size)))
 
598
    if list_size.value == 0:
 
599
        return None
 
600
    cert = cert_list[0]
 
601
    return ctypes.string_at(cert.data, cert.size)
 
602
 
 
603
 
 
604
def fingerprint(openpgp):
 
605
    "Convert an OpenPGP bytestring to a hexdigit fingerprint string"
 
606
    # New GnuTLS "datum" with the OpenPGP public key
 
607
    datum = (gnutls.library.types
 
608
             .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
 
609
                                         ctypes.POINTER
 
610
                                         (ctypes.c_ubyte)),
 
611
                             ctypes.c_uint(len(openpgp))))
 
612
    # New empty GnuTLS certificate
 
613
    crt = gnutls.library.types.gnutls_openpgp_crt_t()
 
614
    (gnutls.library.functions
 
615
     .gnutls_openpgp_crt_init(ctypes.byref(crt)))
 
616
    # Import the OpenPGP public key into the certificate
 
617
    (gnutls.library.functions
 
618
     .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
 
619
                                gnutls.library.constants
 
620
                                .GNUTLS_OPENPGP_FMT_RAW))
 
621
    # Verify the self signature in the key
 
622
    crtverify = ctypes.c_uint()
 
623
    (gnutls.library.functions
 
624
     .gnutls_openpgp_crt_verify_self(crt, 0, ctypes.byref(crtverify)))
 
625
    if crtverify.value != 0:
 
626
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
627
        raise gnutls.errors.CertificateSecurityError("Verify failed")
 
628
    # New buffer for the fingerprint
 
629
    buf = ctypes.create_string_buffer(20)
 
630
    buf_len = ctypes.c_size_t()
 
631
    # Get the fingerprint from the certificate into the buffer
 
632
    (gnutls.library.functions
 
633
     .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
 
634
                                         ctypes.byref(buf_len)))
 
635
    # Deinit the certificate
 
636
    gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
 
637
    # Convert the buffer to a Python bytestring
 
638
    fpr = ctypes.string_at(buf, buf_len.value)
 
639
    # Convert the bytestring to hexadecimal notation
 
640
    hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
 
641
    return hex_fpr
 
642
 
 
643
 
 
644
class TCP_handler(SocketServer.BaseRequestHandler, object):
 
645
    """A TCP request handler class.
 
646
    Instantiated by IPv6_TCPServer for each request to handle it.
695
647
    Note: This will run in its own forked process."""
696
648
    
697
649
    def handle(self):
698
650
        logger.info(u"TCP connection from: %s",
699
651
                    unicode(self.client_address))
700
 
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
701
 
        # Open IPC pipe to parent process
702
 
        with closing(os.fdopen(self.server.pipe[1], "w", 1)) as ipc:
703
 
            session = (gnutls.connection
704
 
                       .ClientSession(self.request,
705
 
                                      gnutls.connection
706
 
                                      .X509Credentials()))
707
 
            
708
 
            line = self.request.makefile().readline()
709
 
            logger.debug(u"Protocol version: %r", line)
710
 
            try:
711
 
                if int(line.strip().split()[0]) > 1:
712
 
                    raise RuntimeError
713
 
            except (ValueError, IndexError, RuntimeError), error:
714
 
                logger.error(u"Unknown protocol version: %s", error)
715
 
                return
716
 
            
717
 
            # Note: gnutls.connection.X509Credentials is really a
718
 
            # generic GnuTLS certificate credentials object so long as
719
 
            # no X.509 keys are added to it.  Therefore, we can use it
720
 
            # here despite using OpenPGP certificates.
721
 
            
722
 
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
723
 
            #                     "+AES-256-CBC", "+SHA1",
724
 
            #                     "+COMP-NULL", "+CTYPE-OPENPGP",
725
 
            #                     "+DHE-DSS"))
726
 
            # Use a fallback default, since this MUST be set.
727
 
            priority = self.server.gnutls_priority
728
 
            if priority is None:
729
 
                priority = "NORMAL"
730
 
            (gnutls.library.functions
731
 
             .gnutls_priority_set_direct(session._c_object,
732
 
                                         priority, None))
733
 
            
734
 
            try:
735
 
                session.handshake()
736
 
            except gnutls.errors.GNUTLSError, error:
737
 
                logger.warning(u"Handshake failed: %s", error)
738
 
                # Do not run session.bye() here: the session is not
739
 
                # established.  Just abandon the request.
740
 
                return
741
 
            logger.debug(u"Handshake succeeded")
742
 
            try:
743
 
                fpr = self.fingerprint(self.peer_certificate(session))
744
 
            except (TypeError, gnutls.errors.GNUTLSError), error:
745
 
                logger.warning(u"Bad certificate: %s", error)
746
 
                session.bye()
747
 
                return
748
 
            logger.debug(u"Fingerprint: %s", fpr)
749
 
            
750
 
            for c in self.server.clients:
751
 
                if c.fingerprint == fpr:
752
 
                    client = c
753
 
                    break
754
 
            else:
755
 
                ipc.write("NOTFOUND %s\n" % fpr)
756
 
                session.bye()
757
 
                return
758
 
            # Have to check if client.still_valid(), since it is
759
 
            # possible that the client timed out while establishing
760
 
            # the GnuTLS session.
761
 
            if not client.still_valid():
762
 
                ipc.write("INVALID %s\n" % client.name)
763
 
                session.bye()
764
 
                return
765
 
            ipc.write("SENDING %s\n" % client.name)
766
 
            sent_size = 0
767
 
            while sent_size < len(client.secret):
768
 
                sent = session.send(client.secret[sent_size:])
769
 
                logger.debug(u"Sent: %d, remaining: %d",
770
 
                             sent, len(client.secret)
771
 
                             - (sent_size + sent))
772
 
                sent_size += sent
773
 
            session.bye()
774
 
    
775
 
    @staticmethod
776
 
    def peer_certificate(session):
777
 
        "Return the peer's OpenPGP certificate as a bytestring"
778
 
        # If not an OpenPGP certificate...
779
 
        if (gnutls.library.functions
780
 
            .gnutls_certificate_type_get(session._c_object)
781
 
            != gnutls.library.constants.GNUTLS_CRT_OPENPGP):
782
 
            # ...do the normal thing
783
 
            return session.peer_certificate
784
 
        list_size = ctypes.c_uint(1)
785
 
        cert_list = (gnutls.library.functions
786
 
                     .gnutls_certificate_get_peers
787
 
                     (session._c_object, ctypes.byref(list_size)))
788
 
        if not bool(cert_list) and list_size.value != 0:
789
 
            raise gnutls.errors.GNUTLSError("error getting peer"
790
 
                                            " certificate")
791
 
        if list_size.value == 0:
792
 
            return None
793
 
        cert = cert_list[0]
794
 
        return ctypes.string_at(cert.data, cert.size)
795
 
    
796
 
    @staticmethod
797
 
    def fingerprint(openpgp):
798
 
        "Convert an OpenPGP bytestring to a hexdigit fingerprint"
799
 
        # New GnuTLS "datum" with the OpenPGP public key
800
 
        datum = (gnutls.library.types
801
 
                 .gnutls_datum_t(ctypes.cast(ctypes.c_char_p(openpgp),
802
 
                                             ctypes.POINTER
803
 
                                             (ctypes.c_ubyte)),
804
 
                                 ctypes.c_uint(len(openpgp))))
805
 
        # New empty GnuTLS certificate
806
 
        crt = gnutls.library.types.gnutls_openpgp_crt_t()
807
 
        (gnutls.library.functions
808
 
         .gnutls_openpgp_crt_init(ctypes.byref(crt)))
809
 
        # Import the OpenPGP public key into the certificate
810
 
        (gnutls.library.functions
811
 
         .gnutls_openpgp_crt_import(crt, ctypes.byref(datum),
812
 
                                    gnutls.library.constants
813
 
                                    .GNUTLS_OPENPGP_FMT_RAW))
814
 
        # Verify the self signature in the key
815
 
        crtverify = ctypes.c_uint()
816
 
        (gnutls.library.functions
817
 
         .gnutls_openpgp_crt_verify_self(crt, 0,
818
 
                                         ctypes.byref(crtverify)))
819
 
        if crtverify.value != 0:
820
 
            gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
821
 
            raise (gnutls.errors.CertificateSecurityError
822
 
                   ("Verify failed"))
823
 
        # New buffer for the fingerprint
824
 
        buf = ctypes.create_string_buffer(20)
825
 
        buf_len = ctypes.c_size_t()
826
 
        # Get the fingerprint from the certificate into the buffer
827
 
        (gnutls.library.functions
828
 
         .gnutls_openpgp_crt_get_fingerprint(crt, ctypes.byref(buf),
829
 
                                             ctypes.byref(buf_len)))
830
 
        # Deinit the certificate
831
 
        gnutls.library.functions.gnutls_openpgp_crt_deinit(crt)
832
 
        # Convert the buffer to a Python bytestring
833
 
        fpr = ctypes.string_at(buf, buf_len.value)
834
 
        # Convert the bytestring to hexadecimal notation
835
 
        hex_fpr = u''.join(u"%02X" % ord(char) for char in fpr)
836
 
        return hex_fpr
837
 
 
838
 
 
839
 
class ForkingMixInWithPipe(SocketServer.ForkingMixIn, object):
840
 
    """Like SocketServer.ForkingMixIn, but also pass a pipe.
841
 
    
842
 
    Assumes a gobject.MainLoop event loop.
843
 
    """
844
 
    def process_request(self, request, client_address):
845
 
        """Overrides and wraps the original process_request().
846
 
        
847
 
        This function creates a new pipe in self.pipe 
848
 
        """
849
 
        self.pipe = os.pipe()
850
 
        super(ForkingMixInWithPipe,
851
 
              self).process_request(request, client_address)
852
 
        os.close(self.pipe[1])  # close write end
853
 
        # Call "handle_ipc" for both data and EOF events
854
 
        gobject.io_add_watch(self.pipe[0],
855
 
                             gobject.IO_IN | gobject.IO_HUP,
856
 
                             self.handle_ipc)
857
 
    def handle_ipc(source, condition):
858
 
        """Dummy function; override as necessary"""
859
 
        os.close(source)
860
 
        return False
861
 
 
862
 
 
863
 
class IPv6_TCPServer(ForkingMixInWithPipe,
 
652
        session = (gnutls.connection
 
653
                   .ClientSession(self.request,
 
654
                                  gnutls.connection
 
655
                                  .X509Credentials()))
 
656
        
 
657
        line = self.request.makefile().readline()
 
658
        logger.debug(u"Protocol version: %r", line)
 
659
        try:
 
660
            if int(line.strip().split()[0]) > 1:
 
661
                raise RuntimeError
 
662
        except (ValueError, IndexError, RuntimeError), error:
 
663
            logger.error(u"Unknown protocol version: %s", error)
 
664
            return
 
665
        
 
666
        # Note: gnutls.connection.X509Credentials is really a generic
 
667
        # GnuTLS certificate credentials object so long as no X.509
 
668
        # keys are added to it.  Therefore, we can use it here despite
 
669
        # using OpenPGP certificates.
 
670
        
 
671
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
 
672
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
 
673
        #                "+DHE-DSS"))
 
674
        # Use a fallback default, since this MUST be set.
 
675
        priority = self.server.settings.get("priority", "NORMAL")
 
676
        (gnutls.library.functions
 
677
         .gnutls_priority_set_direct(session._c_object,
 
678
                                     priority, None))
 
679
        
 
680
        try:
 
681
            session.handshake()
 
682
        except gnutls.errors.GNUTLSError, error:
 
683
            logger.warning(u"Handshake failed: %s", error)
 
684
            # Do not run session.bye() here: the session is not
 
685
            # established.  Just abandon the request.
 
686
            return
 
687
        try:
 
688
            fpr = fingerprint(peer_certificate(session))
 
689
        except (TypeError, gnutls.errors.GNUTLSError), error:
 
690
            logger.warning(u"Bad certificate: %s", error)
 
691
            session.bye()
 
692
            return
 
693
        logger.debug(u"Fingerprint: %s", fpr)
 
694
        for c in self.server.clients:
 
695
            if c.fingerprint == fpr:
 
696
                client = c
 
697
                break
 
698
        else:
 
699
            logger.warning(u"Client not found for fingerprint: %s",
 
700
                           fpr)
 
701
            session.bye()
 
702
            return
 
703
        # Have to check if client.still_valid(), since it is possible
 
704
        # that the client timed out while establishing the GnuTLS
 
705
        # session.
 
706
        if not client.still_valid():
 
707
            logger.warning(u"Client %(name)s is invalid",
 
708
                           vars(client))
 
709
            session.bye()
 
710
            return
 
711
        ## This won't work here, since we're in a fork.
 
712
        # client.bump_timeout()
 
713
        sent_size = 0
 
714
        while sent_size < len(client.secret):
 
715
            sent = session.send(client.secret[sent_size:])
 
716
            logger.debug(u"Sent: %d, remaining: %d",
 
717
                         sent, len(client.secret)
 
718
                         - (sent_size + sent))
 
719
            sent_size += sent
 
720
        session.bye()
 
721
 
 
722
 
 
723
class IPv6_TCPServer(SocketServer.ForkingMixIn,
864
724
                     SocketServer.TCPServer, object):
865
 
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
866
 
    
 
725
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
867
726
    Attributes:
 
727
        settings:       Server settings
 
728
        clients:        Set() of Client objects
868
729
        enabled:        Boolean; whether this server is activated yet
869
 
        interface:      None or a network interface name (string)
870
 
        use_ipv6:       Boolean; to use IPv6 or not
871
 
        ----
872
 
        clients:        Set() of Client objects
873
 
        gnutls_priority GnuTLS priority string
874
 
        use_dbus:       Boolean; to emit D-Bus signals or not
875
730
    """
876
 
    def __init__(self, server_address, RequestHandlerClass,
877
 
                 interface=None, use_ipv6=True, clients=None,
878
 
                 gnutls_priority=None, use_dbus=True):
 
731
    address_family = socket.AF_INET6
 
732
    def __init__(self, *args, **kwargs):
 
733
        if "settings" in kwargs:
 
734
            self.settings = kwargs["settings"]
 
735
            del kwargs["settings"]
 
736
        if "clients" in kwargs:
 
737
            self.clients = kwargs["clients"]
 
738
            del kwargs["clients"]
879
739
        self.enabled = False
880
 
        self.interface = interface
881
 
        if use_ipv6:
882
 
            self.address_family = socket.AF_INET6
883
 
        self.clients = clients
884
 
        self.use_dbus = use_dbus
885
 
        self.gnutls_priority = gnutls_priority
886
 
        SocketServer.TCPServer.__init__(self, server_address,
887
 
                                        RequestHandlerClass)
 
740
        super(IPv6_TCPServer, self).__init__(*args, **kwargs)
888
741
    def server_bind(self):
889
742
        """This overrides the normal server_bind() function
890
743
        to bind to an interface if one was specified, and also NOT to
891
744
        bind to an address or port if they were not specified."""
892
 
        if self.interface is not None:
 
745
        if self.settings["interface"]:
 
746
            # 25 is from /usr/include/asm-i486/socket.h
 
747
            SO_BINDTODEVICE = getattr(socket, "SO_BINDTODEVICE", 25)
893
748
            try:
894
749
                self.socket.setsockopt(socket.SOL_SOCKET,
895
750
                                       SO_BINDTODEVICE,
896
 
                                       self.interface + '\0')
 
751
                                       self.settings["interface"])
897
752
            except socket.error, error:
898
753
                if error[0] == errno.EPERM:
899
754
                    logger.error(u"No permission to"
900
755
                                 u" bind to interface %s",
901
 
                                 self.interface)
 
756
                                 self.settings["interface"])
902
757
                else:
903
 
                    raise
 
758
                    raise error
904
759
        # Only bind(2) the socket if we really need to.
905
760
        if self.server_address[0] or self.server_address[1]:
906
761
            if not self.server_address[0]:
907
 
                if self.address_family == socket.AF_INET6:
908
 
                    any_address = "::" # in6addr_any
909
 
                else:
910
 
                    any_address = socket.INADDR_ANY
911
 
                self.server_address = (any_address,
 
762
                in6addr_any = "::"
 
763
                self.server_address = (in6addr_any,
912
764
                                       self.server_address[1])
913
765
            elif not self.server_address[1]:
914
766
                self.server_address = (self.server_address[0],
915
767
                                       0)
916
 
#                 if self.interface:
 
768
#                 if self.settings["interface"]:
917
769
#                     self.server_address = (self.server_address[0],
918
770
#                                            0, # port
919
771
#                                            0, # flowinfo
920
772
#                                            if_nametoindex
921
 
#                                            (self.interface))
922
 
            return SocketServer.TCPServer.server_bind(self)
 
773
#                                            (self.settings
 
774
#                                             ["interface"]))
 
775
            return super(IPv6_TCPServer, self).server_bind()
923
776
    def server_activate(self):
924
777
        if self.enabled:
925
 
            return SocketServer.TCPServer.server_activate(self)
 
778
            return super(IPv6_TCPServer, self).server_activate()
926
779
    def enable(self):
927
780
        self.enabled = True
928
 
    def handle_ipc(self, source, condition, file_objects={}):
929
 
        condition_names = {
930
 
            gobject.IO_IN: "IN", # There is data to read.
931
 
            gobject.IO_OUT: "OUT", # Data can be written (without
932
 
                                   # blocking).
933
 
            gobject.IO_PRI: "PRI", # There is urgent data to read.
934
 
            gobject.IO_ERR: "ERR", # Error condition.
935
 
            gobject.IO_HUP: "HUP"  # Hung up (the connection has been
936
 
                                   # broken, usually for pipes and
937
 
                                   # sockets).
938
 
            }
939
 
        conditions_string = ' | '.join(name
940
 
                                       for cond, name in
941
 
                                       condition_names.iteritems()
942
 
                                       if cond & condition)
943
 
        logger.debug("Handling IPC: FD = %d, condition = %s", source,
944
 
                     conditions_string)
945
 
        
946
 
        # Turn the pipe file descriptor into a Python file object
947
 
        if source not in file_objects:
948
 
            file_objects[source] = os.fdopen(source, "r", 1)
949
 
        
950
 
        # Read a line from the file object
951
 
        cmdline = file_objects[source].readline()
952
 
        if not cmdline:             # Empty line means end of file
953
 
            # close the IPC pipe
954
 
            file_objects[source].close()
955
 
            del file_objects[source]
956
 
            
957
 
            # Stop calling this function
958
 
            return False
959
 
        
960
 
        logger.debug("IPC command: %r", cmdline)
961
 
        
962
 
        # Parse and act on command
963
 
        cmd, args = cmdline.rstrip("\r\n").split(None, 1)
964
 
        
965
 
        if cmd == "NOTFOUND":
966
 
            logger.warning(u"Client not found for fingerprint: %s",
967
 
                           args)
968
 
            if self.use_dbus:
969
 
                # Emit D-Bus signal
970
 
                mandos_dbus_service.ClientNotFound(args)
971
 
        elif cmd == "INVALID":
972
 
            for client in self.clients:
973
 
                if client.name == args:
974
 
                    logger.warning(u"Client %s is invalid", args)
975
 
                    if self.use_dbus:
976
 
                        # Emit D-Bus signal
977
 
                        client.Rejected()
978
 
                    break
979
 
            else:
980
 
                logger.error(u"Unknown client %s is invalid", args)
981
 
        elif cmd == "SENDING":
982
 
            for client in self.clients:
983
 
                if client.name == args:
984
 
                    logger.info(u"Sending secret to %s", client.name)
985
 
                    client.checked_ok()
986
 
                    if self.use_dbus:
987
 
                        # Emit D-Bus signal
988
 
                        client.ReceivedSecret()
989
 
                    break
990
 
            else:
991
 
                logger.error(u"Sending secret to unknown client %s",
992
 
                             args)
993
 
        else:
994
 
            logger.error("Unknown IPC command: %r", cmdline)
995
 
        
996
 
        # Keep calling this function
997
 
        return True
998
781
 
999
782
 
1000
783
def string_to_delta(interval):
1001
784
    """Parse a string and return a datetime.timedelta
1002
 
    
 
785
 
1003
786
    >>> string_to_delta('7d')
1004
787
    datetime.timedelta(7)
1005
788
    >>> string_to_delta('60s')
1084
867
 
1085
868
def daemon(nochdir = False, noclose = False):
1086
869
    """See daemon(3).  Standard BSD Unix function.
1087
 
    
1088
870
    This should really exist as os.daemon, but it doesn't (yet)."""
1089
871
    if os.fork():
1090
872
        sys.exit()
1107
889
 
1108
890
 
1109
891
def main():
1110
 
    
1111
 
    ######################################################################
1112
 
    # Parsing of options, both command line and config file
1113
 
    
1114
 
    parser = optparse.OptionParser(version = "%%prog %s" % version)
 
892
    parser = OptionParser(version = "%%prog %s" % version)
1115
893
    parser.add_option("-i", "--interface", type="string",
1116
894
                      metavar="IF", help="Bind to interface IF")
1117
895
    parser.add_option("-a", "--address", type="string",
1135
913
                      dest="use_dbus",
1136
914
                      help="Do not provide D-Bus system bus"
1137
915
                      " interface")
1138
 
    parser.add_option("--no-ipv6", action="store_false",
1139
 
                      dest="use_ipv6", help="Do not use IPv6")
1140
916
    options = parser.parse_args()[0]
1141
917
    
1142
918
    if options.check:
1153
929
                        "SECURE256:!CTYPE-X.509:+CTYPE-OPENPGP",
1154
930
                        "servicename": "Mandos",
1155
931
                        "use_dbus": "True",
1156
 
                        "use_ipv6": "True",
1157
932
                        }
1158
933
    
1159
934
    # Parse config file for server-global settings
1162
937
    server_config.read(os.path.join(options.configdir, "mandos.conf"))
1163
938
    # Convert the SafeConfigParser object to a dict
1164
939
    server_settings = server_config.defaults()
1165
 
    # Use the appropriate methods on the non-string config options
1166
 
    server_settings["debug"] = server_config.getboolean("DEFAULT",
1167
 
                                                        "debug")
1168
 
    server_settings["use_dbus"] = server_config.getboolean("DEFAULT",
1169
 
                                                           "use_dbus")
1170
 
    server_settings["use_ipv6"] = server_config.getboolean("DEFAULT",
1171
 
                                                           "use_ipv6")
1172
 
    if server_settings["port"]:
1173
 
        server_settings["port"] = server_config.getint("DEFAULT",
1174
 
                                                       "port")
 
940
    # Use getboolean on the boolean config options
 
941
    server_settings["debug"] = (server_config.getboolean
 
942
                                ("DEFAULT", "debug"))
 
943
    server_settings["use_dbus"] = (server_config.getboolean
 
944
                                   ("DEFAULT", "use_dbus"))
1175
945
    del server_config
1176
946
    
1177
947
    # Override the settings from the config file with command line
1178
948
    # options, if set.
1179
949
    for option in ("interface", "address", "port", "debug",
1180
950
                   "priority", "servicename", "configdir",
1181
 
                   "use_dbus", "use_ipv6"):
 
951
                   "use_dbus"):
1182
952
        value = getattr(options, option)
1183
953
        if value is not None:
1184
954
            server_settings[option] = value
1185
955
    del options
1186
956
    # Now we have our good server settings in "server_settings"
1187
957
    
1188
 
    ##################################################################
1189
 
    
1190
958
    # For convenience
1191
959
    debug = server_settings["debug"]
1192
960
    use_dbus = server_settings["use_dbus"]
1193
 
    use_ipv6 = server_settings["use_ipv6"]
1194
961
    
1195
962
    if not debug:
1196
963
        syslogger.setLevel(logging.WARNING)
1198
965
    
1199
966
    if server_settings["servicename"] != "Mandos":
1200
967
        syslogger.setFormatter(logging.Formatter
1201
 
                               ('Mandos (%s) [%%(process)d]:'
1202
 
                                ' %%(levelname)s: %%(message)s'
 
968
                               ('Mandos (%s): %%(levelname)s:'
 
969
                                ' %%(message)s'
1203
970
                                % server_settings["servicename"]))
1204
971
    
1205
972
    # Parse config file with clients
1206
973
    client_defaults = { "timeout": "1h",
1207
974
                        "interval": "5m",
1208
 
                        "checker": "fping -q -- %%(host)s",
 
975
                        "checker": "fping -q -- %(host)s",
1209
976
                        "host": "",
1210
977
                        }
1211
978
    client_config = ConfigParser.SafeConfigParser(client_defaults)
1212
979
    client_config.read(os.path.join(server_settings["configdir"],
1213
980
                                    "clients.conf"))
1214
 
 
1215
 
    global mandos_dbus_service
1216
 
    mandos_dbus_service = None
1217
981
    
1218
982
    clients = Set()
1219
983
    tcp_server = IPv6_TCPServer((server_settings["address"],
1220
984
                                 server_settings["port"]),
1221
 
                                ClientHandler,
1222
 
                                interface=
1223
 
                                server_settings["interface"],
1224
 
                                use_ipv6=use_ipv6,
1225
 
                                clients=clients,
1226
 
                                gnutls_priority=
1227
 
                                server_settings["priority"],
1228
 
                                use_dbus=use_dbus)
 
985
                                TCP_handler,
 
986
                                settings=server_settings,
 
987
                                clients=clients)
1229
988
    pidfilename = "/var/run/mandos.pid"
1230
989
    try:
1231
990
        pidfile = open(pidfilename, "w")
1232
 
    except IOError:
 
991
    except IOError, error:
1233
992
        logger.error("Could not open file %r", pidfilename)
1234
993
    
1235
994
    try:
1236
995
        uid = pwd.getpwnam("_mandos").pw_uid
 
996
    except KeyError:
 
997
        try:
 
998
            uid = pwd.getpwnam("mandos").pw_uid
 
999
        except KeyError:
 
1000
            try:
 
1001
                uid = pwd.getpwnam("nobody").pw_uid
 
1002
            except KeyError:
 
1003
                uid = 65534
 
1004
    try:
1237
1005
        gid = pwd.getpwnam("_mandos").pw_gid
1238
1006
    except KeyError:
1239
1007
        try:
1240
 
            uid = pwd.getpwnam("mandos").pw_uid
1241
1008
            gid = pwd.getpwnam("mandos").pw_gid
1242
1009
        except KeyError:
1243
1010
            try:
1244
 
                uid = pwd.getpwnam("nobody").pw_uid
1245
1011
                gid = pwd.getpwnam("nogroup").pw_gid
1246
1012
            except KeyError:
1247
 
                uid = 65534
1248
1013
                gid = 65534
1249
1014
    try:
 
1015
        os.setuid(uid)
1250
1016
        os.setgid(gid)
1251
 
        os.setuid(uid)
1252
1017
    except OSError, error:
1253
1018
        if error[0] != errno.EPERM:
1254
1019
            raise error
1255
1020
    
1256
 
    # Enable all possible GnuTLS debugging
1257
 
    if debug:
1258
 
        # "Use a log level over 10 to enable all debugging options."
1259
 
        # - GnuTLS manual
1260
 
        gnutls.library.functions.gnutls_global_set_log_level(11)
1261
 
        
1262
 
        @gnutls.library.types.gnutls_log_func
1263
 
        def debug_gnutls(level, string):
1264
 
            logger.debug("GnuTLS: %s", string[:-1])
1265
 
        
1266
 
        (gnutls.library.functions
1267
 
         .gnutls_global_set_log_function(debug_gnutls))
1268
 
    
1269
1021
    global service
1270
 
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
1271
1022
    service = AvahiService(name = server_settings["servicename"],
1272
 
                           servicetype = "_mandos._tcp",
1273
 
                           protocol = protocol)
 
1023
                           servicetype = "_mandos._tcp", )
1274
1024
    if server_settings["interface"]:
1275
1025
        service.interface = (if_nametoindex
1276
1026
                             (server_settings["interface"]))
1287
1037
                            avahi.DBUS_INTERFACE_SERVER)
1288
1038
    # End of Avahi example code
1289
1039
    if use_dbus:
1290
 
        bus_name = dbus.service.BusName(u"se.bsnet.fukt.Mandos", bus)
 
1040
        bus_name = dbus.service.BusName(u"org.mandos-system.Mandos",
 
1041
                                        bus)
1291
1042
    
1292
 
    client_class = Client
1293
 
    if use_dbus:
1294
 
        client_class = ClientDBus
1295
 
    clients.update(Set(
1296
 
            client_class(name = section,
1297
 
                         config= dict(client_config.items(section)))
1298
 
            for section in client_config.sections()))
 
1043
    clients.update(Set(Client(name = section,
 
1044
                              config
 
1045
                              = dict(client_config.items(section)),
 
1046
                              use_dbus = use_dbus)
 
1047
                       for section in client_config.sections()))
1299
1048
    if not clients:
1300
1049
        logger.warning(u"No clients defined")
1301
1050
    
1312
1061
        daemon()
1313
1062
    
1314
1063
    try:
1315
 
        with closing(pidfile):
1316
 
            pid = os.getpid()
1317
 
            pidfile.write(str(pid) + "\n")
 
1064
        pid = os.getpid()
 
1065
        pidfile.write(str(pid) + "\n")
 
1066
        pidfile.close()
1318
1067
        del pidfile
1319
1068
    except IOError:
1320
1069
        logger.error(u"Could not write to file %r with PID %d",
1346
1095
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1347
1096
    
1348
1097
    if use_dbus:
1349
 
        class MandosDBusService(dbus.service.Object):
 
1098
        class MandosServer(dbus.service.Object):
1350
1099
            """A D-Bus proxy object"""
1351
1100
            def __init__(self):
1352
 
                dbus.service.Object.__init__(self, bus, "/")
1353
 
            _interface = u"se.bsnet.fukt.Mandos"
1354
 
            
 
1101
                dbus.service.Object.__init__(self, bus,
 
1102
                                             "/Mandos")
 
1103
            _interface = u"org.mandos_system.Mandos"
 
1104
 
1355
1105
            @dbus.service.signal(_interface, signature="oa{sv}")
1356
1106
            def ClientAdded(self, objpath, properties):
1357
1107
                "D-Bus signal"
1358
1108
                pass
1359
 
            
1360
 
            @dbus.service.signal(_interface, signature="s")
1361
 
            def ClientNotFound(self, fingerprint):
1362
 
                "D-Bus signal"
1363
 
                pass
1364
 
            
1365
 
            @dbus.service.signal(_interface, signature="os")
1366
 
            def ClientRemoved(self, objpath, name):
1367
 
                "D-Bus signal"
1368
 
                pass
1369
 
            
 
1109
 
 
1110
            @dbus.service.signal(_interface, signature="o")
 
1111
            def ClientRemoved(self, objpath):
 
1112
                "D-Bus signal"
 
1113
                pass
 
1114
 
1370
1115
            @dbus.service.method(_interface, out_signature="ao")
1371
1116
            def GetAllClients(self):
1372
 
                "D-Bus method"
1373
1117
                return dbus.Array(c.dbus_object_path for c in clients)
1374
 
            
 
1118
 
1375
1119
            @dbus.service.method(_interface, out_signature="a{oa{sv}}")
1376
1120
            def GetAllClientsWithProperties(self):
1377
 
                "D-Bus method"
1378
1121
                return dbus.Dictionary(
1379
1122
                    ((c.dbus_object_path, c.GetAllProperties())
1380
1123
                     for c in clients),
1381
1124
                    signature="oa{sv}")
1382
 
            
 
1125
 
1383
1126
            @dbus.service.method(_interface, in_signature="o")
1384
1127
            def RemoveClient(self, object_path):
1385
 
                "D-Bus method"
1386
1128
                for c in clients:
1387
1129
                    if c.dbus_object_path == object_path:
1388
1130
                        clients.remove(c)
1389
 
                        c.remove_from_connection()
1390
1131
                        # Don't signal anything except ClientRemoved
1391
 
                        c.disable(signal=False)
 
1132
                        c.use_dbus = False
 
1133
                        c.disable()
1392
1134
                        # Emit D-Bus signal
1393
 
                        self.ClientRemoved(object_path, c.name)
 
1135
                        self.ClientRemoved(object_path)
1394
1136
                        return
1395
1137
                raise KeyError
1396
 
            
 
1138
            @dbus.service.method(_interface)
 
1139
            def Quit(self):
 
1140
                main_loop.quit()
 
1141
 
1397
1142
            del _interface
1398
 
        
1399
 
        mandos_dbus_service = MandosDBusService()
 
1143
    
 
1144
        mandos_server = MandosServer()
1400
1145
    
1401
1146
    for client in clients:
1402
1147
        if use_dbus:
1403
1148
            # Emit D-Bus signal
1404
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1405
 
                                            client.GetAllProperties())
 
1149
            mandos_server.ClientAdded(client.dbus_object_path,
 
1150
                                      client.GetAllProperties())
1406
1151
        client.enable()
1407
1152
    
1408
1153
    tcp_server.enable()
1410
1155
    
1411
1156
    # Find out what port we got
1412
1157
    service.port = tcp_server.socket.getsockname()[1]
1413
 
    if use_ipv6:
1414
 
        logger.info(u"Now listening on address %r, port %d,"
1415
 
                    " flowinfo %d, scope_id %d"
1416
 
                    % tcp_server.socket.getsockname())
1417
 
    else:                       # IPv4
1418
 
        logger.info(u"Now listening on address %r, port %d"
1419
 
                    % tcp_server.socket.getsockname())
 
1158
    logger.info(u"Now listening on address %r, port %d, flowinfo %d,"
 
1159
                u" scope_id %d" % tcp_server.socket.getsockname())
1420
1160
    
1421
1161
    #service.interface = tcp_server.socket.getsockname()[3]
1422
1162
    
1442
1182
        sys.exit(1)
1443
1183
    except KeyboardInterrupt:
1444
1184
        if debug:
1445
 
            print >> sys.stderr
1446
 
        logger.debug("Server received KeyboardInterrupt")
1447
 
    logger.debug("Server exiting")
 
1185
            print
1448
1186
 
1449
1187
if __name__ == '__main__':
1450
1188
    main()