/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 at bsnet
  • Date: 2011-02-15 19:53:46 UTC
  • mto: This revision was merged to the branch mainline in revision 465.
  • Revision ID: teddy@fukt.bsnet.se-20110215195346-6eps2j007o8xcr0e
* mandos-ctl: Use print function.

Show diffs side-by-side

added added

removed removed

Lines of Context:
11
11
# "AvahiService" class, and some lines in "main".
12
12
13
13
# Everything else is
14
 
# Copyright © 2008-2011 Teddy Hogeborn
15
 
# Copyright © 2008-2011 Björn Påhlsson
 
14
# Copyright © 2008-2010 Teddy Hogeborn
 
15
# Copyright © 2008-2010 Björn Påhlsson
16
16
17
17
# This program is free software: you can redistribute it and/or modify
18
18
# it under the terms of the GNU General Public License as published by
28
28
# along with this program.  If not, see
29
29
# <http://www.gnu.org/licenses/>.
30
30
31
 
# Contact the authors at <mandos@recompile.se>.
 
31
# Contact the authors at <mandos@fukt.bsnet.se>.
32
32
33
33
 
34
34
from __future__ import (division, absolute_import, print_function,
36
36
 
37
37
import SocketServer as socketserver
38
38
import socket
39
 
import argparse
 
39
import optparse
40
40
import datetime
41
41
import errno
42
42
import gnutls.crypto
62
62
import functools
63
63
import cPickle as pickle
64
64
import multiprocessing
65
 
import types
66
65
 
67
66
import dbus
68
67
import dbus.service
83
82
        SO_BINDTODEVICE = None
84
83
 
85
84
 
86
 
version = "1.4.1"
 
85
version = "1.2.3"
87
86
 
88
 
logger = logging.getLogger()
 
87
#logger = logging.getLogger('mandos')
 
88
logger = logging.Logger('mandos')
89
89
syslogger = (logging.handlers.SysLogHandler
90
90
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
91
91
              address = str("/dev/log")))
95
95
logger.addHandler(syslogger)
96
96
 
97
97
console = logging.StreamHandler()
98
 
console.setFormatter(logging.Formatter('%(asctime)s %(name)s'
99
 
                                       ' [%(process)d]:'
 
98
console.setFormatter(logging.Formatter('%(name)s [%(process)d]:'
100
99
                                       ' %(levelname)s:'
101
100
                                       ' %(message)s'))
102
101
logger.addHandler(console)
103
102
 
104
 
 
105
103
class AvahiError(Exception):
106
104
    def __init__(self, value, *args, **kwargs):
107
105
        self.value = value
153
151
        self.group = None       # our entry group
154
152
        self.server = None
155
153
        self.bus = bus
156
 
        self.entry_group_state_changed_match = None
157
154
    def rename(self):
158
155
        """Derived from the Avahi example code"""
159
156
        if self.rename_count >= self.max_renames:
161
158
                            " after %i retries, exiting.",
162
159
                            self.rename_count)
163
160
            raise AvahiServiceError("Too many renames")
164
 
        self.name = unicode(self.server
165
 
                            .GetAlternativeServiceName(self.name))
 
161
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
166
162
        logger.info("Changing Zeroconf service name to %r ...",
167
163
                    self.name)
 
164
        syslogger.setFormatter(logging.Formatter
 
165
                               ('Mandos (%s) [%%(process)d]:'
 
166
                                ' %%(levelname)s: %%(message)s'
 
167
                                % self.name))
168
168
        self.remove()
169
169
        try:
170
170
            self.add()
171
 
        except dbus.exceptions.DBusException as error:
 
171
        except dbus.exceptions.DBusException, error:
172
172
            logger.critical("DBusException: %s", error)
173
173
            self.cleanup()
174
174
            os._exit(1)
175
175
        self.rename_count += 1
176
176
    def remove(self):
177
177
        """Derived from the Avahi example code"""
178
 
        if self.entry_group_state_changed_match is not None:
179
 
            self.entry_group_state_changed_match.remove()
180
 
            self.entry_group_state_changed_match = None
181
178
        if self.group is not None:
182
179
            self.group.Reset()
183
180
    def add(self):
184
181
        """Derived from the Avahi example code"""
185
 
        self.remove()
186
182
        if self.group is None:
187
183
            self.group = dbus.Interface(
188
184
                self.bus.get_object(avahi.DBUS_NAME,
189
185
                                    self.server.EntryGroupNew()),
190
186
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
191
 
        self.entry_group_state_changed_match = (
192
 
            self.group.connect_to_signal(
193
 
                'StateChanged', self.entry_group_state_changed))
 
187
            self.group.connect_to_signal('StateChanged',
 
188
                                         self
 
189
                                         .entry_group_state_changed)
194
190
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
195
191
                     self.name, self.type)
196
192
        self.group.AddService(
219
215
    def cleanup(self):
220
216
        """Derived from the Avahi example code"""
221
217
        if self.group is not None:
222
 
            try:
223
 
                self.group.Free()
224
 
            except (dbus.exceptions.UnknownMethodException,
225
 
                    dbus.exceptions.DBusException) as e:
226
 
                pass
 
218
            self.group.Free()
227
219
            self.group = None
228
 
        self.remove()
229
 
    def server_state_changed(self, state, error=None):
 
220
    def server_state_changed(self, state):
230
221
        """Derived from the Avahi example code"""
231
222
        logger.debug("Avahi server state change: %i", state)
232
 
        bad_states = { avahi.SERVER_INVALID:
233
 
                           "Zeroconf server invalid",
234
 
                       avahi.SERVER_REGISTERING: None,
235
 
                       avahi.SERVER_COLLISION:
236
 
                           "Zeroconf server name collision",
237
 
                       avahi.SERVER_FAILURE:
238
 
                           "Zeroconf server failure" }
239
 
        if state in bad_states:
240
 
            if bad_states[state] is not None:
241
 
                if error is None:
242
 
                    logger.error(bad_states[state])
243
 
                else:
244
 
                    logger.error(bad_states[state] + ": %r", error)
245
 
            self.cleanup()
 
223
        if state == avahi.SERVER_COLLISION:
 
224
            logger.error("Zeroconf server name collision")
 
225
            self.remove()
246
226
        elif state == avahi.SERVER_RUNNING:
247
227
            self.add()
248
 
        else:
249
 
            if error is None:
250
 
                logger.debug("Unknown state: %r", state)
251
 
            else:
252
 
                logger.debug("Unknown state: %r: %r", state, error)
253
228
    def activate(self):
254
229
        """Derived from the Avahi example code"""
255
230
        if self.server is None:
256
231
            self.server = dbus.Interface(
257
232
                self.bus.get_object(avahi.DBUS_NAME,
258
 
                                    avahi.DBUS_PATH_SERVER,
259
 
                                    follow_name_owner_changes=True),
 
233
                                    avahi.DBUS_PATH_SERVER),
260
234
                avahi.DBUS_INTERFACE_SERVER)
261
235
        self.server.connect_to_signal("StateChanged",
262
236
                                 self.server_state_changed)
263
237
        self.server_state_changed(self.server.GetState())
264
238
 
265
 
class AvahiServiceToSyslog(AvahiService):
266
 
    def rename(self):
267
 
        """Add the new name to the syslog messages"""
268
 
        ret = AvahiService.rename(self)
269
 
        syslogger.setFormatter(logging.Formatter
270
 
                               ('Mandos (%s) [%%(process)d]:'
271
 
                                ' %%(levelname)s: %%(message)s'
272
 
                                % self.name))
273
 
        return ret
274
239
 
275
 
def _timedelta_to_milliseconds(td):
276
 
    "Convert a datetime.timedelta() to milliseconds"
277
 
    return ((td.days * 24 * 60 * 60 * 1000)
278
 
            + (td.seconds * 1000)
279
 
            + (td.microseconds // 1000))
280
 
        
281
240
class Client(object):
282
241
    """A representation of a client host served by this server.
283
242
    
311
270
    secret:     bytestring; sent verbatim (over TLS) to client
312
271
    timeout:    datetime.timedelta(); How long from last_checked_ok
313
272
                                      until this client is disabled
314
 
    extended_timeout:   extra long timeout when password has been sent
315
273
    runtime_expansions: Allowed attributes for runtime expansion.
316
 
    expires:    datetime.datetime(); time (UTC) when a client will be
317
 
                disabled, or None
318
274
    """
319
275
    
320
276
    runtime_expansions = ("approval_delay", "approval_duration",
322
278
                          "host", "interval", "last_checked_ok",
323
279
                          "last_enabled", "name", "timeout")
324
280
    
 
281
    @staticmethod
 
282
    def _timedelta_to_milliseconds(td):
 
283
        "Convert a datetime.timedelta() to milliseconds"
 
284
        return ((td.days * 24 * 60 * 60 * 1000)
 
285
                + (td.seconds * 1000)
 
286
                + (td.microseconds // 1000))
 
287
    
325
288
    def timeout_milliseconds(self):
326
289
        "Return the 'timeout' attribute in milliseconds"
327
 
        return _timedelta_to_milliseconds(self.timeout)
328
 
    
329
 
    def extended_timeout_milliseconds(self):
330
 
        "Return the 'extended_timeout' attribute in milliseconds"
331
 
        return _timedelta_to_milliseconds(self.extended_timeout)
 
290
        return self._timedelta_to_milliseconds(self.timeout)
332
291
    
333
292
    def interval_milliseconds(self):
334
293
        "Return the 'interval' attribute in milliseconds"
335
 
        return _timedelta_to_milliseconds(self.interval)
336
 
    
 
294
        return self._timedelta_to_milliseconds(self.interval)
 
295
 
337
296
    def approval_delay_milliseconds(self):
338
 
        return _timedelta_to_milliseconds(self.approval_delay)
 
297
        return self._timedelta_to_milliseconds(self.approval_delay)
339
298
    
340
299
    def __init__(self, name = None, disable_hook=None, config=None):
341
300
        """Note: the 'checker' key in 'config' sets the
368
327
        self.last_enabled = None
369
328
        self.last_checked_ok = None
370
329
        self.timeout = string_to_delta(config["timeout"])
371
 
        self.extended_timeout = string_to_delta(config
372
 
                                                ["extended_timeout"])
373
330
        self.interval = string_to_delta(config["interval"])
374
331
        self.disable_hook = disable_hook
375
332
        self.checker = None
376
333
        self.checker_initiator_tag = None
377
334
        self.disable_initiator_tag = None
378
 
        self.expires = None
379
335
        self.checker_callback_tag = None
380
336
        self.checker_command = config["checker"]
381
337
        self.current_checker_command = None
388
344
            config["approval_delay"])
389
345
        self.approval_duration = string_to_delta(
390
346
            config["approval_duration"])
391
 
        self.changedstate = (multiprocessing_manager
392
 
                             .Condition(multiprocessing_manager
393
 
                                        .Lock()))
 
347
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
394
348
    
395
349
    def send_changedstate(self):
396
350
        self.changedstate.acquire()
397
351
        self.changedstate.notify_all()
398
352
        self.changedstate.release()
399
 
    
 
353
        
400
354
    def enable(self):
401
355
        """Start this client's checker and timeout hooks"""
402
356
        if getattr(self, "enabled", False):
403
357
            # Already enabled
404
358
            return
405
359
        self.send_changedstate()
 
360
        self.last_enabled = datetime.datetime.utcnow()
406
361
        # Schedule a new checker to be started an 'interval' from now,
407
362
        # and every interval from then on.
408
363
        self.checker_initiator_tag = (gobject.timeout_add
409
364
                                      (self.interval_milliseconds(),
410
365
                                       self.start_checker))
411
366
        # Schedule a disable() when 'timeout' has passed
412
 
        self.expires = datetime.datetime.utcnow() + self.timeout
413
367
        self.disable_initiator_tag = (gobject.timeout_add
414
368
                                   (self.timeout_milliseconds(),
415
369
                                    self.disable))
416
370
        self.enabled = True
417
 
        self.last_enabled = datetime.datetime.utcnow()
418
371
        # Also start a new checker *right now*.
419
372
        self.start_checker()
420
373
    
429
382
        if getattr(self, "disable_initiator_tag", False):
430
383
            gobject.source_remove(self.disable_initiator_tag)
431
384
            self.disable_initiator_tag = None
432
 
        self.expires = None
433
385
        if getattr(self, "checker_initiator_tag", False):
434
386
            gobject.source_remove(self.checker_initiator_tag)
435
387
            self.checker_initiator_tag = None
461
413
            logger.warning("Checker for %(name)s crashed?",
462
414
                           vars(self))
463
415
    
464
 
    def checked_ok(self, timeout=None):
 
416
    def checked_ok(self):
465
417
        """Bump up the timeout for this client.
466
418
        
467
419
        This should only be called when the client has been seen,
468
420
        alive and well.
469
421
        """
470
 
        if timeout is None:
471
 
            timeout = self.timeout
472
422
        self.last_checked_ok = datetime.datetime.utcnow()
473
 
        if self.disable_initiator_tag is not None:
474
 
            gobject.source_remove(self.disable_initiator_tag)
475
 
        if getattr(self, "enabled", False):
476
 
            self.disable_initiator_tag = (gobject.timeout_add
477
 
                                          (_timedelta_to_milliseconds
478
 
                                           (timeout), self.disable))
479
 
            self.expires = datetime.datetime.utcnow() + timeout
 
423
        gobject.source_remove(self.disable_initiator_tag)
 
424
        self.disable_initiator_tag = (gobject.timeout_add
 
425
                                      (self.timeout_milliseconds(),
 
426
                                       self.disable))
480
427
    
481
428
    def need_approval(self):
482
429
        self.last_approval_request = datetime.datetime.utcnow()
498
445
        # If a checker exists, make sure it is not a zombie
499
446
        try:
500
447
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
501
 
        except (AttributeError, OSError) as error:
 
448
        except (AttributeError, OSError), error:
502
449
            if (isinstance(error, OSError)
503
450
                and error.errno != errno.ECHILD):
504
451
                raise error
522
469
                                       'replace')))
523
470
                    for attr in
524
471
                    self.runtime_expansions)
525
 
                
 
472
 
526
473
                try:
527
474
                    command = self.checker_command % escaped_attrs
528
 
                except TypeError as error:
 
475
                except TypeError, error:
529
476
                    logger.error('Could not format string "%s":'
530
477
                                 ' %s', self.checker_command, error)
531
478
                    return True # Try again later
550
497
                if pid:
551
498
                    gobject.source_remove(self.checker_callback_tag)
552
499
                    self.checker_callback(pid, status, command)
553
 
            except OSError as error:
 
500
            except OSError, error:
554
501
                logger.error("Failed to start subprocess: %s",
555
502
                             error)
556
503
        # Re-run this periodically if run by gobject.timeout_add
569
516
            #time.sleep(0.5)
570
517
            #if self.checker.poll() is None:
571
518
            #    os.kill(self.checker.pid, signal.SIGKILL)
572
 
        except OSError as error:
 
519
        except OSError, error:
573
520
            if error.errno != errno.ESRCH: # No such process
574
521
                raise
575
522
        self.checker = None
576
523
 
577
 
 
578
524
def dbus_service_property(dbus_interface, signature="v",
579
525
                          access="readwrite", byte_arrays=False):
580
526
    """Decorators for marking methods of a DBusObjectWithProperties to
626
572
 
627
573
class DBusObjectWithProperties(dbus.service.Object):
628
574
    """A D-Bus object with properties.
629
 
    
 
575
 
630
576
    Classes inheriting from this can use the dbus_service_property
631
577
    decorator to expose methods as D-Bus properties.  It exposes the
632
578
    standard Get(), Set(), and GetAll() methods on the D-Bus.
639
585
    def _get_all_dbus_properties(self):
640
586
        """Returns a generator of (name, attribute) pairs
641
587
        """
642
 
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
643
 
                for cls in self.__class__.__mro__
 
588
        return ((prop._dbus_name, prop)
644
589
                for name, prop in
645
 
                inspect.getmembers(cls, self._is_dbus_property))
 
590
                inspect.getmembers(self, self._is_dbus_property))
646
591
    
647
592
    def _get_dbus_property(self, interface_name, property_name):
648
593
        """Returns a bound method if one exists which is a D-Bus
649
594
        property with the specified name and interface.
650
595
        """
651
 
        for cls in  self.__class__.__mro__:
652
 
            for name, value in (inspect.getmembers
653
 
                                (cls, self._is_dbus_property)):
654
 
                if (value._dbus_name == property_name
655
 
                    and value._dbus_interface == interface_name):
656
 
                    return value.__get__(self)
657
 
        
 
596
        for name in (property_name,
 
597
                     property_name + "_dbus_property"):
 
598
            prop = getattr(self, name, None)
 
599
            if (prop is None
 
600
                or not self._is_dbus_property(prop)
 
601
                or prop._dbus_name != property_name
 
602
                or (interface_name and prop._dbus_interface
 
603
                    and interface_name != prop._dbus_interface)):
 
604
                continue
 
605
            return prop
658
606
        # No such property
659
607
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
660
608
                                   + interface_name + "."
694
642
    def GetAll(self, interface_name):
695
643
        """Standard D-Bus property GetAll() method, see D-Bus
696
644
        standard.
697
 
        
 
645
 
698
646
        Note: Will not include properties with access="write".
699
647
        """
700
648
        all = {}
756
704
            xmlstring = document.toxml("utf-8")
757
705
            document.unlink()
758
706
        except (AttributeError, xml.dom.DOMException,
759
 
                xml.parsers.expat.ExpatError) as error:
 
707
                xml.parsers.expat.ExpatError), error:
760
708
            logger.error("Failed to override Introspection method",
761
709
                         error)
762
710
        return xmlstring
763
711
 
764
712
 
765
 
def datetime_to_dbus (dt, variant_level=0):
766
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
767
 
    if dt is None:
768
 
        return dbus.String("", variant_level = variant_level)
769
 
    return dbus.String(dt.isoformat(),
770
 
                       variant_level=variant_level)
771
 
 
772
 
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
773
 
                                  .__metaclass__):
774
 
    """Applied to an empty subclass of a D-Bus object, this metaclass
775
 
    will add additional D-Bus attributes matching a certain pattern.
776
 
    """
777
 
    def __new__(mcs, name, bases, attr):
778
 
        # Go through all the base classes which could have D-Bus
779
 
        # methods, signals, or properties in them
780
 
        for base in (b for b in bases
781
 
                     if issubclass(b, dbus.service.Object)):
782
 
            # Go though all attributes of the base class
783
 
            for attrname, attribute in inspect.getmembers(base):
784
 
                # Ignore non-D-Bus attributes, and D-Bus attributes
785
 
                # with the wrong interface name
786
 
                if (not hasattr(attribute, "_dbus_interface")
787
 
                    or not attribute._dbus_interface
788
 
                    .startswith("se.recompile.Mandos")):
789
 
                    continue
790
 
                # Create an alternate D-Bus interface name based on
791
 
                # the current name
792
 
                alt_interface = (attribute._dbus_interface
793
 
                                 .replace("se.recompile.Mandos",
794
 
                                          "se.bsnet.fukt.Mandos"))
795
 
                # Is this a D-Bus signal?
796
 
                if getattr(attribute, "_dbus_is_signal", False):
797
 
                    # Extract the original non-method function by
798
 
                    # black magic
799
 
                    nonmethod_func = (dict(
800
 
                            zip(attribute.func_code.co_freevars,
801
 
                                attribute.__closure__))["func"]
802
 
                                      .cell_contents)
803
 
                    # Create a new, but exactly alike, function
804
 
                    # object, and decorate it to be a new D-Bus signal
805
 
                    # with the alternate D-Bus interface name
806
 
                    new_function = (dbus.service.signal
807
 
                                    (alt_interface,
808
 
                                     attribute._dbus_signature)
809
 
                                    (types.FunctionType(
810
 
                                nonmethod_func.func_code,
811
 
                                nonmethod_func.func_globals,
812
 
                                nonmethod_func.func_name,
813
 
                                nonmethod_func.func_defaults,
814
 
                                nonmethod_func.func_closure)))
815
 
                    # Define a creator of a function to call both the
816
 
                    # old and new functions, so both the old and new
817
 
                    # signals gets sent when the function is called
818
 
                    def fixscope(func1, func2):
819
 
                        """This function is a scope container to pass
820
 
                        func1 and func2 to the "call_both" function
821
 
                        outside of its arguments"""
822
 
                        def call_both(*args, **kwargs):
823
 
                            """This function will emit two D-Bus
824
 
                            signals by calling func1 and func2"""
825
 
                            func1(*args, **kwargs)
826
 
                            func2(*args, **kwargs)
827
 
                        return call_both
828
 
                    # Create the "call_both" function and add it to
829
 
                    # the class
830
 
                    attr[attrname] = fixscope(attribute,
831
 
                                              new_function)
832
 
                # Is this a D-Bus method?
833
 
                elif getattr(attribute, "_dbus_is_method", False):
834
 
                    # Create a new, but exactly alike, function
835
 
                    # object.  Decorate it to be a new D-Bus method
836
 
                    # with the alternate D-Bus interface name.  Add it
837
 
                    # to the class.
838
 
                    attr[attrname] = (dbus.service.method
839
 
                                      (alt_interface,
840
 
                                       attribute._dbus_in_signature,
841
 
                                       attribute._dbus_out_signature)
842
 
                                      (types.FunctionType
843
 
                                       (attribute.func_code,
844
 
                                        attribute.func_globals,
845
 
                                        attribute.func_name,
846
 
                                        attribute.func_defaults,
847
 
                                        attribute.func_closure)))
848
 
                # Is this a D-Bus property?
849
 
                elif getattr(attribute, "_dbus_is_property", False):
850
 
                    # Create a new, but exactly alike, function
851
 
                    # object, and decorate it to be a new D-Bus
852
 
                    # property with the alternate D-Bus interface
853
 
                    # name.  Add it to the class.
854
 
                    attr[attrname] = (dbus_service_property
855
 
                                      (alt_interface,
856
 
                                       attribute._dbus_signature,
857
 
                                       attribute._dbus_access,
858
 
                                       attribute
859
 
                                       ._dbus_get_args_options
860
 
                                       ["byte_arrays"])
861
 
                                      (types.FunctionType
862
 
                                       (attribute.func_code,
863
 
                                        attribute.func_globals,
864
 
                                        attribute.func_name,
865
 
                                        attribute.func_defaults,
866
 
                                        attribute.func_closure)))
867
 
        return type.__new__(mcs, name, bases, attr)
868
 
 
869
713
class ClientDBus(Client, DBusObjectWithProperties):
870
714
    """A Client class using D-Bus
871
715
    
893
737
        DBusObjectWithProperties.__init__(self, self.bus,
894
738
                                          self.dbus_object_path)
895
739
        
896
 
    def notifychangeproperty(transform_func,
897
 
                             dbus_name, type_func=lambda x: x,
898
 
                             variant_level=1):
899
 
        """ Modify a variable so that it's a property which announces
900
 
        its changes to DBus.
 
740
    def _get_approvals_pending(self):
 
741
        return self._approvals_pending
 
742
    def _set_approvals_pending(self, value):
 
743
        old_value = self._approvals_pending
 
744
        self._approvals_pending = value
 
745
        bval = bool(value)
 
746
        if (hasattr(self, "dbus_object_path")
 
747
            and bval is not bool(old_value)):
 
748
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
749
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
750
                                 dbus_bool)
901
751
 
902
 
        transform_fun: Function that takes a value and a variant_level
903
 
                       and transforms it to a D-Bus type.
904
 
        dbus_name: D-Bus name of the variable
905
 
        type_func: Function that transform the value before sending it
906
 
                   to the D-Bus.  Default: no transform
907
 
        variant_level: D-Bus variant level.  Default: 1
908
 
        """
909
 
        attrname = "_{0}".format(dbus_name)
910
 
        def setter(self, value):
911
 
            if hasattr(self, "dbus_object_path"):
912
 
                if (not hasattr(self, attrname) or
913
 
                    type_func(getattr(self, attrname, None))
914
 
                    != type_func(value)):
915
 
                    dbus_value = transform_func(type_func(value),
916
 
                                                variant_level
917
 
                                                =variant_level)
918
 
                    self.PropertyChanged(dbus.String(dbus_name),
919
 
                                         dbus_value)
920
 
            setattr(self, attrname, value)
921
 
        
922
 
        return property(lambda self: getattr(self, attrname), setter)
923
 
    
924
 
    
925
 
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
926
 
    approvals_pending = notifychangeproperty(dbus.Boolean,
927
 
                                             "ApprovalPending",
928
 
                                             type_func = bool)
929
 
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
930
 
    last_enabled = notifychangeproperty(datetime_to_dbus,
931
 
                                        "LastEnabled")
932
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
933
 
                                   type_func = lambda checker:
934
 
                                       checker is not None)
935
 
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
936
 
                                           "LastCheckedOK")
937
 
    last_approval_request = notifychangeproperty(
938
 
        datetime_to_dbus, "LastApprovalRequest")
939
 
    approved_by_default = notifychangeproperty(dbus.Boolean,
940
 
                                               "ApprovedByDefault")
941
 
    approval_delay = notifychangeproperty(dbus.UInt16,
942
 
                                          "ApprovalDelay",
943
 
                                          type_func =
944
 
                                          _timedelta_to_milliseconds)
945
 
    approval_duration = notifychangeproperty(
946
 
        dbus.UInt16, "ApprovalDuration",
947
 
        type_func = _timedelta_to_milliseconds)
948
 
    host = notifychangeproperty(dbus.String, "Host")
949
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
950
 
                                   type_func =
951
 
                                   _timedelta_to_milliseconds)
952
 
    extended_timeout = notifychangeproperty(
953
 
        dbus.UInt16, "ExtendedTimeout",
954
 
        type_func = _timedelta_to_milliseconds)
955
 
    interval = notifychangeproperty(dbus.UInt16,
956
 
                                    "Interval",
957
 
                                    type_func =
958
 
                                    _timedelta_to_milliseconds)
959
 
    checker_command = notifychangeproperty(dbus.String, "Checker")
960
 
    
961
 
    del notifychangeproperty
 
752
    approvals_pending = property(_get_approvals_pending,
 
753
                                 _set_approvals_pending)
 
754
    del _get_approvals_pending, _set_approvals_pending
 
755
    
 
756
    @staticmethod
 
757
    def _datetime_to_dbus(dt, variant_level=0):
 
758
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
759
        return dbus.String(dt.isoformat(),
 
760
                           variant_level=variant_level)
 
761
    
 
762
    def enable(self):
 
763
        oldstate = getattr(self, "enabled", False)
 
764
        r = Client.enable(self)
 
765
        if oldstate != self.enabled:
 
766
            # Emit D-Bus signals
 
767
            self.PropertyChanged(dbus.String("Enabled"),
 
768
                                 dbus.Boolean(True, variant_level=1))
 
769
            self.PropertyChanged(
 
770
                dbus.String("LastEnabled"),
 
771
                self._datetime_to_dbus(self.last_enabled,
 
772
                                       variant_level=1))
 
773
        return r
 
774
    
 
775
    def disable(self, quiet = False):
 
776
        oldstate = getattr(self, "enabled", False)
 
777
        r = Client.disable(self, quiet=quiet)
 
778
        if not quiet and oldstate != self.enabled:
 
779
            # Emit D-Bus signal
 
780
            self.PropertyChanged(dbus.String("Enabled"),
 
781
                                 dbus.Boolean(False, variant_level=1))
 
782
        return r
962
783
    
963
784
    def __del__(self, *args, **kwargs):
964
785
        try:
973
794
                         *args, **kwargs):
974
795
        self.checker_callback_tag = None
975
796
        self.checker = None
 
797
        # Emit D-Bus signal
 
798
        self.PropertyChanged(dbus.String("CheckerRunning"),
 
799
                             dbus.Boolean(False, variant_level=1))
976
800
        if os.WIFEXITED(condition):
977
801
            exitstatus = os.WEXITSTATUS(condition)
978
802
            # Emit D-Bus signal
988
812
        return Client.checker_callback(self, pid, condition, command,
989
813
                                       *args, **kwargs)
990
814
    
 
815
    def checked_ok(self, *args, **kwargs):
 
816
        r = Client.checked_ok(self, *args, **kwargs)
 
817
        # Emit D-Bus signal
 
818
        self.PropertyChanged(
 
819
            dbus.String("LastCheckedOK"),
 
820
            (self._datetime_to_dbus(self.last_checked_ok,
 
821
                                    variant_level=1)))
 
822
        return r
 
823
    
 
824
    def need_approval(self, *args, **kwargs):
 
825
        r = Client.need_approval(self, *args, **kwargs)
 
826
        # Emit D-Bus signal
 
827
        self.PropertyChanged(
 
828
            dbus.String("LastApprovalRequest"),
 
829
            (self._datetime_to_dbus(self.last_approval_request,
 
830
                                    variant_level=1)))
 
831
        return r
 
832
    
991
833
    def start_checker(self, *args, **kwargs):
992
834
        old_checker = self.checker
993
835
        if self.checker is not None:
1000
842
            and old_checker_pid != self.checker.pid):
1001
843
            # Emit D-Bus signal
1002
844
            self.CheckerStarted(self.current_checker_command)
 
845
            self.PropertyChanged(
 
846
                dbus.String("CheckerRunning"),
 
847
                dbus.Boolean(True, variant_level=1))
1003
848
        return r
1004
849
    
 
850
    def stop_checker(self, *args, **kwargs):
 
851
        old_checker = getattr(self, "checker", None)
 
852
        r = Client.stop_checker(self, *args, **kwargs)
 
853
        if (old_checker is not None
 
854
            and getattr(self, "checker", None) is None):
 
855
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
856
                                 dbus.Boolean(False, variant_level=1))
 
857
        return r
 
858
 
1005
859
    def _reset_approved(self):
1006
860
        self._approved = None
1007
861
        return False
1009
863
    def approve(self, value=True):
1010
864
        self.send_changedstate()
1011
865
        self._approved = value
1012
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
866
        gobject.timeout_add(self._timedelta_to_milliseconds
1013
867
                            (self.approval_duration),
1014
868
                            self._reset_approved)
1015
869
    
1016
870
    
1017
871
    ## D-Bus methods, signals & properties
1018
 
    _interface = "se.recompile.Mandos.Client"
 
872
    _interface = "se.bsnet.fukt.Mandos.Client"
1019
873
    
1020
874
    ## Signals
1021
875
    
1068
922
    # CheckedOK - method
1069
923
    @dbus.service.method(_interface)
1070
924
    def CheckedOK(self):
1071
 
        self.checked_ok()
 
925
        return self.checked_ok()
1072
926
    
1073
927
    # Enable - method
1074
928
    @dbus.service.method(_interface)
1107
961
        if value is None:       # get
1108
962
            return dbus.Boolean(self.approved_by_default)
1109
963
        self.approved_by_default = bool(value)
 
964
        # Emit D-Bus signal
 
965
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
 
966
                             dbus.Boolean(value, variant_level=1))
1110
967
    
1111
968
    # ApprovalDelay - property
1112
969
    @dbus_service_property(_interface, signature="t",
1115
972
        if value is None:       # get
1116
973
            return dbus.UInt64(self.approval_delay_milliseconds())
1117
974
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
975
        # Emit D-Bus signal
 
976
        self.PropertyChanged(dbus.String("ApprovalDelay"),
 
977
                             dbus.UInt64(value, variant_level=1))
1118
978
    
1119
979
    # ApprovalDuration - property
1120
980
    @dbus_service_property(_interface, signature="t",
1121
981
                           access="readwrite")
1122
982
    def ApprovalDuration_dbus_property(self, value=None):
1123
983
        if value is None:       # get
1124
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
984
            return dbus.UInt64(self._timedelta_to_milliseconds(
1125
985
                    self.approval_duration))
1126
986
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
987
        # Emit D-Bus signal
 
988
        self.PropertyChanged(dbus.String("ApprovalDuration"),
 
989
                             dbus.UInt64(value, variant_level=1))
1127
990
    
1128
991
    # Name - property
1129
992
    @dbus_service_property(_interface, signature="s", access="read")
1142
1005
        if value is None:       # get
1143
1006
            return dbus.String(self.host)
1144
1007
        self.host = value
 
1008
        # Emit D-Bus signal
 
1009
        self.PropertyChanged(dbus.String("Host"),
 
1010
                             dbus.String(value, variant_level=1))
1145
1011
    
1146
1012
    # Created - property
1147
1013
    @dbus_service_property(_interface, signature="s", access="read")
1148
1014
    def Created_dbus_property(self):
1149
 
        return dbus.String(datetime_to_dbus(self.created))
 
1015
        return dbus.String(self._datetime_to_dbus(self.created))
1150
1016
    
1151
1017
    # LastEnabled - property
1152
1018
    @dbus_service_property(_interface, signature="s", access="read")
1153
1019
    def LastEnabled_dbus_property(self):
1154
 
        return datetime_to_dbus(self.last_enabled)
 
1020
        if self.last_enabled is None:
 
1021
            return dbus.String("")
 
1022
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
1155
1023
    
1156
1024
    # Enabled - property
1157
1025
    @dbus_service_property(_interface, signature="b",
1171
1039
        if value is not None:
1172
1040
            self.checked_ok()
1173
1041
            return
1174
 
        return datetime_to_dbus(self.last_checked_ok)
1175
 
    
1176
 
    # Expires - property
1177
 
    @dbus_service_property(_interface, signature="s", access="read")
1178
 
    def Expires_dbus_property(self):
1179
 
        return datetime_to_dbus(self.expires)
 
1042
        if self.last_checked_ok is None:
 
1043
            return dbus.String("")
 
1044
        return dbus.String(self._datetime_to_dbus(self
 
1045
                                                  .last_checked_ok))
1180
1046
    
1181
1047
    # LastApprovalRequest - property
1182
1048
    @dbus_service_property(_interface, signature="s", access="read")
1183
1049
    def LastApprovalRequest_dbus_property(self):
1184
 
        return datetime_to_dbus(self.last_approval_request)
 
1050
        if self.last_approval_request is None:
 
1051
            return dbus.String("")
 
1052
        return dbus.String(self.
 
1053
                           _datetime_to_dbus(self
 
1054
                                             .last_approval_request))
1185
1055
    
1186
1056
    # Timeout - property
1187
1057
    @dbus_service_property(_interface, signature="t",
1190
1060
        if value is None:       # get
1191
1061
            return dbus.UInt64(self.timeout_milliseconds())
1192
1062
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1063
        # Emit D-Bus signal
 
1064
        self.PropertyChanged(dbus.String("Timeout"),
 
1065
                             dbus.UInt64(value, variant_level=1))
1193
1066
        if getattr(self, "disable_initiator_tag", None) is None:
1194
1067
            return
1195
1068
        # Reschedule timeout
1196
1069
        gobject.source_remove(self.disable_initiator_tag)
1197
1070
        self.disable_initiator_tag = None
1198
 
        self.expires = None
1199
 
        time_to_die = _timedelta_to_milliseconds((self
1200
 
                                                  .last_checked_ok
1201
 
                                                  + self.timeout)
1202
 
                                                 - datetime.datetime
1203
 
                                                 .utcnow())
 
1071
        time_to_die = (self.
 
1072
                       _timedelta_to_milliseconds((self
 
1073
                                                   .last_checked_ok
 
1074
                                                   + self.timeout)
 
1075
                                                  - datetime.datetime
 
1076
                                                  .utcnow()))
1204
1077
        if time_to_die <= 0:
1205
1078
            # The timeout has passed
1206
1079
            self.disable()
1207
1080
        else:
1208
 
            self.expires = (datetime.datetime.utcnow()
1209
 
                            + datetime.timedelta(milliseconds =
1210
 
                                                 time_to_die))
1211
1081
            self.disable_initiator_tag = (gobject.timeout_add
1212
1082
                                          (time_to_die, self.disable))
1213
1083
    
1214
 
    # ExtendedTimeout - property
1215
 
    @dbus_service_property(_interface, signature="t",
1216
 
                           access="readwrite")
1217
 
    def ExtendedTimeout_dbus_property(self, value=None):
1218
 
        if value is None:       # get
1219
 
            return dbus.UInt64(self.extended_timeout_milliseconds())
1220
 
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1221
 
    
1222
1084
    # Interval - property
1223
1085
    @dbus_service_property(_interface, signature="t",
1224
1086
                           access="readwrite")
1226
1088
        if value is None:       # get
1227
1089
            return dbus.UInt64(self.interval_milliseconds())
1228
1090
        self.interval = datetime.timedelta(0, 0, 0, value)
 
1091
        # Emit D-Bus signal
 
1092
        self.PropertyChanged(dbus.String("Interval"),
 
1093
                             dbus.UInt64(value, variant_level=1))
1229
1094
        if getattr(self, "checker_initiator_tag", None) is None:
1230
1095
            return
1231
1096
        # Reschedule checker run
1233
1098
        self.checker_initiator_tag = (gobject.timeout_add
1234
1099
                                      (value, self.start_checker))
1235
1100
        self.start_checker()    # Start one now, too
1236
 
    
 
1101
 
1237
1102
    # Checker - property
1238
1103
    @dbus_service_property(_interface, signature="s",
1239
1104
                           access="readwrite")
1241
1106
        if value is None:       # get
1242
1107
            return dbus.String(self.checker_command)
1243
1108
        self.checker_command = value
 
1109
        # Emit D-Bus signal
 
1110
        self.PropertyChanged(dbus.String("Checker"),
 
1111
                             dbus.String(self.checker_command,
 
1112
                                         variant_level=1))
1244
1113
    
1245
1114
    # CheckerRunning - property
1246
1115
    @dbus_service_property(_interface, signature="b",
1273
1142
        self._pipe.send(('init', fpr, address))
1274
1143
        if not self._pipe.recv():
1275
1144
            raise KeyError()
1276
 
    
 
1145
 
1277
1146
    def __getattribute__(self, name):
1278
1147
        if(name == '_pipe'):
1279
1148
            return super(ProxyClient, self).__getattribute__(name)
1286
1155
                self._pipe.send(('funcall', name, args, kwargs))
1287
1156
                return self._pipe.recv()[1]
1288
1157
            return func
1289
 
    
 
1158
 
1290
1159
    def __setattr__(self, name, value):
1291
1160
        if(name == '_pipe'):
1292
1161
            return super(ProxyClient, self).__setattr__(name, value)
1293
1162
        self._pipe.send(('setattr', name, value))
1294
1163
 
1295
 
class ClientDBusTransitional(ClientDBus):
1296
 
    __metaclass__ = AlternateDBusNamesMetaclass
1297
1164
 
1298
1165
class ClientHandler(socketserver.BaseRequestHandler, object):
1299
1166
    """A class to handle client connections.
1307
1174
                        unicode(self.client_address))
1308
1175
            logger.debug("Pipe FD: %d",
1309
1176
                         self.server.child_pipe.fileno())
1310
 
            
 
1177
 
1311
1178
            session = (gnutls.connection
1312
1179
                       .ClientSession(self.request,
1313
1180
                                      gnutls.connection
1314
1181
                                      .X509Credentials()))
1315
 
            
 
1182
 
1316
1183
            # Note: gnutls.connection.X509Credentials is really a
1317
1184
            # generic GnuTLS certificate credentials object so long as
1318
1185
            # no X.509 keys are added to it.  Therefore, we can use it
1319
1186
            # here despite using OpenPGP certificates.
1320
 
            
 
1187
 
1321
1188
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1322
1189
            #                      "+AES-256-CBC", "+SHA1",
1323
1190
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1329
1196
            (gnutls.library.functions
1330
1197
             .gnutls_priority_set_direct(session._c_object,
1331
1198
                                         priority, None))
1332
 
            
 
1199
 
1333
1200
            # Start communication using the Mandos protocol
1334
1201
            # Get protocol number
1335
1202
            line = self.request.makefile().readline()
1337
1204
            try:
1338
1205
                if int(line.strip().split()[0]) > 1:
1339
1206
                    raise RuntimeError
1340
 
            except (ValueError, IndexError, RuntimeError) as error:
 
1207
            except (ValueError, IndexError, RuntimeError), error:
1341
1208
                logger.error("Unknown protocol version: %s", error)
1342
1209
                return
1343
 
            
 
1210
 
1344
1211
            # Start GnuTLS connection
1345
1212
            try:
1346
1213
                session.handshake()
1347
 
            except gnutls.errors.GNUTLSError as error:
 
1214
            except gnutls.errors.GNUTLSError, error:
1348
1215
                logger.warning("Handshake failed: %s", error)
1349
1216
                # Do not run session.bye() here: the session is not
1350
1217
                # established.  Just abandon the request.
1351
1218
                return
1352
1219
            logger.debug("Handshake succeeded")
1353
 
            
 
1220
 
1354
1221
            approval_required = False
1355
1222
            try:
1356
1223
                try:
1357
1224
                    fpr = self.fingerprint(self.peer_certificate
1358
1225
                                           (session))
1359
 
                except (TypeError,
1360
 
                        gnutls.errors.GNUTLSError) as error:
 
1226
                except (TypeError, gnutls.errors.GNUTLSError), error:
1361
1227
                    logger.warning("Bad certificate: %s", error)
1362
1228
                    return
1363
1229
                logger.debug("Fingerprint: %s", fpr)
1364
 
                
 
1230
 
1365
1231
                try:
1366
1232
                    client = ProxyClient(child_pipe, fpr,
1367
1233
                                         self.client_address)
1375
1241
                
1376
1242
                while True:
1377
1243
                    if not client.enabled:
1378
 
                        logger.info("Client %s is disabled",
 
1244
                        logger.warning("Client %s is disabled",
1379
1245
                                       client.name)
1380
1246
                        if self.server.use_dbus:
1381
1247
                            # Emit D-Bus signal
1382
 
                            client.Rejected("Disabled")
 
1248
                            client.Rejected("Disabled")                    
1383
1249
                        return
1384
1250
                    
1385
1251
                    if client._approved or not client.approval_delay:
1402
1268
                        return
1403
1269
                    
1404
1270
                    #wait until timeout or approved
 
1271
                    #x = float(client._timedelta_to_milliseconds(delay))
1405
1272
                    time = datetime.datetime.now()
1406
1273
                    client.changedstate.acquire()
1407
 
                    (client.changedstate.wait
1408
 
                     (float(client._timedelta_to_milliseconds(delay)
1409
 
                            / 1000)))
 
1274
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1410
1275
                    client.changedstate.release()
1411
1276
                    time2 = datetime.datetime.now()
1412
1277
                    if (time2 - time) >= delay:
1427
1292
                while sent_size < len(client.secret):
1428
1293
                    try:
1429
1294
                        sent = session.send(client.secret[sent_size:])
1430
 
                    except gnutls.errors.GNUTLSError as error:
 
1295
                    except (gnutls.errors.GNUTLSError), error:
1431
1296
                        logger.warning("gnutls send failed")
1432
1297
                        return
1433
1298
                    logger.debug("Sent: %d, remaining: %d",
1434
1299
                                 sent, len(client.secret)
1435
1300
                                 - (sent_size + sent))
1436
1301
                    sent_size += sent
1437
 
                
 
1302
 
1438
1303
                logger.info("Sending secret to %s", client.name)
1439
 
                # bump the timeout using extended_timeout
1440
 
                client.checked_ok(client.extended_timeout)
 
1304
                # bump the timeout as if seen
 
1305
                client.checked_ok()
1441
1306
                if self.server.use_dbus:
1442
1307
                    # Emit D-Bus signal
1443
1308
                    client.GotSecret()
1447
1312
                    client.approvals_pending -= 1
1448
1313
                try:
1449
1314
                    session.bye()
1450
 
                except gnutls.errors.GNUTLSError as error:
 
1315
                except (gnutls.errors.GNUTLSError), error:
1451
1316
                    logger.warning("GnuTLS bye failed")
1452
1317
    
1453
1318
    @staticmethod
1522
1387
        except:
1523
1388
            self.handle_error(request, address)
1524
1389
        self.close_request(request)
1525
 
    
 
1390
            
1526
1391
    def process_request(self, request, address):
1527
1392
        """Start a new process to process the request."""
1528
 
        proc = multiprocessing.Process(target = self.sub_process_main,
1529
 
                                       args = (request,
1530
 
                                               address))
1531
 
        proc.start()
1532
 
        return proc
1533
 
 
 
1393
        multiprocessing.Process(target = self.sub_process_main,
 
1394
                                args = (request, address)).start()
1534
1395
 
1535
1396
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1536
1397
    """ adds a pipe to the MixIn """
1540
1401
        This function creates a new pipe in self.pipe
1541
1402
        """
1542
1403
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1543
 
        
1544
 
        proc = MultiprocessingMixIn.process_request(self, request,
1545
 
                                                    client_address)
 
1404
 
 
1405
        super(MultiprocessingMixInWithPipe,
 
1406
              self).process_request(request, client_address)
1546
1407
        self.child_pipe.close()
1547
 
        self.add_pipe(parent_pipe, proc)
1548
 
    
1549
 
    def add_pipe(self, parent_pipe, proc):
 
1408
        self.add_pipe(parent_pipe)
 
1409
 
 
1410
    def add_pipe(self, parent_pipe):
1550
1411
        """Dummy function; override as necessary"""
1551
 
        raise NotImplementedError
1552
 
 
 
1412
        pass
1553
1413
 
1554
1414
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1555
1415
                     socketserver.TCPServer, object):
1582
1442
                                           SO_BINDTODEVICE,
1583
1443
                                           str(self.interface
1584
1444
                                               + '\0'))
1585
 
                except socket.error as error:
 
1445
                except socket.error, error:
1586
1446
                    if error[0] == errno.EPERM:
1587
1447
                        logger.error("No permission to"
1588
1448
                                     " bind to interface %s",
1640
1500
    def server_activate(self):
1641
1501
        if self.enabled:
1642
1502
            return socketserver.TCPServer.server_activate(self)
1643
 
    
1644
1503
    def enable(self):
1645
1504
        self.enabled = True
1646
 
    
1647
 
    def add_pipe(self, parent_pipe, proc):
 
1505
    def add_pipe(self, parent_pipe):
1648
1506
        # Call "handle_ipc" for both data and EOF events
1649
1507
        gobject.io_add_watch(parent_pipe.fileno(),
1650
1508
                             gobject.IO_IN | gobject.IO_HUP,
1651
1509
                             functools.partial(self.handle_ipc,
1652
 
                                               parent_pipe =
1653
 
                                               parent_pipe,
1654
 
                                               proc = proc))
1655
 
    
 
1510
                                               parent_pipe = parent_pipe))
 
1511
        
1656
1512
    def handle_ipc(self, source, condition, parent_pipe=None,
1657
 
                   proc = None, client_object=None):
 
1513
                   client_object=None):
1658
1514
        condition_names = {
1659
1515
            gobject.IO_IN: "IN",   # There is data to read.
1660
1516
            gobject.IO_OUT: "OUT", # Data can be written (without
1669
1525
                                       for cond, name in
1670
1526
                                       condition_names.iteritems()
1671
1527
                                       if cond & condition)
1672
 
        # error, or the other end of multiprocessing.Pipe has closed
 
1528
        # error or the other end of multiprocessing.Pipe has closed
1673
1529
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1674
 
            # Wait for other process to exit
1675
 
            proc.join()
1676
1530
            return False
1677
1531
        
1678
1532
        # Read a request from the child
1688
1542
                    client = c
1689
1543
                    break
1690
1544
            else:
1691
 
                logger.info("Client not found for fingerprint: %s, ad"
1692
 
                            "dress: %s", fpr, address)
 
1545
                logger.warning("Client not found for fingerprint: %s, ad"
 
1546
                               "dress: %s", fpr, address)
1693
1547
                if self.use_dbus:
1694
1548
                    # Emit D-Bus signal
1695
 
                    mandos_dbus_service.ClientNotFound(fpr,
1696
 
                                                       address[0])
 
1549
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
1697
1550
                parent_pipe.send(False)
1698
1551
                return False
1699
1552
            
1700
1553
            gobject.io_add_watch(parent_pipe.fileno(),
1701
1554
                                 gobject.IO_IN | gobject.IO_HUP,
1702
1555
                                 functools.partial(self.handle_ipc,
1703
 
                                                   parent_pipe =
1704
 
                                                   parent_pipe,
1705
 
                                                   proc = proc,
1706
 
                                                   client_object =
1707
 
                                                   client))
 
1556
                                                   parent_pipe = parent_pipe,
 
1557
                                                   client_object = client))
1708
1558
            parent_pipe.send(True)
1709
 
            # remove the old hook in favor of the new above hook on
1710
 
            # same fileno
 
1559
            # remove the old hook in favor of the new above hook on same fileno
1711
1560
            return False
1712
1561
        if command == 'funcall':
1713
1562
            funcname = request[1]
1714
1563
            args = request[2]
1715
1564
            kwargs = request[3]
1716
1565
            
1717
 
            parent_pipe.send(('data', getattr(client_object,
1718
 
                                              funcname)(*args,
1719
 
                                                         **kwargs)))
1720
 
        
 
1566
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1567
 
1721
1568
        if command == 'getattr':
1722
1569
            attrname = request[1]
1723
1570
            if callable(client_object.__getattribute__(attrname)):
1724
1571
                parent_pipe.send(('function',))
1725
1572
            else:
1726
 
                parent_pipe.send(('data', client_object
1727
 
                                  .__getattribute__(attrname)))
 
1573
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1728
1574
        
1729
1575
        if command == 'setattr':
1730
1576
            attrname = request[1]
1731
1577
            value = request[2]
1732
1578
            setattr(client_object, attrname, value)
1733
 
        
 
1579
 
1734
1580
        return True
1735
1581
 
1736
1582
 
1767
1613
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1768
1614
            else:
1769
1615
                raise ValueError("Unknown suffix %r" % suffix)
1770
 
        except (ValueError, IndexError) as e:
1771
 
            raise ValueError(*(e.args))
 
1616
        except (ValueError, IndexError), e:
 
1617
            raise ValueError(e.message)
1772
1618
        timevalue += delta
1773
1619
    return timevalue
1774
1620
 
1827
1673
    ##################################################################
1828
1674
    # Parsing of options, both command line and config file
1829
1675
    
1830
 
    parser = argparse.ArgumentParser()
1831
 
    parser.add_argument("-v", "--version", action="version",
1832
 
                        version = "%%(prog)s %s" % version,
1833
 
                        help="show version number and exit")
1834
 
    parser.add_argument("-i", "--interface", metavar="IF",
1835
 
                        help="Bind to interface IF")
1836
 
    parser.add_argument("-a", "--address",
1837
 
                        help="Address to listen for requests on")
1838
 
    parser.add_argument("-p", "--port", type=int,
1839
 
                        help="Port number to receive requests on")
1840
 
    parser.add_argument("--check", action="store_true",
1841
 
                        help="Run self-test")
1842
 
    parser.add_argument("--debug", action="store_true",
1843
 
                        help="Debug mode; run in foreground and log"
1844
 
                        " to terminal")
1845
 
    parser.add_argument("--debuglevel", metavar="LEVEL",
1846
 
                        help="Debug level for stdout output")
1847
 
    parser.add_argument("--priority", help="GnuTLS"
1848
 
                        " priority string (see GnuTLS documentation)")
1849
 
    parser.add_argument("--servicename",
1850
 
                        metavar="NAME", help="Zeroconf service name")
1851
 
    parser.add_argument("--configdir",
1852
 
                        default="/etc/mandos", metavar="DIR",
1853
 
                        help="Directory to search for configuration"
1854
 
                        " files")
1855
 
    parser.add_argument("--no-dbus", action="store_false",
1856
 
                        dest="use_dbus", help="Do not provide D-Bus"
1857
 
                        " system bus interface")
1858
 
    parser.add_argument("--no-ipv6", action="store_false",
1859
 
                        dest="use_ipv6", help="Do not use IPv6")
1860
 
    options = parser.parse_args()
 
1676
    parser = optparse.OptionParser(version = "%%prog %s" % version)
 
1677
    parser.add_option("-i", "--interface", type="string",
 
1678
                      metavar="IF", help="Bind to interface IF")
 
1679
    parser.add_option("-a", "--address", type="string",
 
1680
                      help="Address to listen for requests on")
 
1681
    parser.add_option("-p", "--port", type="int",
 
1682
                      help="Port number to receive requests on")
 
1683
    parser.add_option("--check", action="store_true",
 
1684
                      help="Run self-test")
 
1685
    parser.add_option("--debug", action="store_true",
 
1686
                      help="Debug mode; run in foreground and log to"
 
1687
                      " terminal")
 
1688
    parser.add_option("--debuglevel", type="string", metavar="LEVEL",
 
1689
                      help="Debug level for stdout output")
 
1690
    parser.add_option("--priority", type="string", help="GnuTLS"
 
1691
                      " priority string (see GnuTLS documentation)")
 
1692
    parser.add_option("--servicename", type="string",
 
1693
                      metavar="NAME", help="Zeroconf service name")
 
1694
    parser.add_option("--configdir", type="string",
 
1695
                      default="/etc/mandos", metavar="DIR",
 
1696
                      help="Directory to search for configuration"
 
1697
                      " files")
 
1698
    parser.add_option("--no-dbus", action="store_false",
 
1699
                      dest="use_dbus", help="Do not provide D-Bus"
 
1700
                      " system bus interface")
 
1701
    parser.add_option("--no-ipv6", action="store_false",
 
1702
                      dest="use_ipv6", help="Do not use IPv6")
 
1703
    options = parser.parse_args()[0]
1861
1704
    
1862
1705
    if options.check:
1863
1706
        import doctest
1915
1758
    debuglevel = server_settings["debuglevel"]
1916
1759
    use_dbus = server_settings["use_dbus"]
1917
1760
    use_ipv6 = server_settings["use_ipv6"]
1918
 
    
 
1761
 
1919
1762
    if server_settings["servicename"] != "Mandos":
1920
1763
        syslogger.setFormatter(logging.Formatter
1921
1764
                               ('Mandos (%s) [%%(process)d]:'
1923
1766
                                % server_settings["servicename"]))
1924
1767
    
1925
1768
    # Parse config file with clients
1926
 
    client_defaults = { "timeout": "5m",
1927
 
                        "extended_timeout": "15m",
1928
 
                        "interval": "2m",
 
1769
    client_defaults = { "timeout": "1h",
 
1770
                        "interval": "5m",
1929
1771
                        "checker": "fping -q -- %%(host)s",
1930
1772
                        "host": "",
1931
1773
                        "approval_delay": "0s",
1971
1813
    try:
1972
1814
        os.setgid(gid)
1973
1815
        os.setuid(uid)
1974
 
    except OSError as error:
 
1816
    except OSError, error:
1975
1817
        if error[0] != errno.EPERM:
1976
1818
            raise error
1977
1819
    
1978
1820
    if not debug and not debuglevel:
1979
 
        logger.setLevel(logging.WARNING)
 
1821
        syslogger.setLevel(logging.WARNING)
 
1822
        console.setLevel(logging.WARNING)
1980
1823
    if debuglevel:
1981
1824
        level = getattr(logging, debuglevel.upper())
1982
 
        logger.setLevel(level)
1983
 
    
 
1825
        syslogger.setLevel(level)
 
1826
        console.setLevel(level)
 
1827
 
1984
1828
    if debug:
1985
 
        logger.setLevel(logging.DEBUG)
1986
1829
        # Enable all possible GnuTLS debugging
1987
1830
        
1988
1831
        # "Use a log level over 10 to enable all debugging options."
2018
1861
    # End of Avahi example code
2019
1862
    if use_dbus:
2020
1863
        try:
2021
 
            bus_name = dbus.service.BusName("se.recompile.Mandos",
 
1864
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
2022
1865
                                            bus, do_not_queue=True)
2023
 
            old_bus_name = (dbus.service.BusName
2024
 
                            ("se.bsnet.fukt.Mandos", bus,
2025
 
                             do_not_queue=True))
2026
 
        except dbus.exceptions.NameExistsException as e:
 
1866
        except dbus.exceptions.NameExistsException, e:
2027
1867
            logger.error(unicode(e) + ", disabling D-Bus")
2028
1868
            use_dbus = False
2029
1869
            server_settings["use_dbus"] = False
2030
1870
            tcp_server.use_dbus = False
2031
1871
    protocol = avahi.PROTO_INET6 if use_ipv6 else avahi.PROTO_INET
2032
 
    service = AvahiServiceToSyslog(name =
2033
 
                                   server_settings["servicename"],
2034
 
                                   servicetype = "_mandos._tcp",
2035
 
                                   protocol = protocol, bus = bus)
 
1872
    service = AvahiService(name = server_settings["servicename"],
 
1873
                           servicetype = "_mandos._tcp",
 
1874
                           protocol = protocol, bus = bus)
2036
1875
    if server_settings["interface"]:
2037
1876
        service.interface = (if_nametoindex
2038
1877
                             (str(server_settings["interface"])))
2042
1881
    
2043
1882
    client_class = Client
2044
1883
    if use_dbus:
2045
 
        client_class = functools.partial(ClientDBusTransitional,
2046
 
                                         bus = bus)
 
1884
        client_class = functools.partial(ClientDBus, bus = bus)
2047
1885
    def client_config_items(config, section):
2048
1886
        special_settings = {
2049
1887
            "approved_by_default":
2079
1917
        del pidfilename
2080
1918
        
2081
1919
        signal.signal(signal.SIGINT, signal.SIG_IGN)
2082
 
    
 
1920
 
2083
1921
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
2084
1922
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
2085
1923
    
2088
1926
            """A D-Bus proxy object"""
2089
1927
            def __init__(self):
2090
1928
                dbus.service.Object.__init__(self, bus, "/")
2091
 
            _interface = "se.recompile.Mandos"
 
1929
            _interface = "se.bsnet.fukt.Mandos"
2092
1930
            
2093
1931
            @dbus.service.signal(_interface, signature="o")
2094
1932
            def ClientAdded(self, objpath):
2136
1974
            
2137
1975
            del _interface
2138
1976
        
2139
 
        class MandosDBusServiceTransitional(MandosDBusService):
2140
 
            __metaclass__ = AlternateDBusNamesMetaclass
2141
 
        mandos_dbus_service = MandosDBusServiceTransitional()
 
1977
        mandos_dbus_service = MandosDBusService()
2142
1978
    
2143
1979
    def cleanup():
2144
1980
        "Cleanup function; run on exit"
2145
1981
        service.cleanup()
2146
1982
        
2147
 
        multiprocessing.active_children()
2148
1983
        while tcp_server.clients:
2149
1984
            client = tcp_server.clients.pop()
2150
1985
            if use_dbus:
2154
1989
            client.disable(quiet=True)
2155
1990
            if use_dbus:
2156
1991
                # Emit D-Bus signal
2157
 
                mandos_dbus_service.ClientRemoved(client
2158
 
                                                  .dbus_object_path,
 
1992
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
2159
1993
                                                  client.name)
2160
1994
    
2161
1995
    atexit.register(cleanup)
2185
2019
        # From the Avahi example code
2186
2020
        try:
2187
2021
            service.activate()
2188
 
        except dbus.exceptions.DBusException as error:
 
2022
        except dbus.exceptions.DBusException, error:
2189
2023
            logger.critical("DBusException: %s", error)
2190
2024
            cleanup()
2191
2025
            sys.exit(1)
2198
2032
        
2199
2033
        logger.debug("Starting main loop")
2200
2034
        main_loop.run()
2201
 
    except AvahiError as error:
 
2035
    except AvahiError, error:
2202
2036
        logger.critical("AvahiError: %s", error)
2203
2037
        cleanup()
2204
2038
        sys.exit(1)
2210
2044
    # Must run before the D-Bus bus name gets deregistered
2211
2045
    cleanup()
2212
2046
 
2213
 
 
2214
2047
if __name__ == '__main__':
2215
2048
    main()