/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

* Makefile: Merge branch adding warning messages to "run-*" targets.

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
 
63
import select
64
64
 
65
65
import dbus
66
66
import dbus.service
83
83
 
84
84
version = "1.0.14"
85
85
 
86
 
#logger = logging.getLogger(u'mandos')
87
86
logger = logging.Logger(u'mandos')
88
87
syslogger = (logging.handlers.SysLogHandler
89
88
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
157
156
                            u" after %i retries, exiting.",
158
157
                            self.rename_count)
159
158
            raise AvahiServiceError(u"Too many renames")
160
 
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
 
159
        self.name = self.server.GetAlternativeServiceName(self.name)
161
160
        logger.info(u"Changing Zeroconf service name to %r ...",
162
 
                    self.name)
 
161
                    unicode(self.name))
163
162
        syslogger.setFormatter(logging.Formatter
164
163
                               (u'Mandos (%s) [%%(process)d]:'
165
164
                                u' %%(levelname)s: %%(message)s'
166
165
                                % self.name))
167
166
        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)
 
167
        self.add()
174
168
        self.rename_count += 1
175
169
    def remove(self):
176
170
        """Derived from the Avahi example code"""
265
259
                     runtime with vars(self) as dict, so that for
266
260
                     instance %(name)s can be used in the command.
267
261
    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
262
    """
272
263
    
273
264
    @staticmethod
284
275
    def interval_milliseconds(self):
285
276
        "Return the 'interval' attribute in milliseconds"
286
277
        return self._timedelta_to_milliseconds(self.interval)
287
 
 
288
 
    def approval_delay_milliseconds(self):
289
 
        return self._timedelta_to_milliseconds(self.approval_delay)
290
278
    
291
279
    def __init__(self, name = None, disable_hook=None, config=None):
292
280
        """Note: the 'checker' key in 'config' sets the
327
315
        self.checker_command = config[u"checker"]
328
316
        self.current_checker_command = None
329
317
        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
318
    
340
 
    def send_changedstate(self):
341
 
        self.changedstate.acquire()
342
 
        self.changedstate.notify_all()
343
 
        self.changedstate.release()
344
 
        
345
319
    def enable(self):
346
320
        """Start this client's checker and timeout hooks"""
347
321
        if getattr(self, u"enabled", False):
348
322
            # Already enabled
349
323
            return
350
 
        self.send_changedstate()
351
324
        self.last_enabled = datetime.datetime.utcnow()
352
325
        # Schedule a new checker to be started an 'interval' from now,
353
326
        # and every interval from then on.
367
340
        if not getattr(self, "enabled", False):
368
341
            return False
369
342
        if not quiet:
370
 
            self.send_changedstate()
371
 
        if not quiet:
372
343
            logger.info(u"Disabling client %s", self.name)
373
344
        if getattr(self, u"disable_initiator_tag", False):
374
345
            gobject.source_remove(self.disable_initiator_tag)
507
478
                raise
508
479
        self.checker = None
509
480
 
 
481
 
510
482
def dbus_service_property(dbus_interface, signature=u"v",
511
483
                          access=u"readwrite", byte_arrays=False):
512
484
    """Decorators for marking methods of a DBusObjectWithProperties to
706
678
    # dbus.service.Object doesn't use super(), so we can't either.
707
679
    
708
680
    def __init__(self, bus = None, *args, **kwargs):
709
 
        self._approvals_pending = 0
710
681
        self.bus = bus
711
682
        Client.__init__(self, *args, **kwargs)
712
683
        # Only now, when this client is initialized, can it show up on
716
687
                                  + self.name.replace(u".", u"_")))
717
688
        DBusObjectWithProperties.__init__(self, self.bus,
718
689
                                          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
690
    
736
691
    @staticmethod
737
692
    def _datetime_to_dbus(dt, variant_level=0):
744
699
        r = Client.enable(self)
745
700
        if oldstate != self.enabled:
746
701
            # Emit D-Bus signals
747
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
702
            self.PropertyChanged(dbus.String(u"enabled"),
748
703
                                 dbus.Boolean(True, variant_level=1))
749
704
            self.PropertyChanged(
750
 
                dbus.String(u"LastEnabled"),
 
705
                dbus.String(u"last_enabled"),
751
706
                self._datetime_to_dbus(self.last_enabled,
752
707
                                       variant_level=1))
753
708
        return r
757
712
        r = Client.disable(self, quiet=quiet)
758
713
        if not quiet and oldstate != self.enabled:
759
714
            # Emit D-Bus signal
760
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
715
            self.PropertyChanged(dbus.String(u"enabled"),
761
716
                                 dbus.Boolean(False, variant_level=1))
762
717
        return r
763
718
    
775
730
        self.checker_callback_tag = None
776
731
        self.checker = None
777
732
        # Emit D-Bus signal
778
 
        self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
733
        self.PropertyChanged(dbus.String(u"checker_running"),
779
734
                             dbus.Boolean(False, variant_level=1))
780
735
        if os.WIFEXITED(condition):
781
736
            exitstatus = os.WEXITSTATUS(condition)
796
751
        r = Client.checked_ok(self, *args, **kwargs)
797
752
        # Emit D-Bus signal
798
753
        self.PropertyChanged(
799
 
            dbus.String(u"LastCheckedOK"),
 
754
            dbus.String(u"last_checked_ok"),
800
755
            (self._datetime_to_dbus(self.last_checked_ok,
801
756
                                    variant_level=1)))
802
757
        return r
814
769
            # Emit D-Bus signal
815
770
            self.CheckerStarted(self.current_checker_command)
816
771
            self.PropertyChanged(
817
 
                dbus.String(u"CheckerRunning"),
 
772
                dbus.String(u"checker_running"),
818
773
                dbus.Boolean(True, variant_level=1))
819
774
        return r
820
775
    
823
778
        r = Client.stop_checker(self, *args, **kwargs)
824
779
        if (old_checker is not None
825
780
            and getattr(self, u"checker", None) is None):
826
 
            self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
781
            self.PropertyChanged(dbus.String(u"checker_running"),
827
782
                                 dbus.Boolean(False, variant_level=1))
828
783
        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
784
    
842
785
    ## D-Bus methods, signals & properties
843
786
    _interface = u"se.bsnet.fukt.Mandos.Client"
865
808
    # GotSecret - signal
866
809
    @dbus.service.signal(_interface)
867
810
    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
 
        """
 
811
        "D-Bus signal"
872
812
        pass
873
813
    
874
814
    # 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):
 
815
    @dbus.service.signal(_interface)
 
816
    def Rejected(self):
883
817
        "D-Bus signal"
884
818
        pass
885
819
    
886
820
    ## Methods
887
 
 
888
 
    # Approve - method
889
 
    @dbus.service.method(_interface, in_signature=u"b")
890
 
    def Approve(self, value):
891
 
        self.approve(value)
892
 
 
 
821
    
893
822
    # CheckedOK - method
894
823
    @dbus.service.method(_interface)
895
824
    def CheckedOK(self):
920
849
    
921
850
    ## Properties
922
851
    
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
 
852
    # name - property
963
853
    @dbus_service_property(_interface, signature=u"s", access=u"read")
964
 
    def Name_dbus_property(self):
 
854
    def name_dbus_property(self):
965
855
        return dbus.String(self.name)
966
856
    
967
 
    # Fingerprint - property
 
857
    # fingerprint - property
968
858
    @dbus_service_property(_interface, signature=u"s", access=u"read")
969
 
    def Fingerprint_dbus_property(self):
 
859
    def fingerprint_dbus_property(self):
970
860
        return dbus.String(self.fingerprint)
971
861
    
972
 
    # Host - property
 
862
    # host - property
973
863
    @dbus_service_property(_interface, signature=u"s",
974
864
                           access=u"readwrite")
975
 
    def Host_dbus_property(self, value=None):
 
865
    def host_dbus_property(self, value=None):
976
866
        if value is None:       # get
977
867
            return dbus.String(self.host)
978
868
        self.host = value
979
869
        # Emit D-Bus signal
980
 
        self.PropertyChanged(dbus.String(u"Host"),
 
870
        self.PropertyChanged(dbus.String(u"host"),
981
871
                             dbus.String(value, variant_level=1))
982
872
    
983
 
    # Created - property
 
873
    # created - property
984
874
    @dbus_service_property(_interface, signature=u"s", access=u"read")
985
 
    def Created_dbus_property(self):
 
875
    def created_dbus_property(self):
986
876
        return dbus.String(self._datetime_to_dbus(self.created))
987
877
    
988
 
    # LastEnabled - property
 
878
    # last_enabled - property
989
879
    @dbus_service_property(_interface, signature=u"s", access=u"read")
990
 
    def LastEnabled_dbus_property(self):
 
880
    def last_enabled_dbus_property(self):
991
881
        if self.last_enabled is None:
992
882
            return dbus.String(u"")
993
883
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
994
884
    
995
 
    # Enabled - property
 
885
    # enabled - property
996
886
    @dbus_service_property(_interface, signature=u"b",
997
887
                           access=u"readwrite")
998
 
    def Enabled_dbus_property(self, value=None):
 
888
    def enabled_dbus_property(self, value=None):
999
889
        if value is None:       # get
1000
890
            return dbus.Boolean(self.enabled)
1001
891
        if value:
1003
893
        else:
1004
894
            self.disable()
1005
895
    
1006
 
    # LastCheckedOK - property
 
896
    # last_checked_ok - property
1007
897
    @dbus_service_property(_interface, signature=u"s",
1008
898
                           access=u"readwrite")
1009
 
    def LastCheckedOK_dbus_property(self, value=None):
 
899
    def last_checked_ok_dbus_property(self, value=None):
1010
900
        if value is not None:
1011
901
            self.checked_ok()
1012
902
            return
1015
905
        return dbus.String(self._datetime_to_dbus(self
1016
906
                                                  .last_checked_ok))
1017
907
    
1018
 
    # Timeout - property
 
908
    # timeout - property
1019
909
    @dbus_service_property(_interface, signature=u"t",
1020
910
                           access=u"readwrite")
1021
 
    def Timeout_dbus_property(self, value=None):
 
911
    def timeout_dbus_property(self, value=None):
1022
912
        if value is None:       # get
1023
913
            return dbus.UInt64(self.timeout_milliseconds())
1024
914
        self.timeout = datetime.timedelta(0, 0, 0, value)
1025
915
        # Emit D-Bus signal
1026
 
        self.PropertyChanged(dbus.String(u"Timeout"),
 
916
        self.PropertyChanged(dbus.String(u"timeout"),
1027
917
                             dbus.UInt64(value, variant_level=1))
1028
918
        if getattr(self, u"disable_initiator_tag", None) is None:
1029
919
            return
1043
933
            self.disable_initiator_tag = (gobject.timeout_add
1044
934
                                          (time_to_die, self.disable))
1045
935
    
1046
 
    # Interval - property
 
936
    # interval - property
1047
937
    @dbus_service_property(_interface, signature=u"t",
1048
938
                           access=u"readwrite")
1049
 
    def Interval_dbus_property(self, value=None):
 
939
    def interval_dbus_property(self, value=None):
1050
940
        if value is None:       # get
1051
941
            return dbus.UInt64(self.interval_milliseconds())
1052
942
        self.interval = datetime.timedelta(0, 0, 0, value)
1053
943
        # Emit D-Bus signal
1054
 
        self.PropertyChanged(dbus.String(u"Interval"),
 
944
        self.PropertyChanged(dbus.String(u"interval"),
1055
945
                             dbus.UInt64(value, variant_level=1))
1056
946
        if getattr(self, u"checker_initiator_tag", None) is None:
1057
947
            return
1061
951
                                      (value, self.start_checker))
1062
952
        self.start_checker()    # Start one now, too
1063
953
 
1064
 
    # Checker - property
 
954
    # checker - property
1065
955
    @dbus_service_property(_interface, signature=u"s",
1066
956
                           access=u"readwrite")
1067
 
    def Checker_dbus_property(self, value=None):
 
957
    def checker_dbus_property(self, value=None):
1068
958
        if value is None:       # get
1069
959
            return dbus.String(self.checker_command)
1070
960
        self.checker_command = value
1071
961
        # Emit D-Bus signal
1072
 
        self.PropertyChanged(dbus.String(u"Checker"),
 
962
        self.PropertyChanged(dbus.String(u"checker"),
1073
963
                             dbus.String(self.checker_command,
1074
964
                                         variant_level=1))
1075
965
    
1076
 
    # CheckerRunning - property
 
966
    # checker_running - property
1077
967
    @dbus_service_property(_interface, signature=u"b",
1078
968
                           access=u"readwrite")
1079
 
    def CheckerRunning_dbus_property(self, value=None):
 
969
    def checker_running_dbus_property(self, value=None):
1080
970
        if value is None:       # get
1081
971
            return dbus.Boolean(self.checker is not None)
1082
972
        if value:
1084
974
        else:
1085
975
            self.stop_checker()
1086
976
    
1087
 
    # ObjectPath - property
 
977
    # object_path - property
1088
978
    @dbus_service_property(_interface, signature=u"o", access=u"read")
1089
 
    def ObjectPath_dbus_property(self):
 
979
    def object_path_dbus_property(self):
1090
980
        return self.dbus_object_path # is already a dbus.ObjectPath
1091
981
    
1092
 
    # Secret = property
 
982
    # secret = property
1093
983
    @dbus_service_property(_interface, signature=u"ay",
1094
984
                           access=u"write", byte_arrays=True)
1095
 
    def Secret_dbus_property(self, value):
 
985
    def secret_dbus_property(self, value):
1096
986
        self.secret = str(value)
1097
987
    
1098
988
    del _interface
1099
989
 
1100
990
 
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
991
class ClientHandler(socketserver.BaseRequestHandler, object):
1128
992
    """A class to handle client connections.
1129
993
    
1131
995
    Note: This will run in its own forked process."""
1132
996
    
1133
997
    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
 
 
 
998
        logger.info(u"TCP connection from: %s",
 
999
                    unicode(self.client_address))
 
1000
        logger.debug(u"IPC Pipe FD: %d",
 
1001
                     self.server.child_pipe[1].fileno())
 
1002
        # Open IPC pipe to parent process
 
1003
        with contextlib.nested(self.server.child_pipe[1],
 
1004
                               self.server.parent_pipe[0]
 
1005
                               ) as (ipc, ipc_return):
1140
1006
            session = (gnutls.connection
1141
1007
                       .ClientSession(self.request,
1142
1008
                                      gnutls.connection
1143
1009
                                      .X509Credentials()))
1144
 
 
 
1010
            
1145
1011
            # Note: gnutls.connection.X509Credentials is really a
1146
1012
            # generic GnuTLS certificate credentials object so long as
1147
1013
            # no X.509 keys are added to it.  Therefore, we can use it
1148
1014
            # here despite using OpenPGP certificates.
1149
 
 
 
1015
            
1150
1016
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1151
1017
            #                      u"+AES-256-CBC", u"+SHA1",
1152
1018
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1158
1024
            (gnutls.library.functions
1159
1025
             .gnutls_priority_set_direct(session._c_object,
1160
1026
                                         priority, None))
1161
 
 
 
1027
            
1162
1028
            # Start communication using the Mandos protocol
1163
1029
            # Get protocol number
1164
1030
            line = self.request.makefile().readline()
1169
1035
            except (ValueError, IndexError, RuntimeError), error:
1170
1036
                logger.error(u"Unknown protocol version: %s", error)
1171
1037
                return
1172
 
 
 
1038
            
1173
1039
            # Start GnuTLS connection
1174
1040
            try:
1175
1041
                session.handshake()
1179
1045
                # established.  Just abandon the request.
1180
1046
                return
1181
1047
            logger.debug(u"Handshake succeeded")
1182
 
 
1183
 
            approval_required = False
1184
1048
            try:
1185
1049
                try:
1186
1050
                    fpr = self.fingerprint(self.peer_certificate
1190
1054
                    return
1191
1055
                logger.debug(u"Fingerprint: %s", fpr)
1192
1056
 
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
 
1057
                for c in self.server.clients:
 
1058
                    if c.fingerprint == fpr:
 
1059
                        client = c
1215
1060
                        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
 
                
 
1061
                else:
 
1062
                    ipc.write(u"NOTFOUND %s %s\n"
 
1063
                              % (fpr, unicode(self.client_address)))
 
1064
                    return
 
1065
                
 
1066
                class ClientProxy(object):
 
1067
                    """Client proxy object.  Not for calling methods."""
 
1068
                    def __init__(self, client):
 
1069
                        self.client = client
 
1070
                    def __getattr__(self, name):
 
1071
                        if name.startswith("ipc_"):
 
1072
                            def tempfunc():
 
1073
                                ipc.write("%s %s\n" % (name[4:].upper(),
 
1074
                                                       self.client.name))
 
1075
                            return tempfunc
 
1076
                        if not hasattr(self.client, name):
 
1077
                            raise AttributeError
 
1078
                        ipc.write(u"GETATTR %s %s\n"
 
1079
                                  % (name, self.client.fingerprint))
 
1080
                        return pickle.load(ipc_return)
 
1081
                clientproxy = ClientProxy(client)
 
1082
                # Have to check if client.enabled, since it is
 
1083
                # possible that the client was disabled since the
 
1084
                # GnuTLS session was established.
 
1085
                if not clientproxy.enabled:
 
1086
                    clientproxy.ipc_disabled()
 
1087
                    return
 
1088
                
 
1089
                clientproxy.ipc_sending()
1253
1090
                sent_size = 0
1254
1091
                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
 
1092
                    sent = session.send(client.secret[sent_size:])
1260
1093
                    logger.debug(u"Sent: %d, remaining: %d",
1261
1094
                                 sent, len(client.secret)
1262
1095
                                 - (sent_size + sent))
1263
1096
                    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
1097
            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")
 
1098
                session.bye()
1279
1099
    
1280
1100
    @staticmethod
1281
1101
    def peer_certificate(session):
1341
1161
        return hex_fpr
1342
1162
 
1343
1163
 
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 """
 
1164
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
 
1165
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1360
1166
    def process_request(self, request, client_address):
1361
1167
        """Overrides and wraps the original process_request().
1362
1168
        
1363
1169
        This function creates a new pipe in self.pipe
1364
1170
        """
1365
 
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1366
 
 
1367
 
        super(MultiprocessingMixInWithPipe,
 
1171
        # Child writes to child_pipe
 
1172
        self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1173
        # Parent writes to parent_pipe
 
1174
        self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1175
        super(ForkingMixInWithPipes,
1368
1176
              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):
 
1177
        # Close unused ends for parent
 
1178
        self.parent_pipe[0].close() # close read end
 
1179
        self.child_pipe[1].close()  # close write end
 
1180
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
 
1181
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1373
1182
        """Dummy function; override as necessary"""
1374
 
        pass
1375
 
 
1376
 
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
 
1183
        child_pipe_fd.close()
 
1184
        parent_pipe_fd.close()
 
1185
 
 
1186
 
 
1187
class IPv6_TCPServer(ForkingMixInWithPipes,
1377
1188
                     socketserver.TCPServer, object):
1378
1189
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1379
1190
    
1464
1275
            return socketserver.TCPServer.server_activate(self)
1465
1276
    def enable(self):
1466
1277
        self.enabled = True
1467
 
    def add_pipe(self, parent_pipe):
 
1278
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1468
1279
        # Call "handle_ipc" for both data and EOF events
1469
 
        gobject.io_add_watch(parent_pipe.fileno(),
 
1280
        gobject.io_add_watch(child_pipe_fd.fileno(),
1470
1281
                             gobject.IO_IN | gobject.IO_HUP,
1471
1282
                             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):
 
1283
                                               reply = parent_pipe_fd,
 
1284
                                               sender= child_pipe_fd))
 
1285
    def handle_ipc(self, source, condition, reply=None, sender=None):
1476
1286
        condition_names = {
1477
1287
            gobject.IO_IN: u"IN",   # There is data to read.
1478
1288
            gobject.IO_OUT: u"OUT", # Data can be written (without
1489
1299
                                       if cond & condition)
1490
1300
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1491
1301
                     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
 
 
 
1302
        
 
1303
        # Read a line from the file object
 
1304
        cmdline = sender.readline()
 
1305
        if not cmdline:             # Empty line means end of file
 
1306
            # close the IPC pipes
 
1307
            sender.close()
 
1308
            reply.close()
 
1309
            
 
1310
            # Stop calling this function
 
1311
            return False
 
1312
        
 
1313
        logger.debug(u"IPC command: %r", cmdline)
 
1314
        
 
1315
        # Parse and act on command
 
1316
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
 
1317
        
 
1318
        if cmd == u"NOTFOUND":
 
1319
            fpr, address = args.split(None, 1)
 
1320
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1321
                           u"dress: %s", fpr, address)
 
1322
            if self.use_dbus:
 
1323
                # Emit D-Bus signal
 
1324
                mandos_dbus_service.ClientNotFound(fpr, address)
 
1325
        elif cmd == u"DISABLED":
 
1326
            for client in self.clients:
 
1327
                if client.name == args:
 
1328
                    logger.warning(u"Client %s is disabled", args)
 
1329
                    if self.use_dbus:
 
1330
                        # Emit D-Bus signal
 
1331
                        client.Rejected()
 
1332
                    break
 
1333
            else:
 
1334
                logger.error(u"Unknown client %s is disabled", args)
 
1335
        elif cmd == u"SENDING":
 
1336
            for client in self.clients:
 
1337
                if client.name == args:
 
1338
                    logger.info(u"Sending secret to %s", client.name)
 
1339
                    client.checked_ok()
 
1340
                    if self.use_dbus:
 
1341
                        # Emit D-Bus signal
 
1342
                        client.GotSecret()
 
1343
                    break
 
1344
            else:
 
1345
                logger.error(u"Sending secret to unknown client %s",
 
1346
                             args)
 
1347
        elif cmd == u"GETATTR":
 
1348
            attr_name, fpr = args.split(None, 1)
 
1349
            for client in self.clients:
 
1350
                if client.fingerprint == fpr:
 
1351
                    attr_value = getattr(client, attr_name, None)
 
1352
                    logger.debug("IPC reply: %r", attr_value)
 
1353
                    pickle.dump(attr_value, reply)
 
1354
                    break
 
1355
            else:
 
1356
                logger.error(u"Client %s on address %s requesting "
 
1357
                             u"attribute %s not found", fpr, address,
 
1358
                             attr_name)
 
1359
                pickle.dump(None, reply)
 
1360
        else:
 
1361
            logger.error(u"Unknown IPC command: %r", cmdline)
 
1362
        
 
1363
        # Keep calling this function
1546
1364
        return True
1547
1365
 
1548
1366
 
1651
1469
    parser.add_option("--debug", action=u"store_true",
1652
1470
                      help=u"Debug mode; run in foreground and log to"
1653
1471
                      u" terminal")
1654
 
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
1655
 
                      help=u"Debug level for stdout output")
1656
1472
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1657
1473
                      u" priority string (see GnuTLS documentation)")
1658
1474
    parser.add_option("--servicename", type=u"string",
1683
1499
                        u"servicename": u"Mandos",
1684
1500
                        u"use_dbus": u"True",
1685
1501
                        u"use_ipv6": u"True",
1686
 
                        u"debuglevel": u"",
1687
1502
                        }
1688
1503
    
1689
1504
    # Parse config file for server-global settings
1706
1521
    # options, if set.
1707
1522
    for option in (u"interface", u"address", u"port", u"debug",
1708
1523
                   u"priority", u"servicename", u"configdir",
1709
 
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
 
1524
                   u"use_dbus", u"use_ipv6"):
1710
1525
        value = getattr(options, option)
1711
1526
        if value is not None:
1712
1527
            server_settings[option] = value
1721
1536
    
1722
1537
    # For convenience
1723
1538
    debug = server_settings[u"debug"]
1724
 
    debuglevel = server_settings[u"debuglevel"]
1725
1539
    use_dbus = server_settings[u"use_dbus"]
1726
1540
    use_ipv6 = server_settings[u"use_ipv6"]
1727
 
 
 
1541
    
 
1542
    if not debug:
 
1543
        syslogger.setLevel(logging.WARNING)
 
1544
        console.setLevel(logging.WARNING)
 
1545
    
1728
1546
    if server_settings[u"servicename"] != u"Mandos":
1729
1547
        syslogger.setFormatter(logging.Formatter
1730
1548
                               (u'Mandos (%s) [%%(process)d]:'
1736
1554
                        u"interval": u"5m",
1737
1555
                        u"checker": u"fping -q -- %%(host)s",
1738
1556
                        u"host": u"",
1739
 
                        u"approval_delay": u"0s",
1740
 
                        u"approval_duration": u"1s",
1741
1557
                        }
1742
1558
    client_config = configparser.SafeConfigParser(client_defaults)
1743
1559
    client_config.read(os.path.join(server_settings[u"configdir"],
1782
1598
        if error[0] != errno.EPERM:
1783
1599
            raise error
1784
1600
    
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
 
 
 
1601
    # Enable all possible GnuTLS debugging
1793
1602
    if debug:
1794
 
        # Enable all possible GnuTLS debugging
1795
 
        
1796
1603
        # "Use a log level over 10 to enable all debugging options."
1797
1604
        # - GnuTLS manual
1798
1605
        gnutls.library.functions.gnutls_global_set_log_level(11)
1803
1610
        
1804
1611
        (gnutls.library.functions
1805
1612
         .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
1613
    
1817
1614
    global main_loop
1818
1615
    # From the Avahi example code
1836
1633
    if server_settings["interface"]:
1837
1634
        service.interface = (if_nametoindex
1838
1635
                             (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
1636
    
1847
1637
    client_class = Client
1848
1638
    if use_dbus:
1849
1639
        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
1640
    tcp_server.clients.update(set(
1863
1641
            client_class(name = section,
1864
 
                         config= dict(client_config_items(
1865
 
                        client_config, section)))
 
1642
                         config= dict(client_config.items(section)))
1866
1643
            for section in client_config.sections()))
1867
1644
    if not tcp_server.clients:
1868
1645
        logger.warning(u"No clients defined")
1869
 
        
 
1646
    
 
1647
    if debug:
 
1648
        # Redirect stdin so all checkers get /dev/null
 
1649
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1650
        os.dup2(null, sys.stdin.fileno())
 
1651
        if null > 2:
 
1652
            os.close(null)
 
1653
    else:
 
1654
        # No console logging
 
1655
        logger.removeHandler(console)
 
1656
        # Close all input and output, do double fork, etc.
 
1657
        daemon()
 
1658
    
1870
1659
    try:
1871
1660
        with pidfile:
1872
1661
            pid = os.getpid()