/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-04-02 06:37:18 UTC
  • Revision ID: teddy@fukt.bsnet.se-20110402063718-13ldrcuu0t33sdc4
* mandos: Tolerate restarting Avahi servers.  Also Changed to new
          "except x as y" exception syntax.
  (AvahiService.entry_group_state_changed_match): New; contains the
                                                  SignalMatch object.
  (AvahiService.remove): Really remove the group and the signal
                         connection, if any.
  (AvahiService.add): Always create a new group and signal connection.
  (AvahiService.cleanup): Changed to simply call remove().
  (AvahiService.server_state_changed): Handle and log more bad states.
  (AvahiService.activate): Set "follow_name_owner_changes=True" on the
                           Avahi Server proxy object.
  (ClientDBus.checked_ok): Do not return anything.
  (ClientDBus.CheckedOK): Do not return anything, as documented.
* mandos-monitor: Call D-Bus methods asynchronously.

Show diffs side-by-side

added added

removed removed

Lines of Context:
82
82
        SO_BINDTODEVICE = None
83
83
 
84
84
 
85
 
version = "1.3.1"
 
85
version = "1.3.0"
86
86
 
87
87
#logger = logging.getLogger('mandos')
88
88
logger = logging.Logger('mandos')
176
176
        self.rename_count += 1
177
177
    def remove(self):
178
178
        """Derived from the Avahi example code"""
 
179
        if self.group is not None:
 
180
            try:
 
181
                self.group.Free()
 
182
            except (dbus.exceptions.UnknownMethodException,
 
183
                    dbus.exceptions.DBusException) as e:
 
184
                pass
 
185
            self.group = None
179
186
        if self.entry_group_state_changed_match is not None:
180
187
            self.entry_group_state_changed_match.remove()
181
188
            self.entry_group_state_changed_match = None
182
 
        if self.group is not None:
183
 
            self.group.Reset()
184
189
    def add(self):
185
190
        """Derived from the Avahi example code"""
186
191
        self.remove()
187
 
        if self.group is None:
188
 
            self.group = dbus.Interface(
189
 
                self.bus.get_object(avahi.DBUS_NAME,
190
 
                                    self.server.EntryGroupNew()),
191
 
                avahi.DBUS_INTERFACE_ENTRY_GROUP)
 
192
        self.group = dbus.Interface(
 
193
            self.bus.get_object(avahi.DBUS_NAME,
 
194
                                self.server.EntryGroupNew(),
 
195
                                follow_name_owner_changes=True),
 
196
            avahi.DBUS_INTERFACE_ENTRY_GROUP)
192
197
        self.entry_group_state_changed_match = (
193
198
            self.group.connect_to_signal(
194
199
                'StateChanged', self .entry_group_state_changed))
219
224
                                  % unicode(error))
220
225
    def cleanup(self):
221
226
        """Derived from the Avahi example code"""
222
 
        if self.group is not None:
223
 
            try:
224
 
                self.group.Free()
225
 
            except (dbus.exceptions.UnknownMethodException,
226
 
                    dbus.exceptions.DBusException) as e:
227
 
                pass
228
 
            self.group = None
229
227
        self.remove()
230
228
    def server_state_changed(self, state, error=None):
231
229
        """Derived from the Avahi example code"""
238
236
                       avahi.SERVER_FAILURE:
239
237
                           "Zeroconf server failure" }
240
238
        if state in bad_states:
241
 
            if bad_states[state] is not None:
242
 
                if error is None:
243
 
                    logger.error(bad_states[state])
244
 
                else:
245
 
                    logger.error(bad_states[state] + ": %r", error)
246
 
            self.cleanup()
 
239
            if bad_states[state]:
 
240
                logger.error(bad_states[state])
 
241
            self.remove()
247
242
        elif state == avahi.SERVER_RUNNING:
248
243
            self.add()
249
244
        else:
250
 
            if error is None:
251
 
                logger.debug("Unknown state: %r", state)
252
 
            else:
253
 
                logger.debug("Unknown state: %r: %r", state, error)
 
245
            logger.debug("Unknown state: %r", state)
254
246
    def activate(self):
255
247
        """Derived from the Avahi example code"""
256
248
        if self.server is None:
264
256
        self.server_state_changed(self.server.GetState())
265
257
 
266
258
 
267
 
def _timedelta_to_milliseconds(td):
268
 
    "Convert a datetime.timedelta() to milliseconds"
269
 
    return ((td.days * 24 * 60 * 60 * 1000)
270
 
            + (td.seconds * 1000)
271
 
            + (td.microseconds // 1000))
272
 
        
273
259
class Client(object):
274
260
    """A representation of a client host served by this server.
275
261
    
303
289
    secret:     bytestring; sent verbatim (over TLS) to client
304
290
    timeout:    datetime.timedelta(); How long from last_checked_ok
305
291
                                      until this client is disabled
306
 
    extended_timeout:   extra long timeout when password has been sent
307
292
    runtime_expansions: Allowed attributes for runtime expansion.
308
 
    expires:    datetime.datetime(); time (UTC) when a client will be
309
 
                disabled, or None
310
293
    """
311
294
    
312
295
    runtime_expansions = ("approval_delay", "approval_duration",
314
297
                          "host", "interval", "last_checked_ok",
315
298
                          "last_enabled", "name", "timeout")
316
299
    
 
300
    @staticmethod
 
301
    def _timedelta_to_milliseconds(td):
 
302
        "Convert a datetime.timedelta() to milliseconds"
 
303
        return ((td.days * 24 * 60 * 60 * 1000)
 
304
                + (td.seconds * 1000)
 
305
                + (td.microseconds // 1000))
 
306
    
317
307
    def timeout_milliseconds(self):
318
308
        "Return the 'timeout' attribute in milliseconds"
319
 
        return _timedelta_to_milliseconds(self.timeout)
320
 
    
321
 
    def extended_timeout_milliseconds(self):
322
 
        "Return the 'extended_timeout' attribute in milliseconds"
323
 
        return _timedelta_to_milliseconds(self.extended_timeout)    
 
309
        return self._timedelta_to_milliseconds(self.timeout)
324
310
    
325
311
    def interval_milliseconds(self):
326
312
        "Return the 'interval' attribute in milliseconds"
327
 
        return _timedelta_to_milliseconds(self.interval)
328
 
    
 
313
        return self._timedelta_to_milliseconds(self.interval)
 
314
 
329
315
    def approval_delay_milliseconds(self):
330
 
        return _timedelta_to_milliseconds(self.approval_delay)
 
316
        return self._timedelta_to_milliseconds(self.approval_delay)
331
317
    
332
318
    def __init__(self, name = None, disable_hook=None, config=None):
333
319
        """Note: the 'checker' key in 'config' sets the
360
346
        self.last_enabled = None
361
347
        self.last_checked_ok = None
362
348
        self.timeout = string_to_delta(config["timeout"])
363
 
        self.extended_timeout = string_to_delta(config["extended_timeout"])
364
349
        self.interval = string_to_delta(config["interval"])
365
350
        self.disable_hook = disable_hook
366
351
        self.checker = None
367
352
        self.checker_initiator_tag = None
368
353
        self.disable_initiator_tag = None
369
 
        self.expires = None
370
354
        self.checker_callback_tag = None
371
355
        self.checker_command = config["checker"]
372
356
        self.current_checker_command = None
392
376
            # Already enabled
393
377
            return
394
378
        self.send_changedstate()
 
379
        self.last_enabled = datetime.datetime.utcnow()
395
380
        # Schedule a new checker to be started an 'interval' from now,
396
381
        # and every interval from then on.
397
382
        self.checker_initiator_tag = (gobject.timeout_add
398
383
                                      (self.interval_milliseconds(),
399
384
                                       self.start_checker))
400
385
        # Schedule a disable() when 'timeout' has passed
401
 
        self.expires = datetime.datetime.utcnow() + self.timeout
402
386
        self.disable_initiator_tag = (gobject.timeout_add
403
387
                                   (self.timeout_milliseconds(),
404
388
                                    self.disable))
405
389
        self.enabled = True
406
 
        self.last_enabled = datetime.datetime.utcnow()
407
390
        # Also start a new checker *right now*.
408
391
        self.start_checker()
409
392
    
418
401
        if getattr(self, "disable_initiator_tag", False):
419
402
            gobject.source_remove(self.disable_initiator_tag)
420
403
            self.disable_initiator_tag = None
421
 
        self.expires = None
422
404
        if getattr(self, "checker_initiator_tag", False):
423
405
            gobject.source_remove(self.checker_initiator_tag)
424
406
            self.checker_initiator_tag = None
450
432
            logger.warning("Checker for %(name)s crashed?",
451
433
                           vars(self))
452
434
    
453
 
    def checked_ok(self, timeout=None):
 
435
    def checked_ok(self):
454
436
        """Bump up the timeout for this client.
455
437
        
456
438
        This should only be called when the client has been seen,
457
439
        alive and well.
458
440
        """
459
 
        if timeout is None:
460
 
            timeout = self.timeout
461
441
        self.last_checked_ok = datetime.datetime.utcnow()
462
442
        gobject.source_remove(self.disable_initiator_tag)
463
 
        self.expires = datetime.datetime.utcnow() + timeout
464
443
        self.disable_initiator_tag = (gobject.timeout_add
465
 
                                      (_timedelta_to_milliseconds(timeout),
 
444
                                      (self.timeout_milliseconds(),
466
445
                                       self.disable))
467
446
    
468
447
    def need_approval(self):
509
488
                                       'replace')))
510
489
                    for attr in
511
490
                    self.runtime_expansions)
512
 
                
 
491
 
513
492
                try:
514
493
                    command = self.checker_command % escaped_attrs
515
494
                except TypeError as error:
561
540
                raise
562
541
        self.checker = None
563
542
 
564
 
 
565
543
def dbus_service_property(dbus_interface, signature="v",
566
544
                          access="readwrite", byte_arrays=False):
567
545
    """Decorators for marking methods of a DBusObjectWithProperties to
613
591
 
614
592
class DBusObjectWithProperties(dbus.service.Object):
615
593
    """A D-Bus object with properties.
616
 
    
 
594
 
617
595
    Classes inheriting from this can use the dbus_service_property
618
596
    decorator to expose methods as D-Bus properties.  It exposes the
619
597
    standard Get(), Set(), and GetAll() methods on the D-Bus.
683
661
    def GetAll(self, interface_name):
684
662
        """Standard D-Bus property GetAll() method, see D-Bus
685
663
        standard.
686
 
        
 
664
 
687
665
        Note: Will not include properties with access="write".
688
666
        """
689
667
        all = {}
751
729
        return xmlstring
752
730
 
753
731
 
754
 
def datetime_to_dbus (dt, variant_level=0):
755
 
    """Convert a UTC datetime.datetime() to a D-Bus type."""
756
 
    if dt is None:
757
 
        return dbus.String("", variant_level = variant_level)
758
 
    return dbus.String(dt.isoformat(),
759
 
                       variant_level=variant_level)
760
 
 
761
 
 
762
732
class ClientDBus(Client, DBusObjectWithProperties):
763
733
    """A Client class using D-Bus
764
734
    
786
756
        DBusObjectWithProperties.__init__(self, self.bus,
787
757
                                          self.dbus_object_path)
788
758
        
789
 
    def notifychangeproperty(transform_func,
790
 
                             dbus_name, type_func=lambda x: x,
791
 
                             variant_level=1):
792
 
        """ Modify a variable so that its a property that announce its
793
 
        changes to DBus.
794
 
        transform_fun: Function that takes a value and transform it to
795
 
                       DBus type.
796
 
        dbus_name: DBus name of the variable
797
 
        type_func: Function that transform the value before sending it
798
 
                   to DBus
799
 
        variant_level: DBus variant level. default: 1
800
 
        """
801
 
        real_value = [None,]
802
 
        def setter(self, value):
803
 
            old_value = real_value[0]
804
 
            real_value[0] = value
805
 
            if hasattr(self, "dbus_object_path"):
806
 
                if type_func(old_value) != type_func(real_value[0]):
807
 
                    dbus_value = transform_func(type_func(real_value[0]),
808
 
                                                variant_level)
809
 
                    self.PropertyChanged(dbus.String(dbus_name),
810
 
                                         dbus_value)
811
 
        
812
 
        return property(lambda self: real_value[0], setter)
813
 
    
814
 
    
815
 
    expires = notifychangeproperty(datetime_to_dbus, "Expires")
816
 
    approvals_pending = notifychangeproperty(dbus.Boolean,
817
 
                                             "ApprovalPending",
818
 
                                             type_func = bool)
819
 
    enabled = notifychangeproperty(dbus.Boolean, "Enabled")
820
 
    last_enabled = notifychangeproperty(datetime_to_dbus,
821
 
                                        "LastEnabled")
822
 
    checker = notifychangeproperty(dbus.Boolean, "CheckerRunning",
823
 
                                   type_func = lambda checker: checker is not None)
824
 
    last_checked_ok = notifychangeproperty(datetime_to_dbus,
825
 
                                           "LastCheckedOK")
826
 
    last_approval_request = notifychangeproperty(datetime_to_dbus,
827
 
                                                 "LastApprovalRequest")
828
 
    approved_by_default = notifychangeproperty(dbus.Boolean,
829
 
                                               "ApprovedByDefault")
830
 
    approval_delay = notifychangeproperty(dbus.UInt16, "ApprovalDelay",
831
 
                                          type_func = _timedelta_to_milliseconds)
832
 
    approval_duration = notifychangeproperty(dbus.UInt16, "ApprovalDuration",
833
 
                                             type_func = _timedelta_to_milliseconds)
834
 
    host = notifychangeproperty(dbus.String, "Host")
835
 
    timeout = notifychangeproperty(dbus.UInt16, "Timeout",
836
 
                                   type_func = _timedelta_to_milliseconds)
837
 
    extended_timeout = notifychangeproperty(dbus.UInt16, "ExtendedTimeout",
838
 
                                            type_func = _timedelta_to_milliseconds)
839
 
    interval = notifychangeproperty(dbus.UInt16, "Interval",
840
 
                                    type_func = _timedelta_to_milliseconds)
841
 
    checker_command = notifychangeproperty(dbus.String, "Checker")
842
 
    
843
 
    del notifychangeproperty
 
759
    def _get_approvals_pending(self):
 
760
        return self._approvals_pending
 
761
    def _set_approvals_pending(self, value):
 
762
        old_value = self._approvals_pending
 
763
        self._approvals_pending = value
 
764
        bval = bool(value)
 
765
        if (hasattr(self, "dbus_object_path")
 
766
            and bval is not bool(old_value)):
 
767
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
768
            self.PropertyChanged(dbus.String("ApprovalPending"),
 
769
                                 dbus_bool)
 
770
 
 
771
    approvals_pending = property(_get_approvals_pending,
 
772
                                 _set_approvals_pending)
 
773
    del _get_approvals_pending, _set_approvals_pending
 
774
    
 
775
    @staticmethod
 
776
    def _datetime_to_dbus(dt, variant_level=0):
 
777
        """Convert a UTC datetime.datetime() to a D-Bus type."""
 
778
        return dbus.String(dt.isoformat(),
 
779
                           variant_level=variant_level)
 
780
    
 
781
    def enable(self):
 
782
        oldstate = getattr(self, "enabled", False)
 
783
        r = Client.enable(self)
 
784
        if oldstate != self.enabled:
 
785
            # Emit D-Bus signals
 
786
            self.PropertyChanged(dbus.String("Enabled"),
 
787
                                 dbus.Boolean(True, variant_level=1))
 
788
            self.PropertyChanged(
 
789
                dbus.String("LastEnabled"),
 
790
                self._datetime_to_dbus(self.last_enabled,
 
791
                                       variant_level=1))
 
792
        return r
 
793
    
 
794
    def disable(self, quiet = False):
 
795
        oldstate = getattr(self, "enabled", False)
 
796
        r = Client.disable(self, quiet=quiet)
 
797
        if not quiet and oldstate != self.enabled:
 
798
            # Emit D-Bus signal
 
799
            self.PropertyChanged(dbus.String("Enabled"),
 
800
                                 dbus.Boolean(False, variant_level=1))
 
801
        return r
844
802
    
845
803
    def __del__(self, *args, **kwargs):
846
804
        try:
855
813
                         *args, **kwargs):
856
814
        self.checker_callback_tag = None
857
815
        self.checker = None
 
816
        # Emit D-Bus signal
 
817
        self.PropertyChanged(dbus.String("CheckerRunning"),
 
818
                             dbus.Boolean(False, variant_level=1))
858
819
        if os.WIFEXITED(condition):
859
820
            exitstatus = os.WEXITSTATUS(condition)
860
821
            # Emit D-Bus signal
870
831
        return Client.checker_callback(self, pid, condition, command,
871
832
                                       *args, **kwargs)
872
833
    
 
834
    def checked_ok(self, *args, **kwargs):
 
835
        Client.checked_ok(self, *args, **kwargs)
 
836
        # Emit D-Bus signal
 
837
        self.PropertyChanged(
 
838
            dbus.String("LastCheckedOK"),
 
839
            (self._datetime_to_dbus(self.last_checked_ok,
 
840
                                    variant_level=1)))
 
841
    
 
842
    def need_approval(self, *args, **kwargs):
 
843
        r = Client.need_approval(self, *args, **kwargs)
 
844
        # Emit D-Bus signal
 
845
        self.PropertyChanged(
 
846
            dbus.String("LastApprovalRequest"),
 
847
            (self._datetime_to_dbus(self.last_approval_request,
 
848
                                    variant_level=1)))
 
849
        return r
 
850
    
873
851
    def start_checker(self, *args, **kwargs):
874
852
        old_checker = self.checker
875
853
        if self.checker is not None:
882
860
            and old_checker_pid != self.checker.pid):
883
861
            # Emit D-Bus signal
884
862
            self.CheckerStarted(self.current_checker_command)
 
863
            self.PropertyChanged(
 
864
                dbus.String("CheckerRunning"),
 
865
                dbus.Boolean(True, variant_level=1))
885
866
        return r
886
867
    
 
868
    def stop_checker(self, *args, **kwargs):
 
869
        old_checker = getattr(self, "checker", None)
 
870
        r = Client.stop_checker(self, *args, **kwargs)
 
871
        if (old_checker is not None
 
872
            and getattr(self, "checker", None) is None):
 
873
            self.PropertyChanged(dbus.String("CheckerRunning"),
 
874
                                 dbus.Boolean(False, variant_level=1))
 
875
        return r
 
876
 
887
877
    def _reset_approved(self):
888
878
        self._approved = None
889
879
        return False
891
881
    def approve(self, value=True):
892
882
        self.send_changedstate()
893
883
        self._approved = value
894
 
        gobject.timeout_add(_timedelta_to_milliseconds
 
884
        gobject.timeout_add(self._timedelta_to_milliseconds
895
885
                            (self.approval_duration),
896
886
                            self._reset_approved)
897
887
    
989
979
        if value is None:       # get
990
980
            return dbus.Boolean(self.approved_by_default)
991
981
        self.approved_by_default = bool(value)
 
982
        # Emit D-Bus signal
 
983
        self.PropertyChanged(dbus.String("ApprovedByDefault"),
 
984
                             dbus.Boolean(value, variant_level=1))
992
985
    
993
986
    # ApprovalDelay - property
994
987
    @dbus_service_property(_interface, signature="t",
997
990
        if value is None:       # get
998
991
            return dbus.UInt64(self.approval_delay_milliseconds())
999
992
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
993
        # Emit D-Bus signal
 
994
        self.PropertyChanged(dbus.String("ApprovalDelay"),
 
995
                             dbus.UInt64(value, variant_level=1))
1000
996
    
1001
997
    # ApprovalDuration - property
1002
998
    @dbus_service_property(_interface, signature="t",
1003
999
                           access="readwrite")
1004
1000
    def ApprovalDuration_dbus_property(self, value=None):
1005
1001
        if value is None:       # get
1006
 
            return dbus.UInt64(_timedelta_to_milliseconds(
 
1002
            return dbus.UInt64(self._timedelta_to_milliseconds(
1007
1003
                    self.approval_duration))
1008
1004
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
1005
        # Emit D-Bus signal
 
1006
        self.PropertyChanged(dbus.String("ApprovalDuration"),
 
1007
                             dbus.UInt64(value, variant_level=1))
1009
1008
    
1010
1009
    # Name - property
1011
1010
    @dbus_service_property(_interface, signature="s", access="read")
1024
1023
        if value is None:       # get
1025
1024
            return dbus.String(self.host)
1026
1025
        self.host = value
 
1026
        # Emit D-Bus signal
 
1027
        self.PropertyChanged(dbus.String("Host"),
 
1028
                             dbus.String(value, variant_level=1))
1027
1029
    
1028
1030
    # Created - property
1029
1031
    @dbus_service_property(_interface, signature="s", access="read")
1030
1032
    def Created_dbus_property(self):
1031
 
        return dbus.String(datetime_to_dbus(self.created))
 
1033
        return dbus.String(self._datetime_to_dbus(self.created))
1032
1034
    
1033
1035
    # LastEnabled - property
1034
1036
    @dbus_service_property(_interface, signature="s", access="read")
1035
1037
    def LastEnabled_dbus_property(self):
1036
 
        return datetime_to_dbus(self.last_enabled)
 
1038
        if self.last_enabled is None:
 
1039
            return dbus.String("")
 
1040
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
1037
1041
    
1038
1042
    # Enabled - property
1039
1043
    @dbus_service_property(_interface, signature="b",
1053
1057
        if value is not None:
1054
1058
            self.checked_ok()
1055
1059
            return
1056
 
        return datetime_to_dbus(self.last_checked_ok)
1057
 
    
1058
 
    # Expires - property
1059
 
    @dbus_service_property(_interface, signature="s", access="read")
1060
 
    def Expires_dbus_property(self):
1061
 
        return datetime_to_dbus(self.expires)
 
1060
        if self.last_checked_ok is None:
 
1061
            return dbus.String("")
 
1062
        return dbus.String(self._datetime_to_dbus(self
 
1063
                                                  .last_checked_ok))
1062
1064
    
1063
1065
    # LastApprovalRequest - property
1064
1066
    @dbus_service_property(_interface, signature="s", access="read")
1065
1067
    def LastApprovalRequest_dbus_property(self):
1066
 
        return datetime_to_dbus(self.last_approval_request)
 
1068
        if self.last_approval_request is None:
 
1069
            return dbus.String("")
 
1070
        return dbus.String(self.
 
1071
                           _datetime_to_dbus(self
 
1072
                                             .last_approval_request))
1067
1073
    
1068
1074
    # Timeout - property
1069
1075
    @dbus_service_property(_interface, signature="t",
1072
1078
        if value is None:       # get
1073
1079
            return dbus.UInt64(self.timeout_milliseconds())
1074
1080
        self.timeout = datetime.timedelta(0, 0, 0, value)
 
1081
        # Emit D-Bus signal
 
1082
        self.PropertyChanged(dbus.String("Timeout"),
 
1083
                             dbus.UInt64(value, variant_level=1))
1075
1084
        if getattr(self, "disable_initiator_tag", None) is None:
1076
1085
            return
1077
1086
        # Reschedule timeout
1078
1087
        gobject.source_remove(self.disable_initiator_tag)
1079
1088
        self.disable_initiator_tag = None
1080
 
        self.expires = None
1081
1089
        time_to_die = (self.
1082
1090
                       _timedelta_to_milliseconds((self
1083
1091
                                                   .last_checked_ok
1088
1096
            # The timeout has passed
1089
1097
            self.disable()
1090
1098
        else:
1091
 
            self.expires = (datetime.datetime.utcnow()
1092
 
                            + datetime.timedelta(milliseconds = time_to_die))
1093
1099
            self.disable_initiator_tag = (gobject.timeout_add
1094
1100
                                          (time_to_die, self.disable))
1095
1101
    
1096
 
    # ExtendedTimeout - property
1097
 
    @dbus_service_property(_interface, signature="t",
1098
 
                           access="readwrite")
1099
 
    def ExtendedTimeout_dbus_property(self, value=None):
1100
 
        if value is None:       # get
1101
 
            return dbus.UInt64(self.extended_timeout_milliseconds())
1102
 
        self.extended_timeout = datetime.timedelta(0, 0, 0, value)
1103
 
    
1104
1102
    # Interval - property
1105
1103
    @dbus_service_property(_interface, signature="t",
1106
1104
                           access="readwrite")
1108
1106
        if value is None:       # get
1109
1107
            return dbus.UInt64(self.interval_milliseconds())
1110
1108
        self.interval = datetime.timedelta(0, 0, 0, value)
 
1109
        # Emit D-Bus signal
 
1110
        self.PropertyChanged(dbus.String("Interval"),
 
1111
                             dbus.UInt64(value, variant_level=1))
1111
1112
        if getattr(self, "checker_initiator_tag", None) is None:
1112
1113
            return
1113
1114
        # Reschedule checker run
1115
1116
        self.checker_initiator_tag = (gobject.timeout_add
1116
1117
                                      (value, self.start_checker))
1117
1118
        self.start_checker()    # Start one now, too
1118
 
    
 
1119
 
1119
1120
    # Checker - property
1120
1121
    @dbus_service_property(_interface, signature="s",
1121
1122
                           access="readwrite")
1123
1124
        if value is None:       # get
1124
1125
            return dbus.String(self.checker_command)
1125
1126
        self.checker_command = value
 
1127
        # Emit D-Bus signal
 
1128
        self.PropertyChanged(dbus.String("Checker"),
 
1129
                             dbus.String(self.checker_command,
 
1130
                                         variant_level=1))
1126
1131
    
1127
1132
    # CheckerRunning - property
1128
1133
    @dbus_service_property(_interface, signature="b",
1155
1160
        self._pipe.send(('init', fpr, address))
1156
1161
        if not self._pipe.recv():
1157
1162
            raise KeyError()
1158
 
    
 
1163
 
1159
1164
    def __getattribute__(self, name):
1160
1165
        if(name == '_pipe'):
1161
1166
            return super(ProxyClient, self).__getattribute__(name)
1168
1173
                self._pipe.send(('funcall', name, args, kwargs))
1169
1174
                return self._pipe.recv()[1]
1170
1175
            return func
1171
 
    
 
1176
 
1172
1177
    def __setattr__(self, name, value):
1173
1178
        if(name == '_pipe'):
1174
1179
            return super(ProxyClient, self).__setattr__(name, value)
1187
1192
                        unicode(self.client_address))
1188
1193
            logger.debug("Pipe FD: %d",
1189
1194
                         self.server.child_pipe.fileno())
1190
 
            
 
1195
 
1191
1196
            session = (gnutls.connection
1192
1197
                       .ClientSession(self.request,
1193
1198
                                      gnutls.connection
1194
1199
                                      .X509Credentials()))
1195
 
            
 
1200
 
1196
1201
            # Note: gnutls.connection.X509Credentials is really a
1197
1202
            # generic GnuTLS certificate credentials object so long as
1198
1203
            # no X.509 keys are added to it.  Therefore, we can use it
1199
1204
            # here despite using OpenPGP certificates.
1200
 
            
 
1205
 
1201
1206
            #priority = ':'.join(("NONE", "+VERS-TLS1.1",
1202
1207
            #                      "+AES-256-CBC", "+SHA1",
1203
1208
            #                      "+COMP-NULL", "+CTYPE-OPENPGP",
1209
1214
            (gnutls.library.functions
1210
1215
             .gnutls_priority_set_direct(session._c_object,
1211
1216
                                         priority, None))
1212
 
            
 
1217
 
1213
1218
            # Start communication using the Mandos protocol
1214
1219
            # Get protocol number
1215
1220
            line = self.request.makefile().readline()
1220
1225
            except (ValueError, IndexError, RuntimeError) as error:
1221
1226
                logger.error("Unknown protocol version: %s", error)
1222
1227
                return
1223
 
            
 
1228
 
1224
1229
            # Start GnuTLS connection
1225
1230
            try:
1226
1231
                session.handshake()
1230
1235
                # established.  Just abandon the request.
1231
1236
                return
1232
1237
            logger.debug("Handshake succeeded")
1233
 
            
 
1238
 
1234
1239
            approval_required = False
1235
1240
            try:
1236
1241
                try:
1241
1246
                    logger.warning("Bad certificate: %s", error)
1242
1247
                    return
1243
1248
                logger.debug("Fingerprint: %s", fpr)
1244
 
                
 
1249
 
1245
1250
                try:
1246
1251
                    client = ProxyClient(child_pipe, fpr,
1247
1252
                                         self.client_address)
1255
1260
                
1256
1261
                while True:
1257
1262
                    if not client.enabled:
1258
 
                        logger.info("Client %s is disabled",
 
1263
                        logger.warning("Client %s is disabled",
1259
1264
                                       client.name)
1260
1265
                        if self.server.use_dbus:
1261
1266
                            # Emit D-Bus signal
1313
1318
                                 sent, len(client.secret)
1314
1319
                                 - (sent_size + sent))
1315
1320
                    sent_size += sent
1316
 
                
 
1321
 
1317
1322
                logger.info("Sending secret to %s", client.name)
1318
1323
                # bump the timeout as if seen
1319
 
                client.checked_ok(client.extended_timeout)
 
1324
                client.checked_ok()
1320
1325
                if self.server.use_dbus:
1321
1326
                    # Emit D-Bus signal
1322
1327
                    client.GotSecret()
1407
1412
        multiprocessing.Process(target = self.sub_process_main,
1408
1413
                                args = (request, address)).start()
1409
1414
 
1410
 
 
1411
1415
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1412
1416
    """ adds a pipe to the MixIn """
1413
1417
    def process_request(self, request, client_address):
1416
1420
        This function creates a new pipe in self.pipe
1417
1421
        """
1418
1422
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1419
 
        
 
1423
 
1420
1424
        super(MultiprocessingMixInWithPipe,
1421
1425
              self).process_request(request, client_address)
1422
1426
        self.child_pipe.close()
1423
1427
        self.add_pipe(parent_pipe)
1424
 
    
 
1428
 
1425
1429
    def add_pipe(self, parent_pipe):
1426
1430
        """Dummy function; override as necessary"""
1427
1431
        raise NotImplementedError
1428
1432
 
1429
 
 
1430
1433
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1431
1434
                     socketserver.TCPServer, object):
1432
1435
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1558
1561
                    client = c
1559
1562
                    break
1560
1563
            else:
1561
 
                logger.info("Client not found for fingerprint: %s, ad"
1562
 
                            "dress: %s", fpr, address)
 
1564
                logger.warning("Client not found for fingerprint: %s, ad"
 
1565
                               "dress: %s", fpr, address)
1563
1566
                if self.use_dbus:
1564
1567
                    # Emit D-Bus signal
1565
1568
                    mandos_dbus_service.ClientNotFound(fpr, address[0])
1580
1583
            kwargs = request[3]
1581
1584
            
1582
1585
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1583
 
        
 
1586
 
1584
1587
        if command == 'getattr':
1585
1588
            attrname = request[1]
1586
1589
            if callable(client_object.__getattribute__(attrname)):
1592
1595
            attrname = request[1]
1593
1596
            value = request[2]
1594
1597
            setattr(client_object, attrname, value)
1595
 
        
 
1598
 
1596
1599
        return True
1597
1600
 
1598
1601
 
1777
1780
    debuglevel = server_settings["debuglevel"]
1778
1781
    use_dbus = server_settings["use_dbus"]
1779
1782
    use_ipv6 = server_settings["use_ipv6"]
1780
 
    
 
1783
 
1781
1784
    if server_settings["servicename"] != "Mandos":
1782
1785
        syslogger.setFormatter(logging.Formatter
1783
1786
                               ('Mandos (%s) [%%(process)d]:'
1785
1788
                                % server_settings["servicename"]))
1786
1789
    
1787
1790
    # Parse config file with clients
1788
 
    client_defaults = { "timeout": "5m",
1789
 
                        "extended_timeout": "15m",
1790
 
                        "interval": "2m",
 
1791
    client_defaults = { "timeout": "1h",
 
1792
                        "interval": "5m",
1791
1793
                        "checker": "fping -q -- %%(host)s",
1792
1794
                        "host": "",
1793
1795
                        "approval_delay": "0s",
1844
1846
        level = getattr(logging, debuglevel.upper())
1845
1847
        syslogger.setLevel(level)
1846
1848
        console.setLevel(level)
1847
 
    
 
1849
 
1848
1850
    if debug:
1849
1851
        # Enable all possible GnuTLS debugging
1850
1852
        
1937
1939
        del pidfilename
1938
1940
        
1939
1941
        signal.signal(signal.SIGINT, signal.SIG_IGN)
1940
 
    
 
1942
 
1941
1943
    signal.signal(signal.SIGHUP, lambda signum, frame: sys.exit())
1942
1944
    signal.signal(signal.SIGTERM, lambda signum, frame: sys.exit())
1943
1945
    
2064
2066
    # Must run before the D-Bus bus name gets deregistered
2065
2067
    cleanup()
2066
2068
 
2067
 
 
2068
2069
if __name__ == '__main__':
2069
2070
    main()