In my last column, "Writing a Secure POP3 Server," I talked about protecting your POP3 mail server from attacks, and I identified some of the potential threats that you need to guard against. As I explained, the two main areas of concern when handling user input to your POP3 server are improperly formed commands and handling general network errors.
Request for Comments (RFC) 1939, which you can view at Ohio State's CIS Web site, defines the POP3 standard. Users can give the POP server commands, such as USER, PASS, and LIST. According to RFC 1939, the commands
" consist of a case-insensitive keyword, possibly followed by one or more arguments. All commands are terminated by a CRLF pair. Keywords and arguments consist of printable ASCII characters. Keywords and arguments are each separated by a single SPACE character. Keywords are three or four characters long. Each argument may be up to 40 characters long."
So what do you need to know to protect your POP3 server when handling user input? To begin, you need to come up with a function that lets you retrieve a line of user input from a socket without overflowing the buffers. Windows sockets are based on the UNIX implementation in the Berkeley Software Distribution (BSD) release 4.3. The code samples in this column assume that you are familiar with the basics of sockets programming. You can find additional information and utilities at the Stardust.com Web site. Another good reference is Windows Sockets Network Programming by Quinn and Shute (ISBN 0201633728), a text that does a good job of explaining the differences between Windows socket implementations (known as Winsock) and the UNIX implementation. I’ll cover the pitfalls you’ll encounter when binding sockets to a port in another column.
Let’s start by assuming that a client has connected to your server and wants to feed you a command. For simplicity, we’ll also assume that you have allocated one thread per client to process requests.
The code in Listing 1 shows what it takes to correctly get one line of user input from a socket—more than 100 lines of code! If you're going to use this code for yourself, be sure to test it thoroughly. I wrote this code to be portable, but if portability isn’t a concern, you might consider using WSAEventSelect(), a highly efficient technique that associates an event with the socket. Note that the code checks for network errors every step of the way. Don’t assume that because select() said the socket had data, recv() is always going to retrieve any data. I’ve learned the hard way that this isn’t always true.
Next, look carefully at how the code handles the input, starting at line 106. When you get a character from the user, you know that the standard will allow only printable characters, carriage returns, and linefeeds. This verification is your first line of defense against an attacker. As I mentioned in my previous article, "Overflowing Buffers," attackers might need to feed very specific characters to a program to cause arbitrary code to execute. If the characters the attackers need aren’t printable, the code in Listing 1 will drop the attackers' connection. This code also sets a user-configurable timeout, so that if you don’t hear from the client soon enough, the program will return an error.
Now that you’ve retrieved a line of input and checked for garbage characters without overflowing your buffer, you can parse the string in Listing 2. Start by checking to see whether you have a valid line of input. Next, you know that all arguments are four characters or less, so check for that as well. Finally, you need to check whether the command you received is a supported command—but I’ll leave that as an exercise for you.
If you examine the archives of any good security mailing list, you’ll see numerous references to overflows in both POP and IMAP servers. Writing the code to get one line from a user and check it for errors took me a couple of hours, but I’m really just being lazy—remember, do it right the first time!