Background Information on libSSL's Cache Functions and SIDs

NSS Technical Note: 8

27 February 2006
Nelson B. Bolyard

Here is some background information on libSSL's cache functions and SIDs.

A SID (or sslSessionID struct) contains all the info needed to restart
the ssl session at a later time on another socket.  The protocol code
builds such a structure, and then asks the cache code (client or server)
to save the info.  The protocol code can also ask to remove a SID from
the cache.

Every SSL socket has two function pointers, ss->sec.cache and ss->sec.uncache,
which have the following types:
typedef void (*sslSessionIDCacheFunc)  (sslSessionID *sid);
typedef void (*sslSessionIDUncacheFunc)(sslSessionID *sid);

There are two separate implementations of each function, one for clients
and one for servers.  The client implementation caches or uncaches the
SID in the client session cache.  The server implementation caches or
uncaches the SID in the server session cache.

For servers these pointers point to
       sec->cache   = ssl_sid_cache;
       sec->uncache = ssl_sid_uncache;
which are functions defined in sslsnce.c, the server session cache source file.

For clients these pointers point to
       sec->cache   = CacheSID;
       sec->uncache = LockAndUncacheSID;
which are functions defined in sslnonce.c, the client session cache source file.

The same cache/uncache API is used by both client and server code.
As originally designed, before calling the cache function, the caller was
responsible to fill in the session creation time (which might not be the
same as the time of insertion into the cache) and the session expiration
time, among other things.

Since NSS 1.0, up until NSS 3.4, there were two global variables that
contained the expected session lifetimes for ssl2 and ssl3 sessions.

extern PRUint32 ssl_sid_timeout;    (the ssl2 session lifetime)
extern PRUint32 ssl3_sid_timeout;   (the ssl3 session lifetime)

Each of these variables applied to both client and server sessions.
That is, the client session lifetime was NOT separately settable from the
server session lifetime.

These two variables were private, declared in a private header file.
There was no API function by which client programs could set these values.
However since NSS was delivered as archive libraries, client programs
merely declared these two variables for themselves, and then were able to
alter those variables directly.

For server programs, the function for initializing the server session cache
would set these two variables according to two of the arguments to that
function.

So, SSL protocol code that wanted to cache a SID would do these steps,
whether for client or for server:

For ssl2:
    sid->lastAccessTime = sid->creationTime = ssl_Time();
    sid->expirationTime = sid->creationTime + ssl_sid_timeout;
    (*ss->sec.cache)(sid);
for ssl3:
    sid->lastAccessTime = sid->creationTime = ssl_Time();
    sid->expirationTime = sid->creationTime + ssl3_sid_timeout;
    (*ss->sec.cache)(sid);

The cache API was defined such that the caller MUST set creationTime
properly, and may set expirationTime to the desired value or to zero.
If zero, then the called cache function would compute the correct
expiration time by adding the chosen timeout (from one of those two
global variables) to the SID's creationTime, giving the expirationTime.

However, none of the callers relied on the ability of the respective
cache functions to be able to compute the expiration time.  All callers
computed the expiration times explicitly, as shown above.

The server side of the session cache code was largely rewritten for
NSS 3.4.  The objectives were to make the server session cache faster,
and to fix bugs that caused corruption in multi-process servers, and also
to allow separate virtual servers to have their own session caches.

The new approach was to use shared memory for the server session cache,
and to allow multiple different server session caches to coexist.
As part of that work, I decided that each cache would have its own
variables containing the SSL2 and SSL3 session durations.
This means that client cache session lifetimes are separate from server
session cache lifetimes, and that each server session cache may have its
own lifetimes.

So, in NSS 3.4, the global variables ssl3_sid_timeout and ssl_sid_timeout
were intended to become the definitions for the client cache only, and
each server cache had its own new pair of variables for ssl2 and ssl3
session lifetimes, i.e., cache->ssl2Timeout and cache->ssl3Timeout.
The server cache initialization function was intended to no longer alter
the variables ssl3_sid_timeout and ssl_sid_timeout, but rather to set the
server cache's variables.

Since all the callers of the socket's cache function always initialized
both their creationTime and expirationTime using the client's session
lifetime variables, I changed the server's caching function to IGNORE the
expirationTime computed by the caller, and compute its own expiration
time, using the cache's own timeout values, or that was the intent.

But an implementation flaw caused the caching code to continue to use the
client's timeout time values, not the server cache's own timeout values.
That is the subject of bug 223242.