298 lines
13 KiB
Plaintext
298 lines
13 KiB
Plaintext
I have just checked over and re-worked the session stuff.
|
|
The following brief example will ignore all setup information to do with
|
|
authentication.
|
|
|
|
Things operate as follows.
|
|
|
|
The SSL environment has a 'context', a SSL_CTX structure. This holds the
|
|
cached SSL_SESSIONS (which can be reused) and the certificate lookup
|
|
information. Each SSL structure needs to be associated with a SSL_CTX.
|
|
Normally only one SSL_CTX structure is needed per program.
|
|
|
|
SSL_CTX *SSL_CTX_new(void );
|
|
void SSL_CTX_free(SSL_CTX *);
|
|
These 2 functions create and destroy SSL_CTX structures
|
|
|
|
The SSL_CTX has a session_cache_mode which is by default,
|
|
in SSL_SESS_CACHE_SERVER mode. What this means is that the library
|
|
will automatically add new session-id's to the cache apon sucsessful
|
|
SSL_accept() calls.
|
|
If SSL_SESS_CACHE_CLIENT is set, then client certificates are also added
|
|
to the cache.
|
|
SSL_set_session_cache_mode(ctx,mode) will set the 'mode' and
|
|
SSL_get_session_cache_mode(ctx) will get the cache 'mode'.
|
|
The modes can be
|
|
SSL_SESS_CACHE_OFF - no caching
|
|
SSL_SESS_CACHE_CLIENT - only SSL_connect()
|
|
SSL_SESS_CACHE_SERVER - only SSL_accept()
|
|
SSL_SESS_NO_CACHE_BOTH - Either SSL_accept() or SSL_connect().
|
|
If SSL_SESS_CACHE_NO_AUTO_CLEAR is set, old timed out sessions are
|
|
not automatically removed each 255, SSL_connect()s or SSL_accept()s.
|
|
|
|
By default, apon every 255 successful SSL_connect() or SSL_accept()s,
|
|
the cache is flush. Please note that this could be expensive on
|
|
a heavily loaded SSL server, in which case, turn this off and
|
|
clear the cache of old entries 'manually' (with one of the functions
|
|
listed below) every few hours. Perhaps I should up this number, it is hard
|
|
to say. Remember, the '255' new calls is just a mechanims to get called
|
|
every now and then, in theory at most 255 new session-id's will have been
|
|
added but if 100 are added every minute, you would still have
|
|
500 in the cache before any would start being flushed (assuming a 3 minute
|
|
timeout)..
|
|
|
|
int SSL_CTX_sess_hits(SSL_CTX *ctx);
|
|
int SSL_CTX_sess_misses(SSL_CTX *ctx);
|
|
int SSL_CTX_sess_timeouts(SSL_CTX *ctx);
|
|
These 3 functions return statistics about the SSL_CTX. These 3 are the
|
|
number of session id reuses. hits is the number of reuses, misses are the
|
|
number of lookups that failed, and timeouts is the number of cached
|
|
entries ignored because they had timeouted.
|
|
|
|
ctx->new_session_cb is a function pointer to a function of type
|
|
int new_session_callback(SSL *ssl,SSL_SESSION *new);
|
|
This function, if set in the SSL_CTX structure is called whenever a new
|
|
SSL_SESSION is added to the cache. If the callback returns non-zero, it
|
|
means that the application will have to do a SSL_SESSION_free()
|
|
on the structure (this is
|
|
to do with the cache keeping the reference counts correct, without the
|
|
application needing to know about it.
|
|
The 'active' parameter is the current SSL session for which this connection
|
|
was created.
|
|
|
|
void SSL_CTX_sess_set_new_cb(SSL_CTX *ctx,int (*cb)());
|
|
to set the callback,
|
|
int (*cb)() SSL_CTX_sess_get_new_cb(SSL_CTX *ctx)
|
|
to get the callback.
|
|
|
|
If the 'get session' callback is set, when a session id is looked up and
|
|
it is not in the session-id cache, this callback is called. The callback is
|
|
of the form
|
|
SSL_SESSION *get_session_callback(unsigned char *sess_id,int sess_id_len,
|
|
int *copy);
|
|
|
|
The get_session_callback is intended to return null if no session id is found.
|
|
The reference count on the SSL_SESSION in incremented by the SSL library,
|
|
if copy is 1. Otherwise, the reference count is not modified.
|
|
|
|
void SSL_CTX_sess_set_get_cb(ctx,cb) sets the callback and
|
|
int (*cb)()SSL_CTX_sess_get_get_cb(ctx) returns the callback.
|
|
|
|
These callbacks are basically indended to be used by processes to
|
|
send their session-id's to other processes. I currently have not implemented
|
|
non-blocking semantics for these callbacks, it is upto the appication
|
|
to make the callbacks effiecent if they require blocking (perhaps
|
|
by 'saving' them and then 'posting them' when control returns from
|
|
the SSL_accept().
|
|
|
|
LHASH *SSL_CTX_sessions(SSL_CTX *ctx)
|
|
This returns the session cache. The lhash strucutre can be accessed for
|
|
statistics about the cache.
|
|
|
|
void lh_stats(LHASH *lh, FILE *out);
|
|
void lh_node_stats(LHASH *lh, FILE *out);
|
|
void lh_node_usage_stats(LHASH *lh, FILE *out);
|
|
|
|
can be used to print details about it's activity and current state.
|
|
You can also delve directly into the lhash structure for 14 different
|
|
counters that are kept against the structure. When I wrote the lhash library,
|
|
I was interested in gathering statistics :-).
|
|
Have a read of doc/lhash.doc in the SSLeay distribution area for more details
|
|
on the lhash library.
|
|
|
|
Now as mentioned ealier, when a SSL is created, it needs a SSL_CTX.
|
|
SSL * SSL_new(SSL_CTX *);
|
|
|
|
This stores a session. A session is secret information shared between 2
|
|
SSL contexts. It will only be created if both ends of the connection have
|
|
authenticated their peer to their satisfaction. It basically contains
|
|
the information required to use a particular secret key cipher.
|
|
|
|
To retrieve the SSL_CTX being used by a SSL,
|
|
SSL_CTX *SSL_get_SSL_CTX(SSL *s);
|
|
|
|
Now when a SSL session is established between to programs, the 'session'
|
|
information that is cached in the SSL_CTX can me manipulated by the
|
|
following functions.
|
|
int SSL_set_session(SSL *s, SSL_SESSION *session);
|
|
This will set the SSL_SESSION to use for the next SSL_connect(). If you use
|
|
this function on an already 'open' established SSL connection, 'bad things
|
|
will happen'. This function is meaning-less when used on a ssl strucutre
|
|
that is just about to be used in a SSL_accept() call since the
|
|
SSL_accept() will either create a new session or retrieve one from the
|
|
cache.
|
|
|
|
SSL_SESSION *SSL_get_session(SSL *s);
|
|
This will return the SSL_SESSION for the current SSL, NULL if there is
|
|
no session associated with the SSL structure.
|
|
|
|
The SSL sessions are kept in the SSL_CTX in a hash table, to remove a
|
|
session
|
|
void SSL_CTX_remove_session(SSL_CTX *,SSL_SESSION *c);
|
|
and to add one
|
|
int SSL_CTX_add_session(SSL_CTX *s, SSL_SESSION *c);
|
|
SSL_CTX_add_session() returns 1 if the session was already in the cache (so it
|
|
was not added).
|
|
Whenever a new session is created via SSL_connect()/SSL_accept(),
|
|
they are automatically added to the cache, depending on the session_cache_mode
|
|
settings. SSL_set_session()
|
|
does not add it to the cache. Just call SSL_CTX_add_session() if you do want the
|
|
session added. For a 'client' this would not normally be the case.
|
|
SSL_CTX_add_session() is not normally ever used, except for doing 'evil' things
|
|
which the next 2 funtions help you do.
|
|
|
|
int i2d_SSL_SESSION(SSL_SESSION *in,unsigned char **pp);
|
|
SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a,unsigned char **pp,long length);
|
|
These 2 functions are in the standard ASN1 library form and can be used to
|
|
load and save to a byte format, the SSL_SESSION structure.
|
|
With these functions, you can save and read these structures to a files or
|
|
arbitary byte string.
|
|
The PEM_write_SSL_SESSION(fp,x) and PEM_read_SSL_SESSION(fp,x,cb) will
|
|
write to a file pointer in base64 encoding.
|
|
|
|
What you can do with this, is pass session information between separate
|
|
processes. Please note, that you will probably also need to modify the
|
|
timeout information on the SSL_SESSIONs.
|
|
|
|
long SSL_get_time(SSL_SESSION *s)
|
|
will return the 'time' that the session
|
|
was loaded. The timeout is relative to this time. This information is
|
|
saved when the SSL_SESSION is converted to binarary but it is stored
|
|
in as a unix long, which is rather OS dependant, but easy to convert back.
|
|
|
|
long SSL_set_time(SSL_SESSION *s,long t) will set the above mentioned time.
|
|
The time value is just the value returned from time(3), and should really
|
|
be defined by be to be time_t.
|
|
|
|
long SSL_get_timeout(SSL_SESSION *s);
|
|
long SSL_set_timeout(SSL_SESSION *s,long t);
|
|
These 2 retrieve and set the timeout which is just a number of secconds
|
|
from the 'SSL_get_time()' value. When this time period has elapesed,
|
|
the session will no longer be in the cache (well it will actually be removed
|
|
the next time it is attempted to be retrieved, so you could 'bump'
|
|
the timeout so it remains valid).
|
|
The 'time' and 'timeout' are set on a session when it is created, not reset
|
|
each time it is reused. If you did wish to 'bump it', just after establishing
|
|
a connection, do a
|
|
SSL_set_time(ssl,time(NULL));
|
|
|
|
You can also use
|
|
SSL_CTX_set_timeout(SSL_CTX *ctx,unsigned long t) and
|
|
SSL_CTX_get_timeout(SSL_CTX *ctx) to manipulate the default timeouts for
|
|
all SSL connections created against a SSL_CTX. If you set a timeout in
|
|
an SSL_CTX, all new SSL's created will inherit the timeout. It can be over
|
|
written by the SSL_set_timeout(SSL *s,unsigned long t) function call.
|
|
If you 'set' the timeout back to 0, the system default will be used.
|
|
|
|
SSL_SESSION *SSL_SESSION_new();
|
|
void SSL_SESSION_free(SSL_SESSION *ses);
|
|
These 2 functions are used to create and dispose of SSL_SESSION functions.
|
|
You should not ever normally need to use them unless you are using
|
|
i2d_SSL_SESSION() and/or d2i_SSL_SESSION(). If you 'load' a SSL_SESSION
|
|
via d2i_SSL_SESSION(), you will need to SSL_SESSION_free() it.
|
|
Both SSL_set_session() and SSL_CTX_add_session() will 'take copies' of the
|
|
structure (via reference counts) when it is passed to them.
|
|
|
|
SSL_CTX_flush_sessions(ctx,time);
|
|
The first function will clear all sessions from the cache, which have expired
|
|
relative to 'time' (which could just be time(NULL)).
|
|
|
|
SSL_CTX_flush_sessions(ctx,0);
|
|
This is a special case that clears everything.
|
|
|
|
As a final comment, a 'session' is not enough to establish a new
|
|
connection. If a session has timed out, a certificate and private key
|
|
need to have been associated with the SSL structure.
|
|
SSL_copy_session_id(SSL *to,SSL *from); will copy not only the session
|
|
strucutre but also the private key and certificate associated with
|
|
'from'.
|
|
|
|
EXAMPLES.
|
|
|
|
So lets play at being a wierd SSL server.
|
|
|
|
/* setup a context */
|
|
ctx=SSL_CTX_new();
|
|
|
|
/* Lets load some session from binary into the cache, why one would do
|
|
* this is not toally clear, but passing between programs does make sense
|
|
* Perhaps you are using 4096 bit keys and are happy to keep them
|
|
* valid for a week, to avoid the RSA overhead of 15 seconds, I'm not toally
|
|
* sure, perhaps this is a process called from an SSL inetd and this is being
|
|
* passed to the application. */
|
|
session=d2i_SSL_SESSION(....)
|
|
SSL_CTX_add_session(ctx,session);
|
|
|
|
/* Lets even add a session from a file */
|
|
session=PEM_read_SSL_SESSION(....)
|
|
SSL_CTX_add_session(ctx,session);
|
|
|
|
/* create a new SSL structure */
|
|
ssl=SSL_new(ctx);
|
|
|
|
/* At this point we want to be able to 'create' new session if
|
|
* required, so we need a certificate and RSAkey. */
|
|
SSL_use_RSAPrivateKey_file(ssl,...)
|
|
SSL_use_certificate_file(ssl,...)
|
|
|
|
/* Now since we are a server, it make little sence to load a session against
|
|
* the ssl strucutre since a SSL_accept() will either create a new session or
|
|
* grab an existing one from the cache. */
|
|
|
|
/* grab a socket descriptor */
|
|
fd=accept(...);
|
|
|
|
/* associated it with the ssl strucutre */
|
|
SSL_set_fd(ssl,fd);
|
|
|
|
SSL_accept(ssl); /* 'do' SSL using out cert and RSA key */
|
|
|
|
/* Lets print out the session details or lets save it to a file,
|
|
* perhaps with a secret key cipher, so that we can pass it to the FBI
|
|
* when they want to decode the session :-). While we have RSA
|
|
* this does not matter much but when I do SSLv3, this will allow a mechanism
|
|
* for the server/client to record the information needed to decode
|
|
* the traffic that went over the wire, even when using Diffie-Hellman */
|
|
PEM_write_SSL_SESSION(SSL_get_session(ssl),stdout,....)
|
|
|
|
Lets 'connect' back to the caller using the same session id.
|
|
|
|
ssl2=SSL_new(ctx);
|
|
fd2=connect(them);
|
|
SSL_set_fd(ssl2,fd2);
|
|
SSL_set_session(ssl2,SSL_get_session(ssl));
|
|
SSL_connect(ssl2);
|
|
|
|
/* what the hell, lets accept no more connections using this session */
|
|
SSL_CTX_remove_session(SSL_get_SSL_CTX(ssl),SSL_get_session(ssl));
|
|
|
|
/* we could have just as easily used ssl2 since they both are using the
|
|
* same session.
|
|
* You will note that both ssl and ssl2 are still using the session, and
|
|
* the SSL_SESSION structure will be free()ed when both ssl and ssl2
|
|
* finish using the session. Also note that you could continue to initiate
|
|
* connections using this session by doing SSL_get_session(ssl) to get the
|
|
* existing session, but SSL_accept() will not be able to find it to
|
|
* use for incoming connections.
|
|
* Of corse, the session will timeout at the far end and it will no
|
|
* longer be accepted after a while. The time and timeout are ignored except
|
|
* by SSL_accept(). */
|
|
|
|
/* Since we have had our server running for 10 weeks, and memory is getting
|
|
* short, perhaps we should clear the session cache to remove those
|
|
* 100000 session entries that have expired. Some may consider this
|
|
* a memory leak :-) */
|
|
|
|
SSL_CTX_flush_sessions(ctx,time(NULL));
|
|
|
|
/* Ok, after a bit more time we wish to flush all sessions from the cache
|
|
* so that all new connections will be authenticated and incure the
|
|
* public key operation overhead */
|
|
|
|
SSL_CTX_flush_sessions(ctx,0);
|
|
|
|
/* As a final note, to copy everything to do with a SSL, use */
|
|
SSL_copy_session_id(SSL *to,SSL *from);
|
|
/* as this also copies the certificate and RSA key so new session can
|
|
* be established using the same details */
|
|
|