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