/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: 2011-10-10 20:30:55 UTC
  • mfrom: (505.1.13 teddy)
  • Revision ID: teddy@recompile.se-20111010203055-a4crqy7gw2ln252r
Miscellaneous fixes prompted by lintian:

* debian/control (Conflicts): Changed to "Breaks:".
* debian/copyright: Updated format.
* debian/mandos-client.postinst: Use "set -e" instead of "#!/bin/sh -e".
* debian/mandos-client.postrm: - '' -
* debian/mandos.postinst: - '' -
* debian/mandos.prerm: Consistent magic.
* mandos: Small comment change.
* mandos-clients.conf.xml (OPTIONS/extended_timeout): Fix spelling.

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-2010 Teddy Hogeborn
15
 
# Copyright © 2008-2010 Björn Påhlsson
 
14
# Copyright © 2008-2011 Teddy Hogeborn
 
15
# Copyright © 2008-2011 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@fukt.bsnet.se>.
 
31
# Contact the authors at <mandos@recompile.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 optparse
 
39
import argparse
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
65
66
 
66
67
import dbus
67
68
import dbus.service
82
83
        SO_BINDTODEVICE = None
83
84
 
84
85
 
85
 
version = "1.2.3"
 
86
version = "1.4.0"
86
87
 
87
88
#logger = logging.getLogger('mandos')
88
89
logger = logging.Logger('mandos')
151
152
        self.group = None       # our entry group
152
153
        self.server = None
153
154
        self.bus = bus
 
155
        self.entry_group_state_changed_match = None
154
156
    def rename(self):
155
157
        """Derived from the Avahi example code"""
156
158
        if self.rename_count >= self.max_renames:
158
160
                            " after %i retries, exiting.",
159
161
                            self.rename_count)
160
162
            raise AvahiServiceError("Too many renames")
161
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
163
        self.name = unicode(self.server
 
164
                            .GetAlternativeServiceName(self.name))
162
165
        logger.info("Changing Zeroconf service name to %r ...",
163
166
                    self.name)
164
167
        syslogger.setFormatter(logging.Formatter
168
171
        self.remove()
169
172
        try:
170
173
            self.add()
171
 
        except dbus.exceptions.DBusException, error:
 
174
        except dbus.exceptions.DBusException as error:
172
175
            logger.critical("DBusException: %s", error)
173
176
            self.cleanup()
174
177
            os._exit(1)
175
178
        self.rename_count += 1
176
179
    def remove(self):
177
180
        """Derived from the Avahi example code"""
 
181
        if self.entry_group_state_changed_match is not None:
 
182
            self.entry_group_state_changed_match.remove()
 
183
            self.entry_group_state_changed_match = None
178
184
        if self.group is not None:
179
185
            self.group.Reset()
180
186
    def add(self):
181
187
        """Derived from the Avahi example code"""
 
188
        self.remove()
182
189
        if self.group is None:
183
190
            self.group = dbus.Interface(
184
191
                self.bus.get_object(avahi.DBUS_NAME,
185
192
                                    self.server.EntryGroupNew()),
186
193
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
187
 
            self.group.connect_to_signal('StateChanged',
188
 
                                         self
189
 
                                         .entry_group_state_changed)
 
194
        self.entry_group_state_changed_match = (
 
195
            self.group.connect_to_signal(
 
196
                'StateChanged', self .entry_group_state_changed))
190
197
        logger.debug("Adding Zeroconf service '%s' of type '%s' ...",
191
198
                     self.name, self.type)
192
199
        self.group.AddService(
215
222
    def cleanup(self):
216
223
        """Derived from the Avahi example code"""
217
224
        if self.group is not None:
218
 
            self.group.Free()
 
225
            try:
 
226
                self.group.Free()
 
227
            except (dbus.exceptions.UnknownMethodException,
 
228
                    dbus.exceptions.DBusException) as e:
 
229
                pass
219
230
            self.group = None
220
 
    def server_state_changed(self, state):
 
231
        self.remove()
 
232
    def server_state_changed(self, state, error=None):
221
233
        """Derived from the Avahi example code"""
222
234
        logger.debug("Avahi server state change: %i", state)
223
 
        if state == avahi.SERVER_COLLISION:
224
 
            logger.error("Zeroconf server name collision")
225
 
            self.remove()
 
235
        bad_states = { avahi.SERVER_INVALID:
 
236
                           "Zeroconf server invalid",
 
237
                       avahi.SERVER_REGISTERING: None,
 
238
                       avahi.SERVER_COLLISION:
 
239
                           "Zeroconf server name collision",
 
240
                       avahi.SERVER_FAILURE:
 
241
                           "Zeroconf server failure" }
 
242
        if state in bad_states:
 
243
            if bad_states[state] is not None:
 
244
                if error is None:
 
245
                    logger.error(bad_states[state])
 
246
                else:
 
247
                    logger.error(bad_states[state] + ": %r", error)
 
248
            self.cleanup()
226
249
        elif state == avahi.SERVER_RUNNING:
227
250
            self.add()
 
251
        else:
 
252
            if error is None:
 
253
                logger.debug("Unknown state: %r", state)
 
254
            else:
 
255
                logger.debug("Unknown state: %r: %r", state, error)
228
256
    def activate(self):
229
257
        """Derived from the Avahi example code"""
230
258
        if self.server is None:
231
259
            self.server = dbus.Interface(
232
260
                self.bus.get_object(avahi.DBUS_NAME,
233
 
                                    avahi.DBUS_PATH_SERVER),
 
261
                                    avahi.DBUS_PATH_SERVER,
 
262
                                    follow_name_owner_changes=True),
234
263
                avahi.DBUS_INTERFACE_SERVER)
235
264
        self.server.connect_to_signal("StateChanged",
236
265
                                 self.server_state_changed)
237
266
        self.server_state_changed(self.server.GetState())
238
267
 
239
268
 
 
269
def _timedelta_to_milliseconds(td):
 
270
    "Convert a datetime.timedelta() to milliseconds"
 
271
    return ((td.days * 24 * 60 * 60 * 1000)
 
272
            + (td.seconds * 1000)
 
273
            + (td.microseconds // 1000))
 
274
        
240
275
class Client(object):
241
276
    """A representation of a client host served by this server.
242
277
    
270
305
    secret:     bytestring; sent verbatim (over TLS) to client
271
306
    timeout:    datetime.timedelta(); How long from last_checked_ok
272
307
                                      until this client is disabled
 
308
    extended_timeout:   extra long timeout when password has been sent
273
309
    runtime_expansions: Allowed attributes for runtime expansion.
 
310
    expires:    datetime.datetime(); time (UTC) when a client will be
 
311
                disabled, or None
274
312
    """
275
313
    
276
314
    runtime_expansions = ("approval_delay", "approval_duration",
278
316
                          "host", "interval", "last_checked_ok",
279
317
                          "last_enabled", "name", "timeout")
280
318
    
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
 
    
288
319
    def timeout_milliseconds(self):
289
320
        "Return the 'timeout' attribute in milliseconds"
290
 
        return self._timedelta_to_milliseconds(self.timeout)
 
321
        return _timedelta_to_milliseconds(self.timeout)
 
322
    
 
323
    def extended_timeout_milliseconds(self):
 
324
        "Return the 'extended_timeout' attribute in milliseconds"
 
325
        return _timedelta_to_milliseconds(self.extended_timeout)
291
326
    
292
327
    def interval_milliseconds(self):
293
328
        "Return the 'interval' attribute in milliseconds"
294
 
        return self._timedelta_to_milliseconds(self.interval)
295
 
 
 
329
        return _timedelta_to_milliseconds(self.interval)
 
330
    
296
331
    def approval_delay_milliseconds(self):
297
 
        return self._timedelta_to_milliseconds(self.approval_delay)
 
332
        return _timedelta_to_milliseconds(self.approval_delay)
298
333
    
299
334
    def __init__(self, name = None, disable_hook=None, config=None):
300
335
        """Note: the 'checker' key in 'config' sets the
327
362
        self.last_enabled = None
328
363
        self.last_checked_ok = None
329
364
        self.timeout = string_to_delta(config["timeout"])
 
365
        self.extended_timeout = string_to_delta(config
 
366
                                                ["extended_timeout"])
330
367
        self.interval = string_to_delta(config["interval"])
331
368
        self.disable_hook = disable_hook
332
369
        self.checker = None
333
370
        self.checker_initiator_tag = None
334
371
        self.disable_initiator_tag = None
 
372
        self.expires = None
335
373
        self.checker_callback_tag = None
336
374
        self.checker_command = config["checker"]
337
375
        self.current_checker_command = None
344
382
            config["approval_delay"])
345
383
        self.approval_duration = string_to_delta(
346
384
            config["approval_duration"])
347
 
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
 
385
        self.changedstate = (multiprocessing_manager
 
386
                             .Condition(multiprocessing_manager
 
387
                                        .Lock()))
348
388
    
349
389
    def send_changedstate(self):
350
390
        self.changedstate.acquire()
351
391
        self.changedstate.notify_all()
352
392
        self.changedstate.release()
353
 
        
 
393
    
354
394
    def enable(self):
355
395
        """Start this client's checker and timeout hooks"""
356
396
        if getattr(self, "enabled", False):
357
397
            # Already enabled
358
398
            return
359
399
        self.send_changedstate()
360
 
        self.last_enabled = datetime.datetime.utcnow()
361
400
        # Schedule a new checker to be started an 'interval' from now,
362
401
        # and every interval from then on.
363
402
        self.checker_initiator_tag = (gobject.timeout_add
364
403
                                      (self.interval_milliseconds(),
365
404
                                       self.start_checker))
366
405
        # Schedule a disable() when 'timeout' has passed
 
406
        self.expires = datetime.datetime.utcnow() + self.timeout
367
407
        self.disable_initiator_tag = (gobject.timeout_add
368
408
                                   (self.timeout_milliseconds(),
369
409
                                    self.disable))
370
410
        self.enabled = True
 
411
        self.last_enabled = datetime.datetime.utcnow()
371
412
        # Also start a new checker *right now*.
372
413
        self.start_checker()
373
414
    
382
423
        if getattr(self, "disable_initiator_tag", False):
383
424
            gobject.source_remove(self.disable_initiator_tag)
384
425
            self.disable_initiator_tag = None
 
426
        self.expires = None
385
427
        if getattr(self, "checker_initiator_tag", False):
386
428
            gobject.source_remove(self.checker_initiator_tag)
387
429
            self.checker_initiator_tag = None
413
455
            logger.warning("Checker for %(name)s crashed?",
414
456
                           vars(self))
415
457
    
416
 
    def checked_ok(self):
 
458
    def checked_ok(self, timeout=None):
417
459
        """Bump up the timeout for this client.
418
460
        
419
461
        This should only be called when the client has been seen,
420
462
        alive and well.
421
463
        """
 
464
        if timeout is None:
 
465
            timeout = self.timeout
422
466
        self.last_checked_ok = datetime.datetime.utcnow()
423
467
        gobject.source_remove(self.disable_initiator_tag)
424
468
        self.disable_initiator_tag = (gobject.timeout_add
425
 
                                      (self.timeout_milliseconds(),
426
 
                                       self.disable))
 
469
                                      (_timedelta_to_milliseconds
 
470
                                       (timeout), self.disable))
 
471
        self.expires = datetime.datetime.utcnow() + timeout
427
472
    
428
473
    def need_approval(self):
429
474
        self.last_approval_request = datetime.datetime.utcnow()
445
490
        # If a checker exists, make sure it is not a zombie
446
491
        try:
447
492
            pid, status = os.waitpid(self.checker.pid, os.WNOHANG)
448
 
        except (AttributeError, OSError), error:
 
493
        except (AttributeError, OSError) as error:
449
494
            if (isinstance(error, OSError)
450
495
                and error.errno != errno.ECHILD):
451
496
                raise error
469
514
                                       'replace')))
470
515
                    for attr in
471
516
                    self.runtime_expansions)
472
 
 
 
517
                
473
518
                try:
474
519
                    command = self.checker_command % escaped_attrs
475
 
                except TypeError, error:
 
520
                except TypeError as error:
476
521
                    logger.error('Could not format string "%s":'
477
522
                                 ' %s', self.checker_command, error)
478
523
                    return True # Try again later
497
542
                if pid:
498
543
                    gobject.source_remove(self.checker_callback_tag)
499
544
                    self.checker_callback(pid, status, command)
500
 
            except OSError, error:
 
545
            except OSError as error:
501
546
                logger.error("Failed to start subprocess: %s",
502
547
                             error)
503
548
        # Re-run this periodically if run by gobject.timeout_add
516
561
            #time.sleep(0.5)
517
562
            #if self.checker.poll() is None:
518
563
            #    os.kill(self.checker.pid, signal.SIGKILL)
519
 
        except OSError, error:
 
564
        except OSError as error:
520
565
            if error.errno != errno.ESRCH: # No such process
521
566
                raise
522
567
        self.checker = None
523
568
 
 
569
 
524
570
def dbus_service_property(dbus_interface, signature="v",
525
571
                          access="readwrite", byte_arrays=False):
526
572
    """Decorators for marking methods of a DBusObjectWithProperties to
572
618
 
573
619
class DBusObjectWithProperties(dbus.service.Object):
574
620
    """A D-Bus object with properties.
575
 
 
 
621
    
576
622
    Classes inheriting from this can use the dbus_service_property
577
623
    decorator to expose methods as D-Bus properties.  It exposes the
578
624
    standard Get(), Set(), and GetAll() methods on the D-Bus.
585
631
    def _get_all_dbus_properties(self):
586
632
        """Returns a generator of (name, attribute) pairs
587
633
        """
588
 
        return ((prop._dbus_name, prop)
 
634
        return ((prop.__get__(self)._dbus_name, prop.__get__(self))
 
635
                for cls in self.__class__.__mro__
589
636
                for name, prop in
590
 
                inspect.getmembers(self, self._is_dbus_property))
 
637
                inspect.getmembers(cls, self._is_dbus_property))
591
638
    
592
639
    def _get_dbus_property(self, interface_name, property_name):
593
640
        """Returns a bound method if one exists which is a D-Bus
594
641
        property with the specified name and interface.
595
642
        """
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
 
643
        for cls in  self.__class__.__mro__:
 
644
            for name, value in (inspect.getmembers
 
645
                                (cls, self._is_dbus_property)):
 
646
                if (value._dbus_name == property_name
 
647
                    and value._dbus_interface == interface_name):
 
648
                    return value.__get__(self)
 
649
        
606
650
        # No such property
607
651
        raise DBusPropertyNotFound(self.dbus_object_path + ":"
608
652
                                   + interface_name + "."
642
686
    def GetAll(self, interface_name):
643
687
        """Standard D-Bus property GetAll() method, see D-Bus
644
688
        standard.
645
 
 
 
689
        
646
690
        Note: Will not include properties with access="write".
647
691
        """
648
692
        all = {}
704
748
            xmlstring = document.toxml("utf-8")
705
749
            document.unlink()
706
750
        except (AttributeError, xml.dom.DOMException,
707
 
                xml.parsers.expat.ExpatError), error:
 
751
                xml.parsers.expat.ExpatError) as error:
708
752
            logger.error("Failed to override Introspection method",
709
753
                         error)
710
754
        return xmlstring
711
755
 
712
756
 
 
757
def datetime_to_dbus (dt, variant_level=0):
 
758
    """Convert a UTC datetime.datetime() to a D-Bus type."""
 
759
    if dt is None:
 
760
        return dbus.String("", variant_level = variant_level)
 
761
    return dbus.String(dt.isoformat(),
 
762
                       variant_level=variant_level)
 
763
 
 
764
class AlternateDBusNamesMetaclass(DBusObjectWithProperties
 
765
                                  .__metaclass__):
 
766
    """Applied to an empty subclass of a D-Bus object, this metaclass
 
767
    will add additional D-Bus attributes matching a certain pattern.
 
768
    """
 
769
    def __new__(mcs, name, bases, attr):
 
770
        # Go through all the base classes which could have D-Bus
 
771
        # methods, signals, or properties in them
 
772
        for base in (b for b in bases
 
773
                     if issubclass(b, dbus.service.Object)):
 
774
            # Go though all attributes of the base class
 
775
            for attrname, attribute in inspect.getmembers(base):
 
776
                # Ignore non-D-Bus attributes, and D-Bus attributes
 
777
                # with the wrong interface name
 
778
                if (not hasattr(attribute, "_dbus_interface")
 
779
                    or not attribute._dbus_interface
 
780
                    .startswith("se.recompile.Mandos")):
 
781
                    continue
 
782
                # Create an alternate D-Bus interface name based on
 
783
                # the current name
 
784
                alt_interface = (attribute._dbus_interface
 
785
                                 .replace("se.recompile.Mandos",
 
786
                                          "se.bsnet.fukt.Mandos"))
 
787
                # Is this a D-Bus signal?
 
788
                if getattr(attribute, "_dbus_is_signal", False):
 
789
                    # Extract the original non-method function by
 
790
                    # black magic
 
791
                    nonmethod_func = (dict(
 
792
                            zip(attribute.func_code.co_freevars,
 
793
                                attribute.__closure__))["func"]
 
794
                                      .cell_contents)
 
795
                    # Create a new, but exactly alike, function
 
796
                    # object, and decorate it to be a new D-Bus signal
 
797
                    # with the alternate D-Bus interface name
 
798
                    new_function = (dbus.service.signal
 
799
                                    (alt_interface,
 
800
                                     attribute._dbus_signature)
 
801
                                    (types.FunctionType(
 
802
                                nonmethod_func.func_code,
 
803
                                nonmethod_func.func_globals,
 
804
                                nonmethod_func.func_name,
 
805
                                nonmethod_func.func_defaults,
 
806
                                nonmethod_func.func_closure)))
 
807
                    # Define a creator of a function to call both the
 
808
                    # old and new functions, so both the old and new
 
809
                    # signals gets sent when the function is called
 
810
                    def fixscope(func1, func2):
 
811
                        """This function is a scope container to pass
 
812
                        func1 and func2 to the "call_both" function
 
813
                        outside of its arguments"""
 
814
                        def call_both(*args, **kwargs):
 
815
                            """This function will emit two D-Bus
 
816
                            signals by calling func1 and func2"""
 
817
                            func1(*args, **kwargs)
 
818
                            func2(*args, **kwargs)
 
819
                        return call_both
 
820
                    # Create the "call_both" function and add it to
 
821
                    # the class
 
822
                    attr[attrname] = fixscope(attribute,
 
823
                                              new_function)
 
824
                # Is this a D-Bus method?
 
825
                elif getattr(attribute, "_dbus_is_method", False):
 
826
                    # Create a new, but exactly alike, function
 
827
                    # object.  Decorate it to be a new D-Bus method
 
828
                    # with the alternate D-Bus interface name.  Add it
 
829
                    # to the class.
 
830
                    attr[attrname] = (dbus.service.method
 
831
                                      (alt_interface,
 
832
                                       attribute._dbus_in_signature,
 
833
                                       attribute._dbus_out_signature)
 
834
                                      (types.FunctionType
 
835
                                       (attribute.func_code,
 
836
                                        attribute.func_globals,
 
837
                                        attribute.func_name,
 
838
                                        attribute.func_defaults,
 
839
                                        attribute.func_closure)))
 
840
                # Is this a D-Bus property?
 
841
                elif getattr(attribute, "_dbus_is_property", False):
 
842
                    # Create a new, but exactly alike, function
 
843
                    # object, and decorate it to be a new D-Bus
 
844
                    # property with the alternate D-Bus interface
 
845
                    # name.  Add it to the class.
 
846
                    attr[attrname] = (dbus_service_property
 
847
                                      (alt_interface,
 
848
                                       attribute._dbus_signature,
 
849
                                       attribute._dbus_access,
 
850
                                       attribute
 
851
                                       ._dbus_get_args_options
 
852
                                       ["byte_arrays"])
 
853
                                      (types.FunctionType
 
854
                                       (attribute.func_code,
 
855
                                        attribute.func_globals,
 
856
                                        attribute.func_name,
 
857
                                        attribute.func_defaults,
 
858
                                        attribute.func_closure)))
 
859
        return type.__new__(mcs, name, bases, attr)
 
860
 
713
861
class ClientDBus(Client, DBusObjectWithProperties):
714
862
    """A Client class using D-Bus
715
863
    
737
885
        DBusObjectWithProperties.__init__(self, self.bus,
738
886
                                          self.dbus_object_path)
739
887
        
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)
 
888
    def notifychangeproperty(transform_func,
 
889
                             dbus_name, type_func=lambda x: x,
 
890
                             variant_level=1):
 
891
        """ Modify a variable so that it's a property which announces
 
892
        its changes to DBus.
751
893
 
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
 
894
        transform_fun: Function that takes a value and transforms it
 
895
                       to a D-Bus type.
 
896
        dbus_name: D-Bus name of the variable
 
897
        type_func: Function that transform the value before sending it
 
898
                   to the D-Bus.  Default: no transform
 
899
        variant_level: D-Bus variant level.  Default: 1
 
900
        """
 
901
        attrname = "_{0}".format(dbus_name)
 
902
        def setter(self, value):
 
903
            if hasattr(self, "dbus_object_path"):
 
904
                if (not hasattr(self, attrname) or
 
905
                    type_func(getattr(self, attrname, None))
 
906
                    != type_func(value)):
 
907
                    dbus_value = transform_func(type_func(value),
 
908
                                                variant_level)
 
909
                    self.PropertyChanged(dbus.String(dbus_name),
 
910
                                         dbus_value)
 
911
            setattr(self, attrname, value)
 
912
        
 
913
        return property(lambda self: getattr(self, attrname), setter)
 
914
    
 
915
    
 
916
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
 
917
    approvals_pending = notifychangeproperty(dbus.Boolean,
 
918
                                             "ApprovalPending",
 
919
                                             type_func = bool)
 
920
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
 
921
    last_enabled = notifychangeproperty(datetime_to_dbus,
 
922
                                        "LastEnabled")
 
923
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
 
924
                                   type_func = lambda checker:
 
925
                                       checker is not None)
 
926
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
 
927
                                           "LastCheckedOK")
 
928
    last_approval_request = notifychangeproperty(
 
929
        datetime_to_dbus, "LastApprovalRequest")
 
930
    approved_by_default = notifychangeproperty(dbus.Boolean,
 
931
                                               "ApprovedByDefault")
 
932
    approval_delay = notifychangeproperty(dbus.UInt16,
 
933
                                          "ApprovalDelay",
 
934
                                          type_func =
 
935
                                          _timedelta_to_milliseconds)
 
936
    approval_duration = notifychangeproperty(
 
937
        dbus.UInt16, "ApprovalDuration",
 
938
        type_func = _timedelta_to_milliseconds)
 
939
    host = notifychangeproperty(dbus.String, "Host")
 
940
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
 
941
                                   type_func =
 
942
                                   _timedelta_to_milliseconds)
 
943
    extended_timeout = notifychangeproperty(
 
944
        dbus.UInt16, "ExtendedTimeout",
 
945
        type_func = _timedelta_to_milliseconds)
 
946
    interval = notifychangeproperty(dbus.UInt16,
 
947
                                    "Interval",
 
948
                                    type_func =
 
949
                                    _timedelta_to_milliseconds)
 
950
    checker_command = notifychangeproperty(dbus.String, "Checker")
 
951
    
 
952
    del notifychangeproperty
783
953
    
784
954
    def __del__(self, *args, **kwargs):
785
955
        try:
794
964
                         *args, **kwargs):
795
965
        self.checker_callback_tag = None
796
966
        self.checker = None
797
 
        # Emit D-Bus signal
798
 
        self.PropertyChanged(dbus.String("CheckerRunning"),
799
 
                             dbus.Boolean(False, variant_level=1))
800
967
        if os.WIFEXITED(condition):
801
968
            exitstatus = os.WEXITSTATUS(condition)
802
969
            # Emit D-Bus signal
812
979
        return Client.checker_callback(self, pid, condition, command,
813
980
                                       *args, **kwargs)
814
981
    
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
 
    
833
982
    def start_checker(self, *args, **kwargs):
834
983
        old_checker = self.checker
835
984
        if self.checker is not None:
842
991
            and old_checker_pid != self.checker.pid):
843
992
            # Emit D-Bus signal
844
993
            self.CheckerStarted(self.current_checker_command)
845
 
            self.PropertyChanged(
846
 
                dbus.String("CheckerRunning"),
847
 
                dbus.Boolean(True, variant_level=1))
848
994
        return r
849
995
    
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
 
 
859
996
    def _reset_approved(self):
860
997
        self._approved = None
861
998
        return False
863
1000
    def approve(self, value=True):
864
1001
        self.send_changedstate()
865
1002
        self._approved = value
866
 
        gobject.timeout_add(self._timedelta_to_milliseconds
 
1003
        gobject.timeout_add(_timedelta_to_milliseconds
867
1004
                            (self.approval_duration),
868
1005
                            self._reset_approved)
869
1006
    
870
1007
    
871
1008
    ## D-Bus methods, signals & properties
872
 
    _interface = "se.bsnet.fukt.Mandos.Client"
 
1009
    _interface = "se.recompile.Mandos.Client"
873
1010
    
874
1011
    ## Signals
875
1012
    
922
1059
    # CheckedOK - method
923
1060
    @dbus.service.method(_interface)
924
1061
    def CheckedOK(self):
925
 
        return self.checked_ok()
 
1062
        self.checked_ok()
926
1063
    
927
1064
    # Enable - method
928
1065
    @dbus.service.method(_interface)
961
1098
        if value is None:       # get
962
1099
            return dbus.Boolean(self.approved_by_default)
963
1100
        self.approved_by_default = bool(value)
964
 
        # Emit D-Bus signal
965
 
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
966
 
                             dbus.Boolean(value, variant_level=1))
967
1101
    
968
1102
    # ApprovalDelay - property
969
1103
    @dbus_service_property(_interface, signature="t",
972
1106
        if value is None:       # get
973
1107
            return dbus.UInt64(self.approval_delay_milliseconds())
974
1108
        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))
978
1109
    
979
1110
    # ApprovalDuration - property
980
1111
    @dbus_service_property(_interface, signature="t",
981
1112
                           access="readwrite")
982
1113
    def ApprovalDuration_dbus_property(self, value=None):
983
1114
        if value is None:       # get
984
 
            return dbus.UInt64(self._timedelta_to_milliseconds(
 
1115
            return dbus.UInt64(_timedelta_to_milliseconds(
985
1116
                    self.approval_duration))
986
1117
        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))
990
1118
    
991
1119
    # Name - property
992
1120
    @dbus_service_property(_interface, signature="s", access="read")
1005
1133
        if value is None:       # get
1006
1134
            return dbus.String(self.host)
1007
1135
        self.host = value
1008
 
        # Emit D-Bus signal
1009
 
        self.PropertyChanged(dbus.String("Host"),
1010
 
                             dbus.String(value, variant_level=1))
1011
1136
    
1012
1137
    # Created - property
1013
1138
    @dbus_service_property(_interface, signature="s", access="read")
1014
1139
    def Created_dbus_property(self):
1015
 
        return dbus.String(self._datetime_to_dbus(self.created))
 
1140
        return dbus.String(datetime_to_dbus(self.created))
1016
1141
    
1017
1142
    # LastEnabled - property
1018
1143
    @dbus_service_property(_interface, signature="s", access="read")
1019
1144
    def LastEnabled_dbus_property(self):
1020
 
        if self.last_enabled is None:
1021
 
            return dbus.String("")
1022
 
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
 
1145
        return datetime_to_dbus(self.last_enabled)
1023
1146
    
1024
1147
    # Enabled - property
1025
1148
    @dbus_service_property(_interface, signature="b",
1039
1162
        if value is not None:
1040
1163
            self.checked_ok()
1041
1164
            return
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))
 
1165
        return datetime_to_dbus(self.last_checked_ok)
 
1166
    
 
1167
    # Expires - property
 
1168
    @dbus_service_property(_interface, signature="s", access="read")
 
1169
    def Expires_dbus_property(self):
 
1170
        return datetime_to_dbus(self.expires)
1046
1171
    
1047
1172
    # LastApprovalRequest - property
1048
1173
    @dbus_service_property(_interface, signature="s", access="read")
1049
1174
    def LastApprovalRequest_dbus_property(self):
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))
 
1175
        return datetime_to_dbus(self.last_approval_request)
1055
1176
    
1056
1177
    # Timeout - property
1057
1178
    @dbus_service_property(_interface, signature="t",
1060
1181
        if value is None:       # get
1061
1182
            return dbus.UInt64(self.timeout_milliseconds())
1062
1183
        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))
1066
1184
        if getattr(self, "disable_initiator_tag", None) is None:
1067
1185
            return
1068
1186
        # Reschedule timeout
1069
1187
        gobject.source_remove(self.disable_initiator_tag)
1070
1188
        self.disable_initiator_tag = None
 
1189
        self.expires = None
1071
1190
        time_to_die = (self.
1072
1191
                       _timedelta_to_milliseconds((self
1073
1192
                                                   .last_checked_ok
1078
1197
            # The timeout has passed
1079
1198
            self.disable()
1080
1199
        else:
 
1200
            self.expires = (datetime.datetime.utcnow()
 
1201
                            + datetime.timedelta(milliseconds =
 
1202
                                                 time_to_die))
1081
1203
            self.disable_initiator_tag = (gobject.timeout_add
1082
1204
                                          (time_to_die, self.disable))
1083
1205
    
 
1206
    # ExtendedTimeout - property
 
1207
    @dbus_service_property(_interface, signature="t",
 
1208
                           access="readwrite")
 
1209
    def ExtendedTimeout_dbus_property(self, value=None):
 
1210
        if value is None:       # get
 
1211
            return dbus.UInt64(self.extended_timeout_milliseconds())
 
1212
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
 
1213
    
1084
1214
    # Interval - property
1085
1215
    @dbus_service_property(_interface, signature="t",
1086
1216
                           access="readwrite")
1088
1218
        if value is None:       # get
1089
1219
            return dbus.UInt64(self.interval_milliseconds())
1090
1220
        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))
1094
1221
        if getattr(self, "checker_initiator_tag", None) is None:
1095
1222
            return
1096
1223
        # Reschedule checker run
1098
1225
        self.checker_initiator_tag = (gobject.timeout_add
1099
1226
                                      (value, self.start_checker))
1100
1227
        self.start_checker()    # Start one now, too
1101
 
 
 
1228
    
1102
1229
    # Checker - property
1103
1230
    @dbus_service_property(_interface, signature="s",
1104
1231
                           access="readwrite")
1106
1233
        if value is None:       # get
1107
1234
            return dbus.String(self.checker_command)
1108
1235
        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))
1113
1236
    
1114
1237
    # CheckerRunning - property
1115
1238
    @dbus_service_property(_interface, signature="b",
1142
1265
        self._pipe.send(('init', fpr, address))
1143
1266
        if not self._pipe.recv():
1144
1267
            raise KeyError()
1145
 
 
 
1268
    
1146
1269
    def __getattribute__(self, name):
1147
1270
        if(name == '_pipe'):
1148
1271
            return super(ProxyClient, self).__getattribute__(name)
1155
1278
                self._pipe.send(('funcall', name, args, kwargs))
1156
1279
                return self._pipe.recv()[1]
1157
1280
            return func
1158
 
 
 
1281
    
1159
1282
    def __setattr__(self, name, value):
1160
1283
        if(name == '_pipe'):
1161
1284
            return super(ProxyClient, self).__setattr__(name, value)
1162
1285
        self._pipe.send(('setattr', name, value))
1163
1286
 
 
1287
class ClientDBusTransitional(ClientDBus):
 
1288
    __metaclass__ = AlternateDBusNamesMetaclass
1164
1289
 
1165
1290
class ClientHandler(socketserver.BaseRequestHandler, object):
1166
1291
    """A class to handle client connections.
1174
1299
                        unicode(self.client_address))
1175
1300
            logger.debug("Pipe FD: %d",
1176
1301
                         self.server.child_pipe.fileno())
1177
 
 
 
1302
            
1178
1303
            session = (gnutls.connection
1179
1304
                       .ClientSession(self.request,
1180
1305
                                      gnutls.connection
1181
1306
                                      .X509Credentials()))
1182
 
 
 
1307
            
1183
1308
            # Note: gnutls.connection.X509Credentials is really a
1184
1309
            # generic GnuTLS certificate credentials object so long as
1185
1310
            # no X.509 keys are added to it.  Therefore, we can use it
1186
1311
            # here despite using OpenPGP certificates.
1187
 
 
 
1312
            
1188
1313
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1189
1314
            #                      "+AES-256-CBC", "+SHA1",
1190
1315
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1196
1321
            (gnutls.library.functions
1197
1322
             .gnutls_priority_set_direct(session._c_object,
1198
1323
                                         priority, None))
1199
 
 
 
1324
            
1200
1325
            # Start communication using the Mandos protocol
1201
1326
            # Get protocol number
1202
1327
            line = self.request.makefile().readline()
1204
1329
            try:
1205
1330
                if int(line.strip().split()[0]) > 1:
1206
1331
                    raise RuntimeError
1207
 
            except (ValueError, IndexError, RuntimeError), error:
 
1332
            except (ValueError, IndexError, RuntimeError) as error:
1208
1333
                logger.error("Unknown protocol version: %s", error)
1209
1334
                return
1210
 
 
 
1335
            
1211
1336
            # Start GnuTLS connection
1212
1337
            try:
1213
1338
                session.handshake()
1214
 
            except gnutls.errors.GNUTLSError, error:
 
1339
            except gnutls.errors.GNUTLSError as error:
1215
1340
                logger.warning("Handshake failed: %s", error)
1216
1341
                # Do not run session.bye() here: the session is not
1217
1342
                # established.  Just abandon the request.
1218
1343
                return
1219
1344
            logger.debug("Handshake succeeded")
1220
 
 
 
1345
            
1221
1346
            approval_required = False
1222
1347
            try:
1223
1348
                try:
1224
1349
                    fpr = self.fingerprint(self.peer_certificate
1225
1350
                                           (session))
1226
 
                except (TypeError, gnutls.errors.GNUTLSError), error:
 
1351
                except (TypeError,
 
1352
                        gnutls.errors.GNUTLSError) as error:
1227
1353
                    logger.warning("Bad certificate: %s", error)
1228
1354
                    return
1229
1355
                logger.debug("Fingerprint: %s", fpr)
1230
 
 
 
1356
                
1231
1357
                try:
1232
1358
                    client = ProxyClient(child_pipe, fpr,
1233
1359
                                         self.client_address)
1241
1367
                
1242
1368
                while True:
1243
1369
                    if not client.enabled:
1244
 
                        logger.warning("Client %s is disabled",
 
1370
                        logger.info("Client %s is disabled",
1245
1371
                                       client.name)
1246
1372
                        if self.server.use_dbus:
1247
1373
                            # Emit D-Bus signal
1248
 
                            client.Rejected("Disabled")                    
 
1374
                            client.Rejected("Disabled")
1249
1375
                        return
1250
1376
                    
1251
1377
                    if client._approved or not client.approval_delay:
1268
1394
                        return
1269
1395
                    
1270
1396
                    #wait until timeout or approved
1271
 
                    #x = float(client._timedelta_to_milliseconds(delay))
1272
1397
                    time = datetime.datetime.now()
1273
1398
                    client.changedstate.acquire()
1274
 
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1399
                    (client.changedstate.wait
 
1400
                     (float(client._timedelta_to_milliseconds(delay)
 
1401
                            / 1000)))
1275
1402
                    client.changedstate.release()
1276
1403
                    time2 = datetime.datetime.now()
1277
1404
                    if (time2 - time) >= delay:
1292
1419
                while sent_size < len(client.secret):
1293
1420
                    try:
1294
1421
                        sent = session.send(client.secret[sent_size:])
1295
 
                    except (gnutls.errors.GNUTLSError), error:
 
1422
                    except gnutls.errors.GNUTLSError as error:
1296
1423
                        logger.warning("gnutls send failed")
1297
1424
                        return
1298
1425
                    logger.debug("Sent: %d, remaining: %d",
1299
1426
                                 sent, len(client.secret)
1300
1427
                                 - (sent_size + sent))
1301
1428
                    sent_size += sent
1302
 
 
 
1429
                
1303
1430
                logger.info("Sending secret to %s", client.name)
1304
 
                # bump the timeout as if seen
1305
 
                client.checked_ok()
 
1431
                # bump the timeout using extended_timeout
 
1432
                client.checked_ok(client.extended_timeout)
1306
1433
                if self.server.use_dbus:
1307
1434
                    # Emit D-Bus signal
1308
1435
                    client.GotSecret()
1312
1439
                    client.approvals_pending -= 1
1313
1440
                try:
1314
1441
                    session.bye()
1315
 
                except (gnutls.errors.GNUTLSError), error:
 
1442
                except gnutls.errors.GNUTLSError as error:
1316
1443
                    logger.warning("GnuTLS bye failed")
1317
1444
    
1318
1445
    @staticmethod
1387
1514
        except:
1388
1515
            self.handle_error(request, address)
1389
1516
        self.close_request(request)
1390
 
            
 
1517
    
1391
1518
    def process_request(self, request, address):
1392
1519
        """Start a new process to process the request."""
1393
 
        multiprocessing.Process(target = self.sub_process_main,
1394
 
                                args = (request, address)).start()
 
1520
        proc = multiprocessing.Process(target = self.sub_process_main,
 
1521
                                       args = (request,
 
1522
                                               address))
 
1523
        proc.start()
 
1524
        return proc
 
1525
 
1395
1526
 
1396
1527
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1397
1528
    """ adds a pipe to the MixIn """
1401
1532
        This function creates a new pipe in self.pipe
1402
1533
        """
1403
1534
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1404
 
 
1405
 
        super(MultiprocessingMixInWithPipe,
1406
 
              self).process_request(request, client_address)
 
1535
        
 
1536
        proc = MultiprocessingMixIn.process_request(self, request,
 
1537
                                                    client_address)
1407
1538
        self.child_pipe.close()
1408
 
        self.add_pipe(parent_pipe)
1409
 
 
1410
 
    def add_pipe(self, parent_pipe):
 
1539
        self.add_pipe(parent_pipe, proc)
 
1540
    
 
1541
    def add_pipe(self, parent_pipe, proc):
1411
1542
        """Dummy function; override as necessary"""
1412
1543
        raise NotImplementedError
1413
1544
 
 
1545
 
1414
1546
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1415
1547
                     socketserver.TCPServer, object):
1416
1548
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1442
1574
                                           SO_BINDTODEVICE,
1443
1575
                                           str(self.interface
1444
1576
                                               + '\0'))
1445
 
                except socket.error, error:
 
1577
                except socket.error as error:
1446
1578
                    if error[0] == errno.EPERM:
1447
1579
                        logger.error("No permission to"
1448
1580
                                     " bind to interface %s",
1500
1632
    def server_activate(self):
1501
1633
        if self.enabled:
1502
1634
            return socketserver.TCPServer.server_activate(self)
 
1635
    
1503
1636
    def enable(self):
1504
1637
        self.enabled = True
1505
 
    def add_pipe(self, parent_pipe):
 
1638
    
 
1639
    def add_pipe(self, parent_pipe, proc):
1506
1640
        # Call "handle_ipc" for both data and EOF events
1507
1641
        gobject.io_add_watch(parent_pipe.fileno(),
1508
1642
                             gobject.IO_IN | gobject.IO_HUP,
1509
1643
                             functools.partial(self.handle_ipc,
1510
 
                                               parent_pipe = parent_pipe))
1511
 
        
 
1644
                                               parent_pipe =
 
1645
                                               parent_pipe,
 
1646
                                               proc = proc))
 
1647
    
1512
1648
    def handle_ipc(self, source, condition, parent_pipe=None,
1513
 
                   client_object=None):
 
1649
                   proc = None, client_object=None):
1514
1650
        condition_names = {
1515
1651
            gobject.IO_IN: "IN",   # There is data to read.
1516
1652
            gobject.IO_OUT: "OUT", # Data can be written (without
1525
1661
                                       for cond, name in
1526
1662
                                       condition_names.iteritems()
1527
1663
                                       if cond & condition)
1528
 
        # error or the other end of multiprocessing.Pipe has closed
 
1664
        # error, or the other end of multiprocessing.Pipe has closed
1529
1665
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1666
            # Wait for other process to exit
 
1667
            proc.join()
1530
1668
            return False
1531
1669
        
1532
1670
        # Read a request from the child
1542
1680
                    client = c
1543
1681
                    break
1544
1682
            else:
1545
 
                logger.warning("Client not found for fingerprint: %s, ad"
1546
 
                               "dress: %s", fpr, address)
 
1683
                logger.info("Client not found for fingerprint: %s, ad"
 
1684
                            "dress: %s", fpr, address)
1547
1685
                if self.use_dbus:
1548
1686
                    # Emit D-Bus signal
1549
 
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
 
1687
                    mandos_dbus_service.ClientNotFound(fpr,
 
1688
                                                       address[0])
1550
1689
                parent_pipe.send(False)
1551
1690
                return False
1552
1691
            
1553
1692
            gobject.io_add_watch(parent_pipe.fileno(),
1554
1693
                                 gobject.IO_IN | gobject.IO_HUP,
1555
1694
                                 functools.partial(self.handle_ipc,
1556
 
                                                   parent_pipe = parent_pipe,
1557
 
                                                   client_object = client))
 
1695
                                                   parent_pipe =
 
1696
                                                   parent_pipe,
 
1697
                                                   proc = proc,
 
1698
                                                   client_object =
 
1699
                                                   client))
1558
1700
            parent_pipe.send(True)
1559
 
            # remove the old hook in favor of the new above hook on same fileno
 
1701
            # remove the old hook in favor of the new above hook on
 
1702
            # same fileno
1560
1703
            return False
1561
1704
        if command == 'funcall':
1562
1705
            funcname = request[1]
1563
1706
            args = request[2]
1564
1707
            kwargs = request[3]
1565
1708
            
1566
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1567
 
 
 
1709
            parent_pipe.send(('data', getattr(client_object,
 
1710
                                              funcname)(*args,
 
1711
                                                         **kwargs)))
 
1712
        
1568
1713
        if command == 'getattr':
1569
1714
            attrname = request[1]
1570
1715
            if callable(client_object.__getattribute__(attrname)):
1571
1716
                parent_pipe.send(('function',))
1572
1717
            else:
1573
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1718
                parent_pipe.send(('data', client_object
 
1719
                                  .__getattribute__(attrname)))
1574
1720
        
1575
1721
        if command == 'setattr':
1576
1722
            attrname = request[1]
1577
1723
            value = request[2]
1578
1724
            setattr(client_object, attrname, value)
1579
 
 
 
1725
        
1580
1726
        return True
1581
1727
 
1582
1728
 
1613
1759
                delta = datetime.timedelta(0, 0, 0, 0, 0, 0, value)
1614
1760
            else:
1615
1761
                raise ValueError("Unknown suffix %r" % suffix)
1616
 
        except (ValueError, IndexError), e:
 
1762
        except (ValueError, IndexError) as e:
1617
1763
            raise ValueError(*(e.args))
1618
1764
        timevalue += delta
1619
1765
    return timevalue
1673
1819
    ##################################################################
1674
1820
    # Parsing of options, both command line and config file
1675
1821
    
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]
 
1822
    parser = argparse.ArgumentParser()
 
1823
    parser.add_argument("-v", "--version", action="version",
 
1824
                        version = "%%(prog)s %s" % version,
 
1825
                        help="show version number and exit")
 
1826
    parser.add_argument("-i", "--interface", metavar="IF",
 
1827
                        help="Bind to interface IF")
 
1828
    parser.add_argument("-a", "--address",
 
1829
                        help="Address to listen for requests on")
 
1830
    parser.add_argument("-p", "--port", type=int,
 
1831
                        help="Port number to receive requests on")
 
1832
    parser.add_argument("--check", action="store_true",
 
1833
                        help="Run self-test")
 
1834
    parser.add_argument("--debug", action="store_true",
 
1835
                        help="Debug mode; run in foreground and log"
 
1836
                        " to terminal")
 
1837
    parser.add_argument("--debuglevel", metavar="LEVEL",
 
1838
                        help="Debug level for stdout output")
 
1839
    parser.add_argument("--priority", help="GnuTLS"
 
1840
                        " priority string (see GnuTLS documentation)")
 
1841
    parser.add_argument("--servicename",
 
1842
                        metavar="NAME", help="Zeroconf service name")
 
1843
    parser.add_argument("--configdir",
 
1844
                        default="/etc/mandos", metavar="DIR",
 
1845
                        help="Directory to search for configuration"
 
1846
                        " files")
 
1847
    parser.add_argument("--no-dbus", action="store_false",
 
1848
                        dest="use_dbus", help="Do not provide D-Bus"
 
1849
                        " system bus interface")
 
1850
    parser.add_argument("--no-ipv6", action="store_false",
 
1851
                        dest="use_ipv6", help="Do not use IPv6")
 
1852
    options = parser.parse_args()
1704
1853
    
1705
1854
    if options.check:
1706
1855
        import doctest
1758
1907
    debuglevel = server_settings["debuglevel"]
1759
1908
    use_dbus = server_settings["use_dbus"]
1760
1909
    use_ipv6 = server_settings["use_ipv6"]
1761
 
 
 
1910
    
1762
1911
    if server_settings["servicename"] != "Mandos":
1763
1912
        syslogger.setFormatter(logging.Formatter
1764
1913
                               ('Mandos (%s) [%%(process)d]:'
1766
1915
                                % server_settings["servicename"]))
1767
1916
    
1768
1917
    # Parse config file with clients
1769
 
    client_defaults = { "timeout": "1h",
1770
 
                        "interval": "5m",
 
1918
    client_defaults = { "timeout": "5m",
 
1919
                        "extended_timeout": "15m",
 
1920
                        "interval": "2m",
1771
1921
                        "checker": "fping -q -- %%(host)s",
1772
1922
                        "host": "",
1773
1923
                        "approval_delay": "0s",
1813
1963
    try:
1814
1964
        os.setgid(gid)
1815
1965
        os.setuid(uid)
1816
 
    except OSError, error:
 
1966
    except OSError as error:
1817
1967
        if error[0] != errno.EPERM:
1818
1968
            raise error
1819
1969
    
1824
1974
        level = getattr(logging, debuglevel.upper())
1825
1975
        syslogger.setLevel(level)
1826
1976
        console.setLevel(level)
1827
 
 
 
1977
    
1828
1978
    if debug:
1829
1979
        # Enable all possible GnuTLS debugging
1830
1980
        
1861
2011
    # End of Avahi example code
1862
2012
    if use_dbus:
1863
2013
        try:
1864
 
            bus_name = dbus.service.BusName("se.bsnet.fukt.Mandos",
 
2014
            bus_name = dbus.service.BusName("se.recompile.Mandos",
1865
2015
                                            bus, do_not_queue=True)
1866
 
        except dbus.exceptions.NameExistsException, e:
 
2016
            old_bus_name = (dbus.service.BusName
 
2017
                            ("se.bsnet.fukt.Mandos", bus,
 
2018
                             do_not_queue=True))
 
2019
        except dbus.exceptions.NameExistsException as e:
1867
2020
            logger.error(unicode(e) + ", disabling D-Bus")
1868
2021
            use_dbus = False
1869
2022
            server_settings["use_dbus"] = False
1881
2034
    
1882
2035
    client_class = Client
1883
2036
    if use_dbus:
1884
 
        client_class = functools.partial(ClientDBus, bus = bus)
 
2037
        client_class = functools.partial(ClientDBusTransitional,
 
2038
                                         bus = bus)
1885
2039
    def client_config_items(config, section):
1886
2040
        special_settings = {
1887
2041
            "approved_by_default":
1917
2071
        del pidfilename
1918
2072
        
1919
2073
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1920
 
 
 
2074
    
1921
2075
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1922
2076
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1923
2077
    
1926
2080
            """A D-Bus proxy object"""
1927
2081
            def __init__(self):
1928
2082
                dbus.service.Object.__init__(self, bus, "/")
1929
 
            _interface = "se.bsnet.fukt.Mandos"
 
2083
            _interface = "se.recompile.Mandos"
1930
2084
            
1931
2085
            @dbus.service.signal(_interface, signature="o")
1932
2086
            def ClientAdded(self, objpath):
1974
2128
            
1975
2129
            del _interface
1976
2130
        
1977
 
        mandos_dbus_service = MandosDBusService()
 
2131
        class MandosDBusServiceTransitional(MandosDBusService):
 
2132
            __metaclass__ = AlternateDBusNamesMetaclass
 
2133
        mandos_dbus_service = MandosDBusServiceTransitional()
1978
2134
    
1979
2135
    def cleanup():
1980
2136
        "Cleanup function; run on exit"
1981
2137
        service.cleanup()
1982
2138
        
 
2139
        multiprocessing.active_children()
1983
2140
        while tcp_server.clients:
1984
2141
            client = tcp_server.clients.pop()
1985
2142
            if use_dbus:
1989
2146
            client.disable(quiet=True)
1990
2147
            if use_dbus:
1991
2148
                # Emit D-Bus signal
1992
 
                mandos_dbus_service.ClientRemoved(client.dbus_object_path,
 
2149
                mandos_dbus_service.ClientRemoved(client
 
2150
                                                  .dbus_object_path,
1993
2151
                                                  client.name)
1994
2152
    
1995
2153
    atexit.register(cleanup)
2019
2177
        # From the Avahi example code
2020
2178
        try:
2021
2179
            service.activate()
2022
 
        except dbus.exceptions.DBusException, error:
 
2180
        except dbus.exceptions.DBusException as error:
2023
2181
            logger.critical("DBusException: %s", error)
2024
2182
            cleanup()
2025
2183
            sys.exit(1)
2032
2190
        
2033
2191
        logger.debug("Starting main loop")
2034
2192
        main_loop.run()
2035
 
    except AvahiError, error:
 
2193
    except AvahiError as error:
2036
2194
        logger.critical("AvahiError: %s", error)
2037
2195
        cleanup()
2038
2196
        sys.exit(1)
2044
2202
    # Must run before the D-Bus bus name gets deregistered
2045
2203
    cleanup()
2046
2204
 
 
2205
 
2047
2206
if __name__ == '__main__':
2048
2207
    main()