/* ==================================================================== * Copyright (c) 1998-2001 Ralf S. Engelschall. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by * Ralf S. Engelschall for use in the * mod_ssl project (http://www.modssl.org/)." * * 4. The names "mod_ssl" must not be used to endorse or promote * products derived from this software without prior written * permission. For written permission, please contact * rse@engelschall.com. * * 5. Products derived from this software may not be called "mod_ssl" * nor may "mod_ssl" appear in their names without prior * written permission of Ralf S. Engelschall. * * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RALF S. ENGELSCHALL OR * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * * ==================================================================== */ #include #include #include "lock.h" #include "config.h" char *sess_file; void ssl_scache_dbm_expire(void) { DBM *dbm; datum dbmkey; datum dbmval; time_t tExpiresAt; int nElements = 0; int nDeleted = 0; int bDelete; datum *keylist; int keyidx; int i; static time_t tLast = 0; time_t tNow; /* ** make sure the expiration for still not-accessed session ** cache entries is done only from time to time **/ tNow = time(NULL); if (tNow < tLast + SSL_SESSION_CACHE_TIMEOUT) return; tLast = tNow; /* ** Here we have to be very carefully: Not all DBM libraries are ** smart enough to allow one to iterate over the elements and at the ** same time delete expired ones. Some of them get totally crazy ** while others have no problems. So we have to do it the slower but ** more safe way: we first iterate over all elements and remember ** those which have to be expired. Then in a second pass we delete ** all those expired elements. Additionally we reopen the DBM file ** to be really safe in state. */ #define KEYMAX 1024 ssl_mutex_on(); for (;;) { /* ** allocate memory for the key array ** ** NOTE:- here i am not worried if i am running low on memory it does not cause the system to fail so i shall try in my next attempt ** :-) go happy next time better luck :-) */ if ((keylist = (datum *) malloc(sizeof(dbmkey)*KEYMAX)) == NULL) { break; } /* pass 1: scan DBM database */ keyidx = 0; if ((dbm = dbm_open(sess_file, O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { (void) fprintf(stderr,"Cannot open SSLSessionCache DBM file `%s' for scanning",sess_file); break; } dbmkey = dbm_firstkey(dbm); while (dbmkey.dptr != NULL) { nElements++; bDelete = 0; dbmval = dbm_fetch(dbm, dbmkey); if(dbmval.dsize <= sizeof(time_t) || dbmval.dptr == NULL) bDelete = 1; else { memcpy(&tExpiresAt, dbmval.dptr, sizeof(time_t)); if(tExpiresAt <= tNow) bDelete = 1; } if (bDelete) { if ((keylist[keyidx].dptr = (char *) malloc(dbmkey.dsize)) != NULL) { memcpy(keylist[keyidx].dptr, dbmkey.dptr, dbmkey.dsize); keylist[keyidx].dsize = dbmkey.dsize; keyidx++; if(keyidx == KEYMAX) break; } } dbmkey = dbm_nextkey(dbm); } dbm_close(dbm); /* pass 2: delete expired elements */ if ((dbm = dbm_open(sess_file, O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { (void) fprintf(stderr, "Cannot re-open SSLSessionCache DBM file `%s' for expiring",sess_file); break; } for (i = 0; i < keyidx; i++) { dbm_delete(dbm, keylist[i]); nDeleted++; } dbm_close(dbm); if (keyidx < KEYMAX) break; } ssl_mutex_off(); #if 0 (void) fprintf(stderr, "Inter-Process Session Cache (DBM) Expiry: old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted); #endif return; } void ssl_scache_remove(unsigned char *id, int idlen) { DBM *dbm; datum dbmkey; /* create DBM key and values */ dbmkey.dptr = (char *)id; dbmkey.dsize = idlen; /* and delete it from the DBM file */ ssl_mutex_on(); if ((dbm = dbm_open(sess_file, O_RDWR, SSL_DBM_FILE_MODE)) == NULL) { (void) fprintf(stderr, "Cannot open SSLSessionCache DBM file `%s' for writing (delete)",sess_file); ssl_mutex_off(); return; } dbm_delete(dbm, dbmkey); dbm_close(dbm); ssl_mutex_off(); return; } SSL_SESSION *ssl_scache_retrieve(unsigned char *id, int idlen) { DBM *dbm; datum dbmkey; datum dbmval; SSL_SESSION *sess = NULL; unsigned char *ucpData; int nData; time_t expiry; time_t now; /* allow the regular expiring to occur */ ssl_scache_dbm_expire(); /* create DBM key and values */ dbmkey.dptr = (char *)id; dbmkey.dsize = idlen; /* and fetch it from the DBM file */ ssl_mutex_on(); if ((dbm = dbm_open(sess_file, O_RDONLY, SSL_DBM_FILE_MODE)) == NULL) { (void) fprintf(stderr,"Cannot open SSLSessionCache DBM file `%s' for reading (fetch)",sess_file); ssl_mutex_off(); return NULL; } dbmval = dbm_fetch(dbm, dbmkey); dbm_close(dbm); ssl_mutex_off(); /* immediately return if not found */ if (dbmval.dptr == NULL || dbmval.dsize <= sizeof(time_t)) return NULL; /* parse resulting data */ nData = dbmval.dsize-sizeof(time_t); ucpData = (unsigned char *)malloc(nData); if (ucpData == NULL) return NULL; memcpy(ucpData, (char *)dbmval.dptr+sizeof(time_t), nData); memcpy(&expiry, dbmval.dptr, sizeof(time_t)); /* make sure the stuff is still not expired */ now = time(NULL); if (expiry <= now) { ssl_scache_remove(id, idlen); return NULL; } /* unstreamed SSL_SESSION */ sess = d2i_SSL_SESSION(NULL, &ucpData, nData); return sess; } unsigned int ssl_scache_store(unsigned char *id, int idlen,time_t expiry,SSL_SESSION *sess) { datum dbmkey; datum dbmval; unsigned char ucaData[1024*10]; int nData; unsigned char *ucp; DBM *dbm; /* streamline session of data */ ucp = ucaData; nData = i2d_SSL_SESSION(sess, &ucp); /* be careful : try not to store too much bytes in a DBM file */ if((idlen + nData) >= 950) return 0; /* create DBM key */ dbmkey.dptr = (char *)id; dbmkey.dsize = idlen; /* create DBM value */ dbmval.dsize = sizeof(time_t) + nData; dbmval.dptr = (char *) malloc(dbmval.dsize); if(dbmval.dptr == NULL) return 0; memcpy((char *)dbmval.dptr,&expiry,sizeof(time_t)); memcpy((char *)dbmval.dptr+sizeof(time_t),ucaData,nData); /* store it to the DBM file */ ssl_mutex_on(); if((dbm = dbm_open(sess_file,O_RDWR|O_CREAT,SSL_DBM_FILE_MODE)) == NULL) { (void) fprintf(stderr, "cannot open SSL Session Cache DBM file %s for writing\n", SESS_FILE); ssl_mutex_off(); free(dbmval.dptr); return 0; } if(dbm_store(dbm,dbmkey,dbmval,DBM_INSERT)<0) { (void) fprintf(stderr, "cannot store SSL Session to DBM file %s \n",SESS_FILE); ssl_mutex_off(); free(dbmval.dptr); return 0; } dbm_close(dbm); ssl_mutex_off(); /* free the temperary buffers */ free(dbmval.dptr); /* allow the regular expiring to occur */ ssl_scache_dbm_expire(); return 1; } /* ** borrowed from apache ** temporarily -- gkgodava@archie.uccs.edu */ char *SSL_SESSION_id2sz(unsigned char *id, int idlen) { /* ** SSL_MAX_SSL_SESSION_ID_LENGTH is defined in ssl.h to be of 32 */ static char str[(SSL_MAX_SSL_SESSION_ID_LENGTH+1)*2]; char *cp; int n; cp = str; for (n = 0; n < idlen && n < SSL_MAX_SSL_SESSION_ID_LENGTH; n++) { snprintf(cp, sizeof(str)-(cp-str), "%02X", id[n]); cp += 2; } *cp =(char) NULL; return str; } /* ** This callback function is executed by OPENSSL whenever a new SSL_sessiion is added to the internal ** session_cache. we use this hook to spread the SSL_session also to the inter processes disk-cache ** inorder to make share it with other processes **/ int ssl_callback_NewSessionCacheEntry(SSL *ssl, SSL_SESSION *pNew) { long t; unsigned int flag; t = SSL_SESSION_CACHE_TIMEOUT; SSL_set_timeout(pNew,t); /* ** store the SSL_SESSION in the inter process cache with the same expire time, so that it expires automatically there too. */ t = (SSL_get_time(pNew) + SSL_SESSION_CACHE_TIMEOUT); flag = ssl_scache_store(pNew->session_id, pNew->session_id_length, t, pNew); /* ** log the information */ #if 0 (void) fprintf(stderr,"\n\n new session created for ssl context value id is %s \n",SSL_SESSION_id2sz(pNew->session_id,pNew->session_id_length)); #endif return 0; } /* ** This callback function is executed by OPENSSL whenever a new SSL_sessiion is looked up in the internal OpenSSL ** session_cache and it is not found. we use this hook to lookup the SSL_session in to the inter processes disk-cache ** where it might be stored by other processes **/ SSL_SESSION *ssl_callback_GetSessionCacheEntry(SSL *ssl, unsigned char *id, int idlen, int *pCopy) { SSL_SESSION *pSession; pSession = ssl_scache_retrieve(id,idlen); /* ** log the information */ #if 0 if(pSession != NULL) (void) fprintf(stderr,"\n\n old session of ssl context value id is %s is being lookfor is found \n",SSL_SESSION_id2sz(id,idlen)); else (void) fprintf(stderr,"\n\n old session of ssl context value id is %s is being lookfor is not found \n",SSL_SESSION_id2sz(id,idlen)); #endif *pCopy=0; return pSession; } /* ** This callback function is executed by OPENSSL whenever a new SSL_sessiion is removed from internal OpenSSL ** session_cache. we use this hook to remove the SSL_session from inter processes disk-cache **/ void ssl_callback_DelSessionCacheEntry(SSL_CTX *ctx, SSL_SESSION *pSession) { /* ** log the information */ ssl_scache_remove(pSession->session_id, pSession->session_id_length); (void) fprintf(stderr,"\n\n session dead removing it ssl value id is %s is being being deleted \n", SSL_SESSION_id2sz(pSession->session_id,pSession->session_id_length)); return; }