Please skip this over if the thought of C++ objects, dynamic linking, and other stuff sends you into a coma.
Some readers might be wondering how IMSpector can log the messages without knowing all about the protocols involved. Luckily, you don’t have to understand all of it, only the packets relevent to the events you are interested in. Everything else is simply passed between the client machine and the server. Essentially IMSpector is well capable of transparently proxying pretty much any TCP protocol. Luckily most IM protocols are fairly simple, at least as far as messages between users is concerned. The complicated stuff is in the handling of contact lists etc. IMSpector was written from following the work of some good people who have deconstructed various IM protocols, and by me looking at packet captures, and quite abit of trial and error.
Language wise, IMSpector is written in a strange dialect of C++ called “Lawrence’s C++”. I like the language, but I will not overuse inheritance, operator overloading or any fancy OO techniques. Character arrays are sometimes easier to use then std::strings so I use them.
The plugins are implemented as objects of a defined class which call to an implementation within a single object-less C file through dynamic runtime linking. On program start, the plugins are located via a glob call and then an object is created, the plugins init function called via the object, and then (assuming the init was successful) pushed onto a vector of objects. Logging plugins, protocol plugins, content-manipulation plugins and filtering plugins are very simular at this level.
The main.cpp file contains standard deamon-type code, including code to drop privs etc. The deamon operates in the classical fork-on-connect method. A child handles an outgoing connection. One of the first things a child does is work out the protocol the client is speaking. It does this by looking at the “original address” that the client was trying to reach before it was redirected and iterates down its vector of protocol plugins looking for this port. If it finds a match, it has a protocol class. At this point it also has to complete the connection to the server the client is trying to reach. Otherwise it will drop the connection, and log a message to the effect of “I don’t know how to handle that protocol”.
Inside the client handler (doproxy() in main.cpp) the program shuffles data between the client and the remote IM server using well-known techniques – a socket class (a thin layer atop the socket FD) is used. The protocol plugin is called with the socket object and other parameters. This protocol plugin implementation will then get the packets and assuming it contains a message it will push a so-called imevent onto a vector of imevents, which will then be passed to each logging plugin in turn. The protocol plugin will also copy the data to send back to the other party in a buffer supplied by the connection handler code.
Filtering is achieved by simply discarding packets that do not pass as clean through the filtering plugins, if any have been configured.
These plugins are also able to modify the data, which is then copied back into the packets for sending. Currently only a single plugin of this type has been implemented for the purposes of filtering swear words, but in the future other plugins could be written which don’t replace text but simply generate some kind of alert when a particular word is spotted (for example). Also, it would be trivial to write a plugin to remove conent based on a collection of regular expressions.
Logging action is done in a single child process, with communication to the client handlers via a UNIX socket. The socket class (class Socket) can handle both TCP/UDP and UNIX sockets, in SSL and in the clear. The reason for using a logging process is that some logging mechanisms, most noteably MySQL, do not like having data transfers done on the same handle within different processes, and it would be much to slow and expensive to connect to the DB within each and every child.
SSL man-in-the-middle attack
As a rule, SSL or TLS-encrypted sessions are not monitorable. This is, of course, because the traffic on the wire is encrypted. This makes monitoring some IM traffic, like Gtalk, impossible.
One (probably the only) way around this is to proxy the SSL handshake, such that IMSpector makes an SSL session to the IM server on the SSL clients behalf. The following sequence of events for a client connection to Gtalk, which will always request TLS, describes the process. In this example, we will be creating a certificate on demand:
- Client connects to a Jabber Gtalk server on port 5222.
- IMSpector grabs this outgoing connection and redirects it to a local port.
- IMSpector completes the connection to the IM server.
- Normal Jabber handling continues until IMSpector notices that the server wants TLS.
- IMSpector negoitages a SSL connection, over the original TCP session, with the IM server. This is a standard “client” connection with no certificate. At this point IMSpector will carry out its own checks on the IM server’s certificate, storing the result of the validation check.
- IMSpector gets the Common Name of the IM server’s certificate and creates a certificate for the IM client, with this CN, singing it with its own local CA.
- IMSpector negoitates a SSL connection with the client, but this time it presents its own, freshly created, local certificate.
- On a good IM client, the fact that a bogus certificate is presented instead of the “real” Gtalk certificate will be flagged, but only if the client has not had the IMSpector CA certificate loaded into it.
- The IM traffic is passed between the client and server, using the SSL APIs to encrypt and decrypt the data. In the middle, IMSpector is able to see (and modify) the clear text.
One thing this man-in-the-middle attack illustrates is the importance of certificate validation on the client and, indeed, by the user. Without an “out of band” vallidation of the servers certificate there is no way to know that a secure server is what it purports to be. This is especially important when the client machine itself is untrusted, as it could have any CA loaded. IMSpector is only concered with IM messages, but MITM attacks are possible in pretty much all encryption systems, obviously including things like HTTPS.