/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-09-11 18:34:55 UTC
  • mfrom: (24.1.162 mandos)
  • Revision ID: teddy@fukt.bsnet.se-20100911183455-kmzttvbywdbfbepm
Merge from Björn.

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 select
 
63
import multiprocessing
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')
86
87
logger = logging.Logger(u'mandos')
87
88
syslogger = (logging.handlers.SysLogHandler
88
89
             (facility = logging.handlers.SysLogHandler.LOG_DAEMON,
156
157
                            u" after %i retries, exiting.",
157
158
                            self.rename_count)
158
159
            raise AvahiServiceError(u"Too many renames")
159
 
        self.name = self.server.GetAlternativeServiceName(self.name)
 
160
        self.name = unicode(self.server.GetAlternativeServiceName(self.name))
160
161
        logger.info(u"Changing Zeroconf service name to %r ...",
161
 
                    unicode(self.name))
 
162
                    self.name)
162
163
        syslogger.setFormatter(logging.Formatter
163
164
                               (u'Mandos (%s) [%%(process)d]:'
164
165
                                u' %%(levelname)s: %%(message)s'
165
166
                                % self.name))
166
167
        self.remove()
167
 
        self.add()
 
168
        try:
 
169
            self.add()
 
170
        except dbus.exceptions.DBusException, error:
 
171
            logger.critical(u"DBusException: %s", error)
 
172
            self.cleanup()
 
173
            os._exit(1)
168
174
        self.rename_count += 1
169
175
    def remove(self):
170
176
        """Derived from the Avahi example code"""
259
265
                     runtime with vars(self) as dict, so that for
260
266
                     instance %(name)s can be used in the command.
261
267
    current_checker_command: string; current running checker_command
 
268
    approved_delay: datetime.timedelta(); Time to wait for approval
 
269
    _approved:   bool(); 'None' if not yet approved/disapproved
 
270
    approved_duration: datetime.timedelta(); Duration of one approval
262
271
    """
263
272
    
264
273
    @staticmethod
275
284
    def interval_milliseconds(self):
276
285
        "Return the 'interval' attribute in milliseconds"
277
286
        return self._timedelta_to_milliseconds(self.interval)
 
287
 
 
288
    def approved_delay_milliseconds(self):
 
289
        return self._timedelta_to_milliseconds(self.approved_delay)
278
290
    
279
291
    def __init__(self, name = None, disable_hook=None, config=None):
280
292
        """Note: the 'checker' key in 'config' sets the
315
327
        self.checker_command = config[u"checker"]
316
328
        self.current_checker_command = None
317
329
        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.approved_delay = string_to_delta(
 
335
            config[u"approved_delay"])
 
336
        self.approved_duration = string_to_delta(
 
337
            config[u"approved_duration"])
 
338
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
318
339
    
 
340
    def send_changedstate(self):
 
341
        self.changedstate.acquire()
 
342
        self.changedstate.notify_all()
 
343
        self.changedstate.release()
 
344
        
319
345
    def enable(self):
320
346
        """Start this client's checker and timeout hooks"""
321
347
        if getattr(self, u"enabled", False):
322
348
            # Already enabled
323
349
            return
 
350
        self.send_changedstate()
324
351
        self.last_enabled = datetime.datetime.utcnow()
325
352
        # Schedule a new checker to be started an 'interval' from now,
326
353
        # and every interval from then on.
340
367
        if not getattr(self, "enabled", False):
341
368
            return False
342
369
        if not quiet:
 
370
            self.send_changedstate()
 
371
        if not quiet:
343
372
            logger.info(u"Disabling client %s", self.name)
344
373
        if getattr(self, u"disable_initiator_tag", False):
345
374
            gobject.source_remove(self.disable_initiator_tag)
478
507
                raise
479
508
        self.checker = None
480
509
 
481
 
 
482
510
def dbus_service_property(dbus_interface, signature=u"v",
483
511
                          access=u"readwrite", byte_arrays=False):
484
512
    """Decorators for marking methods of a DBusObjectWithProperties to
678
706
    # dbus.service.Object doesn't use super(), so we can't either.
679
707
    
680
708
    def __init__(self, bus = None, *args, **kwargs):
 
709
        self._approvals_pending = 0
681
710
        self.bus = bus
682
711
        Client.__init__(self, *args, **kwargs)
683
712
        # Only now, when this client is initialized, can it show up on
687
716
                                  + self.name.replace(u".", u"_")))
688
717
        DBusObjectWithProperties.__init__(self, self.bus,
689
718
                                          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"approved_pending"),
 
730
                                 dbus_bool)
 
731
 
 
732
    approvals_pending = property(_get_approvals_pending,
 
733
                                 _set_approvals_pending)
 
734
    del _get_approvals_pending, _set_approvals_pending
690
735
    
691
736
    @staticmethod
692
737
    def _datetime_to_dbus(dt, variant_level=0):
781
826
            self.PropertyChanged(dbus.String(u"checker_running"),
782
827
                                 dbus.Boolean(False, variant_level=1))
783
828
        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(self.approved_duration),
 
838
                            self._reset_approved)
 
839
    
784
840
    
785
841
    ## D-Bus methods, signals & properties
786
842
    _interface = u"se.bsnet.fukt.Mandos.Client"
808
864
    # GotSecret - signal
809
865
    @dbus.service.signal(_interface)
810
866
    def GotSecret(self):
811
 
        "D-Bus signal"
 
867
        """D-Bus signal
 
868
        Is sent after a successful transfer of secret from the Mandos
 
869
        server to mandos-client
 
870
        """
812
871
        pass
813
872
    
814
873
    # Rejected - signal
815
 
    @dbus.service.signal(_interface)
816
 
    def Rejected(self):
 
874
    @dbus.service.signal(_interface, signature=u"s")
 
875
    def Rejected(self, reason):
 
876
        "D-Bus signal"
 
877
        pass
 
878
    
 
879
    # NeedApproval - signal
 
880
    @dbus.service.signal(_interface, signature=u"db")
 
881
    def NeedApproval(self, timeout, default):
817
882
        "D-Bus signal"
818
883
        pass
819
884
    
820
885
    ## Methods
821
 
    
 
886
 
 
887
    # Approve - method
 
888
    @dbus.service.method(_interface, in_signature=u"b")
 
889
    def Approve(self, value):
 
890
        self.approve(value)
 
891
 
822
892
    # CheckedOK - method
823
893
    @dbus.service.method(_interface)
824
894
    def CheckedOK(self):
849
919
    
850
920
    ## Properties
851
921
    
 
922
    # approved_pending - property
 
923
    @dbus_service_property(_interface, signature=u"b", access=u"read")
 
924
    def approved_pending_dbus_property(self):
 
925
        return dbus.Boolean(bool(self.approvals_pending))
 
926
    
 
927
    # approved_by_default - property
 
928
    @dbus_service_property(_interface, signature=u"b",
 
929
                           access=u"readwrite")
 
930
    def approved_by_default_dbus_property(self):
 
931
        return dbus.Boolean(self.approved_by_default)
 
932
    
 
933
    # approved_delay - property
 
934
    @dbus_service_property(_interface, signature=u"t",
 
935
                           access=u"readwrite")
 
936
    def approved_delay_dbus_property(self):
 
937
        return dbus.UInt64(self.approved_delay_milliseconds())
 
938
    
 
939
    # approved_duration - property
 
940
    @dbus_service_property(_interface, signature=u"t",
 
941
                           access=u"readwrite")
 
942
    def approved_duration_dbus_property(self):
 
943
        return dbus.UInt64(self._timedelta_to_milliseconds(
 
944
                self.approved_duration))
 
945
    
852
946
    # name - property
853
947
    @dbus_service_property(_interface, signature=u"s", access=u"read")
854
948
    def name_dbus_property(self):
988
1082
    del _interface
989
1083
 
990
1084
 
 
1085
class ProxyClient(object):
 
1086
    def __init__(self, child_pipe, fpr, address):
 
1087
        self._pipe = child_pipe
 
1088
        self._pipe.send(('init', fpr, address))
 
1089
        if not self._pipe.recv():
 
1090
            raise KeyError()
 
1091
 
 
1092
    def __getattribute__(self, name):
 
1093
        if(name == '_pipe'):
 
1094
            return super(ProxyClient, self).__getattribute__(name)
 
1095
        self._pipe.send(('getattr', name))
 
1096
        data = self._pipe.recv()
 
1097
        if data[0] == 'data':
 
1098
            return data[1]
 
1099
        if data[0] == 'function':
 
1100
            def func(*args, **kwargs):
 
1101
                self._pipe.send(('funcall', name, args, kwargs))
 
1102
                return self._pipe.recv()[1]
 
1103
            return func
 
1104
 
 
1105
    def __setattr__(self, name, value):
 
1106
        if(name == '_pipe'):
 
1107
            return super(ProxyClient, self).__setattr__(name, value)
 
1108
        self._pipe.send(('setattr', name, value))
 
1109
 
 
1110
 
991
1111
class ClientHandler(socketserver.BaseRequestHandler, object):
992
1112
    """A class to handle client connections.
993
1113
    
995
1115
    Note: This will run in its own forked process."""
996
1116
    
997
1117
    def handle(self):
998
 
        logger.info(u"TCP connection from: %s",
999
 
                    unicode(self.client_address))
1000
 
        logger.debug(u"IPC Pipe FD: %d",
1001
 
                     self.server.child_pipe[1].fileno())
1002
 
        # Open IPC pipe to parent process
1003
 
        with contextlib.nested(self.server.child_pipe[1],
1004
 
                               self.server.parent_pipe[0]
1005
 
                               ) as (ipc, ipc_return):
 
1118
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
1119
            logger.info(u"TCP connection from: %s",
 
1120
                        unicode(self.client_address))
 
1121
            logger.debug(u"Pipe FD: %d",
 
1122
                         self.server.child_pipe.fileno())
 
1123
 
1006
1124
            session = (gnutls.connection
1007
1125
                       .ClientSession(self.request,
1008
1126
                                      gnutls.connection
1009
1127
                                      .X509Credentials()))
1010
 
            
 
1128
 
1011
1129
            # Note: gnutls.connection.X509Credentials is really a
1012
1130
            # generic GnuTLS certificate credentials object so long as
1013
1131
            # no X.509 keys are added to it.  Therefore, we can use it
1014
1132
            # here despite using OpenPGP certificates.
1015
 
            
 
1133
 
1016
1134
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1017
1135
            #                      u"+AES-256-CBC", u"+SHA1",
1018
1136
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1024
1142
            (gnutls.library.functions
1025
1143
             .gnutls_priority_set_direct(session._c_object,
1026
1144
                                         priority, None))
1027
 
            
 
1145
 
1028
1146
            # Start communication using the Mandos protocol
1029
1147
            # Get protocol number
1030
1148
            line = self.request.makefile().readline()
1035
1153
            except (ValueError, IndexError, RuntimeError), error:
1036
1154
                logger.error(u"Unknown protocol version: %s", error)
1037
1155
                return
1038
 
            
 
1156
 
1039
1157
            # Start GnuTLS connection
1040
1158
            try:
1041
1159
                session.handshake()
1045
1163
                # established.  Just abandon the request.
1046
1164
                return
1047
1165
            logger.debug(u"Handshake succeeded")
 
1166
 
 
1167
            approval_required = False
1048
1168
            try:
1049
1169
                try:
1050
1170
                    fpr = self.fingerprint(self.peer_certificate
1054
1174
                    return
1055
1175
                logger.debug(u"Fingerprint: %s", fpr)
1056
1176
 
1057
 
                for c in self.server.clients:
1058
 
                    if c.fingerprint == fpr:
1059
 
                        client = c
 
1177
                try:
 
1178
                    client = ProxyClient(child_pipe, fpr,
 
1179
                                         self.client_address)
 
1180
                except KeyError:
 
1181
                    return
 
1182
                
 
1183
                if client.approved_delay:
 
1184
                    delay = client.approved_delay
 
1185
                    client.approvals_pending += 1
 
1186
                    approval_required = True
 
1187
                
 
1188
                while True:
 
1189
                    if not client.enabled:
 
1190
                        logger.warning(u"Client %s is disabled",
 
1191
                                       client.name)
 
1192
                        if self.server.use_dbus:
 
1193
                            # Emit D-Bus signal
 
1194
                            client.Rejected("Disabled")                    
 
1195
                        return
 
1196
                    
 
1197
                    if client._approved or not client.approved_delay:
 
1198
                        #We are approved or approval is disabled
1060
1199
                        break
1061
 
                else:
1062
 
                    ipc.write(u"NOTFOUND %s %s\n"
1063
 
                              % (fpr, unicode(self.client_address)))
1064
 
                    return
1065
 
                
1066
 
                class ClientProxy(object):
1067
 
                    """Client proxy object.  Not for calling methods."""
1068
 
                    def __init__(self, client):
1069
 
                        self.client = client
1070
 
                    def __getattr__(self, name):
1071
 
                        if name.startswith("ipc_"):
1072
 
                            def tempfunc():
1073
 
                                ipc.write("%s %s\n" % (name[4:].upper(),
1074
 
                                                       self.client.name))
1075
 
                            return tempfunc
1076
 
                        if not hasattr(self.client, name):
1077
 
                            raise AttributeError
1078
 
                        ipc.write(u"GETATTR %s %s\n"
1079
 
                                  % (name, self.client.fingerprint))
1080
 
                        return pickle.load(ipc_return)
1081
 
                clientproxy = ClientProxy(client)
1082
 
                # Have to check if client.enabled, since it is
1083
 
                # possible that the client was disabled since the
1084
 
                # GnuTLS session was established.
1085
 
                if not clientproxy.enabled:
1086
 
                    clientproxy.ipc_disabled()
1087
 
                    return
1088
 
                
1089
 
                clientproxy.ipc_sending()
 
1200
                    elif client._approved is None:
 
1201
                        logger.info(u"Client %s need approval",
 
1202
                                    client.name)
 
1203
                        if self.server.use_dbus:
 
1204
                            # Emit D-Bus signal
 
1205
                            client.NeedApproval(
 
1206
                                client.approved_delay_milliseconds(),
 
1207
                                client.approved_by_default)
 
1208
                    else:
 
1209
                        logger.warning(u"Client %s was not approved",
 
1210
                                       client.name)
 
1211
                        if self.server.use_dbus:
 
1212
                            # Emit D-Bus signal
 
1213
                            client.Rejected("Disapproved")
 
1214
                        return
 
1215
                    
 
1216
                    #wait until timeout or approved
 
1217
                    #x = float(client._timedelta_to_milliseconds(delay))
 
1218
                    time = datetime.datetime.now()
 
1219
                    client.changedstate.acquire()
 
1220
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1221
                    client.changedstate.release()
 
1222
                    time2 = datetime.datetime.now()
 
1223
                    if (time2 - time) >= delay:
 
1224
                        if not client.approved_by_default:
 
1225
                            logger.warning("Client %s timed out while"
 
1226
                                           " waiting for approval",
 
1227
                                           client.name)
 
1228
                            if self.server.use_dbus:
 
1229
                                # Emit D-Bus signal
 
1230
                                client.Rejected("Time out")
 
1231
                            return
 
1232
                        else:
 
1233
                            break
 
1234
                    else:
 
1235
                        delay -= time2 - time
 
1236
                
1090
1237
                sent_size = 0
1091
1238
                while sent_size < len(client.secret):
1092
 
                    sent = session.send(client.secret[sent_size:])
 
1239
                    try:
 
1240
                        sent = session.send(client.secret[sent_size:])
 
1241
                    except (gnutls.errors.GNUTLSError), error:
 
1242
                        logger.warning("gnutls send failed")
 
1243
                        return
1093
1244
                    logger.debug(u"Sent: %d, remaining: %d",
1094
1245
                                 sent, len(client.secret)
1095
1246
                                 - (sent_size + sent))
1096
1247
                    sent_size += sent
 
1248
 
 
1249
                logger.info(u"Sending secret to %s", client.name)
 
1250
                # bump the timeout as if seen
 
1251
                client.checked_ok()
 
1252
                if self.server.use_dbus:
 
1253
                    # Emit D-Bus signal
 
1254
                    client.GotSecret()
 
1255
            
1097
1256
            finally:
1098
 
                session.bye()
 
1257
                if approval_required:
 
1258
                    client.approvals_pending -= 1
 
1259
                try:
 
1260
                    session.bye()
 
1261
                except (gnutls.errors.GNUTLSError), error:
 
1262
                    logger.warning("gnutls bye failed")
1099
1263
    
1100
1264
    @staticmethod
1101
1265
    def peer_certificate(session):
1161
1325
        return hex_fpr
1162
1326
 
1163
1327
 
1164
 
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1165
 
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
 
1328
class MultiprocessingMixIn(object):
 
1329
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
1330
    def sub_process_main(self, request, address):
 
1331
        try:
 
1332
            self.finish_request(request, address)
 
1333
        except:
 
1334
            self.handle_error(request, address)
 
1335
        self.close_request(request)
 
1336
            
 
1337
    def process_request(self, request, address):
 
1338
        """Start a new process to process the request."""
 
1339
        multiprocessing.Process(target = self.sub_process_main,
 
1340
                                args = (request, address)).start()
 
1341
 
 
1342
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
1343
    """ adds a pipe to the MixIn """
1166
1344
    def process_request(self, request, client_address):
1167
1345
        """Overrides and wraps the original process_request().
1168
1346
        
1169
1347
        This function creates a new pipe in self.pipe
1170
1348
        """
1171
 
        # Child writes to child_pipe
1172
 
        self.child_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1173
 
        # Parent writes to parent_pipe
1174
 
        self.parent_pipe = map(os.fdopen, os.pipe(), u"rw", (1, 0))
1175
 
        super(ForkingMixInWithPipes,
 
1349
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
 
1350
 
 
1351
        super(MultiprocessingMixInWithPipe,
1176
1352
              self).process_request(request, client_address)
1177
 
        # Close unused ends for parent
1178
 
        self.parent_pipe[0].close() # close read end
1179
 
        self.child_pipe[1].close()  # close write end
1180
 
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1181
 
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
 
1353
        self.child_pipe.close()
 
1354
        self.add_pipe(parent_pipe)
 
1355
 
 
1356
    def add_pipe(self, parent_pipe):
1182
1357
        """Dummy function; override as necessary"""
1183
 
        child_pipe_fd.close()
1184
 
        parent_pipe_fd.close()
1185
 
 
1186
 
 
1187
 
class IPv6_TCPServer(ForkingMixInWithPipes,
 
1358
        pass
 
1359
 
 
1360
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1188
1361
                     socketserver.TCPServer, object):
1189
1362
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1190
1363
    
1275
1448
            return socketserver.TCPServer.server_activate(self)
1276
1449
    def enable(self):
1277
1450
        self.enabled = True
1278
 
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
 
1451
    def add_pipe(self, parent_pipe):
1279
1452
        # Call "handle_ipc" for both data and EOF events
1280
 
        gobject.io_add_watch(child_pipe_fd.fileno(),
 
1453
        gobject.io_add_watch(parent_pipe.fileno(),
1281
1454
                             gobject.IO_IN | gobject.IO_HUP,
1282
1455
                             functools.partial(self.handle_ipc,
1283
 
                                               reply = parent_pipe_fd,
1284
 
                                               sender= child_pipe_fd))
1285
 
    def handle_ipc(self, source, condition, reply=None, sender=None):
 
1456
                                               parent_pipe = parent_pipe))
 
1457
        
 
1458
    def handle_ipc(self, source, condition, parent_pipe=None,
 
1459
                   client_object=None):
1286
1460
        condition_names = {
1287
1461
            gobject.IO_IN: u"IN",   # There is data to read.
1288
1462
            gobject.IO_OUT: u"OUT", # Data can be written (without
1299
1473
                                       if cond & condition)
1300
1474
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1301
1475
                     conditions_string)
1302
 
        
1303
 
        # Read a line from the file object
1304
 
        cmdline = sender.readline()
1305
 
        if not cmdline:             # Empty line means end of file
1306
 
            # close the IPC pipes
1307
 
            sender.close()
1308
 
            reply.close()
1309
 
            
1310
 
            # Stop calling this function
1311
 
            return False
1312
 
        
1313
 
        logger.debug(u"IPC command: %r", cmdline)
1314
 
        
1315
 
        # Parse and act on command
1316
 
        cmd, args = cmdline.rstrip(u"\r\n").split(None, 1)
1317
 
        
1318
 
        if cmd == u"NOTFOUND":
1319
 
            fpr, address = args.split(None, 1)
1320
 
            logger.warning(u"Client not found for fingerprint: %s, ad"
1321
 
                           u"dress: %s", fpr, address)
1322
 
            if self.use_dbus:
1323
 
                # Emit D-Bus signal
1324
 
                mandos_dbus_service.ClientNotFound(fpr, address)
1325
 
        elif cmd == u"DISABLED":
1326
 
            for client in self.clients:
1327
 
                if client.name == args:
1328
 
                    logger.warning(u"Client %s is disabled", args)
1329
 
                    if self.use_dbus:
1330
 
                        # Emit D-Bus signal
1331
 
                        client.Rejected()
1332
 
                    break
1333
 
            else:
1334
 
                logger.error(u"Unknown client %s is disabled", args)
1335
 
        elif cmd == u"SENDING":
1336
 
            for client in self.clients:
1337
 
                if client.name == args:
1338
 
                    logger.info(u"Sending secret to %s", client.name)
1339
 
                    client.checked_ok()
1340
 
                    if self.use_dbus:
1341
 
                        # Emit D-Bus signal
1342
 
                        client.GotSecret()
1343
 
                    break
1344
 
            else:
1345
 
                logger.error(u"Sending secret to unknown client %s",
1346
 
                             args)
1347
 
        elif cmd == u"GETATTR":
1348
 
            attr_name, fpr = args.split(None, 1)
1349
 
            for client in self.clients:
1350
 
                if client.fingerprint == fpr:
1351
 
                    attr_value = getattr(client, attr_name, None)
1352
 
                    logger.debug("IPC reply: %r", attr_value)
1353
 
                    pickle.dump(attr_value, reply)
1354
 
                    break
1355
 
            else:
1356
 
                logger.error(u"Client %s on address %s requesting "
1357
 
                             u"attribute %s not found", fpr, address,
1358
 
                             attr_name)
1359
 
                pickle.dump(None, reply)
1360
 
        else:
1361
 
            logger.error(u"Unknown IPC command: %r", cmdline)
1362
 
        
1363
 
        # Keep calling this function
 
1476
 
 
1477
        # error or the other end of multiprocessing.Pipe has closed
 
1478
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1479
            return False
 
1480
        
 
1481
        # Read a request from the child
 
1482
        request = parent_pipe.recv()
 
1483
        logger.debug(u"IPC request: %s", repr(request))
 
1484
        command = request[0]
 
1485
        
 
1486
        if command == 'init':
 
1487
            fpr = request[1]
 
1488
            address = request[2]
 
1489
            
 
1490
            for c in self.clients:
 
1491
                if c.fingerprint == fpr:
 
1492
                    client = c
 
1493
                    break
 
1494
            else:
 
1495
                logger.warning(u"Client not found for fingerprint: %s, ad"
 
1496
                               u"dress: %s", fpr, address)
 
1497
                if self.use_dbus:
 
1498
                    # Emit D-Bus signal
 
1499
                    mandos_dbus_service.ClientNotFound(fpr, address)
 
1500
                parent_pipe.send(False)
 
1501
                return False
 
1502
            
 
1503
            gobject.io_add_watch(parent_pipe.fileno(),
 
1504
                                 gobject.IO_IN | gobject.IO_HUP,
 
1505
                                 functools.partial(self.handle_ipc,
 
1506
                                                   parent_pipe = parent_pipe,
 
1507
                                                   client_object = client))
 
1508
            parent_pipe.send(True)
 
1509
            # remove the old hook in favor of the new above hook on same fileno
 
1510
            return False
 
1511
        if command == 'funcall':
 
1512
            funcname = request[1]
 
1513
            args = request[2]
 
1514
            kwargs = request[3]
 
1515
            
 
1516
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1517
 
 
1518
        if command == 'getattr':
 
1519
            attrname = request[1]
 
1520
            if callable(client_object.__getattribute__(attrname)):
 
1521
                parent_pipe.send(('function',))
 
1522
            else:
 
1523
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1524
        
 
1525
        if command == 'setattr':
 
1526
            attrname = request[1]
 
1527
            value = request[2]
 
1528
            setattr(client_object, attrname, value)
 
1529
 
1364
1530
        return True
1365
1531
 
1366
1532
 
1469
1635
    parser.add_option("--debug", action=u"store_true",
1470
1636
                      help=u"Debug mode; run in foreground and log to"
1471
1637
                      u" terminal")
 
1638
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
 
1639
                      help=u"Debug level for stdout output")
1472
1640
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1473
1641
                      u" priority string (see GnuTLS documentation)")
1474
1642
    parser.add_option("--servicename", type=u"string",
1499
1667
                        u"servicename": u"Mandos",
1500
1668
                        u"use_dbus": u"True",
1501
1669
                        u"use_ipv6": u"True",
 
1670
                        u"debuglevel": u"",
1502
1671
                        }
1503
1672
    
1504
1673
    # Parse config file for server-global settings
1521
1690
    # options, if set.
1522
1691
    for option in (u"interface", u"address", u"port", u"debug",
1523
1692
                   u"priority", u"servicename", u"configdir",
1524
 
                   u"use_dbus", u"use_ipv6"):
 
1693
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
1525
1694
        value = getattr(options, option)
1526
1695
        if value is not None:
1527
1696
            server_settings[option] = value
1536
1705
    
1537
1706
    # For convenience
1538
1707
    debug = server_settings[u"debug"]
 
1708
    debuglevel = server_settings[u"debuglevel"]
1539
1709
    use_dbus = server_settings[u"use_dbus"]
1540
1710
    use_ipv6 = server_settings[u"use_ipv6"]
1541
 
    
1542
 
    if not debug:
1543
 
        syslogger.setLevel(logging.WARNING)
1544
 
        console.setLevel(logging.WARNING)
1545
 
    
 
1711
 
1546
1712
    if server_settings[u"servicename"] != u"Mandos":
1547
1713
        syslogger.setFormatter(logging.Formatter
1548
1714
                               (u'Mandos (%s) [%%(process)d]:'
1554
1720
                        u"interval": u"5m",
1555
1721
                        u"checker": u"fping -q -- %%(host)s",
1556
1722
                        u"host": u"",
 
1723
                        u"approved_delay": u"0s",
 
1724
                        u"approved_duration": u"1s",
1557
1725
                        }
1558
1726
    client_config = configparser.SafeConfigParser(client_defaults)
1559
1727
    client_config.read(os.path.join(server_settings[u"configdir"],
1599
1767
            raise error
1600
1768
    
1601
1769
    # Enable all possible GnuTLS debugging
 
1770
 
 
1771
 
 
1772
    if not debug and not debuglevel:
 
1773
        syslogger.setLevel(logging.WARNING)
 
1774
        console.setLevel(logging.WARNING)
 
1775
    if debuglevel:
 
1776
        level = getattr(logging, debuglevel.upper())
 
1777
        syslogger.setLevel(level)
 
1778
        console.setLevel(level)
 
1779
 
1602
1780
    if debug:
1603
1781
        # "Use a log level over 10 to enable all debugging options."
1604
1782
        # - GnuTLS manual
1610
1788
        
1611
1789
        (gnutls.library.functions
1612
1790
         .gnutls_global_set_log_function(debug_gnutls))
 
1791
 
 
1792
        # Redirect stdin so all checkers get /dev/null
 
1793
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1794
        os.dup2(null, sys.stdin.fileno())
 
1795
        if null > 2:
 
1796
            os.close(null)
 
1797
    else:
 
1798
        # No console logging
 
1799
        logger.removeHandler(console)
 
1800
 
1613
1801
    
1614
1802
    global main_loop
1615
1803
    # From the Avahi example code
1633
1821
    if server_settings["interface"]:
1634
1822
        service.interface = (if_nametoindex
1635
1823
                             (str(server_settings[u"interface"])))
 
1824
 
 
1825
    if not debug:
 
1826
        # Close all input and output, do double fork, etc.
 
1827
        daemon()
 
1828
        
 
1829
    global multiprocessing_manager
 
1830
    multiprocessing_manager = multiprocessing.Manager()
1636
1831
    
1637
1832
    client_class = Client
1638
1833
    if use_dbus:
1639
1834
        client_class = functools.partial(ClientDBus, bus = bus)
 
1835
    def client_config_items(config, section):
 
1836
        special_settings = {
 
1837
            "approved_by_default":
 
1838
                lambda: config.getboolean(section,
 
1839
                                          "approved_by_default"),
 
1840
            }
 
1841
        for name, value in config.items(section):
 
1842
            try:
 
1843
                yield (name, special_settings[name]())
 
1844
            except KeyError:
 
1845
                yield (name, value)
 
1846
    
1640
1847
    tcp_server.clients.update(set(
1641
1848
            client_class(name = section,
1642
 
                         config= dict(client_config.items(section)))
 
1849
                         config= dict(client_config_items(
 
1850
                        client_config, section)))
1643
1851
            for section in client_config.sections()))
1644
1852
    if not tcp_server.clients:
1645
1853
        logger.warning(u"No clients defined")
1646
 
    
1647
 
    if debug:
1648
 
        # Redirect stdin so all checkers get /dev/null
1649
 
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
1650
 
        os.dup2(null, sys.stdin.fileno())
1651
 
        if null > 2:
1652
 
            os.close(null)
1653
 
    else:
1654
 
        # No console logging
1655
 
        logger.removeHandler(console)
1656
 
        # Close all input and output, do double fork, etc.
1657
 
        daemon()
1658
 
    
 
1854
        
1659
1855
    try:
1660
1856
        with pidfile:
1661
1857
            pid = os.getpid()