/mandos/trunk

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

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Teddy Hogeborn
  • Date: 2008-11-01 02:26:00 UTC
  • Revision ID: teddy@fukt.bsnet.se-20081101022600-bacs9fxmsliqrah1
* initramfs-tools-hook: Also ignore plugins named "*.dpkg-bak".
* plugin-runner.c: - '' -

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
# Contact the authors at <mandos@fukt.bsnet.se>.
31
31
32
32
 
33
 
from __future__ import division, with_statement, absolute_import
 
33
from __future__ import division
34
34
 
35
35
import SocketServer
36
36
import socket
55
55
import logging
56
56
import logging.handlers
57
57
import pwd
58
 
from contextlib import closing
59
58
 
60
59
import dbus
61
 
import dbus.service
62
60
import gobject
63
61
import avahi
64
62
from dbus.mainloop.glib import DBusGMainLoop
172
170
# End of Avahi example code
173
171
 
174
172
 
175
 
class Client(dbus.service.Object):
 
173
class Client(object):
176
174
    """A representation of a client host served by this server.
177
175
    Attributes:
178
176
    name:      string; from the config file, used in log messages
181
179
    secret:    bytestring; sent verbatim (over TLS) to client
182
180
    host:      string; available for use by the checker command
183
181
    created:   datetime.datetime(); object creation, not client host
184
 
    started:   bool()
185
182
    last_checked_ok: datetime.datetime() or None if not yet checked OK
186
183
    timeout:   datetime.timedelta(); How long from last_checked_ok
187
184
                                     until this client is invalid
203
200
    _timeout_milliseconds: Used when calling gobject.timeout_add()
204
201
    _interval_milliseconds: - '' -
205
202
    """
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
 
    
224
203
    def _set_timeout(self, timeout):
225
 
        "Setter function for the 'timeout' attribute"
 
204
        "Setter function for 'timeout' attribute"
226
205
        self._timeout = timeout
227
206
        self._timeout_milliseconds = ((self.timeout.days
228
207
                                       * 24 * 60 * 60 * 1000)
229
208
                                      + (self.timeout.seconds * 1000)
230
209
                                      + (self.timeout.microseconds
231
210
                                         // 1000))
232
 
        # Emit D-Bus signal
233
 
        self.TimeoutChanged(self._timeout_milliseconds)
234
 
    timeout = property(lambda self: self._timeout, _set_timeout)
 
211
    timeout = property(lambda self: self._timeout,
 
212
                       _set_timeout)
235
213
    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
 
    
247
214
    def _set_interval(self, interval):
248
 
        "Setter function for the 'interval' attribute"
 
215
        "Setter function for 'interval' attribute"
249
216
        self._interval = interval
250
217
        self._interval_milliseconds = ((self.interval.days
251
218
                                        * 24 * 60 * 60 * 1000)
253
220
                                          * 1000)
254
221
                                       + (self.interval.microseconds
255
222
                                          // 1000))
256
 
        # Emit D-Bus signal
257
 
        self.IntervalChanged(self._interval_milliseconds)
258
 
    interval = property(lambda self: self._interval, _set_interval)
 
223
    interval = property(lambda self: self._interval,
 
224
                        _set_interval)
259
225
    del _set_interval
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
226
    def __init__(self, name = None, stop_hook=None, config=None):
272
227
        """Note: the 'checker' key in 'config' sets the
273
228
        'checker_command' attribute and *not* the 'checker'
274
229
        attribute."""
275
 
        dbus.service.Object.__init__(self, bus,
276
 
                                     "/Mandos/Clients/%s"
277
 
                                     % name.replace(".", "_"))
278
230
        if config is None:
279
231
            config = {}
280
232
        self.name = name
288
240
        if "secret" in config:
289
241
            self.secret = config["secret"].decode(u"base64")
290
242
        elif "secfile" in config:
291
 
            with closing(open(os.path.expanduser
292
 
                              (os.path.expandvars
293
 
                               (config["secfile"])))) \
294
 
                               as secfile:
295
 
                self.secret = secfile.read()
 
243
            secfile = open(os.path.expanduser(os.path.expandvars
 
244
                                              (config["secfile"])))
 
245
            self.secret = secfile.read()
 
246
            secfile.close()
296
247
        else:
297
248
            raise TypeError(u"No secret or secfile for client %s"
298
249
                            % self.name)
299
250
        self.host = config.get("host", "")
300
251
        self.created = datetime.datetime.now()
301
 
        self.started = False
302
252
        self.last_checked_ok = None
303
253
        self.timeout = string_to_delta(config["timeout"])
304
254
        self.interval = string_to_delta(config["interval"])
308
258
        self.stop_initiator_tag = None
309
259
        self.checker_callback_tag = None
310
260
        self.check_command = config["checker"]
311
 
    
312
261
    def start(self):
313
262
        """Start this client's checker and timeout hooks"""
314
 
        self.started = True
315
263
        # Schedule a new checker to be started an 'interval' from now,
316
264
        # and every interval from then on.
317
265
        self.checker_initiator_tag = gobject.timeout_add\
323
271
        self.stop_initiator_tag = gobject.timeout_add\
324
272
                                  (self._timeout_milliseconds,
325
273
                                   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
 
    
334
274
    def stop(self):
335
 
        """Stop this client."""
336
 
        if getattr(self, "started", False):
 
275
        """Stop this client.
 
276
        The possibility that a client might be restarted is left open,
 
277
        but not currently used."""
 
278
        # If this client doesn't have a secret, it is already stopped.
 
279
        if hasattr(self, "secret") and self.secret:
337
280
            logger.info(u"Stopping client %s", self.name)
 
281
            self.secret = None
338
282
        else:
339
283
            return False
340
284
        if getattr(self, "stop_initiator_tag", False):
346
290
        self.stop_checker()
347
291
        if self.stop_hook:
348
292
            self.stop_hook(self)
349
 
        # Emit D-Bus signal
350
 
        self.StateChanged(False)
351
293
        # Do not run this again if called by a gobject.timeout_add
352
294
        return False
353
 
    # D-Bus variant
354
 
    Stop = dbus.service.method(interface)(stop)
355
 
    
356
295
    def __del__(self):
357
296
        self.stop_hook = None
358
297
        self.stop()
359
 
    
360
298
    def checker_callback(self, pid, condition):
361
299
        """The checker has completed, so take appropriate actions."""
 
300
        now = datetime.datetime.now()
362
301
        self.checker_callback_tag = None
363
302
        self.checker = None
364
303
        if os.WIFEXITED(condition) \
365
304
               and (os.WEXITSTATUS(condition) == 0):
366
305
            logger.info(u"Checker for %(name)s succeeded",
367
306
                        vars(self))
368
 
            # Emit D-Bus signal
369
 
            self.CheckerCompleted(True)
370
 
            self.bump_timeout()
 
307
            self.last_checked_ok = now
 
308
            gobject.source_remove(self.stop_initiator_tag)
 
309
            self.stop_initiator_tag = gobject.timeout_add\
 
310
                                      (self._timeout_milliseconds,
 
311
                                       self.stop)
371
312
        elif not os.WIFEXITED(condition):
372
313
            logger.warning(u"Checker for %(name)s crashed?",
373
314
                           vars(self))
374
 
            # Emit D-Bus signal
375
 
            self.CheckerCompleted(False)
376
315
        else:
377
316
            logger.info(u"Checker for %(name)s failed",
378
317
                        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
 
    
399
318
    def start_checker(self):
400
319
        """Start a new checker subprocess if one is not running.
401
320
        If a checker already exists, leave it running and do
436
355
                self.checker_callback_tag = gobject.child_watch_add\
437
356
                                            (self.checker.pid,
438
357
                                             self.checker_callback)
439
 
                # Emit D-Bus signal
440
 
                self.CheckerStarted(command)
441
358
            except OSError, error:
442
359
                logger.error(u"Failed to start subprocess: %s",
443
360
                             error)
444
361
        # Re-run this periodically if run by gobject.timeout_add
445
362
        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
 
    
456
363
    def stop_checker(self):
457
364
        """Force the checker process, if any, to stop."""
458
365
        if self.checker_callback_tag:
470
377
            if error.errno != errno.ESRCH: # No such process
471
378
                raise
472
379
        self.checker = None
473
 
    # D-Bus variant
474
 
    StopChecker = dbus.service.method(interface)(stop_checker)
475
 
    
476
380
    def still_valid(self):
477
381
        """Has the timeout not yet passed for this client?"""
478
 
        if not self.started:
479
 
            return False
480
382
        now = datetime.datetime.now()
481
383
        if self.last_checked_ok is None:
482
384
            return now < (self.created + self.timeout)
483
385
        else:
484
386
            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
490
387
 
491
388
 
492
389
def peer_certificate(session):
550
447
    
551
448
    def handle(self):
552
449
        logger.info(u"TCP connection from: %s",
553
 
                    unicode(self.client_address))
 
450
                     unicode(self.client_address))
554
451
        session = gnutls.connection.ClientSession\
555
452
                  (self.request, gnutls.connection.X509Credentials())
556
453
        
571
468
        #priority = ':'.join(("NONE", "+VERS-TLS1.1", "+AES-256-CBC",
572
469
        #                "+SHA1", "+COMP-NULL", "+CTYPE-OPENPGP",
573
470
        #                "+DHE-DSS"))
574
 
        # Use a fallback default, since this MUST be set.
575
 
        priority = self.server.settings.get("priority", "NORMAL")
 
471
        priority = "NORMAL"             # Fallback default, since this
 
472
                                        # MUST be set.
 
473
        if self.server.settings["priority"]:
 
474
            priority = self.server.settings["priority"]
576
475
        gnutls.library.functions.gnutls_priority_set_direct\
577
476
            (session._c_object, priority, None)
578
477
        
608
507
                           vars(client))
609
508
            session.bye()
610
509
            return
611
 
        ## This won't work here, since we're in a fork.
612
 
        # client.bump_timeout()
613
510
        sent_size = 0
614
511
        while sent_size < len(client.secret):
615
512
            sent = session.send(client.secret[sent_size:])
620
517
        session.bye()
621
518
 
622
519
 
623
 
class IPv6_TCPServer(SocketServer.ForkingMixIn,
624
 
                     SocketServer.TCPServer, object):
 
520
class IPv6_TCPServer(SocketServer.ForkingTCPServer, object):
625
521
    """IPv6 TCP server.  Accepts 'None' as address and/or port.
626
522
    Attributes:
627
523
        settings:       Server settings
756
652
        def if_nametoindex(interface):
757
653
            "Get an interface index the hard way, i.e. using fcntl()"
758
654
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
759
 
            with closing(socket.socket()) as s:
760
 
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
761
 
                                    struct.pack("16s16x", interface))
 
655
            s = socket.socket()
 
656
            ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
 
657
                                struct.pack("16s16x", interface))
 
658
            s.close()
762
659
            interface_index = struct.unpack("I", ifreq[16:20])[0]
763
660
            return interface_index
764
661
    return if_nametoindex(interface)