/mandos/release

To get this branch, use:
bzr branch http://bzr.recompile.se/loggerhead/mandos/release

« back to all changes in this revision

Viewing changes to mandos

  • Committer: Björn Påhlsson
  • Date: 2010-09-07 18:48:56 UTC
  • mto: (237.7.1 mandos)
  • mto: This revision was merged to the branch mainline in revision 270.
  • Revision ID: belorn@fukt.bsnet.se-20100907184856-waz6cvxbm7ranha2
added the actually plugin file for plymouth

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'
259
260
                     runtime with vars(self) as dict, so that for
260
261
                     instance %(name)s can be used in the command.
261
262
    current_checker_command: string; current running checker_command
 
263
    approved_delay: datetime.timedelta(); Time to wait for approval
 
264
    _approved:   bool(); 'None' if not yet approved/disapproved
 
265
    approved_duration: datetime.timedelta(); Duration of one approval
262
266
    """
263
267
    
264
268
    @staticmethod
275
279
    def interval_milliseconds(self):
276
280
        "Return the 'interval' attribute in milliseconds"
277
281
        return self._timedelta_to_milliseconds(self.interval)
 
282
 
 
283
    def approved_delay_milliseconds(self):
 
284
        return self._timedelta_to_milliseconds(self.approved_delay)
278
285
    
279
286
    def __init__(self, name = None, disable_hook=None, config=None):
280
287
        """Note: the 'checker' key in 'config' sets the
315
322
        self.checker_command = config[u"checker"]
316
323
        self.current_checker_command = None
317
324
        self.last_connect = None
 
325
        self._approved = None
 
326
        self.approved_by_default = config.get(u"approved_by_default",
 
327
                                              True)
 
328
        self.approvals_pending = 0
 
329
        self.approved_delay = string_to_delta(
 
330
            config[u"approved_delay"])
 
331
        self.approved_duration = string_to_delta(
 
332
            config[u"approved_duration"])
 
333
        self.changedstate = multiprocessing_manager.Condition(multiprocessing_manager.Lock())
318
334
    
 
335
    def send_changedstate(self):
 
336
        self.changedstate.acquire()
 
337
        self.changedstate.notify_all()
 
338
        self.changedstate.release()
 
339
        
319
340
    def enable(self):
320
341
        """Start this client's checker and timeout hooks"""
321
342
        if getattr(self, u"enabled", False):
322
343
            # Already enabled
323
344
            return
 
345
        self.send_changedstate()
324
346
        self.last_enabled = datetime.datetime.utcnow()
325
347
        # Schedule a new checker to be started an 'interval' from now,
326
348
        # and every interval from then on.
340
362
        if not getattr(self, "enabled", False):
341
363
            return False
342
364
        if not quiet:
 
365
            self.send_changedstate()
 
366
        if not quiet:
343
367
            logger.info(u"Disabling client %s", self.name)
344
368
        if getattr(self, u"disable_initiator_tag", False):
345
369
            gobject.source_remove(self.disable_initiator_tag)
478
502
                raise
479
503
        self.checker = None
480
504
 
481
 
 
482
505
def dbus_service_property(dbus_interface, signature=u"v",
483
506
                          access=u"readwrite", byte_arrays=False):
484
507
    """Decorators for marking methods of a DBusObjectWithProperties to
678
701
    # dbus.service.Object doesn't use super(), so we can't either.
679
702
    
680
703
    def __init__(self, bus = None, *args, **kwargs):
 
704
        self._approvals_pending = 0
681
705
        self.bus = bus
682
706
        Client.__init__(self, *args, **kwargs)
683
707
        # Only now, when this client is initialized, can it show up on
687
711
                                  + self.name.replace(u".", u"_")))
688
712
        DBusObjectWithProperties.__init__(self, self.bus,
689
713
                                          self.dbus_object_path)
 
714
 
 
715
    def _get_approvals_pending(self):
 
716
        return self._approvals_pending
 
717
    def _set_approvals_pending(self, value):
 
718
        old_value = self._approvals_pending
 
719
        self._approvals_pending = value
 
720
        bval = bool(value)
 
721
        if (hasattr(self, "dbus_object_path")
 
722
            and bval is not bool(old_value)):
 
723
            dbus_bool = dbus.Boolean(bval, variant_level=1)
 
724
            self.PropertyChanged(dbus.String(u"approved_pending"),
 
725
                                 dbus_bool)
 
726
 
 
727
    approvals_pending = property(_get_approvals_pending,
 
728
                                 _set_approvals_pending)
 
729
    del _get_approvals_pending, _set_approvals_pending
690
730
    
691
731
    @staticmethod
692
732
    def _datetime_to_dbus(dt, variant_level=0):
781
821
            self.PropertyChanged(dbus.String(u"checker_running"),
782
822
                                 dbus.Boolean(False, variant_level=1))
783
823
        return r
 
824
 
 
825
    def _reset_approved(self):
 
826
        self._approved = None
 
827
        return False
 
828
    
 
829
    def approve(self, value=True):
 
830
        self.send_changedstate()
 
831
        self._approved = value
 
832
        gobject.timeout_add(self._timedelta_to_milliseconds(self.approved_duration),
 
833
                            self._reset_approved)
 
834
    
784
835
    
785
836
    ## D-Bus methods, signals & properties
786
837
    _interface = u"se.bsnet.fukt.Mandos.Client"
808
859
    # GotSecret - signal
809
860
    @dbus.service.signal(_interface)
810
861
    def GotSecret(self):
811
 
        "D-Bus signal"
 
862
        """D-Bus signal
 
863
        Is sent after a successful transfer of secret from the Mandos
 
864
        server to mandos-client
 
865
        """
812
866
        pass
813
867
    
814
868
    # Rejected - signal
815
 
    @dbus.service.signal(_interface)
816
 
    def Rejected(self):
 
869
    @dbus.service.signal(_interface, signature=u"s")
 
870
    def Rejected(self, reason):
 
871
        "D-Bus signal"
 
872
        pass
 
873
    
 
874
    # NeedApproval - signal
 
875
    @dbus.service.signal(_interface, signature=u"db")
 
876
    def NeedApproval(self, timeout, default):
817
877
        "D-Bus signal"
818
878
        pass
819
879
    
820
880
    ## Methods
821
 
    
 
881
 
 
882
    # Approve - method
 
883
    @dbus.service.method(_interface, in_signature=u"b")
 
884
    def Approve(self, value):
 
885
        self.approve(value)
 
886
 
822
887
    # CheckedOK - method
823
888
    @dbus.service.method(_interface)
824
889
    def CheckedOK(self):
849
914
    
850
915
    ## Properties
851
916
    
 
917
    # approved_pending - property
 
918
    @dbus_service_property(_interface, signature=u"b", access=u"read")
 
919
    def approved_pending_dbus_property(self):
 
920
        return dbus.Boolean(bool(self.approvals_pending))
 
921
    
 
922
    # approved_by_default - property
 
923
    @dbus_service_property(_interface, signature=u"b",
 
924
                           access=u"readwrite")
 
925
    def approved_by_default_dbus_property(self):
 
926
        return dbus.Boolean(self.approved_by_default)
 
927
    
 
928
    # approved_delay - property
 
929
    @dbus_service_property(_interface, signature=u"t",
 
930
                           access=u"readwrite")
 
931
    def approved_delay_dbus_property(self):
 
932
        return dbus.UInt64(self.approved_delay_milliseconds())
 
933
    
 
934
    # approved_duration - property
 
935
    @dbus_service_property(_interface, signature=u"t",
 
936
                           access=u"readwrite")
 
937
    def approved_duration_dbus_property(self):
 
938
        return dbus.UInt64(self._timedelta_to_milliseconds(
 
939
                self.approved_duration))
 
940
    
852
941
    # name - property
853
942
    @dbus_service_property(_interface, signature=u"s", access=u"read")
854
943
    def name_dbus_property(self):
988
1077
    del _interface
989
1078
 
990
1079
 
 
1080
class ProxyClient(object):
 
1081
    def __init__(self, child_pipe, fpr, address):
 
1082
        self._pipe = child_pipe
 
1083
        self._pipe.send(('init', fpr, address))
 
1084
        if not self._pipe.recv():
 
1085
            raise KeyError()
 
1086
 
 
1087
    def __getattribute__(self, name):
 
1088
        if(name == '_pipe'):
 
1089
            return super(ProxyClient, self).__getattribute__(name)
 
1090
        self._pipe.send(('getattr', name))
 
1091
        data = self._pipe.recv()
 
1092
        if data[0] == 'data':
 
1093
            return data[1]
 
1094
        if data[0] == 'function':
 
1095
            def func(*args, **kwargs):
 
1096
                self._pipe.send(('funcall', name, args, kwargs))
 
1097
                return self._pipe.recv()[1]
 
1098
            return func
 
1099
 
 
1100
    def __setattr__(self, name, value):
 
1101
        if(name == '_pipe'):
 
1102
            return super(ProxyClient, self).__setattr__(name, value)
 
1103
        self._pipe.send(('setattr', name, value))
 
1104
 
 
1105
 
991
1106
class ClientHandler(socketserver.BaseRequestHandler, object):
992
1107
    """A class to handle client connections.
993
1108
    
995
1110
    Note: This will run in its own forked process."""
996
1111
    
997
1112
    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):
 
1113
        with contextlib.closing(self.server.child_pipe) as child_pipe:
 
1114
            logger.info(u"TCP connection from: %s",
 
1115
                        unicode(self.client_address))
 
1116
            logger.debug(u"Pipe FD: %d",
 
1117
                         self.server.child_pipe.fileno())
 
1118
 
1006
1119
            session = (gnutls.connection
1007
1120
                       .ClientSession(self.request,
1008
1121
                                      gnutls.connection
1009
1122
                                      .X509Credentials()))
1010
 
            
 
1123
 
1011
1124
            # Note: gnutls.connection.X509Credentials is really a
1012
1125
            # generic GnuTLS certificate credentials object so long as
1013
1126
            # no X.509 keys are added to it.  Therefore, we can use it
1014
1127
            # here despite using OpenPGP certificates.
1015
 
            
 
1128
 
1016
1129
            #priority = u':'.join((u"NONE", u"+VERS-TLS1.1",
1017
1130
            #                      u"+AES-256-CBC", u"+SHA1",
1018
1131
            #                      u"+COMP-NULL", u"+CTYPE-OPENPGP",
1024
1137
            (gnutls.library.functions
1025
1138
             .gnutls_priority_set_direct(session._c_object,
1026
1139
                                         priority, None))
1027
 
            
 
1140
 
1028
1141
            # Start communication using the Mandos protocol
1029
1142
            # Get protocol number
1030
1143
            line = self.request.makefile().readline()
1035
1148
            except (ValueError, IndexError, RuntimeError), error:
1036
1149
                logger.error(u"Unknown protocol version: %s", error)
1037
1150
                return
1038
 
            
 
1151
 
1039
1152
            # Start GnuTLS connection
1040
1153
            try:
1041
1154
                session.handshake()
1045
1158
                # established.  Just abandon the request.
1046
1159
                return
1047
1160
            logger.debug(u"Handshake succeeded")
 
1161
 
 
1162
            approval_required = False
1048
1163
            try:
1049
1164
                try:
1050
1165
                    fpr = self.fingerprint(self.peer_certificate
1054
1169
                    return
1055
1170
                logger.debug(u"Fingerprint: %s", fpr)
1056
1171
 
1057
 
                for c in self.server.clients:
1058
 
                    if c.fingerprint == fpr:
1059
 
                        client = c
 
1172
                try:
 
1173
                    client = ProxyClient(child_pipe, fpr,
 
1174
                                         self.client_address)
 
1175
                except KeyError:
 
1176
                    return
 
1177
                
 
1178
                if client.approved_delay:
 
1179
                    delay = client.approved_delay
 
1180
                    client.approvals_pending += 1
 
1181
                    approval_required = True
 
1182
                
 
1183
                while True:
 
1184
                    if not client.enabled:
 
1185
                        logger.warning(u"Client %s is disabled",
 
1186
                                       client.name)
 
1187
                        if self.server.use_dbus:
 
1188
                            # Emit D-Bus signal
 
1189
                            client.Rejected("Disabled")                    
 
1190
                        return
 
1191
                    
 
1192
                    if client._approved or not client.approved_delay:
 
1193
                        #We are approved or approval is disabled
1060
1194
                        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()
 
1195
                    elif client._approved is None:
 
1196
                        logger.info(u"Client %s need approval",
 
1197
                                    client.name)
 
1198
                        if self.server.use_dbus:
 
1199
                            # Emit D-Bus signal
 
1200
                            client.NeedApproval(
 
1201
                                client.approved_delay_milliseconds(),
 
1202
                                client.approved_by_default)
 
1203
                    else:
 
1204
                        logger.warning(u"Client %s was not approved",
 
1205
                                       client.name)
 
1206
                        if self.server.use_dbus:
 
1207
                            # Emit D-Bus signal
 
1208
                            client.Rejected("Disapproved")
 
1209
                        return
 
1210
                    
 
1211
                    #wait until timeout or approved
 
1212
                    #x = float(client._timedelta_to_milliseconds(delay))
 
1213
                    time = datetime.datetime.now()
 
1214
                    client.changedstate.acquire()
 
1215
                    client.changedstate.wait(float(client._timedelta_to_milliseconds(delay) / 1000))
 
1216
                    client.changedstate.release()
 
1217
                    time2 = datetime.datetime.now()
 
1218
                    if (time2 - time) >= delay:
 
1219
                        if not client.approved_by_default:
 
1220
                            logger.warning("Client %s timed out while"
 
1221
                                           " waiting for approval",
 
1222
                                           client.name)
 
1223
                            if self.server.use_dbus:
 
1224
                                # Emit D-Bus signal
 
1225
                                client.Rejected("Time out")
 
1226
                            return
 
1227
                        else:
 
1228
                            break
 
1229
                    else:
 
1230
                        delay -= time2 - time
 
1231
                
1090
1232
                sent_size = 0
1091
1233
                while sent_size < len(client.secret):
1092
 
                    sent = session.send(client.secret[sent_size:])
 
1234
                    try:
 
1235
                        sent = session.send(client.secret[sent_size:])
 
1236
                    except (gnutls.errors.GNUTLSError), error:
 
1237
                        logger.warning("gnutls send failed")
 
1238
                        return
1093
1239
                    logger.debug(u"Sent: %d, remaining: %d",
1094
1240
                                 sent, len(client.secret)
1095
1241
                                 - (sent_size + sent))
1096
1242
                    sent_size += sent
 
1243
 
 
1244
                logger.info(u"Sending secret to %s", client.name)
 
1245
                # bump the timeout as if seen
 
1246
                client.checked_ok()
 
1247
                if self.server.use_dbus:
 
1248
                    # Emit D-Bus signal
 
1249
                    client.GotSecret()
 
1250
            
1097
1251
            finally:
1098
 
                session.bye()
 
1252
                if approval_required:
 
1253
                    client.approvals_pending -= 1
 
1254
                try:
 
1255
                    session.bye()
 
1256
                except (gnutls.errors.GNUTLSError), error:
 
1257
                    logger.warning("gnutls bye failed")
1099
1258
    
1100
1259
    @staticmethod
1101
1260
    def peer_certificate(session):
1161
1320
        return hex_fpr
1162
1321
 
1163
1322
 
1164
 
class ForkingMixInWithPipes(socketserver.ForkingMixIn, object):
1165
 
    """Like socketserver.ForkingMixIn, but also pass a pipe pair."""
 
1323
class MultiprocessingMixIn(object):
 
1324
    """Like socketserver.ThreadingMixIn, but with multiprocessing"""
 
1325
    def sub_process_main(self, request, address):
 
1326
        try:
 
1327
            self.finish_request(request, address)
 
1328
        except:
 
1329
            self.handle_error(request, address)
 
1330
        self.close_request(request)
 
1331
            
 
1332
    def process_request(self, request, address):
 
1333
        """Start a new process to process the request."""
 
1334
        multiprocessing.Process(target = self.sub_process_main,
 
1335
                                args = (request, address)).start()
 
1336
 
 
1337
class MultiprocessingMixInWithPipe(MultiprocessingMixIn, object):
 
1338
    """ adds a pipe to the MixIn """
1166
1339
    def process_request(self, request, client_address):
1167
1340
        """Overrides and wraps the original process_request().
1168
1341
        
1169
1342
        This function creates a new pipe in self.pipe
1170
1343
        """
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,
 
1344
        parent_pipe, self.child_pipe = multiprocessing.Pipe()
 
1345
 
 
1346
        super(MultiprocessingMixInWithPipe,
1176
1347
              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):
 
1348
        self.child_pipe.close()
 
1349
        self.add_pipe(parent_pipe)
 
1350
 
 
1351
    def add_pipe(self, parent_pipe):
1182
1352
        """Dummy function; override as necessary"""
1183
 
        child_pipe_fd.close()
1184
 
        parent_pipe_fd.close()
1185
 
 
1186
 
 
1187
 
class IPv6_TCPServer(ForkingMixInWithPipes,
 
1353
        pass
 
1354
 
 
1355
class IPv6_TCPServer(MultiprocessingMixInWithPipe,
1188
1356
                     socketserver.TCPServer, object):
1189
1357
    """IPv6-capable TCP server.  Accepts 'None' as address and/or port
1190
1358
    
1275
1443
            return socketserver.TCPServer.server_activate(self)
1276
1444
    def enable(self):
1277
1445
        self.enabled = True
1278
 
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
 
1446
    def add_pipe(self, parent_pipe):
1279
1447
        # Call "handle_ipc" for both data and EOF events
1280
 
        gobject.io_add_watch(child_pipe_fd.fileno(),
 
1448
        gobject.io_add_watch(parent_pipe.fileno(),
1281
1449
                             gobject.IO_IN | gobject.IO_HUP,
1282
1450
                             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):
 
1451
                                               parent_pipe = parent_pipe))
 
1452
        
 
1453
    def handle_ipc(self, source, condition, parent_pipe=None,
 
1454
                   client_object=None):
1286
1455
        condition_names = {
1287
1456
            gobject.IO_IN: u"IN",   # There is data to read.
1288
1457
            gobject.IO_OUT: u"OUT", # Data can be written (without
1299
1468
                                       if cond & condition)
1300
1469
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1301
1470
                     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
 
1471
 
 
1472
        # error or the other end of multiprocessing.Pipe has closed
 
1473
        if condition & (gobject.IO_ERR | condition & gobject.IO_HUP):
 
1474
            return False
 
1475
        
 
1476
        # Read a request from the child
 
1477
        request = parent_pipe.recv()
 
1478
        logger.debug(u"IPC request: %s", repr(request))
 
1479
        command = request[0]
 
1480
        
 
1481
        if command == 'init':
 
1482
            fpr = request[1]
 
1483
            address = request[2]
 
1484
            
 
1485
            for c in self.clients:
 
1486
                if c.fingerprint == fpr:
 
1487
                    client = c
 
1488
                    break
 
1489
            else:
 
1490
                logger.warning(u"Client not found for fingerprint: %s, ad"
 
1491
                               u"dress: %s", fpr, address)
 
1492
                if self.use_dbus:
 
1493
                    # Emit D-Bus signal
 
1494
                    mandos_dbus_service.ClientNotFound(fpr, address)
 
1495
                parent_pipe.send(False)
 
1496
                return False
 
1497
            
 
1498
            gobject.io_add_watch(parent_pipe.fileno(),
 
1499
                                 gobject.IO_IN | gobject.IO_HUP,
 
1500
                                 functools.partial(self.handle_ipc,
 
1501
                                                   parent_pipe = parent_pipe,
 
1502
                                                   client_object = client))
 
1503
            parent_pipe.send(True)
 
1504
            # remove the old hook in favor of the new above hook on same fileno
 
1505
            return False
 
1506
        if command == 'funcall':
 
1507
            funcname = request[1]
 
1508
            args = request[2]
 
1509
            kwargs = request[3]
 
1510
            
 
1511
            parent_pipe.send(('data', getattr(client_object, funcname)(*args, **kwargs)))
 
1512
 
 
1513
        if command == 'getattr':
 
1514
            attrname = request[1]
 
1515
            if callable(client_object.__getattribute__(attrname)):
 
1516
                parent_pipe.send(('function',))
 
1517
            else:
 
1518
                parent_pipe.send(('data', client_object.__getattribute__(attrname)))
 
1519
        
 
1520
        if command == 'setattr':
 
1521
            attrname = request[1]
 
1522
            value = request[2]
 
1523
            setattr(client_object, attrname, value)
 
1524
 
1364
1525
        return True
1365
1526
 
1366
1527
 
1469
1630
    parser.add_option("--debug", action=u"store_true",
1470
1631
                      help=u"Debug mode; run in foreground and log to"
1471
1632
                      u" terminal")
 
1633
    parser.add_option("--debuglevel", type=u"string", metavar="Level",
 
1634
                      help=u"Debug level for stdout output")
1472
1635
    parser.add_option("--priority", type=u"string", help=u"GnuTLS"
1473
1636
                      u" priority string (see GnuTLS documentation)")
1474
1637
    parser.add_option("--servicename", type=u"string",
1499
1662
                        u"servicename": u"Mandos",
1500
1663
                        u"use_dbus": u"True",
1501
1664
                        u"use_ipv6": u"True",
 
1665
                        u"debuglevel": u"",
1502
1666
                        }
1503
1667
    
1504
1668
    # Parse config file for server-global settings
1521
1685
    # options, if set.
1522
1686
    for option in (u"interface", u"address", u"port", u"debug",
1523
1687
                   u"priority", u"servicename", u"configdir",
1524
 
                   u"use_dbus", u"use_ipv6"):
 
1688
                   u"use_dbus", u"use_ipv6", u"debuglevel"):
1525
1689
        value = getattr(options, option)
1526
1690
        if value is not None:
1527
1691
            server_settings[option] = value
1536
1700
    
1537
1701
    # For convenience
1538
1702
    debug = server_settings[u"debug"]
 
1703
    debuglevel = server_settings[u"debuglevel"]
1539
1704
    use_dbus = server_settings[u"use_dbus"]
1540
1705
    use_ipv6 = server_settings[u"use_ipv6"]
1541
 
    
1542
 
    if not debug:
1543
 
        syslogger.setLevel(logging.WARNING)
1544
 
        console.setLevel(logging.WARNING)
1545
 
    
 
1706
 
1546
1707
    if server_settings[u"servicename"] != u"Mandos":
1547
1708
        syslogger.setFormatter(logging.Formatter
1548
1709
                               (u'Mandos (%s) [%%(process)d]:'
1554
1715
                        u"interval": u"5m",
1555
1716
                        u"checker": u"fping -q -- %%(host)s",
1556
1717
                        u"host": u"",
 
1718
                        u"approved_delay": u"0s",
 
1719
                        u"approved_duration": u"1s",
1557
1720
                        }
1558
1721
    client_config = configparser.SafeConfigParser(client_defaults)
1559
1722
    client_config.read(os.path.join(server_settings[u"configdir"],
1565
1728
    tcp_server = MandosServer((server_settings[u"address"],
1566
1729
                               server_settings[u"port"]),
1567
1730
                              ClientHandler,
1568
 
                              interface=(server_settings[u"interface"]
1569
 
                                         or None),
 
1731
                              interface=server_settings[u"interface"],
1570
1732
                              use_ipv6=use_ipv6,
1571
1733
                              gnutls_priority=
1572
1734
                              server_settings[u"priority"],
1599
1761
            raise error
1600
1762
    
1601
1763
    # Enable all possible GnuTLS debugging
 
1764
 
 
1765
 
 
1766
    if not debug and not debuglevel:
 
1767
        syslogger.setLevel(logging.WARNING)
 
1768
        console.setLevel(logging.WARNING)
 
1769
    if debuglevel:
 
1770
        level = getattr(logging, debuglevel.upper())
 
1771
        syslogger.setLevel(level)
 
1772
        console.setLevel(level)
 
1773
 
1602
1774
    if debug:
1603
1775
        # "Use a log level over 10 to enable all debugging options."
1604
1776
        # - GnuTLS manual
1610
1782
        
1611
1783
        (gnutls.library.functions
1612
1784
         .gnutls_global_set_log_function(debug_gnutls))
 
1785
 
 
1786
        # Redirect stdin so all checkers get /dev/null
 
1787
        null = os.open(os.path.devnull, os.O_NOCTTY | os.O_RDWR)
 
1788
        os.dup2(null, sys.stdin.fileno())
 
1789
        if null > 2:
 
1790
            os.close(null)
 
1791
    else:
 
1792
        # No console logging
 
1793
        logger.removeHandler(console)
 
1794
 
1613
1795
    
1614
1796
    global main_loop
1615
1797
    # From the Avahi example code
1633
1815
    if server_settings["interface"]:
1634
1816
        service.interface = (if_nametoindex
1635
1817
                             (str(server_settings[u"interface"])))
 
1818
 
 
1819
    if not debug:
 
1820
        # Close all input and output, do double fork, etc.
 
1821
        daemon()
 
1822
        
 
1823
    global multiprocessing_manager
 
1824
    multiprocessing_manager = multiprocessing.Manager()
1636
1825
    
1637
1826
    client_class = Client
1638
1827
    if use_dbus:
1639
1828
        client_class = functools.partial(ClientDBus, bus = bus)
 
1829
    def client_config_items(config, section):
 
1830
        special_settings = {
 
1831
            "approved_by_default":
 
1832
                lambda: config.getboolean(section,
 
1833
                                          "approved_by_default"),
 
1834
            }
 
1835
        for name, value in config.items(section):
 
1836
            try:
 
1837
                yield (name, special_settings[name]())
 
1838
            except KeyError:
 
1839
                yield (name, value)
 
1840
    
1640
1841
    tcp_server.clients.update(set(
1641
1842
            client_class(name = section,
1642
 
                         config= dict(client_config.items(section)))
 
1843
                         config= dict(client_config_items(
 
1844
                        client_config, section)))
1643
1845
            for section in client_config.sections()))
1644
1846
    if not tcp_server.clients:
1645
1847
        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
 
    
 
1848
        
1659
1849
    try:
1660
1850
        with pidfile:
1661
1851
            pid = os.getpid()