/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: 2010-04-04 23:51:07 UTC
  • mfrom: (24.1.147 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20100404235107-mjnuq6gjdeq030w7
MergeĀ fromĀ Belorn.

Show diffs side-by-side

added added

removed removed

Lines of Context:
60
60
import fcntl
61
61
import functools
62
62
import cPickle as pickle
63
 
import multiprocessing
64
63
 
65
64
import dbus
66
65
import dbus.service
83
82
 
84
83
version = "1.0.14"
85
84
 
86
 
#logger = logging.getLogger(u'mandos')
87
85
logger = logging.Logger(u'mandos')
88
86
syslogger = (logging.handlers.SysLogHandler
89
87
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
157
155
                            u" after %i retries, exiting.",
158
156
                            self.rename_count)
159
157
            raise AvahiServiceError(u"Too many renames")
160
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
158
        self.name = self.server.GetAlternativeServiceName(self.name)
161
159
        logger.info(u"Changing Zeroconf service name to %r ...",
162
 
                    self.name)
 
160
                    unicode(self.name))
163
161
        syslogger.setFormatter(logging.Formatter
164
162
                               (u'Mandos (%s) [%%(process)d]:'
165
163
                                u' %%(levelname)s: %%(message)s'
166
164
                                % self.name))
167
165
        self.remove()
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
        self.add()
174
167
        self.rename_count += 1
175
168
    def remove(self):
176
169
        """Derived from the Avahi example code"""
199
192
        self.group.Commit()
200
193
    def entry_group_state_changed(self, state, error):
201
194
        """Derived from the Avahi example code"""
202
 
        logger.debug(u"Avahi entry group state change: %i", state)
 
195
        logger.debug(u"Avahi state change: %i", state)
203
196
        
204
197
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
198
            logger.debug(u"Zeroconf service established.")
218
211
            self.group = None
219
212
    def server_state_changed(self, state):
220
213
        """Derived from the Avahi example code"""
221
 
        logger.debug(u"Avahi server state change: %i", state)
222
214
        if state == avahi.SERVER_COLLISION:
223
215
            logger.error(u"Zeroconf server name collision")
224
216
            self.remove()
265
257
                     runtime with vars(self) as dict, so that for
266
258
                     instance %(name)s can be used in the command.
267
259
    current_checker_command: string; current running checker_command
268
 
    approval_delay: datetime.timedelta(); Time to wait for approval
269
 
    _approved:   bool(); 'None' if not yet approved/disapproved
270
 
    approval_duration: datetime.timedelta(); Duration of one approval
271
260
    """
272
261
    
273
262
    @staticmethod
284
273
    def interval_milliseconds(self):
285
274
        "Return the 'interval' attribute in milliseconds"
286
275
        return self._timedelta_to_milliseconds(self.interval)
287
 
 
288
 
    def approval_delay_milliseconds(self):
289
 
        return self._timedelta_to_milliseconds(self.approval_delay)
290
276
    
291
277
    def __init__(self, name = None, disable_hook=None, config=None):
292
278
        """Note: the 'checker' key in 'config' sets the
327
313
        self.checker_command = config[u"checker"]
328
314
        self.current_checker_command = None
329
315
        self.last_connect = None
330
 
        self._approved = None
331
 
        self.approved_by_default = config.get(u"approved_by_default",
332
 
                                              True)
333
 
        self.approvals_pending = 0
334
 
        self.approval_delay = string_to_delta(
335
 
            config[u"approval_delay"])
336
 
        self.approval_duration = string_to_delta(
337
 
            config[u"approval_duration"])
338
 
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
339
316
    
340
 
    def send_changedstate(self):
341
 
        self.changedstate.acquire()
342
 
        self.changedstate.notify_all()
343
 
        self.changedstate.release()
344
 
        
345
317
    def enable(self):
346
318
        """Start this client's checker and timeout hooks"""
347
319
        if getattr(self, u"enabled", False):
348
320
            # Already enabled
349
321
            return
350
 
        self.send_changedstate()
351
322
        self.last_enabled = datetime.datetime.utcnow()
352
323
        # Schedule a new checker to be started an 'interval' from now,
353
324
        # and every interval from then on.
367
338
        if not getattr(self, "enabled", False):
368
339
            return False
369
340
        if not quiet:
370
 
            self.send_changedstate()
371
 
        if not quiet:
372
341
            logger.info(u"Disabling client %s", self.name)
373
342
        if getattr(self, u"disable_initiator_tag", False):
374
343
            gobject.source_remove(self.disable_initiator_tag)
507
476
                raise
508
477
        self.checker = None
509
478
 
 
479
 
510
480
def dbus_service_property(dbus_interface, signature=u"v",
511
481
                          access=u"readwrite", byte_arrays=False):
512
482
    """Decorators for marking methods of a DBusObjectWithProperties to
706
676
    # dbus.service.Object doesn't use super(), so we can't either.
707
677
    
708
678
    def __init__(self, bus = None, *args, **kwargs):
709
 
        self._approvals_pending = 0
710
679
        self.bus = bus
711
680
        Client.__init__(self, *args, **kwargs)
712
681
        # Only now, when this client is initialized, can it show up on
716
685
                                  + self.name.replace(u".", u"_")))
717
686
        DBusObjectWithProperties.__init__(self, self.bus,
718
687
                                          self.dbus_object_path)
719
 
        
720
 
    def _get_approvals_pending(self):
721
 
        return self._approvals_pending
722
 
    def _set_approvals_pending(self, value):
723
 
        old_value = self._approvals_pending
724
 
        self._approvals_pending = value
725
 
        bval = bool(value)
726
 
        if (hasattr(self, "dbus_object_path")
727
 
            and bval is not bool(old_value)):
728
 
            dbus_bool = dbus.Boolean(bval, variant_level=1)
729
 
            self.PropertyChanged(dbus.String(u"ApprovalPending"),
730
 
                                 dbus_bool)
731
 
 
732
 
    approvals_pending = property(_get_approvals_pending,
733
 
                                 _set_approvals_pending)
734
 
    del _get_approvals_pending, _set_approvals_pending
735
688
    
736
689
    @staticmethod
737
690
    def _datetime_to_dbus(dt, variant_level=0):
744
697
        r = Client.enable(self)
745
698
        if oldstate != self.enabled:
746
699
            # Emit D-Bus signals
747
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
700
            self.PropertyChanged(dbus.String(u"enabled"),
748
701
                                 dbus.Boolean(True, variant_level=1))
749
702
            self.PropertyChanged(
750
 
                dbus.String(u"LastEnabled"),
 
703
                dbus.String(u"last_enabled"),
751
704
                self._datetime_to_dbus(self.last_enabled,
752
705
                                       variant_level=1))
753
706
        return r
757
710
        r = Client.disable(self, quiet=quiet)
758
711
        if not quiet and oldstate != self.enabled:
759
712
            # Emit D-Bus signal
760
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
713
            self.PropertyChanged(dbus.String(u"enabled"),
761
714
                                 dbus.Boolean(False, variant_level=1))
762
715
        return r
763
716
    
775
728
        self.checker_callback_tag = None
776
729
        self.checker = None
777
730
        # Emit D-Bus signal
778
 
        self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
731
        self.PropertyChanged(dbus.String(u"checker_running"),
779
732
                             dbus.Boolean(False, variant_level=1))
780
733
        if os.WIFEXITED(condition):
781
734
            exitstatus = os.WEXITSTATUS(condition)
796
749
        r = Client.checked_ok(self, *args, **kwargs)
797
750
        # Emit D-Bus signal
798
751
        self.PropertyChanged(
799
 
            dbus.String(u"LastCheckedOK"),
 
752
            dbus.String(u"last_checked_ok"),
800
753
            (self._datetime_to_dbus(self.last_checked_ok,
801
754
                                    variant_level=1)))
802
755
        return r
814
767
            # Emit D-Bus signal
815
768
            self.CheckerStarted(self.current_checker_command)
816
769
            self.PropertyChanged(
817
 
                dbus.String(u"CheckerRunning"),
 
770
                dbus.String(u"checker_running"),
818
771
                dbus.Boolean(True, variant_level=1))
819
772
        return r
820
773
    
823
776
        r = Client.stop_checker(self, *args, **kwargs)
824
777
        if (old_checker is not None
825
778
            and getattr(self, u"checker", None) is None):
826
 
            self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
779
            self.PropertyChanged(dbus.String(u"checker_running"),
827
780
                                 dbus.Boolean(False, variant_level=1))
828
781
        return r
829
 
 
830
 
    def _reset_approved(self):
831
 
        self._approved = None
832
 
        return False
833
 
    
834
 
    def approve(self, value=True):
835
 
        self.send_changedstate()
836
 
        self._approved = value
837
 
        gobject.timeout_add(self._timedelta_to_milliseconds
838
 
                            (self.approval_duration),
839
 
                            self._reset_approved)
840
 
    
841
782
    
842
783
    ## D-Bus methods, signals & properties
843
784
    _interface = u"se.bsnet.fukt.Mandos.Client"
865
806
    # GotSecret - signal
866
807
    @dbus.service.signal(_interface)
867
808
    def GotSecret(self):
868
 
        """D-Bus signal
869
 
        Is sent after a successful transfer of secret from the Mandos
870
 
        server to mandos-client
871
 
        """
 
809
        "D-Bus signal"
872
810
        pass
873
811
    
874
812
    # Rejected - signal
875
 
    @dbus.service.signal(_interface, signature=u"s")
876
 
    def Rejected(self, reason):
877
 
        "D-Bus signal"
878
 
        pass
879
 
    
880
 
    # NeedApproval - signal
881
 
    @dbus.service.signal(_interface, signature=u"tb")
882
 
    def NeedApproval(self, timeout, default):
 
813
    @dbus.service.signal(_interface)
 
814
    def Rejected(self):
883
815
        "D-Bus signal"
884
816
        pass
885
817
    
886
818
    ## Methods
887
 
 
888
 
    # Approve - method
889
 
    @dbus.service.method(_interface, in_signature=u"b")
890
 
    def Approve(self, value):
891
 
        self.approve(value)
892
 
 
 
819
    
893
820
    # CheckedOK - method
894
821
    @dbus.service.method(_interface)
895
822
    def CheckedOK(self):
920
847
    
921
848
    ## Properties
922
849
    
923
 
    # ApprovalPending - property
924
 
    @dbus_service_property(_interface, signature=u"b", access=u"read")
925
 
    def ApprovalPending_dbus_property(self):
926
 
        return dbus.Boolean(bool(self.approvals_pending))
927
 
    
928
 
    # ApprovedByDefault - property
929
 
    @dbus_service_property(_interface, signature=u"b",
930
 
                           access=u"readwrite")
931
 
    def ApprovedByDefault_dbus_property(self, value=None):
932
 
        if value is None:       # get
933
 
            return dbus.Boolean(self.approved_by_default)
934
 
        self.approved_by_default = bool(value)
935
 
        # Emit D-Bus signal
936
 
        self.PropertyChanged(dbus.String(u"ApprovedByDefault"),
937
 
                             dbus.Boolean(value, variant_level=1))
938
 
    
939
 
    # ApprovalDelay - property
940
 
    @dbus_service_property(_interface, signature=u"t",
941
 
                           access=u"readwrite")
942
 
    def ApprovalDelay_dbus_property(self, value=None):
943
 
        if value is None:       # get
944
 
            return dbus.UInt64(self.approval_delay_milliseconds())
945
 
        self.approval_delay = datetime.timedelta(0, 0, 0, value)
946
 
        # Emit D-Bus signal
947
 
        self.PropertyChanged(dbus.String(u"ApprovalDelay"),
948
 
                             dbus.UInt64(value, variant_level=1))
949
 
    
950
 
    # ApprovalDuration - property
951
 
    @dbus_service_property(_interface, signature=u"t",
952
 
                           access=u"readwrite")
953
 
    def ApprovalDuration_dbus_property(self, value=None):
954
 
        if value is None:       # get
955
 
            return dbus.UInt64(self._timedelta_to_milliseconds(
956
 
                    self.approval_duration))
957
 
        self.approval_duration = datetime.timedelta(0, 0, 0, value)
958
 
        # Emit D-Bus signal
959
 
        self.PropertyChanged(dbus.String(u"ApprovalDuration"),
960
 
                             dbus.UInt64(value, variant_level=1))
961
 
    
962
 
    # Name - property
 
850
    # name - property
963
851
    @dbus_service_property(_interface, signature=u"s", access=u"read")
964
 
    def Name_dbus_property(self):
 
852
    def name_dbus_property(self):
965
853
        return dbus.String(self.name)
966
854
    
967
 
    # Fingerprint - property
 
855
    # fingerprint - property
968
856
    @dbus_service_property(_interface, signature=u"s", access=u"read")
969
 
    def Fingerprint_dbus_property(self):
 
857
    def fingerprint_dbus_property(self):
970
858
        return dbus.String(self.fingerprint)
971
859
    
972
 
    # Host - property
 
860
    # host - property
973
861
    @dbus_service_property(_interface, signature=u"s",
974
862
                           access=u"readwrite")
975
 
    def Host_dbus_property(self, value=None):
 
863
    def host_dbus_property(self, value=None):
976
864
        if value is None:       # get
977
865
            return dbus.String(self.host)
978
866
        self.host = value
979
867
        # Emit D-Bus signal
980
 
        self.PropertyChanged(dbus.String(u"Host"),
 
868
        self.PropertyChanged(dbus.String(u"host"),
981
869
                             dbus.String(value, variant_level=1))
982
870
    
983
 
    # Created - property
 
871
    # created - property
984
872
    @dbus_service_property(_interface, signature=u"s", access=u"read")
985
 
    def Created_dbus_property(self):
 
873
    def created_dbus_property(self):
986
874
        return dbus.String(self._datetime_to_dbus(self.created))
987
875
    
988
 
    # LastEnabled - property
 
876
    # last_enabled - property
989
877
    @dbus_service_property(_interface, signature=u"s", access=u"read")
990
 
    def LastEnabled_dbus_property(self):
 
878
    def last_enabled_dbus_property(self):
991
879
        if self.last_enabled is None:
992
880
            return dbus.String(u"")
993
881
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
994
882
    
995
 
    # Enabled - property
 
883
    # enabled - property
996
884
    @dbus_service_property(_interface, signature=u"b",
997
885
                           access=u"readwrite")
998
 
    def Enabled_dbus_property(self, value=None):
 
886
    def enabled_dbus_property(self, value=None):
999
887
        if value is None:       # get
1000
888
            return dbus.Boolean(self.enabled)
1001
889
        if value:
1003
891
        else:
1004
892
            self.disable()
1005
893
    
1006
 
    # LastCheckedOK - property
 
894
    # last_checked_ok - property
1007
895
    @dbus_service_property(_interface, signature=u"s",
1008
896
                           access=u"readwrite")
1009
 
    def LastCheckedOK_dbus_property(self, value=None):
 
897
    def last_checked_ok_dbus_property(self, value=None):
1010
898
        if value is not None:
1011
899
            self.checked_ok()
1012
900
            return
1015
903
        return dbus.String(self._datetime_to_dbus(self
1016
904
                                                  .last_checked_ok))
1017
905
    
1018
 
    # Timeout - property
 
906
    # timeout - property
1019
907
    @dbus_service_property(_interface, signature=u"t",
1020
908
                           access=u"readwrite")
1021
 
    def Timeout_dbus_property(self, value=None):
 
909
    def timeout_dbus_property(self, value=None):
1022
910
        if value is None:       # get
1023
911
            return dbus.UInt64(self.timeout_milliseconds())
1024
912
        self.timeout = datetime.timedelta(0, 0, 0, value)
1025
913
        # Emit D-Bus signal
1026
 
        self.PropertyChanged(dbus.String(u"Timeout"),
 
914
        self.PropertyChanged(dbus.String(u"timeout"),
1027
915
                             dbus.UInt64(value, variant_level=1))
1028
916
        if getattr(self, u"disable_initiator_tag", None) is None:
1029
917
            return
1043
931
            self.disable_initiator_tag = (gobject.timeout_add
1044
932
                                          (time_to_die, self.disable))
1045
933
    
1046
 
    # Interval - property
 
934
    # interval - property
1047
935
    @dbus_service_property(_interface, signature=u"t",
1048
936
                           access=u"readwrite")
1049
 
    def Interval_dbus_property(self, value=None):
 
937
    def interval_dbus_property(self, value=None):
1050
938
        if value is None:       # get
1051
939
            return dbus.UInt64(self.interval_milliseconds())
1052
940
        self.interval = datetime.timedelta(0, 0, 0, value)
1053
941
        # Emit D-Bus signal
1054
 
        self.PropertyChanged(dbus.String(u"Interval"),
 
942
        self.PropertyChanged(dbus.String(u"interval"),
1055
943
                             dbus.UInt64(value, variant_level=1))
1056
944
        if getattr(self, u"checker_initiator_tag", None) is None:
1057
945
            return
1061
949
                                      (value, self.start_checker))
1062
950
        self.start_checker()    # Start one now, too
1063
951
 
1064
 
    # Checker - property
 
952
    # checker - property
1065
953
    @dbus_service_property(_interface, signature=u"s",
1066
954
                           access=u"readwrite")
1067
 
    def Checker_dbus_property(self, value=None):
 
955
    def checker_dbus_property(self, value=None):
1068
956
        if value is None:       # get
1069
957
            return dbus.String(self.checker_command)
1070
958
        self.checker_command = value
1071
959
        # Emit D-Bus signal
1072
 
        self.PropertyChanged(dbus.String(u"Checker"),
 
960
        self.PropertyChanged(dbus.String(u"checker"),
1073
961
                             dbus.String(self.checker_command,
1074
962
                                         variant_level=1))
1075
963
    
1076
 
    # CheckerRunning - property
 
964
    # checker_running - property
1077
965
    @dbus_service_property(_interface, signature=u"b",
1078
966
                           access=u"readwrite")
1079
 
    def CheckerRunning_dbus_property(self, value=None):
 
967
    def checker_running_dbus_property(self, value=None):
1080
968
        if value is None:       # get
1081
969
            return dbus.Boolean(self.checker is not None)
1082
970
        if value:
1084
972
        else:
1085
973
            self.stop_checker()
1086
974
    
1087
 
    # ObjectPath - property
 
975
    # object_path - property
1088
976
    @dbus_service_property(_interface, signature=u"o", access=u"read")
1089
 
    def ObjectPath_dbus_property(self):
 
977
    def object_path_dbus_property(self):
1090
978
        return self.dbus_object_path # is already a dbus.ObjectPath
1091
979
    
1092
 
    # Secret = property
 
980
    # secret = property
1093
981
    @dbus_service_property(_interface, signature=u"ay",
1094
982
                           access=u"write", byte_arrays=True)
1095
 
    def Secret_dbus_property(self, value):
 
983
    def secret_dbus_property(self, value):
1096
984
        self.secret = str(value)
1097
985
    
1098
986
    del _interface
1099
987
 
1100
988
 
1101
 
class ProxyClient(object):
1102
 
    def __init__(self, child_pipe, fpr, address):
1103
 
        self._pipe = child_pipe
1104
 
        self._pipe.send(('init', fpr, address))
1105
 
        if not self._pipe.recv():
1106
 
            raise KeyError()
1107
 
 
1108
 
    def __getattribute__(self, name):
1109
 
        if(name == '_pipe'):
1110
 
            return super(ProxyClient, self).__getattribute__(name)
1111
 
        self._pipe.send(('getattr', name))
1112
 
        data = self._pipe.recv()
1113
 
        if data[0] == 'data':
1114
 
            return data[1]
1115
 
        if data[0] == 'function':
1116
 
            def func(*args, **kwargs):
1117
 
                self._pipe.send(('funcall', name, args, kwargs))
1118
 
                return self._pipe.recv()[1]
1119
 
            return func
1120
 
 
1121
 
    def __setattr__(self, name, value):
1122
 
        if(name == '_pipe'):
1123
 
            return super(ProxyClient, self).__setattr__(name, value)
1124
 
        self._pipe.send(('setattr', name, value))
1125
 
 
1126
 
 
1127
989
class ClientHandler(socketserver.BaseRequestHandler, object):
1128
990
    """A class to handle client connections.
1129
991
    
1131
993
    Note: This will run in its own forked process."""
1132
994
    
1133
995
    def handle(self):
1134
 
        with contextlib.closing(self.server.child_pipe) as child_pipe:
1135
 
            logger.info(u"TCP connection from: %s",
1136
 
                        unicode(self.client_address))
1137
 
            logger.debug(u"Pipe FD: %d",
1138
 
                         self.server.child_pipe.fileno())
1139
 
 
 
996
        logger.info(u"TCP connection from: %s",
 
997
                    unicode(self.client_address))
 
998
        logger.debug(u"IPC Pipe FD: %d",
 
999
                     self.server.child_pipe[1].fileno())
 
1000
        # Open IPC pipe to parent process
 
1001
        with contextlib.nested(self.server.child_pipe[1],
 
1002
                               self.server.parent_pipe[0]
 
1003
                               ) as (ipc, ipc_return):
1140
1004
            session = (gnutls.connection
1141
1005
                       .ClientSession(self.request,
1142
1006
                                      gnutls.connection
1143
1007
                                      .X509Credentials()))
1144
 
 
 
1008
            
 
1009
            line = self.request.makefile().readline()
 
1010
            logger.debug(u"Protocol version: %r", line)
 
1011
            try:
 
1012
                if int(line.strip().split()[0]) > 1:
 
1013
                    raise RuntimeError
 
1014
            except (ValueError, IndexError, RuntimeError), error:
 
1015
                logger.error(u"Unknown protocol version: %s", error)
 
1016
                return
 
1017
            
1145
1018
            # Note: gnutls.connection.X509Credentials is really a
1146
1019
            # generic GnuTLS certificate credentials object so long as
1147
1020
            # no X.509 keys are added to it.  Therefore, we can use it
1148
1021
            # here despite using OpenPGP certificates.
1149
 
 
 
1022
            
1150
1023
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1151
1024
            #                      u"+AES-256-CBC", u"+SHA1",
1152
1025
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1158
1031
            (gnutls.library.functions
1159
1032
             .gnutls_priority_set_direct(session._c_object,
1160
1033
                                         priority, None))
1161
 
 
1162
 
            # Start communication using the Mandos protocol
1163
 
            # Get protocol number
1164
 
            line = self.request.makefile().readline()
1165
 
            logger.debug(u"Protocol version: %r", line)
1166
 
            try:
1167
 
                if int(line.strip().split()[0]) > 1:
1168
 
                    raise RuntimeError
1169
 
            except (ValueError, IndexError, RuntimeError), error:
1170
 
                logger.error(u"Unknown protocol version: %s", error)
1171
 
                return
1172
 
 
1173
 
            # Start GnuTLS connection
 
1034
            
1174
1035
            try:
1175
1036
                session.handshake()
1176
1037
            except gnutls.errors.GNUTLSError, error:
1179
1040
                # established.  Just abandon the request.
1180
1041
                return
1181
1042
            logger.debug(u"Handshake succeeded")
1182
 
 
1183
 
            approval_required = False
1184
1043
            try:
1185
1044
                try:
1186
1045
                    fpr = self.fingerprint(self.peer_certificate
1190
1049
                    return
1191
1050
                logger.debug(u"Fingerprint: %s", fpr)
1192
1051
 
1193
 
                try:
1194
 
                    client = ProxyClient(child_pipe, fpr,
1195
 
                                         self.client_address)
1196
 
                except KeyError:
1197
 
                    return
1198
 
                
1199
 
                if client.approval_delay:
1200
 
                    delay = client.approval_delay
1201
 
                    client.approvals_pending += 1
1202
 
                    approval_required = True
1203
 
                
1204
 
                while True:
1205
 
                    if not client.enabled:
1206
 
                        logger.warning(u"Client %s is disabled",
1207
 
                                       client.name)
1208
 
                        if self.server.use_dbus:
1209
 
                            # Emit D-Bus signal
1210
 
                            client.Rejected("Disabled")                    
1211
 
                        return
1212
 
                    
1213
 
                    if client._approved or not client.approval_delay:
1214
 
                        #We are approved or approval is disabled
 
1052
                for c in self.server.clients:
 
1053
                    if c.fingerprint == fpr:
 
1054
                        client = c
1215
1055
                        break
1216
 
                    elif client._approved is None:
1217
 
                        logger.info(u"Client %s needs approval",
1218
 
                                    client.name)
1219
 
                        if self.server.use_dbus:
1220
 
                            # Emit D-Bus signal
1221
 
                            client.NeedApproval(
1222
 
                                client.approval_delay_milliseconds(),
1223
 
                                client.approved_by_default)
1224
 
                    else:
1225
 
                        logger.warning(u"Client %s was not approved",
1226
 
                                       client.name)
1227
 
                        if self.server.use_dbus:
1228
 
                            # Emit D-Bus signal
1229
 
                            client.Rejected("Denied")
1230
 
                        return
1231
 
                    
1232
 
                    #wait until timeout or approved
1233
 
                    #x = float(client._timedelta_to_milliseconds(delay))
1234
 
                    time = datetime.datetime.now()
1235
 
                    client.changedstate.acquire()
1236
 
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
1237
 
                    client.changedstate.release()
1238
 
                    time2 = datetime.datetime.now()
1239
 
                    if (time2 - time) >= delay:
1240
 
                        if not client.approved_by_default:
1241
 
                            logger.warning("Client %s timed out while"
1242
 
                                           " waiting for approval",
1243
 
                                           client.name)
1244
 
                            if self.server.use_dbus:
1245
 
                                # Emit D-Bus signal
1246
 
                                client.Rejected("Timed out")
1247
 
                            return
1248
 
                        else:
1249
 
                            break
1250
 
                    else:
1251
 
                        delay -= time2 - time
1252
 
                
 
1056
                else:
 
1057
                    ipc.write(u"NOTFOUND %s %s\n"
 
1058
                              % (fpr, unicode(self.client_address)))
 
1059
                    return
 
1060
                # Have to check if client.enabled, since it is
 
1061
                # possible that the client was disabled since the
 
1062
                # GnuTLS session was established.
 
1063
                ipc.write(u"GETATTR enabled %s\n" % fpr)
 
1064
                enabled = pickle.load(ipc_return)
 
1065
                if not enabled:
 
1066
                    ipc.write(u"DISABLED %s\n" % client.name)
 
1067
                    return
 
1068
                ipc.write(u"SENDING %s\n" % client.name)
1253
1069
                sent_size = 0
1254
1070
                while sent_size < len(client.secret):
1255
 
                    try:
1256
 
                        sent = session.send(client.secret[sent_size:])
1257
 
                    except (gnutls.errors.GNUTLSError), error:
1258
 
                        logger.warning("gnutls send failed")
1259
 
                        return
 
1071
                    sent = session.send(client.secret[sent_size:])
1260
1072
                    logger.debug(u"Sent: %d, remaining: %d",
1261
1073
                                 sent, len(client.secret)
1262
1074
                                 - (sent_size + sent))
1263
1075
                    sent_size += sent
1264
 
 
1265
 
                logger.info(u"Sending secret to %s", client.name)
1266
 
                # bump the timeout as if seen
1267
 
                client.checked_ok()
1268
 
                if self.server.use_dbus:
1269
 
                    # Emit D-Bus signal
1270
 
                    client.GotSecret()
1271
 
            
1272
1076
            finally:
1273
 
                if approval_required:
1274
 
                    client.approvals_pending -= 1
1275
 
                try:
1276
 
                    session.bye()
1277
 
                except (gnutls.errors.GNUTLSError), error:
1278
 
                    logger.warning("GnuTLS bye failed")
 
1077
                session.bye()
1279
1078
    
1280
1079
    @staticmethod
1281
1080
    def peer_certificate(session):
1341
1140
        return hex_fpr
1342
1141
 
1343
1142
 
1344
 
class MultiprocessingMixIn(object):
1345
 
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
1346
 
    def sub_process_main(self, request, address):
1347
 
        try:
1348
 
            self.finish_request(request, address)
1349
 
        except:
1350
 
            self.handle_error(request, address)
1351
 
        self.close_request(request)
1352
 
            
1353
 
    def process_request(self, request, address):
1354
 
        """Start a new process to process the request."""
1355
 
        multiprocessing.Process(target = self.sub_process_main,
1356
 
                                args = (request, address)).start()
1357
 
 
1358
 
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
1359
 
    """ adds a pipe to the MixIn """
 
1143
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
 
1144
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1360
1145
    def process_request(self, request, client_address):
1361
1146
        """Overrides and wraps the original process_request().
1362
1147
        
1363
1148
        This function creates a new pipe in self.pipe
1364
1149
        """
1365
 
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1366
 
 
1367
 
        super(MultiprocessingMixInWithPipe,
 
1150
        # Child writes to child_pipe
 
1151
        self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1152
        # Parent writes to parent_pipe
 
1153
        self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1154
        super(ForkingMixInWithPipes,
1368
1155
              self).process_request(request, client_address)
1369
 
        self.child_pipe.close()
1370
 
        self.add_pipe(parent_pipe)
1371
 
 
1372
 
    def add_pipe(self, parent_pipe):
 
1156
        # Close unused ends for parent
 
1157
        self.parent_pipe[0].close() # close read end
 
1158
        self.child_pipe[1].close()  # close write end
 
1159
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
 
1160
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1373
1161
        """Dummy function; override as necessary"""
1374
 
        pass
1375
 
 
1376
 
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
 
1162
        child_pipe_fd.close()
 
1163
        parent_pipe_fd.close()
 
1164
 
 
1165
 
 
1166
class IPv6_TCPServer(ForkingMixInWithPipes,
1377
1167
                     socketserver.TCPServer, object):
1378
1168
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1379
1169
    
1464
1254
            return socketserver.TCPServer.server_activate(self)
1465
1255
    def enable(self):
1466
1256
        self.enabled = True
1467
 
    def add_pipe(self, parent_pipe):
 
1257
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1468
1258
        # Call "handle_ipc" for both data and EOF events
1469
 
        gobject.io_add_watch(parent_pipe.fileno(),
 
1259
        gobject.io_add_watch(child_pipe_fd.fileno(),
1470
1260
                             gobject.IO_IN | gobject.IO_HUP,
1471
1261
                             functools.partial(self.handle_ipc,
1472
 
                                               parent_pipe = parent_pipe))
1473
 
        
1474
 
    def handle_ipc(self, source, condition, parent_pipe=None,
1475
 
                   client_object=None):
 
1262
                                               reply = parent_pipe_fd,
 
1263
                                               sender= child_pipe_fd))
 
1264
    def handle_ipc(self, source, condition, reply=None, sender=None):
1476
1265
        condition_names = {
1477
1266
            gobject.IO_IN: u"IN",   # There is data to read.
1478
1267
            gobject.IO_OUT: u"OUT", # Data can be written (without
1489
1278
                                       if cond & condition)
1490
1279
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1491
1280
                     conditions_string)
1492
 
 
1493
 
        # error or the other end of multiprocessing.Pipe has closed
1494
 
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1495
 
            return False
1496
 
        
1497
 
        # Read a request from the child
1498
 
        request = parent_pipe.recv()
1499
 
        logger.debug(u"IPC request: %s", repr(request))
1500
 
        command = request[0]
1501
 
        
1502
 
        if command == 'init':
1503
 
            fpr = request[1]
1504
 
            address = request[2]
1505
 
            
1506
 
            for c in self.clients:
1507
 
                if c.fingerprint == fpr:
1508
 
                    client = c
1509
 
                    break
1510
 
            else:
1511
 
                logger.warning(u"Client not found for fingerprint: %s, ad"
1512
 
                               u"dress: %s", fpr, address)
1513
 
                if self.use_dbus:
1514
 
                    # Emit D-Bus signal
1515
 
                    mandos_dbus_service.ClientNotFound(fpr, address)
1516
 
                parent_pipe.send(False)
1517
 
                return False
1518
 
            
1519
 
            gobject.io_add_watch(parent_pipe.fileno(),
1520
 
                                 gobject.IO_IN | gobject.IO_HUP,
1521
 
                                 functools.partial(self.handle_ipc,
1522
 
                                                   parent_pipe = parent_pipe,
1523
 
                                                   client_object = client))
1524
 
            parent_pipe.send(True)
1525
 
            # remove the old hook in favor of the new above hook on same fileno
1526
 
            return False
1527
 
        if command == 'funcall':
1528
 
            funcname = request[1]
1529
 
            args = request[2]
1530
 
            kwargs = request[3]
1531
 
            
1532
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1533
 
 
1534
 
        if command == 'getattr':
1535
 
            attrname = request[1]
1536
 
            if callable(client_object.__getattribute__(attrname)):
1537
 
                parent_pipe.send(('function',))
1538
 
            else:
1539
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1540
 
        
1541
 
        if command == 'setattr':
1542
 
            attrname = request[1]
1543
 
            value = request[2]
1544
 
            setattr(client_object, attrname, value)
1545
 
 
 
1281
        
 
1282
        # Read a line from the file object
 
1283
        cmdline = sender.readline()
 
1284
        if not cmdline:             # Empty line means end of file
 
1285
            # close the IPC pipes
 
1286
            sender.close()
 
1287
            reply.close()
 
1288
            
 
1289
            # Stop calling this function
 
1290
            return False
 
1291
        
 
1292
        logger.debug(u"IPC command: %r", cmdline)
 
1293
        
 
1294
        # Parse and act on command
 
1295
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
 
1296
        
 
1297
        if cmd == u"NOTFOUND":
 
1298
            fpr, address = args.split(None, 1)
 
1299
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1300
                           u"dress: %s", fpr, address)
 
1301
            if self.use_dbus:
 
1302
                # Emit D-Bus signal
 
1303
                mandos_dbus_service.ClientNotFound(fpr, address)
 
1304
        elif cmd == u"DISABLED":
 
1305
            for client in self.clients:
 
1306
                if client.name == args:
 
1307
                    logger.warning(u"Client %s is disabled", args)
 
1308
                    if self.use_dbus:
 
1309
                        # Emit D-Bus signal
 
1310
                        client.Rejected()
 
1311
                    break
 
1312
            else:
 
1313
                logger.error(u"Unknown client %s is disabled", args)
 
1314
        elif cmd == u"SENDING":
 
1315
            for client in self.clients:
 
1316
                if client.name == args:
 
1317
                    logger.info(u"Sending secret to %s", client.name)
 
1318
                    client.checked_ok()
 
1319
                    if self.use_dbus:
 
1320
                        # Emit D-Bus signal
 
1321
                        client.GotSecret()
 
1322
                    break
 
1323
            else:
 
1324
                logger.error(u"Sending secret to unknown client %s",
 
1325
                             args)
 
1326
        elif cmd == u"GETATTR":
 
1327
            attr_name, fpr = args.split(None, 1)
 
1328
            for client in self.clients:
 
1329
                if client.fingerprint == fpr:
 
1330
                    attr_value = getattr(client, attr_name, None)
 
1331
                    logger.debug("IPC reply: %r", attr_value)
 
1332
                    pickle.dump(attr_value, reply)
 
1333
                    break
 
1334
            else:
 
1335
                logger.error(u"Client %s on address %s requesting "
 
1336
                             u"attribute %s not found", fpr, address,
 
1337
                             attr_name)
 
1338
                pickle.dump(None, reply)
 
1339
        else:
 
1340
            logger.error(u"Unknown IPC command: %r", cmdline)
 
1341
        
 
1342
        # Keep calling this function
1546
1343
        return True
1547
1344
 
1548
1345
 
1651
1448
    parser.add_option("--debug", action=u"store_true",
1652
1449
                      help=u"Debug mode; run in foreground and log to"
1653
1450
                      u" terminal")
1654
 
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
1655
 
                      help=u"Debug level for stdout output")
1656
1451
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1657
1452
                      u" priority string (see GnuTLS documentation)")
1658
1453
    parser.add_option("--servicename", type=u"string",
1683
1478
                        u"servicename": u"Mandos",
1684
1479
                        u"use_dbus": u"True",
1685
1480
                        u"use_ipv6": u"True",
1686
 
                        u"debuglevel": u"",
1687
1481
                        }
1688
1482
    
1689
1483
    # Parse config file for server-global settings
1706
1500
    # options, if set.
1707
1501
    for option in (u"interface", u"address", u"port", u"debug",
1708
1502
                   u"priority", u"servicename", u"configdir",
1709
 
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
 
1503
                   u"use_dbus", u"use_ipv6"):
1710
1504
        value = getattr(options, option)
1711
1505
        if value is not None:
1712
1506
            server_settings[option] = value
1721
1515
    
1722
1516
    # For convenience
1723
1517
    debug = server_settings[u"debug"]
1724
 
    debuglevel = server_settings[u"debuglevel"]
1725
1518
    use_dbus = server_settings[u"use_dbus"]
1726
1519
    use_ipv6 = server_settings[u"use_ipv6"]
1727
 
 
 
1520
    
 
1521
    if not debug:
 
1522
        syslogger.setLevel(logging.WARNING)
 
1523
        console.setLevel(logging.WARNING)
 
1524
    
1728
1525
    if server_settings[u"servicename"] != u"Mandos":
1729
1526
        syslogger.setFormatter(logging.Formatter
1730
1527
                               (u'Mandos (%s) [%%(process)d]:'
1736
1533
                        u"interval": u"5m",
1737
1534
                        u"checker": u"fping -q -- %%(host)s",
1738
1535
                        u"host": u"",
1739
 
                        u"approval_delay": u"0s",
1740
 
                        u"approval_duration": u"1s",
1741
1536
                        }
1742
1537
    client_config = configparser.SafeConfigParser(client_defaults)
1743
1538
    client_config.read(os.path.join(server_settings[u"configdir"],
1749
1544
    tcp_server = MandosServer((server_settings[u"address"],
1750
1545
                               server_settings[u"port"]),
1751
1546
                              ClientHandler,
1752
 
                              interface=(server_settings[u"interface"]
1753
 
                                         or None),
 
1547
                              interface=server_settings[u"interface"],
1754
1548
                              use_ipv6=use_ipv6,
1755
1549
                              gnutls_priority=
1756
1550
                              server_settings[u"priority"],
1782
1576
        if error[0] != errno.EPERM:
1783
1577
            raise error
1784
1578
    
1785
 
    if not debug and not debuglevel:
1786
 
        syslogger.setLevel(logging.WARNING)
1787
 
        console.setLevel(logging.WARNING)
1788
 
    if debuglevel:
1789
 
        level = getattr(logging, debuglevel.upper())
1790
 
        syslogger.setLevel(level)
1791
 
        console.setLevel(level)
1792
 
 
 
1579
    # Enable all possible GnuTLS debugging
1793
1580
    if debug:
1794
 
        # Enable all possible GnuTLS debugging
1795
 
        
1796
1581
        # "Use a log level over 10 to enable all debugging options."
1797
1582
        # - GnuTLS manual
1798
1583
        gnutls.library.functions.gnutls_global_set_log_level(11)
1803
1588
        
1804
1589
        (gnutls.library.functions
1805
1590
         .gnutls_global_set_log_function(debug_gnutls))
1806
 
        
1807
 
        # Redirect stdin so all checkers get /dev/null
1808
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1809
 
        os.dup2(null, sys.stdin.fileno())
1810
 
        if null > 2:
1811
 
            os.close(null)
1812
 
    else:
1813
 
        # No console logging
1814
 
        logger.removeHandler(console)
1815
 
    
1816
1591
    
1817
1592
    global main_loop
1818
1593
    # From the Avahi example code
1836
1611
    if server_settings["interface"]:
1837
1612
        service.interface = (if_nametoindex
1838
1613
                             (str(server_settings[u"interface"])))
1839
 
 
1840
 
    if not debug:
1841
 
        # Close all input and output, do double fork, etc.
1842
 
        daemon()
1843
 
        
1844
 
    global multiprocessing_manager
1845
 
    multiprocessing_manager = multiprocessing.Manager()
1846
1614
    
1847
1615
    client_class = Client
1848
1616
    if use_dbus:
1849
1617
        client_class = functools.partial(ClientDBus, bus = bus)
1850
 
    def client_config_items(config, section):
1851
 
        special_settings = {
1852
 
            "approved_by_default":
1853
 
                lambda: config.getboolean(section,
1854
 
                                          "approved_by_default"),
1855
 
            }
1856
 
        for name, value in config.items(section):
1857
 
            try:
1858
 
                yield (name, special_settings[name]())
1859
 
            except KeyError:
1860
 
                yield (name, value)
1861
 
    
1862
1618
    tcp_server.clients.update(set(
1863
1619
            client_class(name = section,
1864
 
                         config= dict(client_config_items(
1865
 
                        client_config, section)))
 
1620
                         config= dict(client_config.items(section)))
1866
1621
            for section in client_config.sections()))
1867
1622
    if not tcp_server.clients:
1868
1623
        logger.warning(u"No clients defined")
1869
 
        
 
1624
    
 
1625
    if debug:
 
1626
        # Redirect stdin so all checkers get /dev/null
 
1627
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1628
        os.dup2(null, sys.stdin.fileno())
 
1629
        if null > 2:
 
1630
            os.close(null)
 
1631
    else:
 
1632
        # No console logging
 
1633
        logger.removeHandler(console)
 
1634
        # Close all input and output, do double fork, etc.
 
1635
        daemon()
 
1636
    
1870
1637
    try:
1871
1638
        with pidfile:
1872
1639
            pid = os.getpid()