Reading and writing functions

130 This framework, when implemented, provides a powerful mechanism for session caching. By using the filesystem, the cache isnt constrained by memory restrictions. Additionally, it is easier to use than an in-memory caching scheme since the on-disk scheme allows multiple processes to access the session cache.

5.2.2 IO on SSL Connections

The motivation behind creating an SSL connection is to transfer data back and forth securely. Thus far, weve concentrated on making this connection very secure, but weve avoided the details on how data is transferred. In the earlier examples, we cheated on the IO processing, since the client only writes to the SSL connection, and the server only reads from it. Most real-world applications do not operate on such a simple model. Since the calls we used to perform the reading and writing SSL_read and SSL_write closely mirror the system calls read and write , it seems as though IO would not be significantly different from IO in non-SSL applications. Unfortunately, this is far from the truth. There are many subtleties in performing IO correctly with OpenSSL. We will first look at SSL_read and SSL_write in more detail. Once we understand them, we will get into the detail of the differences between performing blocking and non-blocking IO on SSL connections. By the end of this chapter, we will have built up our knowledge of the pitfalls with IO in OpenSSL. This will enable us to avoid them when implementing real-world applications that require IO paradigms that are more complex than the ones provided in our examples.

5.2.2.1 Reading and writing functions

In the examples, we used the IO functions SSL_read and SSL_write for SSL connections, but we didnt discuss them in any detail. The arguments for these calls are similar to the system calls read and write . The way these calls differ from their system call counterparts is in their return values. Table 5-2 provides the details of the possible return values. Table 5-2. Return values of SSL_read and SSL_write Return value Description Success. The data requested was read or written, and the return value is the number of bytes. Failure. Either an error with the SSL connection occurred or the call failed because the underlying IO could not perform the operation at the time of calling. Failure. Either an error with the SSL connection occurred or the application is required to perform a different function before retrying the failed call. Knowing these return values helps, but we still cant tell if an error actually occured without some more information. OpenSSL provides the function SSL_get_error , which accepts the return value of an SSL IO function and returns different values based on what actually happened. This function inspects the IO routines return value, the SSL object, and the current threads error queue to determine what the effect of the call was. Because the internal errors for OpenSSL are stored on a per-thread basis, the call to check the errors must be made from the same thread as the IO call. Robust applications should always check the detailed error status of all IO operations with SSL_get_error . Remember that functions like SSL_connect and SSL_accept are also IO operations. The return value of SSL_get_error can be many different values. Our application needs to be able to handle a subset of these conditions and provide a reasonable default behavior of shutting 131 down the SSL connection. Table 5-3 provides the descriptions of the values we should always be able to handle. Table 5-3. Some common return values of SSL_get_error Return value Description SSL_ERROR_NONE No error occurred with the IO call. SSL_ERROR_ZERO_RETURN The operation failed due to the SSL session being closed. The underlying connection medium may still be open. SSL_ERROR_WANT_READ The operation couldnt be completed. The underlying medium could not fulfill the read requested. This error code means the call should be retried. SSL_ERROR_WANT_WRITE The operation couldnt be completed. The underlying medium could not fulfill the write requested. This error code means the call should be retried. The handling of the first error code in the table, SSL_ERROR_NONE , is straightforward. SSL_ERROR_ZERO_RETURN should be handled in an application-specific way, knowing that the SSL connection has been closed. In order to handle SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE , we need to retry the IO operation; this is discussed in more detail below. Any of the other possible return values of SSL_get_error should be considered errors. In order to implement a call to an IO function correctly, we should check for these different return values. A sample piece of code is given in Example 5-15 . The handlers for SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE have been omitted; the details on handling them properly are discussed below, since the correct actions vary based on whether the application is using blocking or non-blocking IO. This examples purpose is to provide a template of what a robust IO call should look like. Using the switch statement, we handle all the conditions for which we have interest and error on any others. As stated above, there are other return values possible, and we may wish to handle them with specific error messages, for instance. Example 5-15. A sample IO call template code = SSL_readssl, buf + offset, size - offset; switch SSL_get_errorssl, code { case SSL_ERROR_NONE: update the offset value offset += code; break; case SSL_ERROR_ZERO_RETURN: react to the SSL connection being closed do_cleanupssl; break; case SSL_ERROR_WANT_READ: handle this in an application specific way to retry the SSL_read break; case SSL_ERROR_WANT_WRITE: handle this in an application specific way to retry the SSL_read break; default: an error occurred. shutdown the connection shutdown_connectionssl; } 132 Typically, using blocking IO alleviates the complications of retrying failed calls. With SSL, this is not the case. There are times when a call to SSL_read or SSL_write on a blocking connection requires a retry.

5.2.2.2 Blocking IO