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