/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-01-01 15:17:12 UTC
  • Revision ID: teddy@fukt.bsnet.se-20100101151712-x20p42nhuvf9lxol
Bug fix: mandos-client needs GnuPG but lacked a dependency on it.  The
Mandos server, on the other hand, did not need GnuPG but declared an
incorrect dependency on it.

* debian/control (Package: mandos/Depends): Removed "gnupg (< 2)".
  (Package: mandos-client/Depends): Added "gnupg (<< 2)".

Show diffs side-by-side

added added

removed removed

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