/mandos/release

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

« back to all changes in this revision

Viewing changes to mandos

* mandos (Client.runtime_expansions): New attribute containing the
                                      allowed runtime expansions.

* mandos-clients.conf.xml (OPTIONS): Reordered alphabetically.

* mandos-ctl: Bug fix: print timeout and interval values pretty again.

* mandos-ctl.xml (EXAMPLE): Added more examples.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
import logging
56
56
import logging.handlers
57
57
import pwd
58
 
from contextlib import closing
 
58
import contextlib
59
59
import struct
60
60
import fcntl
61
61
import functools
 
62
import cPickle as pickle
 
63
import multiprocessing
62
64
 
63
65
import dbus
64
66
import dbus.service
81
83
 
82
84
version = "1.0.14"
83
85
 
 
86
#logger = logging.getLogger(u'mandos')
84
87
logger = logging.Logger(u'mandos')
85
88
syslogger = (logging.handlers.SysLogHandler
86
89
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
154
157
                            u" after %i retries, exiting.",
155
158
                            self.rename_count)
156
159
            raise AvahiServiceError(u"Too many renames")
157
 
        self.name = self.server.GetAlternativeServiceName(self.name)
 
160
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
158
161
        logger.info(u"Changing Zeroconf service name to %r ...",
159
 
                    unicode(self.name))
 
162
                    self.name)
160
163
        syslogger.setFormatter(logging.Formatter
161
164
                               (u'Mandos (%s) [%%(process)d]:'
162
165
                                u' %%(levelname)s: %%(message)s'
163
166
                                % self.name))
164
167
        self.remove()
165
 
        self.add()
 
168
        try:
 
169
            self.add()
 
170
        except dbus.exceptions.DBusException, error:
 
171
            logger.critical(u"DBusException: %s", error)
 
172
            self.cleanup()
 
173
            os._exit(1)
166
174
        self.rename_count += 1
167
175
    def remove(self):
168
176
        """Derived from the Avahi example code"""
191
199
        self.group.Commit()
192
200
    def entry_group_state_changed(self, state, error):
193
201
        """Derived from the Avahi example code"""
194
 
        logger.debug(u"Avahi state change: %i", state)
 
202
        logger.debug(u"Avahi entry group state change: %i", state)
195
203
        
196
204
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
197
205
            logger.debug(u"Zeroconf service established.")
210
218
            self.group = None
211
219
    def server_state_changed(self, state):
212
220
        """Derived from the Avahi example code"""
 
221
        logger.debug(u"Avahi server state change: %i", state)
213
222
        if state == avahi.SERVER_COLLISION:
214
223
            logger.error(u"Zeroconf server name collision")
215
224
            self.remove()
231
240
    """A representation of a client host served by this server.
232
241
    
233
242
    Attributes:
234
 
    name:       string; from the config file, used in log messages and
235
 
                        D-Bus identifiers
236
 
    fingerprint: string (40 or 32 hexadecimal digits); used to
237
 
                 uniquely identify the client
238
 
    secret:     bytestring; sent verbatim (over TLS) to client
239
 
    host:       string; available for use by the checker command
240
 
    created:    datetime.datetime(); (UTC) object creation
241
 
    last_enabled: datetime.datetime(); (UTC)
242
 
    enabled:    bool()
243
 
    last_checked_ok: datetime.datetime(); (UTC) or None
244
 
    timeout:    datetime.timedelta(); How long from last_checked_ok
245
 
                                      until this client is invalid
246
 
    interval:   datetime.timedelta(); How often to start a new checker
247
 
    disable_hook:  If set, called by disable() as disable_hook(self)
 
243
    _approved:   bool(); 'None' if not yet approved/disapproved
 
244
    approval_delay: datetime.timedelta(); Time to wait for approval
 
245
    approval_duration: datetime.timedelta(); Duration of one approval
248
246
    checker:    subprocess.Popen(); a running checker process used
249
247
                                    to see if the client lives.
250
248
                                    'None' if no process is running.
251
 
    checker_initiator_tag: a gobject event source tag, or None
252
 
    disable_initiator_tag: - '' -
253
249
    checker_callback_tag:  - '' -
254
 
    checker_command: string; External command which is run to check if
255
 
                     client lives.  %() expansions are done at
 
250
    checker_command: string; External command which is run to check
 
251
                     if client lives.  %() expansions are done at
256
252
                     runtime with vars(self) as dict, so that for
257
253
                     instance %(name)s can be used in the command.
 
254
    checker_initiator_tag: a gobject event source tag, or None
 
255
    created:    datetime.datetime(); (UTC) object creation
258
256
    current_checker_command: string; current running checker_command
 
257
    disable_hook:  If set, called by disable() as disable_hook(self)
 
258
    disable_initiator_tag: - '' -
 
259
    enabled:    bool()
 
260
    fingerprint: string (40 or 32 hexadecimal digits); used to
 
261
                 uniquely identify the client
 
262
    host:       string; available for use by the checker command
 
263
    interval:   datetime.timedelta(); How often to start a new checker
 
264
    last_checked_ok: datetime.datetime(); (UTC) or None
 
265
    last_enabled: datetime.datetime(); (UTC)
 
266
    name:       string; from the config file, used in log messages and
 
267
                        D-Bus identifiers
 
268
    secret:     bytestring; sent verbatim (over TLS) to client
 
269
    timeout:    datetime.timedelta(); How long from last_checked_ok
 
270
                                      until this client is disabled
 
271
    runtime_expansions: Allowed attributes for runtime expansion.
259
272
    """
260
273
    
 
274
    runtime_expansions = (u"approval_delay", u"approval_duration",
 
275
                          u"created", u"enabled", u"fingerprint",
 
276
                          u"host", u"interval", u"last_checked_ok",
 
277
                          u"last_enabled", u"name", u"timeout")
 
278
    
261
279
    @staticmethod
262
280
    def _timedelta_to_milliseconds(td):
263
281
        "Convert a datetime.timedelta() to milliseconds"
272
290
    def interval_milliseconds(self):
273
291
        "Return the 'interval' attribute in milliseconds"
274
292
        return self._timedelta_to_milliseconds(self.interval)
 
293
 
 
294
    def approval_delay_milliseconds(self):
 
295
        return self._timedelta_to_milliseconds(self.approval_delay)
275
296
    
276
297
    def __init__(self, name = None, disable_hook=None, config=None):
277
298
        """Note: the 'checker' key in 'config' sets the
290
311
        if u"secret" in config:
291
312
            self.secret = config[u"secret"].decode(u"base64")
292
313
        elif u"secfile" in config:
293
 
            with closing(open(os.path.expanduser
294
 
                              (os.path.expandvars
295
 
                               (config[u"secfile"])),
296
 
                              "rb")) as secfile:
 
314
            with open(os.path.expanduser(os.path.expandvars
 
315
                                         (config[u"secfile"])),
 
316
                      "rb") as secfile:
297
317
                self.secret = secfile.read()
298
318
        else:
299
319
            raise TypeError(u"No secret or secfile for client %s"
313
333
        self.checker_command = config[u"checker"]
314
334
        self.current_checker_command = None
315
335
        self.last_connect = None
 
336
        self._approved = None
 
337
        self.approved_by_default = config.get(u"approved_by_default",
 
338
                                              True)
 
339
        self.approvals_pending = 0
 
340
        self.approval_delay = string_to_delta(
 
341
            config[u"approval_delay"])
 
342
        self.approval_duration = string_to_delta(
 
343
            config[u"approval_duration"])
 
344
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
316
345
    
 
346
    def send_changedstate(self):
 
347
        self.changedstate.acquire()
 
348
        self.changedstate.notify_all()
 
349
        self.changedstate.release()
 
350
        
317
351
    def enable(self):
318
352
        """Start this client's checker and timeout hooks"""
319
353
        if getattr(self, u"enabled", False):
320
354
            # Already enabled
321
355
            return
 
356
        self.send_changedstate()
322
357
        self.last_enabled = datetime.datetime.utcnow()
323
358
        # Schedule a new checker to be started an 'interval' from now,
324
359
        # and every interval from then on.
338
373
        if not getattr(self, "enabled", False):
339
374
            return False
340
375
        if not quiet:
 
376
            self.send_changedstate()
 
377
        if not quiet:
341
378
            logger.info(u"Disabling client %s", self.name)
342
379
        if getattr(self, u"disable_initiator_tag", False):
343
380
            gobject.source_remove(self.disable_initiator_tag)
396
433
        # client would inevitably timeout, since no checker would get
397
434
        # a chance to run to completion.  If we instead leave running
398
435
        # checkers alone, the checker would have to take more time
399
 
        # than 'timeout' for the client to be declared invalid, which
400
 
        # is as it should be.
 
436
        # than 'timeout' for the client to be disabled, which is as it
 
437
        # should be.
401
438
        
402
439
        # If a checker exists, make sure it is not a zombie
403
440
        try:
419
456
                command = self.checker_command % self.host
420
457
            except TypeError:
421
458
                # Escape attributes for the shell
422
 
                escaped_attrs = dict((key,
423
 
                                      re.escape(unicode(str(val),
424
 
                                                        errors=
425
 
                                                        u'replace')))
426
 
                                     for key, val in
427
 
                                     vars(self).iteritems())
 
459
                escaped_attrs = dict(
 
460
                    (attr,
 
461
                     re.escape(unicode(str(getattr(self, attr, u"")),
 
462
                                       errors=
 
463
                                       u'replace')))
 
464
                    for attr in
 
465
                    self.runtime_expansions)
 
466
 
428
467
                try:
429
468
                    command = self.checker_command % escaped_attrs
430
469
                except TypeError, error:
475
514
            if error.errno != errno.ESRCH: # No such process
476
515
                raise
477
516
        self.checker = None
478
 
    
479
 
    def still_valid(self):
480
 
        """Has the timeout not yet passed for this client?"""
481
 
        if not getattr(self, u"enabled", False):
482
 
            return False
483
 
        now = datetime.datetime.utcnow()
484
 
        if self.last_checked_ok is None:
485
 
            return now < (self.created + self.timeout)
486
 
        else:
487
 
            return now < (self.last_checked_ok + self.timeout)
488
 
 
489
517
 
490
518
def dbus_service_property(dbus_interface, signature=u"v",
491
519
                          access=u"readwrite", byte_arrays=False):
499
527
    dbus.service.method, except there is only "signature", since the
500
528
    type from Get() and the type sent to Set() is the same.
501
529
    """
 
530
    # Encoding deeply encoded byte arrays is not supported yet by the
 
531
    # "Set" method, so we fail early here:
 
532
    if byte_arrays and signature != u"ay":
 
533
        raise ValueError(u"Byte arrays not supported for non-'ay'"
 
534
                         u" signature %r" % signature)
502
535
    def decorator(func):
503
536
        func._dbus_is_property = True
504
537
        func._dbus_interface = dbus_interface
590
623
        if prop._dbus_access == u"read":
591
624
            raise DBusPropertyAccessException(property_name)
592
625
        if prop._dbus_get_args_options[u"byte_arrays"]:
 
626
            # The byte_arrays option is not supported yet on
 
627
            # signatures other than "ay".
 
628
            if prop._dbus_signature != u"ay":
 
629
                raise ValueError
593
630
            value = dbus.ByteArray(''.join(unichr(byte)
594
631
                                           for byte in value))
595
632
        prop(value)
674
711
    dbus_object_path: dbus.ObjectPath
675
712
    bus: dbus.SystemBus()
676
713
    """
 
714
    
 
715
    runtime_expansions = (Client.runtime_expansions
 
716
                          + (u"dbus_object_path",))
 
717
    
677
718
    # dbus.service.Object doesn't use super(), so we can't either.
678
719
    
679
720
    def __init__(self, bus = None, *args, **kwargs):
 
721
        self._approvals_pending = 0
680
722
        self.bus = bus
681
723
        Client.__init__(self, *args, **kwargs)
682
724
        # Only now, when this client is initialized, can it show up on
686
728
                                  + self.name.replace(u".", u"_")))
687
729
        DBusObjectWithProperties.__init__(self, self.bus,
688
730
                                          self.dbus_object_path)
 
731
        
 
732
    def _get_approvals_pending(self):
 
733
        return self._approvals_pending
 
734
    def _set_approvals_pending(self, value):
 
735
        old_value = self._approvals_pending
 
736
        self._approvals_pending = value
 
737
        bval = bool(value)
 
738
        if (hasattr(self, "dbus_object_path")
 
739
            and bval is not bool(old_value)):
 
740
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
741
            self.PropertyChanged(dbus.String(u"ApprovalPending"),
 
742
                                 dbus_bool)
 
743
 
 
744
    approvals_pending = property(_get_approvals_pending,
 
745
                                 _set_approvals_pending)
 
746
    del _get_approvals_pending, _set_approvals_pending
689
747
    
690
748
    @staticmethod
691
749
    def _datetime_to_dbus(dt, variant_level=0):
698
756
        r = Client.enable(self)
699
757
        if oldstate != self.enabled:
700
758
            # Emit D-Bus signals
701
 
            self.PropertyChanged(dbus.String(u"enabled"),
 
759
            self.PropertyChanged(dbus.String(u"Enabled"),
702
760
                                 dbus.Boolean(True, variant_level=1))
703
761
            self.PropertyChanged(
704
 
                dbus.String(u"last_enabled"),
 
762
                dbus.String(u"LastEnabled"),
705
763
                self._datetime_to_dbus(self.last_enabled,
706
764
                                       variant_level=1))
707
765
        return r
711
769
        r = Client.disable(self, quiet=quiet)
712
770
        if not quiet and oldstate != self.enabled:
713
771
            # Emit D-Bus signal
714
 
            self.PropertyChanged(dbus.String(u"enabled"),
 
772
            self.PropertyChanged(dbus.String(u"Enabled"),
715
773
                                 dbus.Boolean(False, variant_level=1))
716
774
        return r
717
775
    
729
787
        self.checker_callback_tag = None
730
788
        self.checker = None
731
789
        # Emit D-Bus signal
732
 
        self.PropertyChanged(dbus.String(u"checker_running"),
 
790
        self.PropertyChanged(dbus.String(u"CheckerRunning"),
733
791
                             dbus.Boolean(False, variant_level=1))
734
792
        if os.WIFEXITED(condition):
735
793
            exitstatus = os.WEXITSTATUS(condition)
750
808
        r = Client.checked_ok(self, *args, **kwargs)
751
809
        # Emit D-Bus signal
752
810
        self.PropertyChanged(
753
 
            dbus.String(u"last_checked_ok"),
 
811
            dbus.String(u"LastCheckedOK"),
754
812
            (self._datetime_to_dbus(self.last_checked_ok,
755
813
                                    variant_level=1)))
756
814
        return r
768
826
            # Emit D-Bus signal
769
827
            self.CheckerStarted(self.current_checker_command)
770
828
            self.PropertyChanged(
771
 
                dbus.String(u"checker_running"),
 
829
                dbus.String(u"CheckerRunning"),
772
830
                dbus.Boolean(True, variant_level=1))
773
831
        return r
774
832
    
777
835
        r = Client.stop_checker(self, *args, **kwargs)
778
836
        if (old_checker is not None
779
837
            and getattr(self, u"checker", None) is None):
780
 
            self.PropertyChanged(dbus.String(u"checker_running"),
 
838
            self.PropertyChanged(dbus.String(u"CheckerRunning"),
781
839
                                 dbus.Boolean(False, variant_level=1))
782
840
        return r
783
 
    
784
 
    ## D-Bus methods & signals
 
841
 
 
842
    def _reset_approved(self):
 
843
        self._approved = None
 
844
        return False
 
845
    
 
846
    def approve(self, value=True):
 
847
        self.send_changedstate()
 
848
        self._approved = value
 
849
        gobject.timeout_add(self._timedelta_to_milliseconds
 
850
                            (self.approval_duration),
 
851
                            self._reset_approved)
 
852
    
 
853
    
 
854
    ## D-Bus methods, signals & properties
785
855
    _interface = u"se.bsnet.fukt.Mandos.Client"
786
856
    
787
 
    # CheckedOK - method
788
 
    @dbus.service.method(_interface)
789
 
    def CheckedOK(self):
790
 
        return self.checked_ok()
 
857
    ## Signals
791
858
    
792
859
    # CheckerCompleted - signal
793
860
    @dbus.service.signal(_interface, signature=u"nxs")
810
877
    # GotSecret - signal
811
878
    @dbus.service.signal(_interface)
812
879
    def GotSecret(self):
813
 
        "D-Bus signal"
 
880
        """D-Bus signal
 
881
        Is sent after a successful transfer of secret from the Mandos
 
882
        server to mandos-client
 
883
        """
814
884
        pass
815
885
    
816
886
    # Rejected - signal
817
 
    @dbus.service.signal(_interface)
818
 
    def Rejected(self):
819
 
        "D-Bus signal"
820
 
        pass
 
887
    @dbus.service.signal(_interface, signature=u"s")
 
888
    def Rejected(self, reason):
 
889
        "D-Bus signal"
 
890
        pass
 
891
    
 
892
    # NeedApproval - signal
 
893
    @dbus.service.signal(_interface, signature=u"tb")
 
894
    def NeedApproval(self, timeout, default):
 
895
        "D-Bus signal"
 
896
        pass
 
897
    
 
898
    ## Methods
 
899
 
 
900
    # Approve - method
 
901
    @dbus.service.method(_interface, in_signature=u"b")
 
902
    def Approve(self, value):
 
903
        self.approve(value)
 
904
 
 
905
    # CheckedOK - method
 
906
    @dbus.service.method(_interface)
 
907
    def CheckedOK(self):
 
908
        return self.checked_ok()
821
909
    
822
910
    # Enable - method
823
911
    @dbus.service.method(_interface)
842
930
    def StopChecker(self):
843
931
        self.stop_checker()
844
932
    
845
 
    # name - property
 
933
    ## Properties
 
934
    
 
935
    # ApprovalPending - property
 
936
    @dbus_service_property(_interface, signature=u"b", access=u"read")
 
937
    def ApprovalPending_dbus_property(self):
 
938
        return dbus.Boolean(bool(self.approvals_pending))
 
939
    
 
940
    # ApprovedByDefault - property
 
941
    @dbus_service_property(_interface, signature=u"b",
 
942
                           access=u"readwrite")
 
943
    def ApprovedByDefault_dbus_property(self, value=None):
 
944
        if value is None:       # get
 
945
            return dbus.Boolean(self.approved_by_default)
 
946
        self.approved_by_default = bool(value)
 
947
        # Emit D-Bus signal
 
948
        self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
 
949
                             dbus.Boolean(value, variant_level=1))
 
950
    
 
951
    # ApprovalDelay - property
 
952
    @dbus_service_property(_interface, signature=u"t",
 
953
                           access=u"readwrite")
 
954
    def ApprovalDelay_dbus_property(self, value=None):
 
955
        if value is None:       # get
 
956
            return dbus.UInt64(self.approval_delay_milliseconds())
 
957
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
 
958
        # Emit D-Bus signal
 
959
        self.PropertyChanged(dbus.String(u"ApprovalDelay"),
 
960
                             dbus.UInt64(value, variant_level=1))
 
961
    
 
962
    # ApprovalDuration - property
 
963
    @dbus_service_property(_interface, signature=u"t",
 
964
                           access=u"readwrite")
 
965
    def ApprovalDuration_dbus_property(self, value=None):
 
966
        if value is None:       # get
 
967
            return dbus.UInt64(self._timedelta_to_milliseconds(
 
968
                    self.approval_duration))
 
969
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
 
970
        # Emit D-Bus signal
 
971
        self.PropertyChanged(dbus.String(u"ApprovalDuration"),
 
972
                             dbus.UInt64(value, variant_level=1))
 
973
    
 
974
    # Name - property
846
975
    @dbus_service_property(_interface, signature=u"s", access=u"read")
847
 
    def name_dbus_property(self):
 
976
    def Name_dbus_property(self):
848
977
        return dbus.String(self.name)
849
978
    
850
 
    # fingerprint - property
 
979
    # Fingerprint - property
851
980
    @dbus_service_property(_interface, signature=u"s", access=u"read")
852
 
    def fingerprint_dbus_property(self):
 
981
    def Fingerprint_dbus_property(self):
853
982
        return dbus.String(self.fingerprint)
854
983
    
855
 
    # host - property
 
984
    # Host - property
856
985
    @dbus_service_property(_interface, signature=u"s",
857
986
                           access=u"readwrite")
858
 
    def host_dbus_property(self, value=None):
 
987
    def Host_dbus_property(self, value=None):
859
988
        if value is None:       # get
860
989
            return dbus.String(self.host)
861
990
        self.host = value
862
991
        # Emit D-Bus signal
863
 
        self.PropertyChanged(dbus.String(u"host"),
 
992
        self.PropertyChanged(dbus.String(u"Host"),
864
993
                             dbus.String(value, variant_level=1))
865
994
    
866
 
    # created - property
 
995
    # Created - property
867
996
    @dbus_service_property(_interface, signature=u"s", access=u"read")
868
 
    def created_dbus_property(self):
 
997
    def Created_dbus_property(self):
869
998
        return dbus.String(self._datetime_to_dbus(self.created))
870
999
    
871
 
    # last_enabled - property
 
1000
    # LastEnabled - property
872
1001
    @dbus_service_property(_interface, signature=u"s", access=u"read")
873
 
    def last_enabled_dbus_property(self):
 
1002
    def LastEnabled_dbus_property(self):
874
1003
        if self.last_enabled is None:
875
1004
            return dbus.String(u"")
876
1005
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
877
1006
    
878
 
    # enabled - property
 
1007
    # Enabled - property
879
1008
    @dbus_service_property(_interface, signature=u"b",
880
1009
                           access=u"readwrite")
881
 
    def enabled_dbus_property(self, value=None):
 
1010
    def Enabled_dbus_property(self, value=None):
882
1011
        if value is None:       # get
883
1012
            return dbus.Boolean(self.enabled)
884
1013
        if value:
886
1015
        else:
887
1016
            self.disable()
888
1017
    
889
 
    # last_checked_ok - property
 
1018
    # LastCheckedOK - property
890
1019
    @dbus_service_property(_interface, signature=u"s",
891
1020
                           access=u"readwrite")
892
 
    def last_checked_ok_dbus_property(self, value=None):
 
1021
    def LastCheckedOK_dbus_property(self, value=None):
893
1022
        if value is not None:
894
1023
            self.checked_ok()
895
1024
            return
898
1027
        return dbus.String(self._datetime_to_dbus(self
899
1028
                                                  .last_checked_ok))
900
1029
    
901
 
    # timeout - property
 
1030
    # Timeout - property
902
1031
    @dbus_service_property(_interface, signature=u"t",
903
1032
                           access=u"readwrite")
904
 
    def timeout_dbus_property(self, value=None):
 
1033
    def Timeout_dbus_property(self, value=None):
905
1034
        if value is None:       # get
906
1035
            return dbus.UInt64(self.timeout_milliseconds())
907
1036
        self.timeout = datetime.timedelta(0, 0, 0, value)
908
1037
        # Emit D-Bus signal
909
 
        self.PropertyChanged(dbus.String(u"timeout"),
 
1038
        self.PropertyChanged(dbus.String(u"Timeout"),
910
1039
                             dbus.UInt64(value, variant_level=1))
911
1040
        if getattr(self, u"disable_initiator_tag", None) is None:
912
1041
            return
926
1055
            self.disable_initiator_tag = (gobject.timeout_add
927
1056
                                          (time_to_die, self.disable))
928
1057
    
929
 
    # interval - property
 
1058
    # Interval - property
930
1059
    @dbus_service_property(_interface, signature=u"t",
931
1060
                           access=u"readwrite")
932
 
    def interval_dbus_property(self, value=None):
 
1061
    def Interval_dbus_property(self, value=None):
933
1062
        if value is None:       # get
934
1063
            return dbus.UInt64(self.interval_milliseconds())
935
1064
        self.interval = datetime.timedelta(0, 0, 0, value)
936
1065
        # Emit D-Bus signal
937
 
        self.PropertyChanged(dbus.String(u"interval"),
 
1066
        self.PropertyChanged(dbus.String(u"Interval"),
938
1067
                             dbus.UInt64(value, variant_level=1))
939
1068
        if getattr(self, u"checker_initiator_tag", None) is None:
940
1069
            return
944
1073
                                      (value, self.start_checker))
945
1074
        self.start_checker()    # Start one now, too
946
1075
 
947
 
    # checker - property
 
1076
    # Checker - property
948
1077
    @dbus_service_property(_interface, signature=u"s",
949
1078
                           access=u"readwrite")
950
 
    def checker_dbus_property(self, value=None):
 
1079
    def Checker_dbus_property(self, value=None):
951
1080
        if value is None:       # get
952
1081
            return dbus.String(self.checker_command)
953
1082
        self.checker_command = value
954
1083
        # Emit D-Bus signal
955
 
        self.PropertyChanged(dbus.String(u"checker"),
 
1084
        self.PropertyChanged(dbus.String(u"Checker"),
956
1085
                             dbus.String(self.checker_command,
957
1086
                                         variant_level=1))
958
1087
    
959
 
    # checker_running - property
 
1088
    # CheckerRunning - property
960
1089
    @dbus_service_property(_interface, signature=u"b",
961
1090
                           access=u"readwrite")
962
 
    def checker_running_dbus_property(self, value=None):
 
1091
    def CheckerRunning_dbus_property(self, value=None):
963
1092
        if value is None:       # get
964
1093
            return dbus.Boolean(self.checker is not None)
965
1094
        if value:
967
1096
        else:
968
1097
            self.stop_checker()
969
1098
    
970
 
    # object_path - property
 
1099
    # ObjectPath - property
971
1100
    @dbus_service_property(_interface, signature=u"o", access=u"read")
972
 
    def object_path_dbus_property(self):
 
1101
    def ObjectPath_dbus_property(self):
973
1102
        return self.dbus_object_path # is already a dbus.ObjectPath
974
1103
    
975
 
    # secret = property
 
1104
    # Secret = property
976
1105
    @dbus_service_property(_interface, signature=u"ay",
977
1106
                           access=u"write", byte_arrays=True)
978
 
    def secret_dbus_property(self, value):
 
1107
    def Secret_dbus_property(self, value):
979
1108
        self.secret = str(value)
980
1109
    
981
1110
    del _interface
982
1111
 
983
1112
 
 
1113
class ProxyClient(object):
 
1114
    def __init__(self, child_pipe, fpr, address):
 
1115
        self._pipe = child_pipe
 
1116
        self._pipe.send(('init', fpr, address))
 
1117
        if not self._pipe.recv():
 
1118
            raise KeyError()
 
1119
 
 
1120
    def __getattribute__(self, name):
 
1121
        if(name == '_pipe'):
 
1122
            return super(ProxyClient, self).__getattribute__(name)
 
1123
        self._pipe.send(('getattr', name))
 
1124
        data = self._pipe.recv()
 
1125
        if data[0] == 'data':
 
1126
            return data[1]
 
1127
        if data[0] == 'function':
 
1128
            def func(*args, **kwargs):
 
1129
                self._pipe.send(('funcall', name, args, kwargs))
 
1130
                return self._pipe.recv()[1]
 
1131
            return func
 
1132
 
 
1133
    def __setattr__(self, name, value):
 
1134
        if(name == '_pipe'):
 
1135
            return super(ProxyClient, self).__setattr__(name, value)
 
1136
        self._pipe.send(('setattr', name, value))
 
1137
 
 
1138
 
984
1139
class ClientHandler(socketserver.BaseRequestHandler, object):
985
1140
    """A class to handle client connections.
986
1141
    
988
1143
    Note: This will run in its own forked process."""
989
1144
    
990
1145
    def handle(self):
991
 
        logger.info(u"TCP connection from: %s",
992
 
                    unicode(self.client_address))
993
 
        logger.debug(u"IPC Pipe FD: %d", self.server.pipe[1])
994
 
        # Open IPC pipe to parent process
995
 
        with closing(os.fdopen(self.server.pipe[1], u"w", 1)) as ipc:
 
1146
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
1147
            logger.info(u"TCP connection from: %s",
 
1148
                        unicode(self.client_address))
 
1149
            logger.debug(u"Pipe FD: %d",
 
1150
                         self.server.child_pipe.fileno())
 
1151
 
996
1152
            session = (gnutls.connection
997
1153
                       .ClientSession(self.request,
998
1154
                                      gnutls.connection
999
1155
                                      .X509Credentials()))
1000
 
            
1001
 
            line = self.request.makefile().readline()
1002
 
            logger.debug(u"Protocol version: %r", line)
1003
 
            try:
1004
 
                if int(line.strip().split()[0]) > 1:
1005
 
                    raise RuntimeError
1006
 
            except (ValueError, IndexError, RuntimeError), error:
1007
 
                logger.error(u"Unknown protocol version: %s", error)
1008
 
                return
1009
 
            
 
1156
 
1010
1157
            # Note: gnutls.connection.X509Credentials is really a
1011
1158
            # generic GnuTLS certificate credentials object so long as
1012
1159
            # no X.509 keys are added to it.  Therefore, we can use it
1013
1160
            # here despite using OpenPGP certificates.
1014
 
            
 
1161
 
1015
1162
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1016
1163
            #                      u"+AES-256-CBC", u"+SHA1",
1017
1164
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1023
1170
            (gnutls.library.functions
1024
1171
             .gnutls_priority_set_direct(session._c_object,
1025
1172
                                         priority, None))
1026
 
            
 
1173
 
 
1174
            # Start communication using the Mandos protocol
 
1175
            # Get protocol number
 
1176
            line = self.request.makefile().readline()
 
1177
            logger.debug(u"Protocol version: %r", line)
 
1178
            try:
 
1179
                if int(line.strip().split()[0]) > 1:
 
1180
                    raise RuntimeError
 
1181
            except (ValueError, IndexError, RuntimeError), error:
 
1182
                logger.error(u"Unknown protocol version: %s", error)
 
1183
                return
 
1184
 
 
1185
            # Start GnuTLS connection
1027
1186
            try:
1028
1187
                session.handshake()
1029
1188
            except gnutls.errors.GNUTLSError, error:
1032
1191
                # established.  Just abandon the request.
1033
1192
                return
1034
1193
            logger.debug(u"Handshake succeeded")
 
1194
 
 
1195
            approval_required = False
1035
1196
            try:
1036
 
                fpr = self.fingerprint(self.peer_certificate(session))
1037
 
            except (TypeError, gnutls.errors.GNUTLSError), error:
1038
 
                logger.warning(u"Bad certificate: %s", error)
1039
 
                session.bye()
1040
 
                return
1041
 
            logger.debug(u"Fingerprint: %s", fpr)
 
1197
                try:
 
1198
                    fpr = self.fingerprint(self.peer_certificate
 
1199
                                           (session))
 
1200
                except (TypeError, gnutls.errors.GNUTLSError), error:
 
1201
                    logger.warning(u"Bad certificate: %s", error)
 
1202
                    return
 
1203
                logger.debug(u"Fingerprint: %s", fpr)
 
1204
 
 
1205
                try:
 
1206
                    client = ProxyClient(child_pipe, fpr,
 
1207
                                         self.client_address)
 
1208
                except KeyError:
 
1209
                    return
 
1210
                
 
1211
                if client.approval_delay:
 
1212
                    delay = client.approval_delay
 
1213
                    client.approvals_pending += 1
 
1214
                    approval_required = True
 
1215
                
 
1216
                while True:
 
1217
                    if not client.enabled:
 
1218
                        logger.warning(u"Client %s is disabled",
 
1219
                                       client.name)
 
1220
                        if self.server.use_dbus:
 
1221
                            # Emit D-Bus signal
 
1222
                            client.Rejected("Disabled")                    
 
1223
                        return
 
1224
                    
 
1225
                    if client._approved or not client.approval_delay:
 
1226
                        #We are approved or approval is disabled
 
1227
                        break
 
1228
                    elif client._approved is None:
 
1229
                        logger.info(u"Client %s needs approval",
 
1230
                                    client.name)
 
1231
                        if self.server.use_dbus:
 
1232
                            # Emit D-Bus signal
 
1233
                            client.NeedApproval(
 
1234
                                client.approval_delay_milliseconds(),
 
1235
                                client.approved_by_default)
 
1236
                    else:
 
1237
                        logger.warning(u"Client %s was not approved",
 
1238
                                       client.name)
 
1239
                        if self.server.use_dbus:
 
1240
                            # Emit D-Bus signal
 
1241
                            client.Rejected("Denied")
 
1242
                        return
 
1243
                    
 
1244
                    #wait until timeout or approved
 
1245
                    #x = float(client._timedelta_to_milliseconds(delay))
 
1246
                    time = datetime.datetime.now()
 
1247
                    client.changedstate.acquire()
 
1248
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1249
                    client.changedstate.release()
 
1250
                    time2 = datetime.datetime.now()
 
1251
                    if (time2 - time) >= delay:
 
1252
                        if not client.approved_by_default:
 
1253
                            logger.warning("Client %s timed out while"
 
1254
                                           " waiting for approval",
 
1255
                                           client.name)
 
1256
                            if self.server.use_dbus:
 
1257
                                # Emit D-Bus signal
 
1258
                                client.Rejected("Approval timed out")
 
1259
                            return
 
1260
                        else:
 
1261
                            break
 
1262
                    else:
 
1263
                        delay -= time2 - time
 
1264
                
 
1265
                sent_size = 0
 
1266
                while sent_size < len(client.secret):
 
1267
                    try:
 
1268
                        sent = session.send(client.secret[sent_size:])
 
1269
                    except (gnutls.errors.GNUTLSError), error:
 
1270
                        logger.warning("gnutls send failed")
 
1271
                        return
 
1272
                    logger.debug(u"Sent: %d, remaining: %d",
 
1273
                                 sent, len(client.secret)
 
1274
                                 - (sent_size + sent))
 
1275
                    sent_size += sent
 
1276
 
 
1277
                logger.info(u"Sending secret to %s", client.name)
 
1278
                # bump the timeout as if seen
 
1279
                client.checked_ok()
 
1280
                if self.server.use_dbus:
 
1281
                    # Emit D-Bus signal
 
1282
                    client.GotSecret()
1042
1283
            
1043
 
            for c in self.server.clients:
1044
 
                if c.fingerprint == fpr:
1045
 
                    client = c
1046
 
                    break
1047
 
            else:
1048
 
                ipc.write(u"NOTFOUND %s %s\n"
1049
 
                          % (fpr, unicode(self.client_address)))
1050
 
                session.bye()
1051
 
                return
1052
 
            # Have to check if client.still_valid(), since it is
1053
 
            # possible that the client timed out while establishing
1054
 
            # the GnuTLS session.
1055
 
            if not client.still_valid():
1056
 
                ipc.write(u"INVALID %s\n" % client.name)
1057
 
                session.bye()
1058
 
                return
1059
 
            ipc.write(u"SENDING %s\n" % client.name)
1060
 
            sent_size = 0
1061
 
            while sent_size < len(client.secret):
1062
 
                sent = session.send(client.secret[sent_size:])
1063
 
                logger.debug(u"Sent: %d, remaining: %d",
1064
 
                             sent, len(client.secret)
1065
 
                             - (sent_size + sent))
1066
 
                sent_size += sent
1067
 
            session.bye()
 
1284
            finally:
 
1285
                if approval_required:
 
1286
                    client.approvals_pending -= 1
 
1287
                try:
 
1288
                    session.bye()
 
1289
                except (gnutls.errors.GNUTLSError), error:
 
1290
                    logger.warning("GnuTLS bye failed")
1068
1291
    
1069
1292
    @staticmethod
1070
1293
    def peer_certificate(session):
1130
1353
        return hex_fpr
1131
1354
 
1132
1355
 
1133
 
class ForkingMixInWithPipe(socketserver.ForkingMixIn, object):
1134
 
    """Like socketserver.ForkingMixIn, but also pass a pipe."""
 
1356
class MultiprocessingMixIn(object):
 
1357
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
1358
    def sub_process_main(self, request, address):
 
1359
        try:
 
1360
            self.finish_request(request, address)
 
1361
        except:
 
1362
            self.handle_error(request, address)
 
1363
        self.close_request(request)
 
1364
            
 
1365
    def process_request(self, request, address):
 
1366
        """Start a new process to process the request."""
 
1367
        multiprocessing.Process(target = self.sub_process_main,
 
1368
                                args = (request, address)).start()
 
1369
 
 
1370
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
1371
    """ adds a pipe to the MixIn """
1135
1372
    def process_request(self, request, client_address):
1136
1373
        """Overrides and wraps the original process_request().
1137
1374
        
1138
1375
        This function creates a new pipe in self.pipe
1139
1376
        """
1140
 
        self.pipe = os.pipe()
1141
 
        super(ForkingMixInWithPipe,
 
1377
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
 
1378
 
 
1379
        super(MultiprocessingMixInWithPipe,
1142
1380
              self).process_request(request, client_address)
1143
 
        os.close(self.pipe[1])  # close write end
1144
 
        self.add_pipe(self.pipe[0])
1145
 
    def add_pipe(self, pipe):
 
1381
        self.child_pipe.close()
 
1382
        self.add_pipe(parent_pipe)
 
1383
 
 
1384
    def add_pipe(self, parent_pipe):
1146
1385
        """Dummy function; override as necessary"""
1147
 
        os.close(pipe)
1148
 
 
1149
 
 
1150
 
class IPv6_TCPServer(ForkingMixInWithPipe,
 
1386
        pass
 
1387
 
 
1388
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1151
1389
                     socketserver.TCPServer, object):
1152
1390
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1153
1391
    
1238
1476
            return socketserver.TCPServer.server_activate(self)
1239
1477
    def enable(self):
1240
1478
        self.enabled = True
1241
 
    def add_pipe(self, pipe):
 
1479
    def add_pipe(self, parent_pipe):
1242
1480
        # Call "handle_ipc" for both data and EOF events
1243
 
        gobject.io_add_watch(pipe, gobject.IO_IN | gobject.IO_HUP,
1244
 
                             self.handle_ipc)
1245
 
    def handle_ipc(self, source, condition, file_objects={}):
 
1481
        gobject.io_add_watch(parent_pipe.fileno(),
 
1482
                             gobject.IO_IN | gobject.IO_HUP,
 
1483
                             functools.partial(self.handle_ipc,
 
1484
                                               parent_pipe = parent_pipe))
 
1485
        
 
1486
    def handle_ipc(self, source, condition, parent_pipe=None,
 
1487
                   client_object=None):
1246
1488
        condition_names = {
1247
1489
            gobject.IO_IN: u"IN",   # There is data to read.
1248
1490
            gobject.IO_OUT: u"OUT", # Data can be written (without
1257
1499
                                       for cond, name in
1258
1500
                                       condition_names.iteritems()
1259
1501
                                       if cond & condition)
1260
 
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1261
 
                     conditions_string)
1262
 
        
1263
 
        # Turn the pipe file descriptor into a Python file object
1264
 
        if source not in file_objects:
1265
 
            file_objects[source] = os.fdopen(source, u"r", 1)
1266
 
        
1267
 
        # Read a line from the file object
1268
 
        cmdline = file_objects[source].readline()
1269
 
        if not cmdline:             # Empty line means end of file
1270
 
            # close the IPC pipe
1271
 
            file_objects[source].close()
1272
 
            del file_objects[source]
1273
 
            
1274
 
            # Stop calling this function
1275
 
            return False
1276
 
        
1277
 
        logger.debug(u"IPC command: %r", cmdline)
1278
 
        
1279
 
        # Parse and act on command
1280
 
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1281
 
        
1282
 
        if cmd == u"NOTFOUND":
1283
 
            logger.warning(u"Client not found for fingerprint: %s",
1284
 
                           args)
1285
 
            if self.use_dbus:
1286
 
                # Emit D-Bus signal
1287
 
                mandos_dbus_service.ClientNotFound(args)
1288
 
        elif cmd == u"INVALID":
1289
 
            for client in self.clients:
1290
 
                if client.name == args:
1291
 
                    logger.warning(u"Client %s is invalid", args)
1292
 
                    if self.use_dbus:
1293
 
                        # Emit D-Bus signal
1294
 
                        client.Rejected()
1295
 
                    break
1296
 
            else:
1297
 
                logger.error(u"Unknown client %s is invalid", args)
1298
 
        elif cmd == u"SENDING":
1299
 
            for client in self.clients:
1300
 
                if client.name == args:
1301
 
                    logger.info(u"Sending secret to %s", client.name)
1302
 
                    client.checked_ok()
1303
 
                    if self.use_dbus:
1304
 
                        # Emit D-Bus signal
1305
 
                        client.GotSecret()
1306
 
                    break
1307
 
            else:
1308
 
                logger.error(u"Sending secret to unknown client %s",
1309
 
                             args)
1310
 
        else:
1311
 
            logger.error(u"Unknown IPC command: %r", cmdline)
1312
 
        
1313
 
        # Keep calling this function
 
1502
        # error or the other end of multiprocessing.Pipe has closed
 
1503
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1504
            return False
 
1505
        
 
1506
        # Read a request from the child
 
1507
        request = parent_pipe.recv()
 
1508
        command = request[0]
 
1509
        
 
1510
        if command == 'init':
 
1511
            fpr = request[1]
 
1512
            address = request[2]
 
1513
            
 
1514
            for c in self.clients:
 
1515
                if c.fingerprint == fpr:
 
1516
                    client = c
 
1517
                    break
 
1518
            else:
 
1519
                logger.warning(u"Client not found for fingerprint: %s, ad"
 
1520
                               u"dress: %s", fpr, address)
 
1521
                if self.use_dbus:
 
1522
                    # Emit D-Bus signal
 
1523
                    mandos_dbus_service.ClientNotFound(fpr, address)
 
1524
                parent_pipe.send(False)
 
1525
                return False
 
1526
            
 
1527
            gobject.io_add_watch(parent_pipe.fileno(),
 
1528
                                 gobject.IO_IN | gobject.IO_HUP,
 
1529
                                 functools.partial(self.handle_ipc,
 
1530
                                                   parent_pipe = parent_pipe,
 
1531
                                                   client_object = client))
 
1532
            parent_pipe.send(True)
 
1533
            # remove the old hook in favor of the new above hook on same fileno
 
1534
            return False
 
1535
        if command == 'funcall':
 
1536
            funcname = request[1]
 
1537
            args = request[2]
 
1538
            kwargs = request[3]
 
1539
            
 
1540
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1541
 
 
1542
        if command == 'getattr':
 
1543
            attrname = request[1]
 
1544
            if callable(client_object.__getattribute__(attrname)):
 
1545
                parent_pipe.send(('function',))
 
1546
            else:
 
1547
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1548
        
 
1549
        if command == 'setattr':
 
1550
            attrname = request[1]
 
1551
            value = request[2]
 
1552
            setattr(client_object, attrname, value)
 
1553
 
1314
1554
        return True
1315
1555
 
1316
1556
 
1367
1607
        def if_nametoindex(interface):
1368
1608
            "Get an interface index the hard way, i.e. using fcntl()"
1369
1609
            SIOCGIFINDEX = 0x8933  # From /usr/include/linux/sockios.h
1370
 
            with closing(socket.socket()) as s:
 
1610
            with contextlib.closing(socket.socket()) as s:
1371
1611
                ifreq = fcntl.ioctl(s, SIOCGIFINDEX,
1372
1612
                                    struct.pack(str(u"16s16x"),
1373
1613
                                                interface))
1419
1659
    parser.add_option("--debug", action=u"store_true",
1420
1660
                      help=u"Debug mode; run in foreground and log to"
1421
1661
                      u" terminal")
 
1662
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
 
1663
                      help=u"Debug level for stdout output")
1422
1664
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1423
1665
                      u" priority string (see GnuTLS documentation)")
1424
1666
    parser.add_option("--servicename", type=u"string",
1449
1691
                        u"servicename": u"Mandos",
1450
1692
                        u"use_dbus": u"True",
1451
1693
                        u"use_ipv6": u"True",
 
1694
                        u"debuglevel": u"",
1452
1695
                        }
1453
1696
    
1454
1697
    # Parse config file for server-global settings
1471
1714
    # options, if set.
1472
1715
    for option in (u"interface", u"address", u"port", u"debug",
1473
1716
                   u"priority", u"servicename", u"configdir",
1474
 
                   u"use_dbus", u"use_ipv6"):
 
1717
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
1475
1718
        value = getattr(options, option)
1476
1719
        if value is not None:
1477
1720
            server_settings[option] = value
1486
1729
    
1487
1730
    # For convenience
1488
1731
    debug = server_settings[u"debug"]
 
1732
    debuglevel = server_settings[u"debuglevel"]
1489
1733
    use_dbus = server_settings[u"use_dbus"]
1490
1734
    use_ipv6 = server_settings[u"use_ipv6"]
1491
 
    
1492
 
    if not debug:
1493
 
        syslogger.setLevel(logging.WARNING)
1494
 
        console.setLevel(logging.WARNING)
1495
 
    
 
1735
 
1496
1736
    if server_settings[u"servicename"] != u"Mandos":
1497
1737
        syslogger.setFormatter(logging.Formatter
1498
1738
                               (u'Mandos (%s) [%%(process)d]:'
1504
1744
                        u"interval": u"5m",
1505
1745
                        u"checker": u"fping -q -- %%(host)s",
1506
1746
                        u"host": u"",
 
1747
                        u"approval_delay": u"0s",
 
1748
                        u"approval_duration": u"1s",
1507
1749
                        }
1508
1750
    client_config = configparser.SafeConfigParser(client_defaults)
1509
1751
    client_config.read(os.path.join(server_settings[u"configdir"],
1515
1757
    tcp_server = MandosServer((server_settings[u"address"],
1516
1758
                               server_settings[u"port"]),
1517
1759
                              ClientHandler,
1518
 
                              interface=server_settings[u"interface"],
 
1760
                              interface=(server_settings[u"interface"]
 
1761
                                         or None),
1519
1762
                              use_ipv6=use_ipv6,
1520
1763
                              gnutls_priority=
1521
1764
                              server_settings[u"priority"],
1547
1790
        if error[0] != errno.EPERM:
1548
1791
            raise error
1549
1792
    
1550
 
    # Enable all possible GnuTLS debugging
 
1793
    if not debug and not debuglevel:
 
1794
        syslogger.setLevel(logging.WARNING)
 
1795
        console.setLevel(logging.WARNING)
 
1796
    if debuglevel:
 
1797
        level = getattr(logging, debuglevel.upper())
 
1798
        syslogger.setLevel(level)
 
1799
        console.setLevel(level)
 
1800
 
1551
1801
    if debug:
 
1802
        # Enable all possible GnuTLS debugging
 
1803
        
1552
1804
        # "Use a log level over 10 to enable all debugging options."
1553
1805
        # - GnuTLS manual
1554
1806
        gnutls.library.functions.gnutls_global_set_log_level(11)
1559
1811
        
1560
1812
        (gnutls.library.functions
1561
1813
         .gnutls_global_set_log_function(debug_gnutls))
 
1814
        
 
1815
        # Redirect stdin so all checkers get /dev/null
 
1816
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1817
        os.dup2(null, sys.stdin.fileno())
 
1818
        if null > 2:
 
1819
            os.close(null)
 
1820
    else:
 
1821
        # No console logging
 
1822
        logger.removeHandler(console)
 
1823
    
1562
1824
    
1563
1825
    global main_loop
1564
1826
    # From the Avahi example code
1582
1844
    if server_settings["interface"]:
1583
1845
        service.interface = (if_nametoindex
1584
1846
                             (str(server_settings[u"interface"])))
 
1847
 
 
1848
    if not debug:
 
1849
        # Close all input and output, do double fork, etc.
 
1850
        daemon()
 
1851
        
 
1852
    global multiprocessing_manager
 
1853
    multiprocessing_manager = multiprocessing.Manager()
1585
1854
    
1586
1855
    client_class = Client
1587
1856
    if use_dbus:
1588
1857
        client_class = functools.partial(ClientDBus, bus = bus)
 
1858
    def client_config_items(config, section):
 
1859
        special_settings = {
 
1860
            "approved_by_default":
 
1861
                lambda: config.getboolean(section,
 
1862
                                          "approved_by_default"),
 
1863
            }
 
1864
        for name, value in config.items(section):
 
1865
            try:
 
1866
                yield (name, special_settings[name]())
 
1867
            except KeyError:
 
1868
                yield (name, value)
 
1869
    
1589
1870
    tcp_server.clients.update(set(
1590
1871
            client_class(name = section,
1591
 
                         config= dict(client_config.items(section)))
 
1872
                         config= dict(client_config_items(
 
1873
                        client_config, section)))
1592
1874
            for section in client_config.sections()))
1593
1875
    if not tcp_server.clients:
1594
1876
        logger.warning(u"No clients defined")
1595
 
    
1596
 
    if debug:
1597
 
        # Redirect stdin so all checkers get /dev/null
1598
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1599
 
        os.dup2(null, sys.stdin.fileno())
1600
 
        if null > 2:
1601
 
            os.close(null)
1602
 
    else:
1603
 
        # No console logging
1604
 
        logger.removeHandler(console)
1605
 
        # Close all input and output, do double fork, etc.
1606
 
        daemon()
1607
 
    
 
1877
        
1608
1878
    try:
1609
 
        with closing(pidfile):
 
1879
        with pidfile:
1610
1880
            pid = os.getpid()
1611
1881
            pidfile.write(str(pid) + "\n")
1612
1882
        del pidfile
1630
1900
                dbus.service.Object.__init__(self, bus, u"/")
1631
1901
            _interface = u"se.bsnet.fukt.Mandos"
1632
1902
            
1633
 
            @dbus.service.signal(_interface, signature=u"oa{sv}")
1634
 
            def ClientAdded(self, objpath, properties):
 
1903
            @dbus.service.signal(_interface, signature=u"o")
 
1904
            def ClientAdded(self, objpath):
1635
1905
                "D-Bus signal"
1636
1906
                pass
1637
1907
            
1638
 
            @dbus.service.signal(_interface, signature=u"s")
1639
 
            def ClientNotFound(self, fingerprint):
 
1908
            @dbus.service.signal(_interface, signature=u"ss")
 
1909
            def ClientNotFound(self, fingerprint, address):
1640
1910
                "D-Bus signal"
1641
1911
                pass
1642
1912
            
1699
1969
    for client in tcp_server.clients:
1700
1970
        if use_dbus:
1701
1971
            # Emit D-Bus signal
1702
 
            mandos_dbus_service.ClientAdded(client.dbus_object_path,
1703
 
                                            client.GetAll(u""))
 
1972
            mandos_dbus_service.ClientAdded(client.dbus_object_path)
1704
1973
        client.enable()
1705
1974
    
1706
1975
    tcp_server.enable()