/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-06-19 00:37:04 UTC
  • mto: (24.1.149 mandos)
  • mto: This revision was merged to the branch mainline in revision 417.
  • Revision ID: teddy@fukt.bsnet.se-20100619003704-vpicvssvv1ktg2om
* mandos (ClientHandler.handle): Set up the GnuTLS session object
                                 before reading the protocol number.
 (ClientHandler.handle/ProxyObject): New.

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"""
199
193
        self.group.Commit()
200
194
    def entry_group_state_changed(self, state, error):
201
195
        """Derived from the Avahi example code"""
202
 
        logger.debug(u"Avahi entry group state change: %i", state)
 
196
        logger.debug(u"Avahi state change: %i", state)
203
197
        
204
198
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
205
199
            logger.debug(u"Zeroconf service established.")
218
212
            self.group = None
219
213
    def server_state_changed(self, state):
220
214
        """Derived from the Avahi example code"""
221
 
        logger.debug(u"Avahi server state change: %i", state)
222
215
        if state == avahi.SERVER_COLLISION:
223
216
            logger.error(u"Zeroconf server name collision")
224
217
            self.remove()
265
258
                     runtime with vars(self) as dict, so that for
266
259
                     instance %(name)s can be used in the command.
267
260
    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
261
    """
272
262
    
273
263
    @staticmethod
284
274
    def interval_milliseconds(self):
285
275
        "Return the 'interval' attribute in milliseconds"
286
276
        return self._timedelta_to_milliseconds(self.interval)
287
 
 
288
 
    def approval_delay_milliseconds(self):
289
 
        return self._timedelta_to_milliseconds(self.approval_delay)
290
277
    
291
278
    def __init__(self, name = None, disable_hook=None, config=None):
292
279
        """Note: the 'checker' key in 'config' sets the
327
314
        self.checker_command = config[u"checker"]
328
315
        self.current_checker_command = None
329
316
        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
317
    
340
 
    def send_changedstate(self):
341
 
        self.changedstate.acquire()
342
 
        self.changedstate.notify_all()
343
 
        self.changedstate.release()
344
 
        
345
318
    def enable(self):
346
319
        """Start this client's checker and timeout hooks"""
347
320
        if getattr(self, u"enabled", False):
348
321
            # Already enabled
349
322
            return
350
 
        self.send_changedstate()
351
323
        self.last_enabled = datetime.datetime.utcnow()
352
324
        # Schedule a new checker to be started an 'interval' from now,
353
325
        # and every interval from then on.
367
339
        if not getattr(self, "enabled", False):
368
340
            return False
369
341
        if not quiet:
370
 
            self.send_changedstate()
371
 
        if not quiet:
372
342
            logger.info(u"Disabling client %s", self.name)
373
343
        if getattr(self, u"disable_initiator_tag", False):
374
344
            gobject.source_remove(self.disable_initiator_tag)
507
477
                raise
508
478
        self.checker = None
509
479
 
 
480
 
510
481
def dbus_service_property(dbus_interface, signature=u"v",
511
482
                          access=u"readwrite", byte_arrays=False):
512
483
    """Decorators for marking methods of a DBusObjectWithProperties to
706
677
    # dbus.service.Object doesn't use super(), so we can't either.
707
678
    
708
679
    def __init__(self, bus = None, *args, **kwargs):
709
 
        self._approvals_pending = 0
710
680
        self.bus = bus
711
681
        Client.__init__(self, *args, **kwargs)
712
682
        # Only now, when this client is initialized, can it show up on
716
686
                                  + self.name.replace(u".", u"_")))
717
687
        DBusObjectWithProperties.__init__(self, self.bus,
718
688
                                          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
689
    
736
690
    @staticmethod
737
691
    def _datetime_to_dbus(dt, variant_level=0):
744
698
        r = Client.enable(self)
745
699
        if oldstate != self.enabled:
746
700
            # Emit D-Bus signals
747
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
701
            self.PropertyChanged(dbus.String(u"enabled"),
748
702
                                 dbus.Boolean(True, variant_level=1))
749
703
            self.PropertyChanged(
750
 
                dbus.String(u"LastEnabled"),
 
704
                dbus.String(u"last_enabled"),
751
705
                self._datetime_to_dbus(self.last_enabled,
752
706
                                       variant_level=1))
753
707
        return r
757
711
        r = Client.disable(self, quiet=quiet)
758
712
        if not quiet and oldstate != self.enabled:
759
713
            # Emit D-Bus signal
760
 
            self.PropertyChanged(dbus.String(u"Enabled"),
 
714
            self.PropertyChanged(dbus.String(u"enabled"),
761
715
                                 dbus.Boolean(False, variant_level=1))
762
716
        return r
763
717
    
775
729
        self.checker_callback_tag = None
776
730
        self.checker = None
777
731
        # Emit D-Bus signal
778
 
        self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
732
        self.PropertyChanged(dbus.String(u"checker_running"),
779
733
                             dbus.Boolean(False, variant_level=1))
780
734
        if os.WIFEXITED(condition):
781
735
            exitstatus = os.WEXITSTATUS(condition)
796
750
        r = Client.checked_ok(self, *args, **kwargs)
797
751
        # Emit D-Bus signal
798
752
        self.PropertyChanged(
799
 
            dbus.String(u"LastCheckedOK"),
 
753
            dbus.String(u"last_checked_ok"),
800
754
            (self._datetime_to_dbus(self.last_checked_ok,
801
755
                                    variant_level=1)))
802
756
        return r
814
768
            # Emit D-Bus signal
815
769
            self.CheckerStarted(self.current_checker_command)
816
770
            self.PropertyChanged(
817
 
                dbus.String(u"CheckerRunning"),
 
771
                dbus.String(u"checker_running"),
818
772
                dbus.Boolean(True, variant_level=1))
819
773
        return r
820
774
    
823
777
        r = Client.stop_checker(self, *args, **kwargs)
824
778
        if (old_checker is not None
825
779
            and getattr(self, u"checker", None) is None):
826
 
            self.PropertyChanged(dbus.String(u"CheckerRunning"),
 
780
            self.PropertyChanged(dbus.String(u"checker_running"),
827
781
                                 dbus.Boolean(False, variant_level=1))
828
782
        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
783
    
842
784
    ## D-Bus methods, signals & properties
843
785
    _interface = u"se.bsnet.fukt.Mandos.Client"
865
807
    # GotSecret - signal
866
808
    @dbus.service.signal(_interface)
867
809
    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
 
        """
 
810
        "D-Bus signal"
872
811
        pass
873
812
    
874
813
    # 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):
 
814
    @dbus.service.signal(_interface)
 
815
    def Rejected(self):
883
816
        "D-Bus signal"
884
817
        pass
885
818
    
886
819
    ## Methods
887
 
 
888
 
    # Approve - method
889
 
    @dbus.service.method(_interface, in_signature=u"b")
890
 
    def Approve(self, value):
891
 
        self.approve(value)
892
 
 
 
820
    
893
821
    # CheckedOK - method
894
822
    @dbus.service.method(_interface)
895
823
    def CheckedOK(self):
920
848
    
921
849
    ## Properties
922
850
    
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
 
851
    # name - property
963
852
    @dbus_service_property(_interface, signature=u"s", access=u"read")
964
 
    def Name_dbus_property(self):
 
853
    def name_dbus_property(self):
965
854
        return dbus.String(self.name)
966
855
    
967
 
    # Fingerprint - property
 
856
    # fingerprint - property
968
857
    @dbus_service_property(_interface, signature=u"s", access=u"read")
969
 
    def Fingerprint_dbus_property(self):
 
858
    def fingerprint_dbus_property(self):
970
859
        return dbus.String(self.fingerprint)
971
860
    
972
 
    # Host - property
 
861
    # host - property
973
862
    @dbus_service_property(_interface, signature=u"s",
974
863
                           access=u"readwrite")
975
 
    def Host_dbus_property(self, value=None):
 
864
    def host_dbus_property(self, value=None):
976
865
        if value is None:       # get
977
866
            return dbus.String(self.host)
978
867
        self.host = value
979
868
        # Emit D-Bus signal
980
 
        self.PropertyChanged(dbus.String(u"Host"),
 
869
        self.PropertyChanged(dbus.String(u"host"),
981
870
                             dbus.String(value, variant_level=1))
982
871
    
983
 
    # Created - property
 
872
    # created - property
984
873
    @dbus_service_property(_interface, signature=u"s", access=u"read")
985
 
    def Created_dbus_property(self):
 
874
    def created_dbus_property(self):
986
875
        return dbus.String(self._datetime_to_dbus(self.created))
987
876
    
988
 
    # LastEnabled - property
 
877
    # last_enabled - property
989
878
    @dbus_service_property(_interface, signature=u"s", access=u"read")
990
 
    def LastEnabled_dbus_property(self):
 
879
    def last_enabled_dbus_property(self):
991
880
        if self.last_enabled is None:
992
881
            return dbus.String(u"")
993
882
        return dbus.String(self._datetime_to_dbus(self.last_enabled))
994
883
    
995
 
    # Enabled - property
 
884
    # enabled - property
996
885
    @dbus_service_property(_interface, signature=u"b",
997
886
                           access=u"readwrite")
998
 
    def Enabled_dbus_property(self, value=None):
 
887
    def enabled_dbus_property(self, value=None):
999
888
        if value is None:       # get
1000
889
            return dbus.Boolean(self.enabled)
1001
890
        if value:
1003
892
        else:
1004
893
            self.disable()
1005
894
    
1006
 
    # LastCheckedOK - property
 
895
    # last_checked_ok - property
1007
896
    @dbus_service_property(_interface, signature=u"s",
1008
897
                           access=u"readwrite")
1009
 
    def LastCheckedOK_dbus_property(self, value=None):
 
898
    def last_checked_ok_dbus_property(self, value=None):
1010
899
        if value is not None:
1011
900
            self.checked_ok()
1012
901
            return
1015
904
        return dbus.String(self._datetime_to_dbus(self
1016
905
                                                  .last_checked_ok))
1017
906
    
1018
 
    # Timeout - property
 
907
    # timeout - property
1019
908
    @dbus_service_property(_interface, signature=u"t",
1020
909
                           access=u"readwrite")
1021
 
    def Timeout_dbus_property(self, value=None):
 
910
    def timeout_dbus_property(self, value=None):
1022
911
        if value is None:       # get
1023
912
            return dbus.UInt64(self.timeout_milliseconds())
1024
913
        self.timeout = datetime.timedelta(0, 0, 0, value)
1025
914
        # Emit D-Bus signal
1026
 
        self.PropertyChanged(dbus.String(u"Timeout"),
 
915
        self.PropertyChanged(dbus.String(u"timeout"),
1027
916
                             dbus.UInt64(value, variant_level=1))
1028
917
        if getattr(self, u"disable_initiator_tag", None) is None:
1029
918
            return
1043
932
            self.disable_initiator_tag = (gobject.timeout_add
1044
933
                                          (time_to_die, self.disable))
1045
934
    
1046
 
    # Interval - property
 
935
    # interval - property
1047
936
    @dbus_service_property(_interface, signature=u"t",
1048
937
                           access=u"readwrite")
1049
 
    def Interval_dbus_property(self, value=None):
 
938
    def interval_dbus_property(self, value=None):
1050
939
        if value is None:       # get
1051
940
            return dbus.UInt64(self.interval_milliseconds())
1052
941
        self.interval = datetime.timedelta(0, 0, 0, value)
1053
942
        # Emit D-Bus signal
1054
 
        self.PropertyChanged(dbus.String(u"Interval"),
 
943
        self.PropertyChanged(dbus.String(u"interval"),
1055
944
                             dbus.UInt64(value, variant_level=1))
1056
945
        if getattr(self, u"checker_initiator_tag", None) is None:
1057
946
            return
1061
950
                                      (value, self.start_checker))
1062
951
        self.start_checker()    # Start one now, too
1063
952
 
1064
 
    # Checker - property
 
953
    # checker - property
1065
954
    @dbus_service_property(_interface, signature=u"s",
1066
955
                           access=u"readwrite")
1067
 
    def Checker_dbus_property(self, value=None):
 
956
    def checker_dbus_property(self, value=None):
1068
957
        if value is None:       # get
1069
958
            return dbus.String(self.checker_command)
1070
959
        self.checker_command = value
1071
960
        # Emit D-Bus signal
1072
 
        self.PropertyChanged(dbus.String(u"Checker"),
 
961
        self.PropertyChanged(dbus.String(u"checker"),
1073
962
                             dbus.String(self.checker_command,
1074
963
                                         variant_level=1))
1075
964
    
1076
 
    # CheckerRunning - property
 
965
    # checker_running - property
1077
966
    @dbus_service_property(_interface, signature=u"b",
1078
967
                           access=u"readwrite")
1079
 
    def CheckerRunning_dbus_property(self, value=None):
 
968
    def checker_running_dbus_property(self, value=None):
1080
969
        if value is None:       # get
1081
970
            return dbus.Boolean(self.checker is not None)
1082
971
        if value:
1084
973
        else:
1085
974
            self.stop_checker()
1086
975
    
1087
 
    # ObjectPath - property
 
976
    # object_path - property
1088
977
    @dbus_service_property(_interface, signature=u"o", access=u"read")
1089
 
    def ObjectPath_dbus_property(self):
 
978
    def object_path_dbus_property(self):
1090
979
        return self.dbus_object_path # is already a dbus.ObjectPath
1091
980
    
1092
 
    # Secret = property
 
981
    # secret = property
1093
982
    @dbus_service_property(_interface, signature=u"ay",
1094
983
                           access=u"write", byte_arrays=True)
1095
 
    def Secret_dbus_property(self, value):
 
984
    def secret_dbus_property(self, value):
1096
985
        self.secret = str(value)
1097
986
    
1098
987
    del _interface
1099
988
 
1100
989
 
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
990
class ClientHandler(socketserver.BaseRequestHandler, object):
1128
991
    """A class to handle client connections.
1129
992
    
1131
994
    Note: This will run in its own forked process."""
1132
995
    
1133
996
    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
 
 
 
997
        logger.info(u"TCP connection from: %s",
 
998
                    unicode(self.client_address))
 
999
        logger.debug(u"IPC Pipe FD: %d",
 
1000
                     self.server.child_pipe[1].fileno())
 
1001
        # Open IPC pipe to parent process
 
1002
        with contextlib.nested(self.server.child_pipe[1],
 
1003
                               self.server.parent_pipe[0]
 
1004
                               ) as (ipc, ipc_return):
1140
1005
            session = (gnutls.connection
1141
1006
                       .ClientSession(self.request,
1142
1007
                                      gnutls.connection
1143
1008
                                      .X509Credentials()))
1144
 
 
 
1009
            
1145
1010
            # Note: gnutls.connection.X509Credentials is really a
1146
1011
            # generic GnuTLS certificate credentials object so long as
1147
1012
            # no X.509 keys are added to it.  Therefore, we can use it
1148
1013
            # here despite using OpenPGP certificates.
1149
 
 
 
1014
            
1150
1015
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1151
1016
            #                      u"+AES-256-CBC", u"+SHA1",
1152
1017
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1158
1023
            (gnutls.library.functions
1159
1024
             .gnutls_priority_set_direct(session._c_object,
1160
1025
                                         priority, None))
1161
 
 
 
1026
            
1162
1027
            # Start communication using the Mandos protocol
1163
1028
            # Get protocol number
1164
1029
            line = self.request.makefile().readline()
1169
1034
            except (ValueError, IndexError, RuntimeError), error:
1170
1035
                logger.error(u"Unknown protocol version: %s", error)
1171
1036
                return
1172
 
 
 
1037
            
1173
1038
            # Start GnuTLS connection
1174
1039
            try:
1175
1040
                session.handshake()
1179
1044
                # established.  Just abandon the request.
1180
1045
                return
1181
1046
            logger.debug(u"Handshake succeeded")
1182
 
 
1183
 
            approval_required = False
1184
1047
            try:
1185
1048
                try:
1186
1049
                    fpr = self.fingerprint(self.peer_certificate
1190
1053
                    return
1191
1054
                logger.debug(u"Fingerprint: %s", fpr)
1192
1055
 
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
 
1056
                for c in self.server.clients:
 
1057
                    if c.fingerprint == fpr:
 
1058
                        client = c
1215
1059
                        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("Approval timed out")
1247
 
                            return
1248
 
                        else:
1249
 
                            break
1250
 
                    else:
1251
 
                        delay -= time2 - time
1252
 
                
 
1060
                else:
 
1061
                    ipc.write(u"NOTFOUND %s %s\n"
 
1062
                              % (fpr, unicode(self.client_address)))
 
1063
                    return
 
1064
                
 
1065
                class ClientProxy(object):
 
1066
                    """Client proxy object.  Not for calling methods."""
 
1067
                    def __init__(self, client):
 
1068
                        self.client = client
 
1069
                    def __getattr__(self, name):
 
1070
                        if name.startswith("ipc_"):
 
1071
                            def tempfunc():
 
1072
                                ipc.write("%s %s\n" % (name[4:].upper(),
 
1073
                                                       self.client.name))
 
1074
                            return tempfunc
 
1075
                        if not hasattr(self.client, name):
 
1076
                            raise AttributeError
 
1077
                        ipc.write(u"GETATTR %s %s\n"
 
1078
                                  % (name, self.client.fingerprint))
 
1079
                        return pickle.load(ipc_return)
 
1080
                clientproxy = ClientProxy(client)
 
1081
                # Have to check if client.enabled, since it is
 
1082
                # possible that the client was disabled since the
 
1083
                # GnuTLS session was established.
 
1084
                if not clientproxy.enabled:
 
1085
                    clientproxy.ipc_disabled()
 
1086
                    return
 
1087
                
 
1088
                clientproxy.ipc_sending()
1253
1089
                sent_size = 0
1254
1090
                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
 
1091
                    sent = session.send(client.secret[sent_size:])
1260
1092
                    logger.debug(u"Sent: %d, remaining: %d",
1261
1093
                                 sent, len(client.secret)
1262
1094
                                 - (sent_size + sent))
1263
1095
                    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
1096
            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")
 
1097
                session.bye()
1279
1098
    
1280
1099
    @staticmethod
1281
1100
    def peer_certificate(session):
1341
1160
        return hex_fpr
1342
1161
 
1343
1162
 
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 """
 
1163
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
 
1164
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
1360
1165
    def process_request(self, request, client_address):
1361
1166
        """Overrides and wraps the original process_request().
1362
1167
        
1363
1168
        This function creates a new pipe in self.pipe
1364
1169
        """
1365
 
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
1366
 
 
1367
 
        super(MultiprocessingMixInWithPipe,
 
1170
        # Child writes to child_pipe
 
1171
        self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1172
        # Parent writes to parent_pipe
 
1173
        self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
 
1174
        super(ForkingMixInWithPipes,
1368
1175
              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):
 
1176
        # Close unused ends for parent
 
1177
        self.parent_pipe[0].close() # close read end
 
1178
        self.child_pipe[1].close()  # close write end
 
1179
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
 
1180
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1373
1181
        """Dummy function; override as necessary"""
1374
 
        pass
1375
 
 
1376
 
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
 
1182
        child_pipe_fd.close()
 
1183
        parent_pipe_fd.close()
 
1184
 
 
1185
 
 
1186
class IPv6_TCPServer(ForkingMixInWithPipes,
1377
1187
                     socketserver.TCPServer, object):
1378
1188
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1379
1189
    
1464
1274
            return socketserver.TCPServer.server_activate(self)
1465
1275
    def enable(self):
1466
1276
        self.enabled = True
1467
 
    def add_pipe(self, parent_pipe):
 
1277
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1468
1278
        # Call "handle_ipc" for both data and EOF events
1469
 
        gobject.io_add_watch(parent_pipe.fileno(),
 
1279
        gobject.io_add_watch(child_pipe_fd.fileno(),
1470
1280
                             gobject.IO_IN | gobject.IO_HUP,
1471
1281
                             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):
 
1282
                                               reply = parent_pipe_fd,
 
1283
                                               sender= child_pipe_fd))
 
1284
    def handle_ipc(self, source, condition, reply=None, sender=None):
1476
1285
        condition_names = {
1477
1286
            gobject.IO_IN: u"IN",   # There is data to read.
1478
1287
            gobject.IO_OUT: u"OUT", # Data can be written (without
1487
1296
                                       for cond, name in
1488
1297
                                       condition_names.iteritems()
1489
1298
                                       if cond & condition)
1490
 
        # error or the other end of multiprocessing.Pipe has closed
1491
 
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
1492
 
            return False
1493
 
        
1494
 
        # Read a request from the child
1495
 
        request = parent_pipe.recv()
1496
 
        command = request[0]
1497
 
        
1498
 
        if command == 'init':
1499
 
            fpr = request[1]
1500
 
            address = request[2]
1501
 
            
1502
 
            for c in self.clients:
1503
 
                if c.fingerprint == fpr:
1504
 
                    client = c
1505
 
                    break
1506
 
            else:
1507
 
                logger.warning(u"Client not found for fingerprint: %s, ad"
1508
 
                               u"dress: %s", fpr, address)
1509
 
                if self.use_dbus:
1510
 
                    # Emit D-Bus signal
1511
 
                    mandos_dbus_service.ClientNotFound(fpr, address)
1512
 
                parent_pipe.send(False)
1513
 
                return False
1514
 
            
1515
 
            gobject.io_add_watch(parent_pipe.fileno(),
1516
 
                                 gobject.IO_IN | gobject.IO_HUP,
1517
 
                                 functools.partial(self.handle_ipc,
1518
 
                                                   parent_pipe = parent_pipe,
1519
 
                                                   client_object = client))
1520
 
            parent_pipe.send(True)
1521
 
            # remove the old hook in favor of the new above hook on same fileno
1522
 
            return False
1523
 
        if command == 'funcall':
1524
 
            funcname = request[1]
1525
 
            args = request[2]
1526
 
            kwargs = request[3]
1527
 
            
1528
 
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
1529
 
 
1530
 
        if command == 'getattr':
1531
 
            attrname = request[1]
1532
 
            if callable(client_object.__getattribute__(attrname)):
1533
 
                parent_pipe.send(('function',))
1534
 
            else:
1535
 
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
1536
 
        
1537
 
        if command == 'setattr':
1538
 
            attrname = request[1]
1539
 
            value = request[2]
1540
 
            setattr(client_object, attrname, value)
1541
 
 
 
1299
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
 
1300
                     conditions_string)
 
1301
        
 
1302
        # Read a line from the file object
 
1303
        cmdline = sender.readline()
 
1304
        if not cmdline:             # Empty line means end of file
 
1305
            # close the IPC pipes
 
1306
            sender.close()
 
1307
            reply.close()
 
1308
            
 
1309
            # Stop calling this function
 
1310
            return False
 
1311
        
 
1312
        logger.debug(u"IPC command: %r", cmdline)
 
1313
        
 
1314
        # Parse and act on command
 
1315
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
 
1316
        
 
1317
        if cmd == u"NOTFOUND":
 
1318
            fpr, address = args.split(None, 1)
 
1319
            logger.warning(u"Client not found for fingerprint: %s, ad"
 
1320
                           u"dress: %s", fpr, address)
 
1321
            if self.use_dbus:
 
1322
                # Emit D-Bus signal
 
1323
                mandos_dbus_service.ClientNotFound(fpr, address)
 
1324
        elif cmd == u"DISABLED":
 
1325
            for client in self.clients:
 
1326
                if client.name == args:
 
1327
                    logger.warning(u"Client %s is disabled", args)
 
1328
                    if self.use_dbus:
 
1329
                        # Emit D-Bus signal
 
1330
                        client.Rejected()
 
1331
                    break
 
1332
            else:
 
1333
                logger.error(u"Unknown client %s is disabled", args)
 
1334
        elif cmd == u"SENDING":
 
1335
            for client in self.clients:
 
1336
                if client.name == args:
 
1337
                    logger.info(u"Sending secret to %s", client.name)
 
1338
                    client.checked_ok()
 
1339
                    if self.use_dbus:
 
1340
                        # Emit D-Bus signal
 
1341
                        client.GotSecret()
 
1342
                    break
 
1343
            else:
 
1344
                logger.error(u"Sending secret to unknown client %s",
 
1345
                             args)
 
1346
        elif cmd == u"GETATTR":
 
1347
            attr_name, fpr = args.split(None, 1)
 
1348
            for client in self.clients:
 
1349
                if client.fingerprint == fpr:
 
1350
                    attr_value = getattr(client, attr_name, None)
 
1351
                    logger.debug("IPC reply: %r", attr_value)
 
1352
                    pickle.dump(attr_value, reply)
 
1353
                    break
 
1354
            else:
 
1355
                logger.error(u"Client %s on address %s requesting "
 
1356
                             u"attribute %s not found", fpr, address,
 
1357
                             attr_name)
 
1358
                pickle.dump(None, reply)
 
1359
        else:
 
1360
            logger.error(u"Unknown IPC command: %r", cmdline)
 
1361
        
 
1362
        # Keep calling this function
1542
1363
        return True
1543
1364
 
1544
1365
 
1647
1468
    parser.add_option("--debug", action=u"store_true",
1648
1469
                      help=u"Debug mode; run in foreground and log to"
1649
1470
                      u" terminal")
1650
 
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
1651
 
                      help=u"Debug level for stdout output")
1652
1471
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1653
1472
                      u" priority string (see GnuTLS documentation)")
1654
1473
    parser.add_option("--servicename", type=u"string",
1679
1498
                        u"servicename": u"Mandos",
1680
1499
                        u"use_dbus": u"True",
1681
1500
                        u"use_ipv6": u"True",
1682
 
                        u"debuglevel": u"",
1683
1501
                        }
1684
1502
    
1685
1503
    # Parse config file for server-global settings
1702
1520
    # options, if set.
1703
1521
    for option in (u"interface", u"address", u"port", u"debug",
1704
1522
                   u"priority", u"servicename", u"configdir",
1705
 
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
 
1523
                   u"use_dbus", u"use_ipv6"):
1706
1524
        value = getattr(options, option)
1707
1525
        if value is not None:
1708
1526
            server_settings[option] = value
1717
1535
    
1718
1536
    # For convenience
1719
1537
    debug = server_settings[u"debug"]
1720
 
    debuglevel = server_settings[u"debuglevel"]
1721
1538
    use_dbus = server_settings[u"use_dbus"]
1722
1539
    use_ipv6 = server_settings[u"use_ipv6"]
1723
 
 
 
1540
    
 
1541
    if not debug:
 
1542
        syslogger.setLevel(logging.WARNING)
 
1543
        console.setLevel(logging.WARNING)
 
1544
    
1724
1545
    if server_settings[u"servicename"] != u"Mandos":
1725
1546
        syslogger.setFormatter(logging.Formatter
1726
1547
                               (u'Mandos (%s) [%%(process)d]:'
1732
1553
                        u"interval": u"5m",
1733
1554
                        u"checker": u"fping -q -- %%(host)s",
1734
1555
                        u"host": u"",
1735
 
                        u"approval_delay": u"0s",
1736
 
                        u"approval_duration": u"1s",
1737
1556
                        }
1738
1557
    client_config = configparser.SafeConfigParser(client_defaults)
1739
1558
    client_config.read(os.path.join(server_settings[u"configdir"],
1745
1564
    tcp_server = MandosServer((server_settings[u"address"],
1746
1565
                               server_settings[u"port"]),
1747
1566
                              ClientHandler,
1748
 
                              interface=(server_settings[u"interface"]
1749
 
                                         or None),
 
1567
                              interface=server_settings[u"interface"],
1750
1568
                              use_ipv6=use_ipv6,
1751
1569
                              gnutls_priority=
1752
1570
                              server_settings[u"priority"],
1778
1596
        if error[0] != errno.EPERM:
1779
1597
            raise error
1780
1598
    
1781
 
    if not debug and not debuglevel:
1782
 
        syslogger.setLevel(logging.WARNING)
1783
 
        console.setLevel(logging.WARNING)
1784
 
    if debuglevel:
1785
 
        level = getattr(logging, debuglevel.upper())
1786
 
        syslogger.setLevel(level)
1787
 
        console.setLevel(level)
1788
 
 
 
1599
    # Enable all possible GnuTLS debugging
1789
1600
    if debug:
1790
 
        # Enable all possible GnuTLS debugging
1791
 
        
1792
1601
        # "Use a log level over 10 to enable all debugging options."
1793
1602
        # - GnuTLS manual
1794
1603
        gnutls.library.functions.gnutls_global_set_log_level(11)
1799
1608
        
1800
1609
        (gnutls.library.functions
1801
1610
         .gnutls_global_set_log_function(debug_gnutls))
1802
 
        
1803
 
        # Redirect stdin so all checkers get /dev/null
1804
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1805
 
        os.dup2(null, sys.stdin.fileno())
1806
 
        if null > 2:
1807
 
            os.close(null)
1808
 
    else:
1809
 
        # No console logging
1810
 
        logger.removeHandler(console)
1811
 
    
1812
1611
    
1813
1612
    global main_loop
1814
1613
    # From the Avahi example code
1832
1631
    if server_settings["interface"]:
1833
1632
        service.interface = (if_nametoindex
1834
1633
                             (str(server_settings[u"interface"])))
1835
 
 
1836
 
    if not debug:
1837
 
        # Close all input and output, do double fork, etc.
1838
 
        daemon()
1839
 
        
1840
 
    global multiprocessing_manager
1841
 
    multiprocessing_manager = multiprocessing.Manager()
1842
1634
    
1843
1635
    client_class = Client
1844
1636
    if use_dbus:
1845
1637
        client_class = functools.partial(ClientDBus, bus = bus)
1846
 
    def client_config_items(config, section):
1847
 
        special_settings = {
1848
 
            "approved_by_default":
1849
 
                lambda: config.getboolean(section,
1850
 
                                          "approved_by_default"),
1851
 
            }
1852
 
        for name, value in config.items(section):
1853
 
            try:
1854
 
                yield (name, special_settings[name]())
1855
 
            except KeyError:
1856
 
                yield (name, value)
1857
 
    
1858
1638
    tcp_server.clients.update(set(
1859
1639
            client_class(name = section,
1860
 
                         config= dict(client_config_items(
1861
 
                        client_config, section)))
 
1640
                         config= dict(client_config.items(section)))
1862
1641
            for section in client_config.sections()))
1863
1642
    if not tcp_server.clients:
1864
1643
        logger.warning(u"No clients defined")
1865
 
        
 
1644
    
 
1645
    if debug:
 
1646
        # Redirect stdin so all checkers get /dev/null
 
1647
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1648
        os.dup2(null, sys.stdin.fileno())
 
1649
        if null > 2:
 
1650
            os.close(null)
 
1651
    else:
 
1652
        # No console logging
 
1653
        logger.removeHandler(console)
 
1654
        # Close all input and output, do double fork, etc.
 
1655
        daemon()
 
1656
    
1866
1657
    try:
1867
1658
        with pidfile:
1868
1659
            pid = os.getpid()