1 /*
2 * ProFTPD: mod_tls_memcache -- a module which provides shared SSL session
3 * and OCSP response caches using memcached servers
4 * Copyright (c) 2011-2020 TJ Saunders
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
19 *
20 * As a special exemption, TJ Saunders and other respective copyright holders
21 * give permission to link this program with OpenSSL, and distribute the
22 * resulting executable, without including the source code for OpenSSL in the
23 * source distribution.
24 *
25 * This is mod_tls_memcache, contrib software for proftpd 1.3.x and above.
26 * For more information contact TJ Saunders <tj@castaglia.org>.
27 */
28
29 #include "conf.h"
30 #include "privs.h"
31 #include "mod_tls.h"
32 #include "json.h"
33 #include "hanson-tpl.h"
34
35 #define MOD_TLS_MEMCACHE_VERSION "mod_tls_memcache/0.2"
36
37 /* Make sure the version of proftpd is as necessary. */
38 #if PROFTPD_VERSION_NUMBER < 0x0001030602
39 # error "ProFTPD 1.3.6rc2 or later required"
40 #endif
41
42 module tls_memcache_module;
43
44 /* For communicating with memcached servers for shared data. */
45 static pr_memcache_t *sess_mcache = NULL;
46
47 /* Assume a maximum SSL session (serialized) length of 10K. Note that this
48 * is different from the SSL_MAX_SSL_SESSION_ID_LENGTH provided by OpenSSL.
49 * There is no limit imposed on the length of the ASN1 description of the
50 * SSL session data.
51 */
52 #ifndef TLS_MAX_SSL_SESSION_SIZE
53 # define TLS_MAX_SSL_SESSION_SIZE 1024 * 10
54 #endif
55
56 static unsigned long sess_cache_opts = 0UL;
57 #define SESS_CACHE_OPT_USE_JSON 0x0001
58
59 struct sesscache_entry {
60 uint32_t expires;
61 unsigned int sess_datalen;
62 unsigned char sess_data[TLS_MAX_SSL_SESSION_SIZE];
63 };
64
65 /* These are tpl format strings */
66 #define SESS_CACHE_TPL_KEY_FMT "s"
67 #define SESS_CACHE_TPL_VALUE_FMT "S(uic#)"
68
69 /* These are the JSON format field names */
70 #define SESS_CACHE_JSON_KEY_EXPIRES "expires"
71 #define SESS_CACHE_JSON_KEY_DATA "data"
72 #define SESS_CACHE_JSON_KEY_DATA_LENGTH "data_len"
73
74 /* The difference between sesscache_entry and sesscache_large_entry is that the
75 * buffers in the latter are dynamically allocated from the heap, not
76 * stored in memcached (given that memcached has limits on how much it can
77 * store). The large_entry struct is used for storing sessions which don't
78 * fit into memcached; this also means that these large entries are NOT shared
79 * across processes.
80 */
81 struct sesscache_large_entry {
82 time_t expires;
83 unsigned int sess_id_len;
84 const unsigned char *sess_id;
85 unsigned int sess_datalen;
86 const unsigned char *sess_data;
87 };
88
89 /* These stats are stored in memcached as well, so that the status command can
90 * be run on _any_ proftpd in the cluster.
91 */
92 struct sesscache_key {
93 const char *key;
94 const char *desc;
95 };
96
97 static struct sesscache_key sesscache_keys[] = {
98 { "cache_hits", "Cache lifetime hits" },
99 { "cache_misses", "Cache lifetime misses" },
100 { "cache_stores", "Cache lifetime sessions stored" },
101 { "cache_deletes", "Cache lifetime sessions deleted" },
102 { "cache_errors", "Cache lifetime errors handling sessions in cache" },
103 { "cache_exceeds", "Cache lifetime sessions exceeding max entry size" },
104 { "cache_max_sess_len", "Largest session exceeding max entry size" },
105 { NULL, NULL }
106 };
107
108 /* Indexes into the sesscache_keys array */
109 #define SESSCACHE_KEY_HITS 0
110 #define SESSCACHE_KEY_MISSES 1
111 #define SESSCACHE_KEY_STORES 2
112 #define SESSCACHE_KEY_DELETES 3
113 #define SESSCACHE_KEY_ERRORS 4
114 #define SESSCACHE_KEY_EXCEEDS 5
115 #define SESSCACHE_KEY_MAX_LEN 6
116
117 static tls_sess_cache_t sess_cache;
118 static array_header *sesscache_sess_list = NULL;
119
120 #if defined(PR_USE_OPENSSL_OCSP)
121 static pr_memcache_t *ocsp_mcache = NULL;
122
123 /* Assume a maximum OCSP response (serialized) length of 4K. */
124 # ifndef TLS_MAX_OCSP_RESPONSE_SIZE
125 # define TLS_MAX_OCSP_RESPONSE_SIZE 1024 * 4
126 # endif
127
128 struct ocspcache_entry {
129 time_t age;
130 unsigned int fingerprint_len;
131 char fingerprint[EVP_MAX_MD_SIZE];
132 unsigned int resp_derlen;
133 unsigned char resp_der[TLS_MAX_OCSP_RESPONSE_SIZE];
134 };
135
136 /* These are the JSON format field names */
137 #define OCSP_CACHE_JSON_KEY_AGE "expires"
138 #define OCSP_CACHE_JSON_KEY_RESPONSE "response"
139 #define OCSP_CACHE_JSON_KEY_RESPONSE_LENGTH "response_len"
140
141 /* The difference between ocspcache_entry and ocspcache_large_entry is that the
142 * buffers in the latter are dynamically allocated from the heap, not
143 * stored in memcached (given that memcached has limits on how much it can
144 * store). The large_entry struct is used for storing responses which don't
145 * fit into memcached; this also means that these large entries are NOT shared
146 * across processes.
147 */
148 struct ocspcache_large_entry {
149 time_t age;
150 unsigned int fingerprint_len;
151 char *fingerprint;
152 unsigned int resp_derlen;
153 unsigned char *resp_der;
154 };
155
156 /* These stats are stored in memcached as well, so that the status command can
157 * be run on _any_ proftpd in the cluster.
158 */
159 struct ocspcache_key {
160 const char *key;
161 const char *desc;
162 };
163
164 static struct ocspcache_key ocspcache_keys[] = {
165 { "cache_hits", "Cache lifetime hits" },
166 { "cache_misses", "Cache lifetime misses" },
167 { "cache_stores", "Cache lifetime responses stored" },
168 { "cache_deletes", "Cache lifetime responses deleted" },
169 { "cache_errors", "Cache lifetime errors handling responses in cache" },
170 { "cache_exceeds", "Cache lifetime responses exceeding max entry size" },
171 { "cache_max_resp_len", "Largest response exceeding max entry size" },
172 { NULL, NULL }
173 };
174
175 /* Indexes into the ocspcache_keys array */
176 #define OCSPCACHE_KEY_HITS 0
177 #define OCSPCACHE_KEY_MISSES 1
178 #define OCSPCACHE_KEY_STORES 2
179 #define OCSPCACHE_KEY_DELETES 3
180 #define OCSPCACHE_KEY_ERRORS 4
181 #define OCSPCACHE_KEY_EXCEEDS 5
182 #define OCSPCACHE_KEY_MAX_LEN 6
183
184 static tls_ocsp_cache_t ocsp_cache;
185 static array_header *ocspcache_resp_list = NULL;
186 #endif
187
188 static const char *trace_channel = "tls.memcache";
189
190 static int sess_cache_close(tls_sess_cache_t *);
191 #if defined(PR_USE_OPENSSL_OCSP)
192 static int ocsp_cache_close(tls_ocsp_cache_t *);
193 #endif /* PR_USE_OPENSSL_OCSP */
194 static int tls_mcache_sess_init(void);
195
mcache_get_errors(void)196 static const char *mcache_get_errors(void) {
197 unsigned int count = 0;
198 unsigned long error_code;
199 BIO *bio = NULL;
200 char *data = NULL;
201 long datalen;
202 const char *error_data = NULL, *str = "(unknown)";
203 int error_flags = 0;
204
205 /* Use ERR_print_errors() and a memory BIO to build up a string with
206 * all of the error messages from the error queue.
207 */
208
209 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
210 if (error_code) {
211 bio = BIO_new(BIO_s_mem());
212 }
213
214 while (error_code) {
215 pr_signals_handle();
216
217 if (error_flags & ERR_TXT_STRING) {
218 BIO_printf(bio, "\n (%u) %s [%s]", ++count,
219 ERR_error_string(error_code, NULL), error_data);
220
221 } else {
222 BIO_printf(bio, "\n (%u) %s", ++count,
223 ERR_error_string(error_code, NULL));
224 }
225
226 error_data = NULL;
227 error_flags = 0;
228 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
229 }
230
231 datalen = BIO_get_mem_data(bio, &data);
232 if (data) {
233 data[datalen] = '\0';
234 str = pstrdup(permanent_pool, data);
235 }
236
237 if (bio != NULL) {
238 BIO_free(bio);
239 }
240
241 return str;
242 }
243
244 /* SSL session cache implementation callbacks.
245 */
246
247 /* Functions for marshalling key/value data to/from memcached. */
248
sess_cache_get_tpl_key(pool * p,const unsigned char * sess_id,unsigned int sess_id_len,void ** key,size_t * keysz)249 static int sess_cache_get_tpl_key(pool *p, const unsigned char *sess_id,
250 unsigned int sess_id_len, void **key, size_t *keysz) {
251 char *sess_id_hex;
252 void *data = NULL;
253 size_t datasz = 0;
254 int res;
255
256 sess_id_hex = pr_str_bin2hex(p, sess_id, sess_id_len, 0);
257
258 res = tpl_jot(TPL_MEM, &data, &datasz, SESS_CACHE_TPL_KEY_FMT, &sess_id_hex);
259 if (res < 0) {
260 return -1;
261 }
262
263 *keysz = datasz;
264 *key = palloc(p, datasz);
265 memcpy(*key, data, datasz);
266 free(data);
267
268 return 0;
269 }
270
sess_cache_get_json_key(pool * p,const unsigned char * sess_id,unsigned int sess_id_len,void ** key,size_t * keysz)271 static int sess_cache_get_json_key(pool *p, const unsigned char *sess_id,
272 unsigned int sess_id_len, void **key, size_t *keysz) {
273 char *sess_id_hex, *json_text;
274 pr_json_object_t *json;
275
276 sess_id_hex = pr_str_bin2hex(p, sess_id, sess_id_len, 0);
277 json = pr_json_object_alloc(p);
278 (void) pr_json_object_set_string(p, json, "id", sess_id_hex);
279
280 json_text = pr_json_object_to_text(p, json, "");
281
282 /* Include the terminating NUL in the key. */
283 *keysz = strlen(json_text) + 1;
284 *key = pstrndup(p, json_text, *keysz - 1);
285 (void) pr_json_object_free(json);
286
287 return 0;
288 }
289
sess_cache_get_key(pool * p,const unsigned char * sess_id,unsigned int sess_id_len,void ** key,size_t * keysz)290 static int sess_cache_get_key(pool *p, const unsigned char *sess_id,
291 unsigned int sess_id_len, void **key, size_t *keysz) {
292 int res;
293 const char *key_type = "unknown";
294
295 if (sess_cache_opts & SESS_CACHE_OPT_USE_JSON) {
296 key_type = "JSON";
297 res = sess_cache_get_json_key(p, sess_id, sess_id_len, key, keysz);
298
299 } else {
300 key_type = "TPL";
301 res = sess_cache_get_tpl_key(p, sess_id, sess_id_len, key, keysz);
302 }
303
304 if (res < 0) {
305 pr_trace_msg(trace_channel, 3,
306 "error constructing cache %s lookup key for session ID (%lu bytes)",
307 key_type, (unsigned long) keysz);
308 return -1;
309 }
310
311 return 0;
312 }
313
sess_cache_entry_decode_tpl(pool * p,void * value,size_t valuesz,struct sesscache_entry * se)314 static int sess_cache_entry_decode_tpl(pool *p, void *value, size_t valuesz,
315 struct sesscache_entry *se) {
316 int res;
317 tpl_node *tn;
318
319 tn = tpl_map(SESS_CACHE_TPL_VALUE_FMT, se, TLS_MAX_SSL_SESSION_SIZE);
320 if (tn == NULL) {
321 tls_log(MOD_TLS_MEMCACHE_VERSION
322 ": error allocating tpl_map for format '%s'", SESS_CACHE_TPL_VALUE_FMT);
323 errno = ENOMEM;
324 return -1;
325 }
326
327 res = tpl_load(tn, TPL_MEM, value, valuesz);
328 if (res < 0) {
329 pr_trace_msg(trace_channel, 3, "%s",
330 "error loading TPL memcache session data");
331 tpl_free(tn);
332 errno = EINVAL;
333 return -1;
334 }
335
336 res = tpl_unpack(tn, 0);
337 if (res < 0) {
338 pr_trace_msg(trace_channel, 3, "%s",
339 "error unpacking TPL memcache session data");
340 tpl_free(tn);
341 errno = EINVAL;
342 return -1;
343 }
344
345 tpl_free(tn);
346
347 return 0;
348 }
349
entry_get_json_number(pool * p,pr_json_object_t * json,const char * key,double * val,const char * text)350 static int entry_get_json_number(pool *p, pr_json_object_t *json,
351 const char *key, double *val, const char *text) {
352 if (pr_json_object_get_number(p, json, key, val) < 0) {
353 if (errno == EEXIST) {
354 pr_trace_msg(trace_channel, 3,
355 "ignoring non-number '%s' JSON field in '%s'", key, text);
356
357 } else {
358 tls_log(MOD_TLS_MEMCACHE_VERSION
359 ": missing required '%s' JSON field in '%s'", key, text);
360 }
361
362 (void) pr_json_object_free(json);
363 errno = EINVAL;
364 return -1;
365 }
366
367 return 0;
368 }
369
entry_get_json_string(pool * p,pr_json_object_t * json,const char * key,char ** val,const char * text)370 static int entry_get_json_string(pool *p, pr_json_object_t *json,
371 const char *key, char **val, const char *text) {
372 if (pr_json_object_get_string(p, json, key, val) < 0) {
373 if (errno == EEXIST) {
374 pr_trace_msg(trace_channel, 3,
375 "ignoring non-string '%s' JSON field in '%s'", key, text);
376
377 } else {
378 tls_log(MOD_TLS_MEMCACHE_VERSION
379 ": missing required '%s' JSON field in '%s'", key, text);
380 }
381
382 (void) pr_json_object_free(json);
383 errno = EINVAL;
384 return -1;
385 }
386
387 return 0;
388 }
389
sess_cache_entry_decode_json(pool * p,void * value,size_t valuesz,struct sesscache_entry * se)390 static int sess_cache_entry_decode_json(pool *p, void *value, size_t valuesz,
391 struct sesscache_entry *se) {
392 int res;
393 pr_json_object_t *json;
394 const char *key;
395 char *entry, *text;
396 double number;
397
398 entry = value;
399 if (pr_json_text_validate(p, entry) == FALSE) {
400 tls_log(MOD_TLS_MEMCACHE_VERSION
401 ": unable to decode invalid JSON session cache entry: '%s'", entry);
402 errno = EINVAL;
403 return -1;
404 }
405
406 json = pr_json_object_from_text(p, entry);
407
408 key = SESS_CACHE_JSON_KEY_EXPIRES;
409 res = entry_get_json_number(p, json, key, &number, entry);
410 if (res < 0) {
411 return -1;
412 }
413 se->expires = (uint32_t) number;
414
415 key = SESS_CACHE_JSON_KEY_DATA;
416 res = entry_get_json_string(p, json, key, &text, entry);
417 if (res == 0) {
418 int have_padding = FALSE;
419 char *base64_data;
420 size_t base64_datalen;
421 unsigned char *data;
422
423 base64_data = text;
424 base64_datalen = strlen(base64_data);
425
426 /* Due to Base64's padding, we need to detect if the last block was
427 * padded with zeros; we do this by looking for '=' characters at the
428 * end of the text being decoded. If we see these characters, then we
429 * will "trim" off any trailing zero values in the decoded data, on the
430 * ASSUMPTION that they are the auto-added padding bytes.
431 */
432 if (base64_data[base64_datalen-1] == '=') {
433 have_padding = TRUE;
434 }
435
436 data = se->sess_data;
437 res = EVP_DecodeBlock(data, (unsigned char *) base64_data,
438 (int) base64_datalen);
439 if (res <= 0) {
440 /* Base64-decoding error. */
441 pr_trace_msg(trace_channel, 5,
442 "error base64-decoding session data in '%s', rejecting", entry);
443 (void) pr_json_object_free(json);
444 errno = EINVAL;
445 return -1;
446 }
447
448 if (have_padding) {
449 /* Assume that only one or two zero bytes of padding were added. */
450 if (data[res-1] == '\0') {
451 res -= 1;
452
453 if (data[res-1] == '\0') {
454 res -= 1;
455 }
456 }
457 }
458 } else {
459 return -1;
460 }
461
462 key = SESS_CACHE_JSON_KEY_DATA_LENGTH;
463 res = entry_get_json_number(p, json, key, &number, entry);
464 if (res < 0) {
465 return -1;
466 }
467 se->sess_datalen = (unsigned int) number;
468
469 (void) pr_json_object_free(json);
470 return 0;
471 }
472
sess_cache_mcache_entry_get(pool * p,const unsigned char * sess_id,unsigned int sess_id_len,struct sesscache_entry * se)473 static int sess_cache_mcache_entry_get(pool *p, const unsigned char *sess_id,
474 unsigned int sess_id_len, struct sesscache_entry *se) {
475 int res;
476 void *key = NULL, *value = NULL;
477 size_t keysz = 0, valuesz = 0;
478 uint32_t flags = 0;
479
480 res = sess_cache_get_key(p, sess_id, sess_id_len, &key, &keysz);
481 if (res < 0) {
482 pr_trace_msg(trace_channel, 1,
483 "unable to get cache entry: error getting cache key: %s",
484 strerror(errno));
485
486 return -1;
487 }
488
489 value = pr_memcache_kget(sess_mcache, &tls_memcache_module,
490 (const char *) key, keysz, &valuesz, &flags);
491 if (value == NULL) {
492 pr_trace_msg(trace_channel, 3,
493 "no matching memcache entry found for session ID (%lu bytes)",
494 (unsigned long) keysz);
495 errno = ENOENT;
496 return -1;
497 }
498
499 /* Decode the cached session data. */
500 if (sess_cache_opts & SESS_CACHE_OPT_USE_JSON) {
501 res = sess_cache_entry_decode_json(p, value, valuesz, se);
502
503 } else {
504 res = sess_cache_entry_decode_tpl(p, value, valuesz, se);
505 }
506
507 if (res == 0) {
508 time_t now;
509
510 /* Check for expired cache entries. */
511 time(&now);
512
513 if (se->expires <= now) {
514 pr_trace_msg(trace_channel, 4,
515 "ignoring expired cached session data (expires %lu <= now %lu)",
516 (unsigned long) se->expires, (unsigned long) now);
517 errno = EPERM;
518 return -1;
519 }
520
521 pr_trace_msg(trace_channel, 9, "retrieved session data from cache using %s",
522 sess_cache_opts & SESS_CACHE_OPT_USE_JSON ? "JSON" : "TPL");
523 }
524
525 return 0;
526 }
527
sess_cache_mcache_entry_delete(pool * p,const unsigned char * sess_id,unsigned int sess_id_len)528 static int sess_cache_mcache_entry_delete(pool *p, const unsigned char *sess_id,
529 unsigned int sess_id_len) {
530 int res;
531 void *key = NULL;
532 size_t keysz = 0;
533
534 res = sess_cache_get_key(p, sess_id, sess_id_len, &key, &keysz);
535 if (res < 0) {
536 pr_trace_msg(trace_channel, 1,
537 "unable to remove cache entry: error getting cache key: %s",
538 strerror(errno));
539
540 return -1;
541 }
542
543 res = pr_memcache_kremove(sess_mcache, &tls_memcache_module,
544 (const char *) key, keysz, 0);
545 if (res < 0) {
546 int xerrno = errno;
547
548 pr_trace_msg(trace_channel, 2,
549 "unable to remove memcache entry for session ID (%lu bytes): %s",
550 (unsigned long) keysz, strerror(xerrno));
551
552 errno = xerrno;
553 return -1;
554 }
555
556 return 0;
557 }
558
sess_cache_entry_encode_tpl(pool * p,void ** value,size_t * valuesz,struct sesscache_entry * se)559 static int sess_cache_entry_encode_tpl(pool *p, void **value, size_t *valuesz,
560 struct sesscache_entry *se) {
561 int res;
562 tpl_node *tn;
563 void *ptr = NULL;
564
565 tn = tpl_map(SESS_CACHE_TPL_VALUE_FMT, se, TLS_MAX_SSL_SESSION_SIZE);
566 if (tn == NULL) {
567 pr_trace_msg(trace_channel, 1,
568 "error allocating tpl_map for format '%s'", SESS_CACHE_TPL_VALUE_FMT);
569 return -1;
570 }
571
572 res = tpl_pack(tn, 0);
573 if (res < 0) {
574 pr_trace_msg(trace_channel, 1, "%s",
575 "error marshalling TPL memcache session data");
576 return -1;
577 }
578
579 res = tpl_dump(tn, TPL_MEM, &ptr, valuesz);
580 if (res < 0) {
581 pr_trace_msg(trace_channel, 1, "%s",
582 "error dumping marshalled TPL memcache session data");
583 return -1;
584 }
585
586 /* Duplicate the value using the given pool, so that we can free up the
587 * memory allocated by tpl_dump().
588 */
589 *value = palloc(p, *valuesz);
590 memcpy(*value, ptr, *valuesz);
591
592 tpl_free(tn);
593 free(ptr);
594
595 return 0;
596 }
597
sess_cache_entry_encode_json(pool * p,void ** value,size_t * valuesz,struct sesscache_entry * se)598 static int sess_cache_entry_encode_json(pool *p, void **value, size_t *valuesz,
599 struct sesscache_entry *se) {
600 pr_json_object_t *json;
601 pool *tmp_pool;
602 char *base64_data = NULL, *json_text;
603
604 json = pr_json_object_alloc(p);
605 (void) pr_json_object_set_number(p, json, SESS_CACHE_JSON_KEY_EXPIRES,
606 (double) se->expires);
607
608 /* Base64-encode the session data. Note that EVP_EncodeBlock does
609 * NUL-terminate the encoded data.
610 */
611 tmp_pool = make_sub_pool(p);
612 base64_data = pcalloc(tmp_pool, se->sess_datalen * 2);
613
614 EVP_EncodeBlock((unsigned char *) base64_data, se->sess_data,
615 (int) se->sess_datalen);
616 (void) pr_json_object_set_string(p, json, SESS_CACHE_JSON_KEY_DATA,
617 base64_data);
618 (void) pr_json_object_set_number(p, json, SESS_CACHE_JSON_KEY_DATA_LENGTH,
619 (double) se->sess_datalen);
620
621 destroy_pool(tmp_pool);
622
623 json_text = pr_json_object_to_text(p, json, "");
624 (void) pr_json_object_free(json);
625
626 if (json_text == NULL) {
627 errno = ENOMEM;
628 return -1;
629 }
630
631 /* Safety check */
632 if (pr_json_text_validate(p, json_text) == FALSE) {
633 pr_trace_msg(trace_channel, 1, "invalid JSON emitted: '%s'", json_text);
634 errno = EINVAL;
635 return -1;
636 }
637
638 /* Include the terminating NUL in the value. */
639 *valuesz = strlen(json_text) + 1;
640 *value = pstrndup(p, json_text, *valuesz - 1);
641
642 return 0;
643 }
644
sess_cache_mcache_entry_set(pool * p,const unsigned char * sess_id,unsigned int sess_id_len,struct sesscache_entry * se)645 static int sess_cache_mcache_entry_set(pool *p, const unsigned char *sess_id,
646 unsigned int sess_id_len, struct sesscache_entry *se) {
647 int res, xerrno = 0;
648 void *key = NULL, *value = NULL;
649 size_t keysz = 0, valuesz = 0;
650 uint32_t flags = 0;
651
652 /* Encode the SSL session data. */
653 if (sess_cache_opts & SESS_CACHE_OPT_USE_JSON) {
654 res = sess_cache_entry_encode_json(p, &value, &valuesz, se);
655
656 } else {
657 res = sess_cache_entry_encode_tpl(p, &value, &valuesz, se);
658 }
659
660 if (res < 0) {
661 xerrno = errno;
662
663 pr_trace_msg(trace_channel, 4, "error %s encoding session data: %s",
664 sess_cache_opts & SESS_CACHE_OPT_USE_JSON ? "JSON" : "TPL",
665 strerror(xerrno));
666
667 errno = xerrno;
668 return -1;
669 }
670
671 res = sess_cache_get_key(p, sess_id, sess_id_len, &key, &keysz);
672 xerrno = errno;
673 if (res < 0) {
674 pr_trace_msg(trace_channel, 1,
675 "unable to set cache entry: error getting cache key: %s",
676 strerror(xerrno));
677
678 errno = xerrno;
679 return -1;
680 }
681
682 res = pr_memcache_kset(sess_mcache, &tls_memcache_module, (const char *) key,
683 keysz, value, valuesz, se->expires, flags);
684 xerrno = errno;
685
686 if (res < 0) {
687 pr_trace_msg(trace_channel, 2,
688 "unable to add memcache entry for session ID (%lu bytes): %s",
689 (unsigned long) keysz, strerror(xerrno));
690
691 errno = xerrno;
692 return -1;
693 }
694
695 pr_trace_msg(trace_channel, 9, "stored session data in cache using %s",
696 sess_cache_opts & SESS_CACHE_OPT_USE_JSON ? "JSON" : "TPL");
697 return 0;
698 }
699
sess_cache_open(tls_sess_cache_t * cache,char * info,long timeout)700 static int sess_cache_open(tls_sess_cache_t *cache, char *info, long timeout) {
701 config_rec *c;
702
703 cache->cache_pool = make_sub_pool(permanent_pool);
704 pr_pool_tag(cache->cache_pool, MOD_TLS_MEMCACHE_VERSION);
705
706 pr_trace_msg(trace_channel, 9, "opening memcache cache %p (info '%s')",
707 cache, info ? info : "(none)");
708
709 /* This is a little messy, but necessary. The mod_memcache module does
710 * not set the configured list of memcached servers until a connection
711 * arrives. But mod_tls opens its session cache prior to that, when the
712 * server is starting up. Thus we need to set the configured list of
713 * memcached servers ourselves.
714 */
715 c = find_config(main_server->conf, CONF_PARAM, "MemcacheEngine", FALSE);
716 if (c != NULL) {
717 int engine;
718
719 engine = *((int *) c->argv[0]);
720 if (engine == FALSE) {
721 pr_trace_msg(trace_channel, 2, "%s",
722 "memcache support disabled (see MemcacheEngine directive)");
723 errno = EPERM;
724 return -1;
725 }
726 }
727
728 sess_mcache = pr_memcache_conn_new(cache->cache_pool,
729 &tls_memcache_module, 0, 0);
730 if (sess_mcache == NULL) {
731 pr_trace_msg(trace_channel, 2,
732 "error connecting to memcached: %s", strerror(errno));
733 errno = EPERM;
734 return -1;
735 }
736
737 /* Configure a namespace prefix for our memcached keys. */
738 if (pr_memcache_conn_set_namespace(sess_mcache, &tls_memcache_module,
739 "mod_tls_memcache.sessions.") < 0) {
740 pr_trace_msg(trace_channel, 2,
741 "error setting memcache namespace prefix: %s", strerror(errno));
742 }
743
744 cache->cache_timeout = timeout;
745
746 if (info != NULL &&
747 strcasecmp(info, "/json") == 0) {
748 sess_cache_opts |= SESS_CACHE_OPT_USE_JSON;
749 }
750
751 return 0;
752 }
753
sess_cache_close(tls_sess_cache_t * cache)754 static int sess_cache_close(tls_sess_cache_t *cache) {
755 pr_trace_msg(trace_channel, 9, "closing memcache session cache %p", cache);
756
757 if (cache != NULL &&
758 cache->cache_pool != NULL) {
759
760 /* We do NOT destroy the cache_pool here or close the mcache connection;
761 * both were created at daemon startup, and should live as long as
762 * the daemon lives.
763 */
764
765 if (sesscache_sess_list != NULL) {
766 register unsigned int i;
767 struct sesscache_large_entry *entries;
768
769 entries = sesscache_sess_list->elts;
770 for (i = 0; i < sesscache_sess_list->nelts; i++) {
771 struct sesscache_large_entry *entry;
772
773 entry = &(entries[i]);
774 if (entry->expires > 0) {
775 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
776 }
777 }
778
779 clear_array(sesscache_sess_list);
780 }
781 }
782
783 return 0;
784 }
785
sess_cache_add_large_sess(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len,time_t expires,SSL_SESSION * sess,int sess_len)786 static int sess_cache_add_large_sess(tls_sess_cache_t *cache,
787 const unsigned char *sess_id, unsigned int sess_id_len, time_t expires,
788 SSL_SESSION *sess, int sess_len) {
789 struct sesscache_large_entry *entry = NULL;
790
791 if (sess_len > TLS_MAX_SSL_SESSION_SIZE) {
792 const char *exceeds_key = sesscache_keys[SESSCACHE_KEY_EXCEEDS].key,
793 *max_len_key = sesscache_keys[SESSCACHE_KEY_MAX_LEN].key;
794 void *value = NULL;
795 size_t valuesz = 0;
796
797 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, exceeds_key,
798 1, NULL) < 0) {
799 pr_trace_msg(trace_channel, 2,
800 "error incrementing '%s' value: %s", exceeds_key, strerror(errno));
801 }
802
803 /* XXX Yes, this is subject to race conditions; other proftpd servers
804 * might also be modifying this value in memcached. Oh well.
805 */
806
807 value = pr_memcache_get(sess_mcache, &tls_memcache_module, max_len_key,
808 &valuesz, NULL);
809 if (value != NULL) {
810 uint64_t max_len;
811
812 memcpy(&max_len, value, valuesz);
813 if ((uint64_t) sess_len > max_len) {
814 if (pr_memcache_set(sess_mcache, &tls_memcache_module, max_len_key,
815 &max_len, sizeof(max_len), 0, 0) < 0) {
816 pr_trace_msg(trace_channel, 2,
817 "error setting '%s' value: %s", max_len_key, strerror(errno));
818 }
819 }
820
821 } else {
822 pr_trace_msg(trace_channel, 2,
823 "error getting '%s' value: %s", max_len_key, strerror(errno));
824 }
825 }
826
827 if (sesscache_sess_list != NULL) {
828 register unsigned int i;
829 struct sesscache_large_entry *entries;
830 time_t now;
831 int ok = FALSE;
832
833 /* Look for any expired sessions in the list to overwrite/reuse. */
834 entries = sesscache_sess_list->elts;
835 time(&now);
836 for (i = 0; i < sesscache_sess_list->nelts; i++) {
837 entry = &(entries[i]);
838
839 if (entry->expires <= now) {
840 /* This entry has expired; clear and reuse its slot. */
841 entry->expires = 0;
842 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
843
844 ok = TRUE;
845 break;
846 }
847 }
848
849 if (!ok) {
850 /* We didn't find an open slot in the list. Need to add one. */
851 entry = push_array(sesscache_sess_list);
852 }
853
854 } else {
855 sesscache_sess_list = make_array(cache->cache_pool, 1,
856 sizeof(struct sesscache_large_entry));
857 entry = push_array(sesscache_sess_list);
858 }
859
860 entry->expires = expires;
861 entry->sess_id_len = sess_id_len;
862 entry->sess_id = palloc(cache->cache_pool, sess_id_len);
863 memcpy((unsigned char *) entry->sess_id, sess_id, sess_id_len);
864 entry->sess_datalen = sess_len;
865 entry->sess_data = palloc(cache->cache_pool, sess_len);
866 i2d_SSL_SESSION(sess, (unsigned char **) &(entry->sess_data));
867
868 return 0;
869 }
870
sess_cache_add(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len,time_t expires,SSL_SESSION * sess)871 static int sess_cache_add(tls_sess_cache_t *cache, const unsigned char *sess_id,
872 unsigned int sess_id_len, time_t expires, SSL_SESSION *sess) {
873 struct sesscache_entry entry;
874 int sess_len;
875 unsigned char *ptr;
876 time_t now;
877
878 time(&now);
879 pr_trace_msg(trace_channel, 9,
880 "adding session to memcache cache %p (expires = %lu, now = %lu)", cache,
881 (unsigned long) expires, (unsigned long) now);
882
883 /* First we need to find out how much space is needed for the serialized
884 * session data. There is no known maximum size for SSL session data;
885 * this module is currently designed to allow only up to a certain size.
886 */
887 sess_len = i2d_SSL_SESSION(sess, NULL);
888 if (sess_len > TLS_MAX_SSL_SESSION_SIZE) {
889 pr_trace_msg(trace_channel, 2,
890 "length of serialized SSL session data (%d) exceeds maximum size (%u), "
891 "unable to add to shared memcache, adding to list", sess_len,
892 TLS_MAX_SSL_SESSION_SIZE);
893
894 /* Instead of rejecting the add here, we add the session to a "large
895 * session" list. Thus the large session would still be cached per process
896 * and will not be lost.
897 */
898
899 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
900 sess, sess_len);
901 }
902
903 entry.expires = expires;
904 entry.sess_datalen = sess_len;
905 ptr = entry.sess_data;
906 i2d_SSL_SESSION(sess, &ptr);
907
908 if (sess_cache_mcache_entry_set(cache->cache_pool, sess_id, sess_id_len,
909 &entry) < 0) {
910 pr_trace_msg(trace_channel, 2,
911 "error adding session to memcache: %s", strerror(errno));
912
913 /* Add this session to the "large session" list instead as a fallback. */
914 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
915 sess, sess_len);
916
917 } else {
918 const char *key = sesscache_keys[SESSCACHE_KEY_STORES].key;
919
920 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
921 pr_trace_msg(trace_channel, 2,
922 "error incrementing '%s' value: %s", key, strerror(errno));
923 }
924 }
925
926 return 0;
927 }
928
sess_cache_get(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len)929 static SSL_SESSION *sess_cache_get(tls_sess_cache_t *cache,
930 const unsigned char *sess_id, unsigned int sess_id_len) {
931 struct sesscache_entry entry;
932 time_t now;
933 SSL_SESSION *sess = NULL;
934
935 pr_trace_msg(trace_channel, 9, "getting session from memcache cache %p",
936 cache);
937
938 /* Look for the requested session in the "large session" list first. */
939 if (sesscache_sess_list != NULL) {
940 register unsigned int i;
941 struct sesscache_large_entry *entries;
942
943 entries = sesscache_sess_list->elts;
944 for (i = 0; i < sesscache_sess_list->nelts; i++) {
945 struct sesscache_large_entry *large_entry;
946
947 large_entry = &(entries[i]);
948 if (large_entry->expires > 0 &&
949 large_entry->sess_id_len == sess_id_len &&
950 memcmp(large_entry->sess_id, sess_id,
951 large_entry->sess_id_len) == 0) {
952
953 now = time(NULL);
954 if (large_entry->expires > now) {
955 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
956
957 ptr = large_entry->sess_data;
958 sess = d2i_SSL_SESSION(NULL, &ptr, large_entry->sess_datalen);
959 if (sess == NULL) {
960 pr_trace_msg(trace_channel, 2,
961 "error retrieving session from cache: %s", mcache_get_errors());
962
963 } else {
964 break;
965 }
966 }
967 }
968 }
969 }
970
971 if (sess) {
972 return sess;
973 }
974
975 if (sess_cache_mcache_entry_get(cache->cache_pool, sess_id, sess_id_len,
976 &entry) < 0) {
977 return NULL;
978 }
979
980 now = time(NULL);
981 if (entry.expires > now) {
982 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
983
984 ptr = entry.sess_data;
985 sess = d2i_SSL_SESSION(NULL, &ptr, entry.sess_datalen);
986 if (sess != NULL) {
987 const char *key = sesscache_keys[SESSCACHE_KEY_HITS].key;
988
989 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, key, 1,
990 NULL) < 0) {
991 pr_trace_msg(trace_channel, 2,
992 "error incrementing '%s' value: %s", key, strerror(errno));
993 }
994
995 } else {
996 const char *key = sesscache_keys[SESSCACHE_KEY_ERRORS].key;
997
998 pr_trace_msg(trace_channel, 2,
999 "error retrieving session from cache: %s", mcache_get_errors());
1000
1001 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, key, 1,
1002 NULL) < 0) {
1003 pr_trace_msg(trace_channel, 2,
1004 "error incrementing '%s' value: %s", key, strerror(errno));
1005 }
1006 }
1007 }
1008
1009 if (sess == NULL) {
1010 const char *key = sesscache_keys[SESSCACHE_KEY_MISSES].key;
1011
1012 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1013 pr_trace_msg(trace_channel, 2,
1014 "error incrementing '%s' value: %s", key, strerror(errno));
1015 }
1016
1017 errno = ENOENT;
1018 }
1019
1020 return sess;
1021 }
1022
sess_cache_delete(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len)1023 static int sess_cache_delete(tls_sess_cache_t *cache,
1024 const unsigned char *sess_id, unsigned int sess_id_len) {
1025 const char *key = sesscache_keys[SESSCACHE_KEY_DELETES].key;
1026 int res;
1027
1028 pr_trace_msg(trace_channel, 9, "removing session from memcache cache %p",
1029 cache);
1030
1031 /* Look for the requested session in the "large session" list first. */
1032 if (sesscache_sess_list != NULL) {
1033 register unsigned int i;
1034 struct sesscache_large_entry *entries;
1035
1036 entries = sesscache_sess_list->elts;
1037 for (i = 0; i < sesscache_sess_list->nelts; i++) {
1038 struct sesscache_large_entry *entry;
1039
1040 entry = &(entries[i]);
1041 if (entry->sess_id_len == sess_id_len &&
1042 memcmp(entry->sess_id, sess_id, entry->sess_id_len) == 0) {
1043
1044 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1045 entry->expires = 0;
1046 return 0;
1047 }
1048 }
1049 }
1050
1051 res = sess_cache_mcache_entry_delete(cache->cache_pool, sess_id, sess_id_len);
1052 if (res < 0) {
1053 return -1;
1054 }
1055
1056 /* Don't forget to update the stats. */
1057
1058 if (pr_memcache_incr(sess_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1059 pr_trace_msg(trace_channel, 2,
1060 "error incrementing '%s' value: %s", key, strerror(errno));
1061 }
1062
1063 return res;
1064 }
1065
sess_cache_clear(tls_sess_cache_t * cache)1066 static int sess_cache_clear(tls_sess_cache_t *cache) {
1067 register unsigned int i;
1068 int res = 0;
1069
1070 if (sess_mcache == NULL) {
1071 pr_trace_msg(trace_channel, 9, "missing required memcached connection");
1072 errno = EINVAL;
1073 return -1;
1074 }
1075
1076 pr_trace_msg(trace_channel, 9, "clearing memcache session cache %p", cache);
1077
1078 if (sesscache_sess_list != NULL) {
1079 struct sesscache_large_entry *entries;
1080
1081 entries = sesscache_sess_list->elts;
1082 for (i = 0; i < sesscache_sess_list->nelts; i++) {
1083 struct sesscache_large_entry *entry;
1084
1085 entry = &(entries[i]);
1086 entry->expires = 0;
1087 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1088 }
1089 }
1090
1091 /* XXX iterate through keys, kremoving any "mod_tls_memcache" prefixed keys */
1092
1093 return res;
1094 }
1095
sess_cache_remove(tls_sess_cache_t * cache)1096 static int sess_cache_remove(tls_sess_cache_t *cache) {
1097 int res;
1098
1099 pr_trace_msg(trace_channel, 9, "removing memcache session cache %p", cache);
1100
1101 res = sess_cache_clear(cache);
1102 /* XXX close memcache conn */
1103
1104 return res;
1105 }
1106
sess_cache_status(tls_sess_cache_t * cache,void (* statusf)(void *,const char *,...),void * arg,int flags)1107 static int sess_cache_status(tls_sess_cache_t *cache,
1108 void (*statusf)(void *, const char *, ...), void *arg, int flags) {
1109 register unsigned int i;
1110 pool *tmp_pool;
1111
1112 pr_trace_msg(trace_channel, 9, "checking memcache session cache %p", cache);
1113
1114 tmp_pool = make_sub_pool(permanent_pool);
1115
1116 statusf(arg, "%s", "Memcache SSL session cache provided by "
1117 MOD_TLS_MEMCACHE_VERSION);
1118 statusf(arg, "%s", "");
1119 statusf(arg, "Memcache servers: ");
1120
1121 for (i = 0; sesscache_keys[i].key != NULL; i++) {
1122 const char *key, *desc;
1123 void *value = NULL;
1124 size_t valuesz = 0;
1125 uint32_t stat_flags = 0;
1126
1127 key = sesscache_keys[i].key;
1128 desc = sesscache_keys[i].desc;
1129
1130 value = pr_memcache_get(sess_mcache, &tls_memcache_module, key, &valuesz,
1131 &stat_flags);
1132 if (value != NULL) {
1133 uint64_t num = 0;
1134 memcpy(&num, value, valuesz);
1135 statusf(arg, "%s: %lu", desc, (unsigned long) num);
1136 }
1137 }
1138
1139 /* XXX run stats on memcached servers? */
1140
1141 #if 0
1142 if (flags & TLS_SESS_CACHE_STATUS_FL_SHOW_SESSIONS) {
1143 statusf(arg, "%s", "");
1144 statusf(arg, "%s", "Cached sessions:");
1145
1146 /* XXX Get keys, looking for our namespace prefix, dump each one */
1147
1148 /* We _could_ use SSL_SESSION_print(), which is what the sess_id
1149 * command-line tool does. The problem is that SSL_SESSION_print() shows
1150 * too much (particularly, it shows the master secret). And
1151 * SSL_SESSION_print() does not support a flags argument to use for
1152 * specifying which bits of the session we want to print.
1153 *
1154 * Instead, we get to do the more dangerous (compatibility-wise) approach
1155 * of rolling our own printing function.
1156 */
1157
1158 for (i = 0; i < 0; i++) {
1159 struct sesscache_entry *entry;
1160
1161 pr_signals_handle();
1162
1163 /* XXX Get entries */
1164 if (entry->expires > 0) {
1165 SSL_SESSION *sess;
1166 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
1167 time_t ts;
1168
1169 ptr = entry->sess_data;
1170 sess = d2i_SSL_SESSION(NULL, &ptr, entry->sess_datalen);
1171 if (sess == NULL) {
1172 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_MEMCACHE_VERSION
1173 ": error retrieving session from cache: %s", mcache_get_errors());
1174 continue;
1175 }
1176
1177 statusf(arg, "%s", " -----BEGIN SSL SESSION PARAMETERS-----");
1178
1179 /* XXX Directly accessing these fields cannot be a Good Thing. */
1180 if (sess->session_id_length > 0) {
1181 char *sess_id_str;
1182
1183 sess_id_str = pr_str2hex(tmp_pool, sess->session_id,
1184 sess->session_id_length, PR_STR_FL_HEX_USE_UC);
1185
1186 statusf(arg, " Session ID: %s", sess_id_str);
1187 }
1188
1189 if (sess->sid_ctx_length > 0) {
1190 char *sid_ctx_str;
1191
1192 sid_ctx_str = pr_str2hex(tmp_pool, sess->sid_ctx,
1193 sess->sid_ctx_length, PR_STR_FL_HEX_USE_UC);
1194
1195 statusf(arg, " Session ID Context: %s", sid_ctx_str);
1196 }
1197
1198 switch (sess->ssl_version) {
1199 case SSL3_VERSION:
1200 statusf(arg, " Protocol: %s", "SSLv3");
1201 break;
1202
1203 case TLS1_VERSION:
1204 statusf(arg, " Protocol: %s", "TLSv1");
1205 break;
1206
1207 #if defined(TLS1_1_VERSION)
1208 case TLS1_1_VERSION:
1209 statusf(arg, " Protocol: %s", "TLSv1.1");
1210 break;
1211 #endif /* TLS1_1_VERSION */
1212
1213 #if defined(TLS1_2_VERSION)
1214 case TLS1_2_VERSION:
1215 statusf(arg, " Protocol: %s", "TLSv1.2");
1216 break;
1217 #endif /* TLS1_2_VERSION */
1218
1219 #if defined(TLS1_3_VERSION)
1220 case TLS1_3_VERSION:
1221 statusf(arg, " Protocol: %s", "TLSv1.3");
1222 break;
1223 #endif /* TLS1_3_VERSION */
1224
1225 default:
1226 statusf(arg, " Protocol: %s", "unknown");
1227 }
1228
1229 ts = SSL_SESSION_get_time(sess);
1230 statusf(arg, " Started: %s", pr_strtime3(tmp_pool, ts, FALSE));
1231 ts = entry->expires;
1232 statusf(arg, " Expires: %s (%u secs)",
1233 pr_strtime3(tmp_pool, ts, FALSE), SSL_SESSION_get_timeout(sess));
1234
1235 SSL_SESSION_free(sess);
1236 statusf(arg, "%s", " -----END SSL SESSION PARAMETERS-----");
1237 statusf(arg, "%s", "");
1238 }
1239 }
1240 }
1241 #endif
1242
1243 destroy_pool(tmp_pool);
1244 return 0;
1245 }
1246
1247 #if defined(PR_USE_OPENSSL_OCSP)
1248 /* OCSP response cache implementation callbacks.
1249 */
1250
1251 /* Functions for marshalling key/value data to/from memcached. */
1252
ocsp_cache_get_json_key(pool * p,const char * fingerprint,void ** key,size_t * keysz)1253 static int ocsp_cache_get_json_key(pool *p, const char *fingerprint,
1254 void **key, size_t *keysz) {
1255 pr_json_object_t *json;
1256 char *json_text;
1257
1258 json = pr_json_object_alloc(p);
1259 (void) pr_json_object_set_string(p, json, "fingerprint", fingerprint);
1260
1261 json_text = pr_json_object_to_text(p, json, "");
1262 (void) pr_json_object_free(json);
1263
1264 /* Include the terminating NUL in the key. */
1265 *keysz = strlen(json_text) + 1;
1266 *key = pstrndup(p, json_text, *keysz - 1);
1267
1268 return 0;
1269 }
1270
ocsp_cache_get_key(pool * p,const char * fingerprint,void ** key,size_t * keysz)1271 static int ocsp_cache_get_key(pool *p, const char *fingerprint, void **key,
1272 size_t *keysz) {
1273 int res;
1274
1275 res = ocsp_cache_get_json_key(p, fingerprint, key, keysz);
1276 if (res < 0) {
1277 pr_trace_msg(trace_channel, 3,
1278 "error constructing ocsp cache JSON lookup key for fingerprint '%s'",
1279 fingerprint);
1280 return -1;
1281 }
1282
1283 return 0;
1284 }
1285
ocsp_cache_entry_decode_json(pool * p,void * value,size_t valuesz,struct ocspcache_entry * oe)1286 static int ocsp_cache_entry_decode_json(pool *p, void *value, size_t valuesz,
1287 struct ocspcache_entry *oe) {
1288 int res;
1289 pr_json_object_t *json;
1290 const char *key;
1291 char *entry, *text;
1292 double number;
1293
1294 entry = value;
1295 if (pr_json_text_validate(p, entry) == FALSE) {
1296 tls_log(MOD_TLS_MEMCACHE_VERSION
1297 ": unable to decode invalid JSON ocsp cache entry: '%s'", entry);
1298 errno = EINVAL;
1299 return -1;
1300 }
1301
1302 json = pr_json_object_from_text(p, entry);
1303
1304 key = OCSP_CACHE_JSON_KEY_AGE;
1305 res = entry_get_json_number(p, json, key, &number, entry);
1306 if (res < 0) {
1307 return -1;
1308 }
1309 oe->age = (uint32_t) number;
1310
1311 key = OCSP_CACHE_JSON_KEY_RESPONSE;
1312 res = entry_get_json_string(p, json, key, &text, entry);
1313 if (res == 0) {
1314 int have_padding = FALSE;
1315 char *base64_data;
1316 size_t base64_datalen;
1317 unsigned char *data;
1318
1319 base64_data = text;
1320 base64_datalen = strlen(base64_data);
1321
1322 /* Due to Base64's padding, we need to detect if the last block was
1323 * padded with zeros; we do this by looking for '=' characters at the
1324 * end of the text being decoded. If we see these characters, then we
1325 * will "trim" off any trailing zero values in the decoded data, on the
1326 * ASSUMPTION that they are the auto-added padding bytes.
1327 */
1328 if (base64_data[base64_datalen-1] == '=') {
1329 have_padding = TRUE;
1330 }
1331
1332 data = oe->resp_der;
1333 res = EVP_DecodeBlock(data, (unsigned char *) base64_data,
1334 (int) base64_datalen);
1335 if (res <= 0) {
1336 /* Base64-decoding error. */
1337 pr_trace_msg(trace_channel, 5,
1338 "error base64-decoding OCSP data in '%s', rejecting", entry);
1339 pr_json_object_free(json);
1340 errno = EINVAL;
1341 return -1;
1342 }
1343
1344 if (have_padding) {
1345 /* Assume that only one or two zero bytes of padding were added. */
1346 if (data[res-1] == '\0') {
1347 res -= 1;
1348
1349 if (data[res-1] == '\0') {
1350 res -= 1;
1351 }
1352 }
1353 }
1354
1355 } else {
1356 return -1;
1357 }
1358
1359 key = OCSP_CACHE_JSON_KEY_RESPONSE_LENGTH;
1360 res = entry_get_json_number(p, json, key, &number, entry);
1361 if (res < 0) {
1362 return -1;
1363 }
1364 oe->resp_derlen = (unsigned int) number;
1365
1366 (void) pr_json_object_free(json);
1367 return 0;
1368 }
1369
ocsp_cache_mcache_entry_get(pool * p,const char * fingerprint,struct ocspcache_entry * oe)1370 static int ocsp_cache_mcache_entry_get(pool *p, const char *fingerprint,
1371 struct ocspcache_entry *oe) {
1372 int res;
1373 void *key = NULL, *value = NULL;
1374 size_t keysz = 0, valuesz = 0;
1375 uint32_t flags = 0;
1376
1377 res = ocsp_cache_get_key(p, fingerprint, &key, &keysz);
1378 if (res < 0) {
1379 pr_trace_msg(trace_channel, 1,
1380 "unable to get ocsp cache entry: error getting cache key: %s",
1381 strerror(errno));
1382
1383 return -1;
1384 }
1385
1386 value = pr_memcache_kget(ocsp_mcache, &tls_memcache_module,
1387 (const char *) key, keysz, &valuesz, &flags);
1388 if (value == NULL) {
1389 pr_trace_msg(trace_channel, 3,
1390 "no matching memcache entry found for fingerprint '%s'", fingerprint);
1391 errno = ENOENT;
1392 return -1;
1393 }
1394
1395 /* Decode the cached response data. */
1396 res = ocsp_cache_entry_decode_json(p, value, valuesz, oe);
1397 if (res == 0) {
1398 pr_trace_msg(trace_channel, 9,
1399 "retrieved response data from cache using JSON");
1400 }
1401
1402 return 0;
1403 }
1404
ocsp_cache_mcache_entry_delete(pool * p,const char * fingerprint)1405 static int ocsp_cache_mcache_entry_delete(pool *p, const char *fingerprint) {
1406 int res;
1407 void *key = NULL;
1408 size_t keysz = 0;
1409
1410 res = ocsp_cache_get_key(p, fingerprint, &key, &keysz);
1411 if (res < 0) {
1412 pr_trace_msg(trace_channel, 1,
1413 "unable to remove ocsp cache entry: error getting cache key: %s",
1414 strerror(errno));
1415
1416 return -1;
1417 }
1418
1419 res = pr_memcache_kremove(ocsp_mcache, &tls_memcache_module,
1420 (const char *) key, keysz, 0);
1421 if (res < 0) {
1422 int xerrno = errno;
1423
1424 pr_trace_msg(trace_channel, 2,
1425 "unable to remove memcache entry for fingerpring '%s': %s", fingerprint,
1426 strerror(xerrno));
1427
1428 errno = xerrno;
1429 return -1;
1430 }
1431
1432 return 0;
1433 }
1434
ocsp_cache_entry_encode_json(pool * p,void ** value,size_t * valuesz,struct ocspcache_entry * oe)1435 static int ocsp_cache_entry_encode_json(pool *p, void **value, size_t *valuesz,
1436 struct ocspcache_entry *oe) {
1437 pr_json_object_t *json;
1438 pool *tmp_pool;
1439 char *base64_data = NULL, *json_text;
1440
1441 json = pr_json_object_alloc(p);
1442 (void) pr_json_object_set_number(p, json, OCSP_CACHE_JSON_KEY_AGE,
1443 (double) oe->age);
1444
1445 /* Base64-encode the response data. Note that EVP_EncodeBlock does
1446 * NUL-terminate the encoded data.
1447 */
1448 tmp_pool = make_sub_pool(p);
1449 base64_data = pcalloc(tmp_pool, (oe->resp_derlen * 2) + 1);
1450
1451 EVP_EncodeBlock((unsigned char *) base64_data, oe->resp_der,
1452 (int) oe->resp_derlen);
1453 (void) pr_json_object_set_string(p, json, OCSP_CACHE_JSON_KEY_RESPONSE,
1454 base64_data);
1455 (void) pr_json_object_set_number(p, json, OCSP_CACHE_JSON_KEY_RESPONSE_LENGTH,
1456 (double) oe->resp_derlen);
1457 destroy_pool(tmp_pool);
1458
1459 json_text = pr_json_object_to_text(p, json, "");
1460 (void) pr_json_object_free(json);
1461
1462 /* Safety check */
1463 if (pr_json_text_validate(p, json_text) == FALSE) {
1464 pr_trace_msg(trace_channel, 1, "invalid JSON emitted: '%s'", json_text);
1465 errno = EINVAL;
1466 return -1;
1467 }
1468
1469 /* Include the terminating NUL in the value. */
1470 *valuesz = strlen(json_text) + 1;
1471 *value = pstrndup(p, json_text, *valuesz - 1);
1472
1473 return 0;
1474 }
1475
ocsp_cache_mcache_entry_set(pool * p,const char * fingerprint,struct ocspcache_entry * oe)1476 static int ocsp_cache_mcache_entry_set(pool *p, const char *fingerprint,
1477 struct ocspcache_entry *oe) {
1478 int res, xerrno = 0;
1479 void *key = NULL, *value = NULL;
1480 size_t keysz = 0, valuesz = 0;
1481 uint32_t flags = 0;
1482
1483 /* Encode the OCSP response data. */
1484 res = ocsp_cache_entry_encode_json(p, &value, &valuesz, oe);
1485 if (res < 0) {
1486 xerrno = errno;
1487
1488 pr_trace_msg(trace_channel, 4, "error JSON encoding OCSP response data: %s",
1489 strerror(xerrno));
1490
1491 errno = xerrno;
1492 return -1;
1493 }
1494
1495 res = ocsp_cache_get_key(p, fingerprint, &key, &keysz);
1496 xerrno = errno;
1497 if (res < 0) {
1498 pr_trace_msg(trace_channel, 1,
1499 "unable to set ocsp cache entry: error getting cache key: %s",
1500 strerror(xerrno));
1501
1502 errno = xerrno;
1503 return -1;
1504 }
1505
1506 res = pr_memcache_kset(ocsp_mcache, &tls_memcache_module, (const char *) key,
1507 keysz, value, valuesz, 0, flags);
1508 xerrno = errno;
1509
1510 if (res < 0) {
1511 pr_trace_msg(trace_channel, 2,
1512 "unable to add memcache entry for fingerprint '%s': %s", fingerprint,
1513 strerror(xerrno));
1514
1515 errno = xerrno;
1516 return -1;
1517 }
1518
1519 pr_trace_msg(trace_channel, 9,
1520 "stored OCSP response data in cache using JSON");
1521 return 0;
1522 }
1523
ocsp_cache_open(tls_ocsp_cache_t * cache,char * info)1524 static int ocsp_cache_open(tls_ocsp_cache_t *cache, char *info) {
1525 config_rec *c;
1526
1527 pr_trace_msg(trace_channel, 9, "opening memcache cache %p (info '%s')",
1528 cache, info ? info : "(none)");
1529
1530 cache->cache_pool = make_sub_pool(permanent_pool);
1531 pr_pool_tag(cache->cache_pool, MOD_TLS_MEMCACHE_VERSION);
1532
1533 /* This is a little messy, but necessary. The mod_memcache module does
1534 * not set the configured list of memcached servers until a connection
1535 * arrives. But mod_tls opens its session cache prior to that, when the
1536 * server is starting up. Thus we need to set the configured list of
1537 * memcached servers ourselves.
1538 */
1539 c = find_config(main_server->conf, CONF_PARAM, "MemcacheEngine", FALSE);
1540 if (c != NULL) {
1541 int engine;
1542
1543 engine = *((int *) c->argv[0]);
1544 if (engine == FALSE) {
1545 pr_trace_msg(trace_channel, 2, "%s",
1546 "memcache support disabled (see MemcacheEngine directive)");
1547 errno = EPERM;
1548 return -1;
1549 }
1550 }
1551
1552 ocsp_mcache = pr_memcache_conn_new(cache->cache_pool,
1553 &tls_memcache_module, 0, 0);
1554 if (ocsp_mcache == NULL) {
1555 pr_trace_msg(trace_channel, 2,
1556 "error connecting to memcached: %s", strerror(errno));
1557 errno = EPERM;
1558 return -1;
1559 }
1560
1561 /* Configure a namespace prefix for our memcached keys. */
1562 if (pr_memcache_conn_set_namespace(ocsp_mcache, &tls_memcache_module,
1563 "mod_tls_memcache.ocsp.") < 0) {
1564 pr_trace_msg(trace_channel, 2,
1565 "error setting memcache namespace prefix: %s", strerror(errno));
1566 }
1567
1568 return 0;
1569 }
1570
ocsp_cache_close(tls_ocsp_cache_t * cache)1571 static int ocsp_cache_close(tls_ocsp_cache_t *cache) {
1572 pr_trace_msg(trace_channel, 9, "closing memcache ocsp cache %p", cache);
1573
1574 if (cache != NULL &&
1575 cache->cache_pool != NULL) {
1576
1577 /* We do NOT destroy the cache_pool here or close the mcache connection;
1578 * both were created at daemon startup, and should live as long as
1579 * the daemon lives.
1580 */
1581
1582 if (ocspcache_resp_list != NULL) {
1583 register unsigned int i;
1584 struct ocspcache_large_entry *entries;
1585
1586 entries = ocspcache_resp_list->elts;
1587 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1588 struct ocspcache_large_entry *entry;
1589
1590 entry = &(entries[i]);
1591 pr_memscrub(entry->resp_der, entry->resp_derlen);
1592 entry->resp_derlen = 0;
1593 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1594 entry->fingerprint_len = 0;
1595 entry->age = 0;
1596 }
1597
1598 clear_array(ocspcache_resp_list);
1599 }
1600 }
1601
1602 return 0;
1603 }
1604
ocsp_cache_add_large_resp(tls_ocsp_cache_t * cache,const char * fingerprint,OCSP_RESPONSE * resp,time_t resp_age)1605 static int ocsp_cache_add_large_resp(tls_ocsp_cache_t *cache,
1606 const char *fingerprint, OCSP_RESPONSE *resp, time_t resp_age) {
1607 struct ocspcache_large_entry *entry = NULL;
1608 int resp_derlen;
1609 unsigned char *ptr;
1610
1611 resp_derlen = i2d_OCSP_RESPONSE(resp, NULL);
1612 if (resp_derlen > TLS_MAX_OCSP_RESPONSE_SIZE) {
1613 const char *exceeds_key = ocspcache_keys[OCSPCACHE_KEY_EXCEEDS].key,
1614 *max_len_key = ocspcache_keys[OCSPCACHE_KEY_MAX_LEN].key;
1615 void *value = NULL;
1616 size_t valuesz = 0;
1617
1618 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, exceeds_key,
1619 1, NULL) < 0) {
1620 pr_trace_msg(trace_channel, 2,
1621 "error incrementing '%s' value: %s", exceeds_key, strerror(errno));
1622 }
1623
1624 /* XXX Yes, this is subject to race conditions; other proftpd servers
1625 * might also be modifying this value in memcached. Oh well.
1626 */
1627
1628 value = pr_memcache_get(ocsp_mcache, &tls_memcache_module, max_len_key,
1629 &valuesz, NULL);
1630 if (value != NULL) {
1631 uint64_t max_len;
1632
1633 memcpy(&max_len, value, valuesz);
1634 if ((uint64_t) resp_derlen > max_len) {
1635 if (pr_memcache_set(ocsp_mcache, &tls_memcache_module, max_len_key,
1636 &max_len, sizeof(max_len), 0, 0) < 0) {
1637 pr_trace_msg(trace_channel, 2,
1638 "error setting '%s' value: %s", max_len_key, strerror(errno));
1639 }
1640 }
1641
1642 } else {
1643 pr_trace_msg(trace_channel, 2,
1644 "error getting '%s' value: %s", max_len_key, strerror(errno));
1645 }
1646 }
1647
1648 if (ocspcache_resp_list != NULL) {
1649 register unsigned int i;
1650 struct ocspcache_large_entry *entries;
1651 time_t now;
1652 int ok = FALSE;
1653
1654 /* Look for any expired sessions in the list to overwrite/reuse. */
1655 entries = ocspcache_resp_list->elts;
1656 time(&now);
1657 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1658 entry = &(entries[i]);
1659
1660 if (entry->age > (now - 3600)) {
1661 /* This entry has expired; clear and reuse its slot. */
1662 entry->age = 0;
1663 pr_memscrub(entry->resp_der, entry->resp_derlen);
1664 entry->resp_derlen = 0;
1665 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1666 entry->fingerprint_len = 0;
1667
1668 ok = TRUE;
1669 break;
1670 }
1671 }
1672
1673 if (!ok) {
1674 /* We didn't find an open slot in the list. Need to add one. */
1675 entry = push_array(ocspcache_resp_list);
1676 }
1677
1678 } else {
1679 ocspcache_resp_list = make_array(cache->cache_pool, 1,
1680 sizeof(struct ocspcache_large_entry));
1681 entry = push_array(ocspcache_resp_list);
1682 }
1683
1684 entry->age = resp_age;
1685 entry->fingerprint_len = strlen(fingerprint);
1686 entry->fingerprint = pstrdup(cache->cache_pool, fingerprint);
1687 entry->resp_derlen = resp_derlen;
1688 entry->resp_der = ptr = palloc(cache->cache_pool, resp_derlen);
1689 i2d_OCSP_RESPONSE(resp, &ptr);
1690
1691 return 0;
1692 }
1693
ocsp_cache_add(tls_ocsp_cache_t * cache,const char * fingerprint,OCSP_RESPONSE * resp,time_t resp_age)1694 static int ocsp_cache_add(tls_ocsp_cache_t *cache, const char *fingerprint,
1695 OCSP_RESPONSE *resp, time_t resp_age) {
1696 struct ocspcache_entry entry;
1697 int resp_derlen;
1698 unsigned char *ptr;
1699
1700 pr_trace_msg(trace_channel, 9,
1701 "adding response to memcache ocsp cache %p", cache);
1702
1703 /* First we need to find out how much space is needed for the serialized
1704 * response data. There is no known maximum size for OCSP response data;
1705 * this module is currently designed to allow only up to a certain size.
1706 */
1707 resp_derlen = i2d_OCSP_RESPONSE(resp, NULL);
1708 if (resp_derlen > TLS_MAX_OCSP_RESPONSE_SIZE) {
1709 pr_trace_msg(trace_channel, 2,
1710 "length of serialized OCSP response data (%d) exceeds maximum size (%u), "
1711 "unable to add to shared memcache, adding to list", resp_derlen,
1712 TLS_MAX_OCSP_RESPONSE_SIZE);
1713
1714 /* Instead of rejecting the add here, we add the response to a "large
1715 * response" list. Thus the large response would still be cached per
1716 * process and will not be lost.
1717 */
1718
1719 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
1720 }
1721
1722 entry.age = resp_age;
1723 entry.resp_derlen = resp_derlen;
1724 ptr = entry.resp_der;
1725 i2d_OCSP_RESPONSE(resp, &ptr);
1726
1727 if (ocsp_cache_mcache_entry_set(cache->cache_pool, fingerprint, &entry) < 0) {
1728 pr_trace_msg(trace_channel, 2,
1729 "error adding response to memcache: %s", strerror(errno));
1730
1731 /* Add this response to the "large response" list instead as a fallback. */
1732 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
1733
1734 } else {
1735 const char *key = ocspcache_keys[OCSPCACHE_KEY_STORES].key;
1736
1737 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1738 pr_trace_msg(trace_channel, 2,
1739 "error incrementing '%s' value: %s", key, strerror(errno));
1740 }
1741 }
1742
1743 return 0;
1744 }
1745
ocsp_cache_get(tls_ocsp_cache_t * cache,const char * fingerprint,time_t * resp_age)1746 static OCSP_RESPONSE *ocsp_cache_get(tls_ocsp_cache_t *cache,
1747 const char *fingerprint, time_t *resp_age) {
1748 struct ocspcache_entry entry;
1749 OCSP_RESPONSE *resp = NULL;
1750 size_t fingerprint_len;
1751 const unsigned char *ptr;
1752
1753 pr_trace_msg(trace_channel, 9, "getting response from memcache ocsp cache %p",
1754 cache);
1755
1756 fingerprint_len = strlen(fingerprint);
1757
1758 /* Look for the requested response in the "large response" list first. */
1759 if (ocspcache_resp_list != NULL) {
1760 register unsigned int i;
1761 struct ocspcache_large_entry *entries;
1762
1763 entries = ocspcache_resp_list->elts;
1764 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1765 struct ocspcache_large_entry *large_entry;
1766
1767 large_entry = &(entries[i]);
1768 if (large_entry->fingerprint_len > 0 &&
1769 large_entry->fingerprint_len == fingerprint_len &&
1770 memcmp(large_entry->fingerprint, fingerprint, fingerprint_len) == 0) {
1771 ptr = large_entry->resp_der;
1772 resp = d2i_OCSP_RESPONSE(NULL, &ptr, large_entry->resp_derlen);
1773 if (resp == NULL) {
1774 pr_trace_msg(trace_channel, 2,
1775 "error retrieving response from ocsp cache: %s",
1776 mcache_get_errors());
1777
1778 } else {
1779 *resp_age = large_entry->age;
1780 break;
1781 }
1782 }
1783 }
1784 }
1785
1786 if (resp) {
1787 return resp;
1788 }
1789
1790 if (ocsp_cache_mcache_entry_get(cache->cache_pool, fingerprint, &entry) < 0) {
1791 return NULL;
1792 }
1793
1794 ptr = entry.resp_der;
1795 resp = d2i_OCSP_RESPONSE(NULL, &ptr, entry.resp_derlen);
1796 if (resp != NULL) {
1797 const char *key = ocspcache_keys[OCSPCACHE_KEY_HITS].key;
1798
1799 *resp_age = entry.age;
1800
1801 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1802 pr_trace_msg(trace_channel, 2,
1803 "error incrementing '%s' value: %s", key, strerror(errno));
1804 }
1805
1806 } else {
1807 const char *key = ocspcache_keys[OCSPCACHE_KEY_ERRORS].key;
1808
1809 pr_trace_msg(trace_channel, 2,
1810 "error retrieving response from ocsp cache: %s", mcache_get_errors());
1811
1812 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1813 pr_trace_msg(trace_channel, 2,
1814 "error incrementing '%s' value: %s", key, strerror(errno));
1815 }
1816 }
1817
1818 if (resp == NULL) {
1819 const char *key = ocspcache_keys[OCSPCACHE_KEY_MISSES].key;
1820
1821 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1822 pr_trace_msg(trace_channel, 2,
1823 "error incrementing '%s' value: %s", key, strerror(errno));
1824 }
1825
1826 errno = ENOENT;
1827 }
1828
1829 return resp;
1830 }
1831
ocsp_cache_delete(tls_ocsp_cache_t * cache,const char * fingerprint)1832 static int ocsp_cache_delete(tls_ocsp_cache_t *cache,
1833 const char *fingerprint) {
1834 const char *key = ocspcache_keys[OCSPCACHE_KEY_DELETES].key;
1835 int res;
1836 size_t fingerprint_len;
1837
1838 pr_trace_msg(trace_channel, 9,
1839 "deleting response from memcache ocsp cache %p", cache);
1840
1841 fingerprint_len = strlen(fingerprint);
1842
1843 /* Look for the requested response in the "large response" list first. */
1844 if (ocspcache_resp_list != NULL) {
1845 register unsigned int i;
1846 struct ocspcache_large_entry *entries;
1847
1848 entries = ocspcache_resp_list->elts;
1849 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1850 struct ocspcache_large_entry *entry;
1851
1852 entry = &(entries[i]);
1853 if (entry->fingerprint_len == fingerprint_len &&
1854 memcmp(entry->fingerprint, fingerprint, fingerprint_len) == 0) {
1855
1856 pr_memscrub(entry->resp_der, entry->resp_derlen);
1857 entry->resp_derlen = 0;
1858 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1859 entry->fingerprint_len = 0;
1860 entry->age = 0;
1861
1862 return 0;
1863 }
1864 }
1865 }
1866
1867 res = ocsp_cache_mcache_entry_delete(cache->cache_pool, fingerprint);
1868 if (res < 0) {
1869 return -1;
1870 }
1871
1872 /* Don't forget to update the stats. */
1873
1874 if (pr_memcache_incr(ocsp_mcache, &tls_memcache_module, key, 1, NULL) < 0) {
1875 pr_trace_msg(trace_channel, 2,
1876 "error incrementing '%s' value: %s", key, strerror(errno));
1877 }
1878
1879 return res;
1880 }
1881
ocsp_cache_clear(tls_ocsp_cache_t * cache)1882 static int ocsp_cache_clear(tls_ocsp_cache_t *cache) {
1883 register unsigned int i;
1884 int res = 0;
1885
1886 if (ocsp_mcache == NULL) {
1887 pr_trace_msg(trace_channel, 9, "missing required memcached connection");
1888 errno = EINVAL;
1889 return -1;
1890 }
1891
1892 pr_trace_msg(trace_channel, 9, "clearing memcache ocsp cache %p", cache);
1893
1894 if (ocspcache_resp_list != NULL) {
1895 struct ocspcache_large_entry *entries;
1896
1897 entries = ocspcache_resp_list->elts;
1898 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1899 struct ocspcache_large_entry *entry;
1900
1901 entry = &(entries[i]);
1902 entry->age = 0;
1903 pr_memscrub(entry->resp_der, entry->resp_derlen);
1904 entry->resp_derlen = 0;
1905 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1906 entry->fingerprint_len = 0;
1907 }
1908 }
1909
1910 /* XXX iterate through keys, kremoving any "mod_tls_memcache" prefixed keys */
1911
1912 return res;
1913 }
1914
ocsp_cache_remove(tls_ocsp_cache_t * cache)1915 static int ocsp_cache_remove(tls_ocsp_cache_t *cache) {
1916 int res;
1917
1918 pr_trace_msg(trace_channel, 9, "removing memcache ocsp cache %p", cache);
1919
1920 res = ocsp_cache_clear(cache);
1921 /* XXX close memcache conn */
1922
1923 return res;
1924 }
1925
ocsp_cache_status(tls_ocsp_cache_t * cache,void (* statusf)(void *,const char *,...),void * arg,int flags)1926 static int ocsp_cache_status(tls_ocsp_cache_t *cache,
1927 void (*statusf)(void *, const char *, ...), void *arg, int flags) {
1928 register unsigned int i;
1929 pool *tmp_pool;
1930
1931 pr_trace_msg(trace_channel, 9, "checking memcache ocsp cache %p", cache);
1932
1933 tmp_pool = make_sub_pool(permanent_pool);
1934
1935 statusf(arg, "%s", "Memcache OCSP response cache provided by "
1936 MOD_TLS_MEMCACHE_VERSION);
1937 statusf(arg, "%s", "");
1938 statusf(arg, "Memcache servers: ");
1939
1940 for (i = 0; ocspcache_keys[i].key != NULL; i++) {
1941 const char *key, *desc;
1942 void *value = NULL;
1943 size_t valuesz = 0;
1944 uint32_t stat_flags = 0;
1945
1946 key = ocspcache_keys[i].key;
1947 desc = ocspcache_keys[i].desc;
1948
1949 value = pr_memcache_get(ocsp_mcache, &tls_memcache_module, key, &valuesz,
1950 &stat_flags);
1951 if (value != NULL) {
1952 uint64_t num = 0;
1953 memcpy(&num, value, valuesz);
1954 statusf(arg, "%s: %lu", desc, (unsigned long) num);
1955 }
1956 }
1957
1958 /* XXX run stats on memcached servers? */
1959
1960 destroy_pool(tmp_pool);
1961 return 0;
1962 }
1963 #endif /* PR_USE_OPENSSL_OCSP */
1964
1965 /* Event Handlers
1966 */
1967
1968 #if defined(PR_SHARED_MODULE)
tls_mcache_mod_unload_ev(const void * event_data,void * user_data)1969 static void tls_mcache_mod_unload_ev(const void *event_data, void *user_data) {
1970 if (strcmp("mod_tls_memcache.c", (const char *) event_data) == 0) {
1971 pr_event_unregister(&tls_memcache_module, NULL, NULL);
1972 tls_sess_cache_unregister("memcache");
1973 # if defined(PR_USE_OPENSSL_OCSP)
1974 tls_ocsp_cache_unregister("memcache");
1975 # endif /* PR_USE_OPENSSL_OCSP */
1976 }
1977 }
1978 #endif /* !PR_SHARED_MODULE */
1979
1980 /* Initialization functions
1981 */
1982
tls_mcache_init(void)1983 static int tls_mcache_init(void) {
1984 #if defined(PR_SHARED_MODULE)
1985 pr_event_register(&tls_memcache_module, "core.module-unload",
1986 tls_mcache_mod_unload_ev, NULL);
1987 #endif /* !PR_SHARED_MODULE */
1988
1989 /* Prepare our SSL session cache handler. */
1990 memset(&sess_cache, 0, sizeof(sess_cache));
1991
1992 sess_cache.cache_name = "memcache";
1993 pr_pool_tag(sess_cache.cache_pool, MOD_TLS_MEMCACHE_VERSION);
1994
1995 sess_cache.open = sess_cache_open;
1996 sess_cache.close = sess_cache_close;
1997 sess_cache.add = sess_cache_add;
1998 sess_cache.get = sess_cache_get;
1999 sess_cache.delete = sess_cache_delete;
2000 sess_cache.clear = sess_cache_clear;
2001 sess_cache.remove = sess_cache_remove;
2002 sess_cache.status = sess_cache_status;
2003
2004 #ifdef SSL_SESS_CACHE_NO_INTERNAL
2005 /* Take a chance, and inform OpenSSL that it does not need to use its own
2006 * internal session cache lookups/storage; using the external session cache
2007 * (i.e. us) will be enough.
2008 */
2009 sess_cache.cache_mode = SSL_SESS_CACHE_NO_INTERNAL;
2010 #endif
2011
2012 #if defined(PR_USE_OPENSSL_OCSP)
2013 /* Prepare our OCSP response cache handler. */
2014 memset(&ocsp_cache, 0, sizeof(ocsp_cache));
2015
2016 ocsp_cache.cache_name = "memcache";
2017 pr_pool_tag(ocsp_cache.cache_pool, MOD_TLS_MEMCACHE_VERSION);
2018
2019 ocsp_cache.open = ocsp_cache_open;
2020 ocsp_cache.close = ocsp_cache_close;
2021 ocsp_cache.add = ocsp_cache_add;
2022 ocsp_cache.get = ocsp_cache_get;
2023 ocsp_cache.delete = ocsp_cache_delete;
2024 ocsp_cache.clear = ocsp_cache_clear;
2025 ocsp_cache.remove = ocsp_cache_remove;
2026 ocsp_cache.status = ocsp_cache_status;
2027 #endif /* PR_USE_OPENSSL_OCSP */
2028
2029 #ifdef PR_USE_MEMCACHE
2030 if (tls_sess_cache_register("memcache", &sess_cache) < 0) {
2031 pr_log_debug(DEBUG1, MOD_TLS_MEMCACHE_VERSION
2032 ": notice: error registering 'memcache' SSL session cache: %s",
2033 strerror(errno));
2034 return -1;
2035 }
2036
2037 # if defined(PR_USE_OPENSSL_OCSP)
2038 if (tls_ocsp_cache_register("memcache", &ocsp_cache) < 0) {
2039 pr_log_debug(DEBUG1, MOD_TLS_MEMCACHE_VERSION
2040 ": notice: error registering 'memcache' OCSP response cache: %s",
2041 strerror(errno));
2042 return -1;
2043 }
2044 # endif /* PR_USE_OPENSSL_OCSP */
2045
2046 #else
2047 pr_log_debug(DEBUG1, MOD_TLS_MEMCACHE_VERSION
2048 ": unable to register 'memcache' SSL session cache: Memcache support not enabled");
2049 # if defined(PR_USE_OPENSSL_OCSP)
2050 pr_log_debug(DEBUG1, MOD_TLS_MEMCACHE_VERSION
2051 ": unable to register 'memcache' OCSP response cache: Memcache support not enabled");
2052 # endif /* PR_USE_OPENSSL_OCSP */
2053 #endif /* PR_USE_MEMCACHE */
2054
2055 return 0;
2056 }
2057
tls_mcache_sess_init(void)2058 static int tls_mcache_sess_init(void) {
2059 /* Reset our memcache handles. */
2060
2061 if (sess_mcache != NULL) {
2062 if (pr_memcache_conn_clone(session.pool, sess_mcache) < 0) {
2063 tls_log(MOD_TLS_MEMCACHE_VERSION
2064 ": error resetting memcache handle: %s", strerror(errno));
2065 }
2066 }
2067
2068 #if defined(PR_USE_OPENSSL_OCSP)
2069 if (ocsp_mcache != NULL) {
2070 if (pr_memcache_conn_clone(session.pool, ocsp_mcache) < 0) {
2071 tls_log(MOD_TLS_MEMCACHE_VERSION
2072 ": error resetting memcache handle: %s", strerror(errno));
2073 }
2074 }
2075 #endif /* PR_USE_OPENSSL_OCSP */
2076
2077 return 0;
2078 }
2079
2080 /* Module API tables
2081 */
2082
2083 module tls_memcache_module = {
2084 NULL, NULL,
2085
2086 /* Module API version 2.0 */
2087 0x20,
2088
2089 /* Module name */
2090 "tls_memcache",
2091
2092 /* Module configuration handler table */
2093 NULL,
2094
2095 /* Module command handler table */
2096 NULL,
2097
2098 /* Module authentication handler table */
2099 NULL,
2100
2101 /* Module initialization function */
2102 tls_mcache_init,
2103
2104 /* Session initialization function */
2105 tls_mcache_sess_init,
2106
2107 /* Module version */
2108 MOD_TLS_MEMCACHE_VERSION
2109 };
2110