/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: 2009-12-25 23:13:47 UTC
  • Revision ID: teddy@fukt.bsnet.se-20091225231347-gg9u9ru0wj0f24hh
More consistent terminology: Clients are no longer "invalid" - they
are "disabled".  All code and documentation changed to reflect this.

D=Bus API change: The "properties" argument was removed from the
"ClientAdded" signal on interface "se.bsnet.fukt.Mandos".  All code in
both "mandos" and "mandos-monitor" changed to reflect this.

* mandos: Replaced "with closing(F)" with simply "with F" in all
          places where F is a file object.
  (Client.still_valid): Removed.  All callers changed to look at
                        "Client.enabled" instead.
  (dbus_service_property): Check for unsupported signatures with the
                           "byte_arrays" option.
  (DBusObjectWithProperties.Set): - '' -
  (ClientHandler.handle): Use the reverse pipe to receive the
                          "Client.enabled" attribute instead of the
                          now-removed "Client.still_valid()" method.
  (ForkingMixInWithPipe): Renamed to "ForkingMixInWithPipes" (all
                          users changed).  Now also create a reverse
                          pipe for sending data to the child process.
  (ForkingMixInWithPipes.add_pipe): Now takes two pipe fd's as
                                    arguments.  All callers changed.
  (IPv6_TCPServer.handle_ipc): Take an additional "reply_fd" argument
                               (all callers changed).  Close the reply
                               pipe when the child data pipe is
                               closed.  New "GETATTR" IPC method; will
                               pickle client attribute and send it
                               over the reply pipe FD.
  (MandosDBusService.ClientAdded): Removed "properties" argument.  All
                                   emitters changed.
* mandos-clients.conf.xml (DESCRIPTION, OPTIONS): Use
                                                  "enabled/disabled"
                                                  terminology.
* mandos-ctl: Option "--is-valid" renamed to "--is-enabled".
* mandos-monitor: Enable user locale.  Try to log exceptions.
  (MandosClientPropertyCache.__init__): Removed "properties" argument.
                                        All callers changed.
  (UserInterface.add_new_client): Remove "properties" argument.  All
                                  callers changed.  Supply "logger"
                                  argument to MandosClientWidget().
  (UserInterface.add_client): New "logger" argument.  All callers
                              changed.
* mandos.xml (BUGS, SECURITY/CLIENTS): Use "enabled/disabled"
                                       terminology.

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
64
63
 
65
64
import dbus
66
65
import dbus.service
193
192
        self.group.Commit()
194
193
    def entry_group_state_changed(self, state, error):
195
194
        """Derived from the Avahi example code"""
196
 
        logger.debug(u"Avahi entry group state change: %i", state)
 
195
        logger.debug(u"Avahi state change: %i", state)
197
196
        
198
197
        if state == avahi.ENTRY_GROUP_ESTABLISHED:
199
198
            logger.debug(u"Zeroconf service established.")
212
211
            self.group = None
213
212
    def server_state_changed(self, state):
214
213
        """Derived from the Avahi example code"""
215
 
        logger.debug(u"Avahi server state change: %i", state)
216
214
        if state == avahi.SERVER_COLLISION:
217
215
            logger.error(u"Zeroconf server name collision")
218
216
            self.remove()
782
780
                                 dbus.Boolean(False, variant_level=1))
783
781
        return r
784
782
    
785
 
    ## D-Bus methods, signals & properties
 
783
    ## D-Bus methods & signals
786
784
    _interface = u"se.bsnet.fukt.Mandos.Client"
787
785
    
788
 
    ## Signals
 
786
    # CheckedOK - method
 
787
    @dbus.service.method(_interface)
 
788
    def CheckedOK(self):
 
789
        return self.checked_ok()
789
790
    
790
791
    # CheckerCompleted - signal
791
792
    @dbus.service.signal(_interface, signature=u"nxs")
817
818
        "D-Bus signal"
818
819
        pass
819
820
    
820
 
    ## Methods
821
 
    
822
 
    # CheckedOK - method
823
 
    @dbus.service.method(_interface)
824
 
    def CheckedOK(self):
825
 
        return self.checked_ok()
826
 
    
827
821
    # Enable - method
828
822
    @dbus.service.method(_interface)
829
823
    def Enable(self):
847
841
    def StopChecker(self):
848
842
        self.stop_checker()
849
843
    
850
 
    ## Properties
851
 
    
852
844
    # name - property
853
845
    @dbus_service_property(_interface, signature=u"s", access=u"read")
854
846
    def name_dbus_property(self):
997
989
    def handle(self):
998
990
        logger.info(u"TCP connection from: %s",
999
991
                    unicode(self.client_address))
1000
 
        logger.debug(u"IPC Pipe FD: %d",
1001
 
                     self.server.child_pipe[1].fileno())
 
992
        logger.debug(u"IPC Pipe FD: %d", self.server.child_pipe[1])
1002
993
        # 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):
 
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):
1006
999
            session = (gnutls.connection
1007
1000
                       .ClientSession(self.request,
1008
1001
                                      gnutls.connection
1009
1002
                                      .X509Credentials()))
1010
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
            
1011
1013
            # Note: gnutls.connection.X509Credentials is really a
1012
1014
            # generic GnuTLS certificate credentials object so long as
1013
1015
            # no X.509 keys are added to it.  Therefore, we can use it
1025
1027
             .gnutls_priority_set_direct(session._c_object,
1026
1028
                                         priority, None))
1027
1029
            
1028
 
            # Start communication using the Mandos protocol
1029
 
            # Get protocol number
1030
 
            line = self.request.makefile().readline()
1031
 
            logger.debug(u"Protocol version: %r", line)
1032
 
            try:
1033
 
                if int(line.strip().split()[0]) > 1:
1034
 
                    raise RuntimeError
1035
 
            except (ValueError, IndexError, RuntimeError), error:
1036
 
                logger.error(u"Unknown protocol version: %s", error)
1037
 
                return
1038
 
            
1039
 
            # Start GnuTLS connection
1040
1030
            try:
1041
1031
                session.handshake()
1042
1032
            except gnutls.errors.GNUTLSError, error:
1062
1052
                    ipc.write(u"NOTFOUND %s %s\n"
1063
1053
                              % (fpr, unicode(self.client_address)))
1064
1054
                    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
1055
                # Have to check if client.enabled, since it is
1083
1056
                # possible that the client was disabled since the
1084
1057
                # GnuTLS session was established.
1085
 
                if not clientproxy.enabled:
1086
 
                    clientproxy.ipc_disabled()
 
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)
1087
1062
                    return
1088
 
                
1089
 
                clientproxy.ipc_sending()
 
1063
                ipc.write(u"SENDING %s\n" % client.name)
1090
1064
                sent_size = 0
1091
1065
                while sent_size < len(client.secret):
1092
1066
                    sent = session.send(client.secret[sent_size:])
1168
1142
        
1169
1143
        This function creates a new pipe in self.pipe
1170
1144
        """
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))
 
1145
        self.child_pipe = os.pipe() # Child writes here
 
1146
        self.parent_pipe = os.pipe() # Parent writes here
1175
1147
        super(ForkingMixInWithPipes,
1176
1148
              self).process_request(request, client_address)
1177
1149
        # Close unused ends for parent
1178
 
        self.parent_pipe[0].close() # close read end
1179
 
        self.child_pipe[1].close()  # close write end
 
1150
        os.close(self.parent_pipe[0]) # close read end
 
1151
        os.close(self.child_pipe[1])  # close write end
1180
1152
        self.add_pipe_fds(self.child_pipe[0], self.parent_pipe[1])
1181
1153
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1182
1154
        """Dummy function; override as necessary"""
1183
 
        child_pipe_fd.close()
1184
 
        parent_pipe_fd.close()
 
1155
        os.close(child_pipe_fd)
 
1156
        os.close(parent_pipe_fd)
1185
1157
 
1186
1158
 
1187
1159
class IPv6_TCPServer(ForkingMixInWithPipes,
1277
1249
        self.enabled = True
1278
1250
    def add_pipe_fds(self, child_pipe_fd, parent_pipe_fd):
1279
1251
        # Call "handle_ipc" for both data and EOF events
1280
 
        gobject.io_add_watch(child_pipe_fd.fileno(),
 
1252
        gobject.io_add_watch(child_pipe_fd,
1281
1253
                             gobject.IO_IN | gobject.IO_HUP,
1282
1254
                             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):
 
1255
                                               reply_fd
 
1256
                                               =parent_pipe_fd))
 
1257
    def handle_ipc(self, source, condition, reply_fd=None,
 
1258
                   file_objects={}):
1286
1259
        condition_names = {
1287
1260
            gobject.IO_IN: u"IN",   # There is data to read.
1288
1261
            gobject.IO_OUT: u"OUT", # Data can be written (without
1300
1273
        logger.debug(u"Handling IPC: FD = %d, condition = %s", source,
1301
1274
                     conditions_string)
1302
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
        
1303
1282
        # Read a line from the file object
1304
 
        cmdline = sender.readline()
 
1283
        cmdline = file_objects[source].readline()
1305
1284
        if not cmdline:             # Empty line means end of file
1306
1285
            # close the IPC pipes
1307
 
            sender.close()
1308
 
            reply.close()
 
1286
            file_objects[source].close()
 
1287
            del file_objects[source]
 
1288
            file_objects[reply_fd].close()
 
1289
            del file_objects[reply_fd]
1309
1290
            
1310
1291
            # Stop calling this function
1311
1292
            return False
1350
1331
                if client.fingerprint == fpr:
1351
1332
                    attr_value = getattr(client, attr_name, None)
1352
1333
                    logger.debug("IPC reply: %r", attr_value)
1353
 
                    pickle.dump(attr_value, reply)
 
1334
                    pickle.dump(attr_value, file_objects[reply_fd])
1354
1335
                    break
1355
1336
            else:
1356
1337
                logger.error(u"Client %s on address %s requesting "
1357
1338
                             u"attribute %s not found", fpr, address,
1358
1339
                             attr_name)
1359
 
                pickle.dump(None, reply)
 
1340
                pickle.dump(None, file_objects[reply_fd])
1360
1341
        else:
1361
1342
            logger.error(u"Unknown IPC command: %r", cmdline)
1362
1343
        
1565
1546
    tcp_server = MandosServer((server_settings[u"address"],
1566
1547
                               server_settings[u"port"]),
1567
1548
                              ClientHandler,
1568
 
                              interface=(server_settings[u"interface"]
1569
 
                                         or None),
 
1549
                              interface=server_settings[u"interface"],
1570
1550
                              use_ipv6=use_ipv6,
1571
1551
                              gnutls_priority=
1572
1552
                              server_settings[u"priority"],