1 /*                      _             _
2 **  _ __ ___   ___   __| |    ___ ___| |  mod_ssl
3 ** | '_ ` _ \ / _ \ / _` |   / __/ __| |  Apache Interface to OpenSSL
4 ** | | | | | | (_) | (_| |   \__ \__ \ |  www.modssl.org
5 ** |_| |_| |_|\___/ \__,_|___|___/___/_|  ftp.modssl.org
6 **                      |_____|
7 **  ssl_scache_shmht.c
8 **  Session Cache via Shared Memory (Hash Table Variant)
9 */
10 
11 /* ====================================================================
12  * Copyright (c) 1998-2003 Ralf S. Engelschall. All rights reserved.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  *
18  * 1. Redistributions of source code must retain the above copyright
19  *    notice, this list of conditions and the following disclaimer.
20  *
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following
23  *    disclaimer in the documentation and/or other materials
24  *    provided with the distribution.
25  *
26  * 3. All advertising materials mentioning features or use of this
27  *    software must display the following acknowledgment:
28  *    "This product includes software developed by
29  *     Ralf S. Engelschall <rse@engelschall.com> for use in the
30  *     mod_ssl project (http://www.modssl.org/)."
31  *
32  * 4. The names "mod_ssl" must not be used to endorse or promote
33  *    products derived from this software without prior written
34  *    permission. For written permission, please contact
35  *    rse@engelschall.com.
36  *
37  * 5. Products derived from this software may not be called "mod_ssl"
38  *    nor may "mod_ssl" appear in their names without prior
39  *    written permission of Ralf S. Engelschall.
40  *
41  * 6. Redistributions of any form whatsoever must retain the following
42  *    acknowledgment:
43  *    "This product includes software developed by
44  *     Ralf S. Engelschall <rse@engelschall.com> for use in the
45  *     mod_ssl project (http://www.modssl.org/)."
46  *
47  * THIS SOFTWARE IS PROVIDED BY RALF S. ENGELSCHALL ``AS IS'' AND ANY
48  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
49  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
50  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL RALF S. ENGELSCHALL OR
51  * HIS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
52  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
53  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
55  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
56  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
58  * OF THE POSSIBILITY OF SUCH DAMAGE.
59  * ====================================================================
60  */
61 
62 #include "mod_ssl.h"
63 
64 /*
65  *  Wrapper functions for table library which resemble malloc(3) & Co
66  *  but use the variants from the MM shared memory library.
67  */
68 
ssl_scache_shmht_malloc(size_t size)69 static void *ssl_scache_shmht_malloc(size_t size)
70 {
71     SSLModConfigRec *mc = myModConfig();
72     return ap_mm_malloc(mc->pSessionCacheDataMM, size);
73 }
74 
ssl_scache_shmht_calloc(size_t number,size_t size)75 static void *ssl_scache_shmht_calloc(size_t number, size_t size)
76 {
77     SSLModConfigRec *mc = myModConfig();
78     return ap_mm_calloc(mc->pSessionCacheDataMM, number, size);
79 }
80 
ssl_scache_shmht_realloc(void * ptr,size_t size)81 static void *ssl_scache_shmht_realloc(void *ptr, size_t size)
82 {
83     SSLModConfigRec *mc = myModConfig();
84     return ap_mm_realloc(mc->pSessionCacheDataMM, ptr, size);
85 }
86 
ssl_scache_shmht_free(void * ptr)87 static void ssl_scache_shmht_free(void *ptr)
88 {
89     SSLModConfigRec *mc = myModConfig();
90     ap_mm_free(mc->pSessionCacheDataMM, ptr);
91     return;
92 }
93 
94 /*
95  * Now the actual session cache implementation
96  * based on a hash table inside a shared memory segment.
97  */
98 
ssl_scache_shmht_init(server_rec * s,pool * p)99 void ssl_scache_shmht_init(server_rec *s, pool *p)
100 {
101     SSLModConfigRec *mc = myModConfig();
102     AP_MM *mm;
103     table_t *ta;
104     int ta_errno;
105     int avail;
106     int n;
107 
108     /*
109      * Create shared memory segment
110      */
111     if (mc->szSessionCacheDataFile == NULL) {
112         ssl_log(s, SSL_LOG_ERROR, "SSLSessionCache required");
113         ssl_die();
114     }
115     if ((mm = ap_mm_create(mc->nSessionCacheDataSize,
116                            mc->szSessionCacheDataFile)) == NULL) {
117         ssl_log(s, SSL_LOG_ERROR,
118                 "Cannot allocate shared memory: %s", ap_mm_error());
119         ssl_die();
120     }
121     mc->pSessionCacheDataMM = mm;
122 
123     /*
124      * Make sure the childs have access to the underlaying files
125      */
126     ap_mm_permission(mm, SSL_MM_FILE_MODE, ap_user_id, -1);
127 
128     /*
129      * Create hash table in shared memory segment
130      */
131     avail = ap_mm_available(mm);
132     n = (avail/2) / 1024;
133     n = n < 10 ? 10 : n;
134     if ((ta = table_alloc(n, &ta_errno,
135                           ssl_scache_shmht_malloc,
136                           ssl_scache_shmht_calloc,
137                           ssl_scache_shmht_realloc,
138                           ssl_scache_shmht_free    )) == NULL) {
139         ssl_log(s, SSL_LOG_ERROR,
140                 "Cannot allocate hash table in shared memory: %s",
141                 table_strerror(ta_errno));
142         ssl_die();
143     }
144     table_attr(ta, TABLE_FLAG_AUTO_ADJUST|TABLE_FLAG_ADJUST_DOWN);
145     table_set_data_alignment(ta, sizeof(char *));
146     table_clear(ta);
147     mc->tSessionCacheDataTable = ta;
148 
149     /*
150      * Log the done work
151      */
152     ssl_log(s, SSL_LOG_INFO,
153             "Init: Created hash-table (%d buckets) "
154             "in shared memory (%d bytes) for SSL session cache", n, avail);
155     return;
156 }
157 
ssl_scache_shmht_kill(server_rec * s)158 void ssl_scache_shmht_kill(server_rec *s)
159 {
160     SSLModConfigRec *mc = myModConfig();
161 
162     if (mc->pSessionCacheDataMM != NULL) {
163         ap_mm_destroy(mc->pSessionCacheDataMM);
164         mc->pSessionCacheDataMM = NULL;
165     }
166     return;
167 }
168 
ssl_scache_shmht_store(server_rec * s,UCHAR * id,int idlen,time_t expiry,SSL_SESSION * sess)169 BOOL ssl_scache_shmht_store(server_rec *s, UCHAR *id, int idlen, time_t expiry, SSL_SESSION *sess)
170 {
171     SSLModConfigRec *mc = myModConfig();
172     void *vp;
173     UCHAR ucaData[SSL_SESSION_MAX_DER];
174     int nData;
175     UCHAR *ucp;
176 
177     /* streamline session data */
178     if ((nData = i2d_SSL_SESSION(sess, NULL)) > sizeof(ucaData))
179         return FALSE;
180     ucp = ucaData;
181     i2d_SSL_SESSION(sess, &ucp);
182 
183     ssl_mutex_on(s);
184     if (table_insert_kd(mc->tSessionCacheDataTable,
185                         id, idlen, NULL, sizeof(time_t)+nData,
186                         NULL, &vp, 1) != TABLE_ERROR_NONE) {
187         ssl_mutex_off(s);
188         return FALSE;
189     }
190     memcpy(vp, &expiry, sizeof(time_t));
191     memcpy((char *)vp+sizeof(time_t), ucaData, nData);
192     ssl_mutex_off(s);
193 
194     /* allow the regular expiring to occur */
195     ssl_scache_shmht_expire(s);
196 
197     return TRUE;
198 }
199 
ssl_scache_shmht_retrieve(server_rec * s,UCHAR * id,int idlen)200 SSL_SESSION *ssl_scache_shmht_retrieve(server_rec *s, UCHAR *id, int idlen)
201 {
202     SSLModConfigRec *mc = myModConfig();
203     void *vp;
204     SSL_SESSION *sess = NULL;
205     UCHAR *ucpData;
206     int nData;
207     time_t expiry;
208     time_t now;
209     int n;
210 
211     /* allow the regular expiring to occur */
212     ssl_scache_shmht_expire(s);
213 
214     /* lookup key in table */
215     ssl_mutex_on(s);
216     if (table_retrieve(mc->tSessionCacheDataTable,
217                        id, idlen, &vp, &n) != TABLE_ERROR_NONE) {
218         ssl_mutex_off(s);
219         return NULL;
220     }
221 
222     /* copy over the information to the SCI */
223     nData = n-sizeof(time_t);
224     ucpData = (UCHAR *)malloc(nData);
225     if (ucpData == NULL) {
226         ssl_mutex_off(s);
227         return NULL;
228     }
229     memcpy(&expiry, vp, sizeof(time_t));
230     memcpy(ucpData, (char *)vp+sizeof(time_t), nData);
231     ssl_mutex_off(s);
232 
233     /* make sure the stuff is still not expired */
234     now = time(NULL);
235     if (expiry <= now) {
236         ssl_scache_shmht_remove(s, id, idlen);
237         return NULL;
238     }
239 
240     /* unstreamed SSL_SESSION */
241     sess = d2i_SSL_SESSION(NULL, (void *)(&ucpData), nData);
242 
243     return sess;
244 }
245 
ssl_scache_shmht_remove(server_rec * s,UCHAR * id,int idlen)246 void ssl_scache_shmht_remove(server_rec *s, UCHAR *id, int idlen)
247 {
248     SSLModConfigRec *mc = myModConfig();
249 
250     /* remove value under key in table */
251     ssl_mutex_on(s);
252     table_delete(mc->tSessionCacheDataTable, id, idlen, NULL, NULL);
253     ssl_mutex_off(s);
254     return;
255 }
256 
ssl_scache_shmht_expire(server_rec * s)257 void ssl_scache_shmht_expire(server_rec *s)
258 {
259     SSLModConfigRec *mc = myModConfig();
260     SSLSrvConfigRec *sc = mySrvConfig(s);
261     static time_t tLast = 0;
262     table_linear_t iterator;
263     time_t tExpiresAt;
264     void *vpKey;
265     void *vpKeyThis;
266     void *vpData;
267     int nKey;
268     int nKeyThis;
269     int nData;
270     int nElements = 0;
271     int nDeleted = 0;
272     int bDelete;
273     int rc;
274     time_t tNow;
275 
276     /*
277      * make sure the expiration for still not-accessed session
278      * cache entries is done only from time to time
279      */
280     tNow = time(NULL);
281     if (tNow < tLast+sc->nSessionCacheTimeout)
282         return;
283     tLast = tNow;
284 
285     ssl_mutex_on(s);
286     if (table_first_r(mc->tSessionCacheDataTable, &iterator,
287                       &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
288         do {
289             bDelete = FALSE;
290             nElements++;
291             if (nData < sizeof(time_t) || vpData == NULL)
292                 bDelete = TRUE;
293             else {
294                 memcpy(&tExpiresAt, vpData, sizeof(time_t));
295                 if (tExpiresAt <= tNow)
296                    bDelete = TRUE;
297             }
298             vpKeyThis = vpKey;
299             nKeyThis  = nKey;
300             rc = table_next_r(mc->tSessionCacheDataTable, &iterator,
301                               &vpKey, &nKey, &vpData, &nData);
302             if (bDelete) {
303                 table_delete(mc->tSessionCacheDataTable,
304                              vpKeyThis, nKeyThis, NULL, NULL);
305                 nDeleted++;
306             }
307         } while (rc == TABLE_ERROR_NONE);
308     }
309     ssl_mutex_off(s);
310     ssl_log(s, SSL_LOG_TRACE, "Inter-Process Session Cache (SHMHT) Expiry: "
311             "old: %d, new: %d, removed: %d", nElements, nElements-nDeleted, nDeleted);
312     return;
313 }
314 
ssl_scache_shmht_status(server_rec * s,pool * p,void (* func)(char *,void *),void * arg)315 void ssl_scache_shmht_status(server_rec *s, pool *p, void (*func)(char *, void *), void *arg)
316 {
317     SSLModConfigRec *mc = myModConfig();
318     void *vpKey;
319     void *vpData;
320     int nKey;
321     int nData;
322     int nElem;
323     int nSize;
324     int nAverage;
325 
326     nElem = 0;
327     nSize = 0;
328     ssl_mutex_on(s);
329     if (table_first(mc->tSessionCacheDataTable,
330                     &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE) {
331         do {
332             if (vpKey == NULL || vpData == NULL)
333                 continue;
334             nElem += 1;
335             nSize += nData;
336         } while (table_next(mc->tSessionCacheDataTable,
337                             &vpKey, &nKey, &vpData, &nData) == TABLE_ERROR_NONE);
338     }
339     ssl_mutex_off(s);
340     if (nSize > 0 && nElem > 0)
341         nAverage = nSize / nElem;
342     else
343         nAverage = 0;
344     func(ap_psprintf(p, "cache type: <b>SHMHT</b>, maximum size: <b>%d</b> bytes<br>", mc->nSessionCacheDataSize), arg);
345     func(ap_psprintf(p, "current sessions: <b>%d</b>, current size: <b>%d</b> bytes<br>", nElem, nSize), arg);
346     func(ap_psprintf(p, "average session size: <b>%d</b> bytes<br>", nAverage), arg);
347     return;
348 }
349 
350