1 /*
2 * ProFTPD: mod_tls_shmcache -- a module which provides shared SSL session
3 * and OCSP response caches using SysV shared
4 * memory segments
5 * Copyright (c) 2009-2020 TJ Saunders
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20 *
21 * As a special exemption, TJ Saunders and other respective copyright holders
22 * give permission to link this program with OpenSSL, and distribute the
23 * resulting executable, without including the source code for OpenSSL in the
24 * source distribution.
25 *
26 * This is mod_tls_shmcache, contrib software for proftpd 1.3.x and above.
27 * For more information contact TJ Saunders <tj@castaglia.org>.
28 */
29
30 #include "conf.h"
31 #include "privs.h"
32 #include "mod_tls.h"
33
34 #include <sys/ipc.h>
35 #include <sys/shm.h>
36
37 #ifdef HAVE_MLOCK
38 # include <sys/mman.h>
39 #endif
40
41 /* Define if you have the LibreSSL library. */
42 #if defined(LIBRESSL_VERSION_NUMBER)
43 # define HAVE_LIBRESSL 1
44 #endif
45
46 #define MOD_TLS_SHMCACHE_VERSION "mod_tls_shmcache/0.2"
47
48 /* Make sure the version of proftpd is as necessary. */
49 #if PROFTPD_VERSION_NUMBER < 0x0001030602
50 # error "ProFTPD 1.3.6rc2 or later required"
51 #endif
52
53 module tls_shmcache_module;
54
55 #define TLS_SHMCACHE_SESS_PROJECT_ID 247
56
57 /* Assume a maximum SSL session (serialized) length of 10K. Note that this
58 * is different from the SSL_MAX_SSL_SESSION_ID_LENGTH provided by OpenSSL.
59 * There is no limit imposed on the length of the ASN1 description of the
60 * SSL session data.
61 */
62 #ifndef TLS_MAX_SSL_SESSION_SIZE
63 # define TLS_MAX_SSL_SESSION_SIZE 1024 * 10
64 #endif
65
66 /* The default number of SSL sessions cached in OpenSSL's internal cache
67 * is SSL_SESSION_CACHE_MAX_SIZE_DEFAULT, which is defined as 1024*20.
68 * This is NOT a size in _bytes_, but is a size in _counts_ of sessions.
69 *
70 * Thus the default size of our shm segment should also, in theory, be
71 * able to hold the same number of sessions.
72 *
73 * The recommended default size for Apache's mod_ssl shm segment is 512000
74 * bytes (500KB).
75 */
76
77 struct sesscache_entry {
78 time_t expires;
79 unsigned int sess_id_len;
80 unsigned char sess_id[SSL_MAX_SSL_SESSION_ID_LENGTH];
81 unsigned int sess_datalen;
82 unsigned char sess_data[TLS_MAX_SSL_SESSION_SIZE];
83 };
84
85 /* The difference between sesscache_entry and sesscache_large_entry is that the
86 * buffers in the latter are dynamically allocated from the heap, not
87 * allocated out of the shm segment. The large_entry struct is used for
88 * storing sessions which don't fit into the normal entry struct; this also
89 * means that these large entries are NOT shared across processes.
90 */
91 struct sesscache_large_entry {
92 time_t expires;
93 unsigned int sess_id_len;
94 const unsigned char *sess_id;
95 unsigned int sess_datalen;
96 const unsigned char *sess_data;
97 };
98
99 /* The number of entries in the list is determined at run-time, based on
100 * the maximum desired size of the shared memory segment.
101 */
102 struct sesscache_data {
103
104 /* Cache metadata. */
105 unsigned int nhits;
106 unsigned int nmisses;
107
108 unsigned int nstored;
109 unsigned int ndeleted;
110 unsigned int nexpired;
111 unsigned int nerrors;
112
113 /* This tracks the number of sessions that could not be added because
114 * they exceeded TLS_MAX_SSL_SESSION_SIZE.
115 */
116 unsigned int nexceeded;
117 unsigned int exceeded_maxsz;
118
119 /* Track the timestamp of the next session to expire in the cache; used
120 * as an optimization when flushing the cache of expired sessions.
121 */
122 time_t next_expiring;
123
124 /* These listlen/listsz track the number of entries in the cache and total
125 * entries possible, and thus can be used for determining the fullness of
126 * the cache.
127 */
128 unsigned int sd_listlen, sd_listsz;
129
130 /* It is important that this field be the last in the struct! */
131 struct sesscache_entry *sd_entries;
132 };
133
134 static tls_sess_cache_t sess_cache;
135 static struct sesscache_data *sesscache_data = NULL;
136 static size_t sesscache_datasz = 0;
137 static int sesscache_shmid = -1;
138 static pr_fh_t *sesscache_fh = NULL;
139 static array_header *sesscache_sess_list = NULL;
140
141 #if defined(PR_USE_OPENSSL_OCSP)
142 # define TLS_SHMCACHE_OCSP_PROJECT_ID 249
143
144 /* Assume a maximum OCSP response (serialized) length of 4K.
145 */
146 # ifndef TLS_MAX_OCSP_RESPONSE_SIZE
147 # define TLS_MAX_OCSP_RESPONSE_SIZE 1024 * 4
148 # endif
149
150 struct ocspcache_entry {
151 time_t age;
152 unsigned int fingerprint_len;
153 unsigned char fingerprint[EVP_MAX_MD_SIZE];
154 unsigned int resp_derlen;
155 unsigned char resp_der[TLS_MAX_OCSP_RESPONSE_SIZE];
156 };
157
158 /* The difference between ocspcache_entry and ocspcache_large_entry is that the
159 * buffers in the latter are dynamically allocated from the heap, not
160 * allocated out of the shm segment. The large_entry struct is used for
161 * storing sessions which don't fit into the normal entry struct; this also
162 * means that these large entries are NOT shared across processes.
163 */
164 struct ocspcache_large_entry {
165 time_t age;
166 unsigned int fingerprint_len;
167 unsigned char *fingerprint;
168 unsigned int resp_derlen;
169 unsigned char *resp_der;
170 };
171
172 /* The number of entries in the list is determined at run-time, based on
173 * the maximum desired size of the shared memory segment.
174 */
175 struct ocspcache_data {
176
177 /* Cache metadata. */
178 unsigned int nhits;
179 unsigned int nmisses;
180
181 unsigned int nstored;
182 unsigned int ndeleted;
183 unsigned int nexpired;
184 unsigned int nerrors;
185
186 /* This tracks the number of sessions that could not be added because
187 * they exceeded TLS_MAX_OCSP_RESPONSE_SIZE.
188 */
189 unsigned int nexceeded;
190 unsigned int exceeded_maxsz;
191
192 /* These listlen/listsz track the number of entries in the cache and total
193 * entries possible, and thus can be used for determining the fullness of
194 * the cache.
195 */
196 unsigned int od_listlen, od_listsz;
197
198 /* It is important that this field be the last in the struct! */
199 struct ocspcache_entry *od_entries;
200 };
201
202 static tls_ocsp_cache_t ocsp_cache;
203 static struct ocspcache_data *ocspcache_data = NULL;
204 static size_t ocspcache_datasz = 0;
205 static int ocspcache_shmid = -1;
206 static pr_fh_t *ocspcache_fh = NULL;
207 static array_header *ocspcache_resp_list = NULL;
208 #endif /* PR_USE_OPENSSL_OCSP */
209
210 static const char *trace_channel = "tls.shmcache";
211
212 static int sess_cache_close(tls_sess_cache_t *);
213 #if defined(PR_USE_OPENSSL_OCSP)
214 static int ocsp_cache_close(tls_ocsp_cache_t *);
215 #endif /* PR_USE_OPENSSL_OCSP */
216
shmcache_get_errors(void)217 static const char *shmcache_get_errors(void) {
218 unsigned int count = 0;
219 unsigned long error_code;
220 BIO *bio = NULL;
221 char *data = NULL;
222 long datalen;
223 const char *error_data = NULL, *str = "(unknown)";
224 int error_flags = 0;
225
226 /* Use ERR_print_errors() and a memory BIO to build up a string with
227 * all of the error messages from the error queue.
228 */
229
230 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
231 if (error_code) {
232 bio = BIO_new(BIO_s_mem());
233 }
234
235 while (error_code) {
236 pr_signals_handle();
237
238 if (error_flags & ERR_TXT_STRING) {
239 BIO_printf(bio, "\n (%u) %s [%s]", ++count,
240 ERR_error_string(error_code, NULL), error_data);
241
242 } else {
243 BIO_printf(bio, "\n (%u) %s", ++count,
244 ERR_error_string(error_code, NULL));
245 }
246
247 error_data = NULL;
248 error_flags = 0;
249 error_code = ERR_get_error_line_data(NULL, NULL, &error_data, &error_flags);
250 }
251
252 datalen = BIO_get_mem_data(bio, &data);
253 if (data) {
254 data[datalen] = '\0';
255 str = pstrdup(permanent_pool, data);
256 }
257
258 if (bio != NULL) {
259 BIO_free(bio);
260 }
261
262 return str;
263 }
264
shmcache_get_lock_desc(int lock_type)265 static const char *shmcache_get_lock_desc(int lock_type) {
266 const char *lock_desc;
267
268 switch (lock_type) {
269 case F_RDLCK:
270 lock_desc = "read-lock";
271 break;
272
273 case F_WRLCK:
274 lock_desc = "write-lock";
275 break;
276
277 case F_UNLCK:
278 lock_desc = "unlock";
279 break;
280
281 default:
282 lock_desc = "[unknown]";
283 }
284
285 return lock_desc;
286 }
287
288 /* XXX There is anecdotal (and real) evidence that using SysV semaphores
289 * is faster than fcntl(2)/flock(3). However, semaphores are not cleaned up
290 * if the process dies tragically. Could possibly deal with this in an
291 * exit event handler, though. Something to keep in mind.
292 */
shmcache_lock_shm(pr_fh_t * fh,int lock_type)293 static int shmcache_lock_shm(pr_fh_t *fh, int lock_type) {
294 const char *lock_desc;
295 int fd;
296 struct flock lock;
297 unsigned int nattempts = 1;
298
299 lock.l_type = lock_type;
300 lock.l_whence = SEEK_SET;
301 lock.l_start = 0;
302 lock.l_len = 0;
303
304 fd = PR_FH_FD(fh);
305 lock_desc = shmcache_get_lock_desc(lock_type);
306
307 pr_trace_msg(trace_channel, 19, "attempting to %s shmcache fd %d", lock_desc,
308 fd);
309
310 while (fcntl(fd, F_SETLK, &lock) < 0) {
311 int xerrno = errno;
312
313 if (xerrno == EINTR) {
314 pr_signals_handle();
315 continue;
316 }
317
318 pr_trace_msg(trace_channel, 3, "%s of shmcache fd %d failed: %s",
319 lock_desc, fd, strerror(xerrno));
320 if (xerrno == EACCES) {
321 struct flock locker;
322
323 /* Get the PID of the process blocking this lock. */
324 if (fcntl(fd, F_GETLK, &locker) == 0) {
325 pr_trace_msg(trace_channel, 3, "process ID %lu has blocking %s on "
326 "shmcache fd %d", (unsigned long) locker.l_pid,
327 shmcache_get_lock_desc(locker.l_type), fd);
328 }
329
330 /* Treat this as an interrupted call, call pr_signals_handle() (which
331 * will delay for a few msecs because of EINTR), and try again.
332 * After 10 attempts, give up altogether.
333 */
334
335 nattempts++;
336 if (nattempts <= 10) {
337 errno = EINTR;
338
339 pr_signals_handle();
340 continue;
341 }
342
343 errno = xerrno;
344 return -1;
345 }
346
347 errno = xerrno;
348 return -1;
349 }
350
351 pr_trace_msg(trace_channel, 19, "%s of shmcache fd %d succeeded", lock_desc,
352 fd);
353 return 0;
354 }
355
356 /* Use a hash function to hash the given lookup key to a slot in the entries
357 * list. This hash, module the number of entries, is the initial iteration
358 * start point. This will hopefully avoid having to do many linear scans for
359 * the add/get/delete operations.
360 *
361 * Use Perl's hashing algorithm.
362 */
shmcache_hash(const unsigned char * id,unsigned int len)363 static unsigned int shmcache_hash(const unsigned char *id, unsigned int len) {
364 unsigned int i = 0;
365 size_t sz = len;
366
367 while (sz--) {
368 const unsigned char *k = id;
369 unsigned int c = *k;
370 k++;
371
372 /* Always handle signals in potentially long-running while loops. */
373 pr_signals_handle();
374
375 i = (i * 33) + c;
376 }
377
378 return i;
379 }
380
shmcache_get_shm(pr_fh_t * fh,size_t * shm_size,int project_id,int * shm_id)381 static void *shmcache_get_shm(pr_fh_t *fh, size_t *shm_size, int project_id,
382 int *shm_id) {
383 int rem, shm_existed = FALSE, xerrno = 0;
384 key_t key;
385 void *data = NULL;
386
387 key = ftok(fh->fh_path, project_id);
388 if (key == (key_t) -1) {
389 xerrno = errno;
390
391 pr_trace_msg(trace_channel, 1,
392 "unable to get key for path '%s': %s", fh->fh_path, strerror(xerrno));
393
394 errno = xerrno;
395 return NULL;
396 }
397
398 /* Round the requested segment size up to the nearest SHMBLA boundary. */
399 rem = *shm_size % SHMLBA;
400 if (rem != 0) {
401 *shm_size = (*shm_size - rem + SHMLBA);
402 pr_trace_msg(trace_channel, 9,
403 "rounded requested size up to %lu bytes", (unsigned long) *shm_size);
404 }
405
406 /* Try first using IPC_CREAT|IPC_EXCL, to check if there is an existing
407 * shm for this key. If so, use a flags value of zero.
408 *
409 * We use root privs for this, to make sure that the shm can only be
410 * access by a process with root privs. This is equivalent to having
411 * a root-owned file in the filesystem. We need to protect the sensitive
412 * session data (which contains master keys and such) from prying eyes.
413 */
414
415 PRIVS_ROOT
416 *shm_id = shmget(key, *shm_size, IPC_CREAT|IPC_EXCL|0600);
417 xerrno = errno;
418 PRIVS_RELINQUISH
419
420 if (*shm_id < 0) {
421 if (xerrno == EEXIST) {
422 shm_existed = TRUE;
423
424 PRIVS_ROOT
425 *shm_id = shmget(key, 0, 0);
426 xerrno = errno;
427 PRIVS_RELINQUISH
428
429 if (*shm_id < 0) {
430 pr_trace_msg(trace_channel, 1,
431 "unable to get shm for existing key: %s", strerror(xerrno));
432 errno = xerrno;
433 return NULL;
434 }
435
436 } else {
437 /* Try to provide more helpful/informative log messages. */
438 if (xerrno == ENOMEM) {
439 pr_trace_msg(trace_channel, 1,
440 "not enough memory for %lu shm bytes; try specifying a smaller size",
441 (unsigned long) *shm_size);
442
443 } else if (xerrno == ENOSPC) {
444 pr_trace_msg(trace_channel, 1, "%s",
445 "unable to allocate a new shm ID; system limit of shm IDs reached");
446 }
447
448 errno = xerrno;
449 return NULL;
450 }
451 }
452
453 /* Attach to the shm. */
454 pr_trace_msg(trace_channel, 10, "attempting to attach to shm ID %d",
455 *shm_id);
456
457 PRIVS_ROOT
458 data = shmat(*shm_id, NULL, 0);
459 xerrno = errno;
460 PRIVS_RELINQUISH
461
462 if (data == NULL) {
463 pr_trace_msg(trace_channel, 1,
464 "unable to attach to shm ID %d: %s", *shm_id, strerror(xerrno));
465 errno = xerrno;
466 return NULL;
467 }
468
469 if (shm_existed) {
470 struct shmid_ds ds;
471 int res;
472
473 /* If we already have a shmid, check for size differences; the admin
474 * may have configured a larger/smaller cache size. Use shmctl(IP_STAT)
475 * to determine the existing segment size.
476 */
477
478 PRIVS_ROOT
479 res = shmctl(*shm_id, IPC_STAT, &ds);
480 xerrno = errno;
481 PRIVS_RELINQUISH
482
483 if (res == 0) {
484 pr_trace_msg(trace_channel, 10,
485 "existing shm size: %u bytes", (unsigned int) ds.shm_segsz);
486
487 if ((unsigned long) ds.shm_segsz != (unsigned long) *shm_size) {
488 if ((unsigned long) ds.shm_segsz > (unsigned long) *shm_size) {
489 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
490 ": requested shm size (%lu bytes) is smaller than existing shm "
491 "size, migrating to smaller shm (may result in loss of cache data)",
492 (unsigned long) *shm_size);
493
494 } else if ((unsigned long) ds.shm_segsz < (unsigned long) *shm_size) {
495 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
496 ": requested shm size (%lu bytes) is larger than existing shm "
497 "size, migrating to larger shm", (unsigned long) *shm_size);
498 }
499
500 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
501 ": remove existing shmcache using 'ftpdctl tls sesscache remove' "
502 "or 'ftpdctl tls ocspcache remove' before using new size");
503
504 errno = EEXIST;
505 return NULL;
506 }
507
508 } else {
509 pr_trace_msg(trace_channel, 1,
510 "unable to stat shm ID %d: %s", *shm_id, strerror(xerrno));
511 errno = xerrno;
512 }
513
514 } else {
515 /* Make sure the memory is initialized. */
516 if (shmcache_lock_shm(fh, F_WRLCK) < 0) {
517 pr_trace_msg(trace_channel, 1, "error write-locking shm: %s",
518 strerror(errno));
519 }
520
521 memset(data, 0, *shm_size);
522
523 if (shmcache_lock_shm(fh, F_UNLCK) < 0) {
524 pr_trace_msg(trace_channel, 1, "error unlocking shm: %s",
525 strerror(errno));
526 }
527 }
528
529 return data;
530 }
531
sess_cache_get_shm(pr_fh_t * fh,size_t requested_size)532 static struct sesscache_data *sess_cache_get_shm(pr_fh_t *fh,
533 size_t requested_size) {
534 int shmid, xerrno = 0;
535 struct sesscache_data *data = NULL;
536 size_t shm_size;
537 unsigned int shm_sess_max = 0;
538
539 /* Calculate the size to allocate. First, calculate the maximum number
540 * of sessions we can cache, given the configured size. Then
541 * calculate the shm segment size to allocate to hold that number of
542 * sessions.
543 */
544 shm_sess_max = (requested_size - sizeof(struct sesscache_data)) /
545 (sizeof(struct sesscache_entry));
546 shm_size = sizeof(struct sesscache_data) +
547 (shm_sess_max * sizeof(struct sesscache_entry));
548
549 data = shmcache_get_shm(fh, &shm_size, TLS_SHMCACHE_SESS_PROJECT_ID, &shmid);
550 if (data == NULL) {
551 xerrno = errno;
552
553 if (errno == EEXIST) {
554 sess_cache_close(NULL);
555 }
556
557 errno = xerrno;
558 return NULL;
559 }
560
561 sesscache_datasz = shm_size;
562 sesscache_shmid = shmid;
563 pr_trace_msg(trace_channel, 9,
564 "using shm ID %d for sesscache path '%s' (%u sessions)", sesscache_shmid,
565 fh->fh_path, shm_sess_max);
566
567 data->sd_entries = (struct sesscache_entry *) (data + sizeof(struct sesscache_data));
568 data->sd_listsz = shm_sess_max;
569
570 return data;
571 }
572
573 #if defined(PR_USE_OPENSSL_OCSP)
ocsp_cache_get_shm(pr_fh_t * fh,size_t requested_size)574 static struct ocspcache_data *ocsp_cache_get_shm(pr_fh_t *fh,
575 size_t requested_size) {
576 int shmid, xerrno = 0;
577 struct ocspcache_data *data = NULL;
578 size_t shm_size;
579 unsigned int shm_resp_max = 0;
580
581 /* Calculate the size to allocate. First, calculate the maximum number
582 * of responses we can cache, given the configured size. Then
583 * calculate the shm segment size to allocate to hold that number of
584 * responses.
585 */
586 shm_resp_max = (requested_size - sizeof(struct ocspcache_data)) /
587 (sizeof(struct ocspcache_entry));
588 shm_size = sizeof(struct ocspcache_data) +
589 (shm_resp_max * sizeof(struct ocspcache_entry));
590
591 data = shmcache_get_shm(fh, &shm_size, TLS_SHMCACHE_OCSP_PROJECT_ID, &shmid);
592 if (data == NULL) {
593 xerrno = errno;
594
595 if (errno == EEXIST) {
596 ocsp_cache_close(NULL);
597 }
598
599 errno = xerrno;
600 return NULL;
601 }
602
603 ocspcache_datasz = shm_size;
604 ocspcache_shmid = shmid;
605 pr_trace_msg(trace_channel, 9,
606 "using shm ID %d for ocspcache path '%s' (%u responses)", ocspcache_shmid,
607 fh->fh_path, shm_resp_max);
608
609 data->od_entries = (struct ocspcache_entry *) (data + sizeof(struct ocspcache_data));
610 data->od_listsz = shm_resp_max;
611
612 return data;
613 }
614 #endif /* PR_USE_OPENSSL_OCSP */
615
616 /* SSL session cache implementation callbacks.
617 */
618
619 /* Scan the entire list, clearing out expired sessions. Logs the number
620 * of sessions that expired and updates the header stat.
621 *
622 * NOTE: Callers are assumed to handle the locking of the shm before/after
623 * calling this function!
624 */
sess_cache_flush(void)625 static unsigned int sess_cache_flush(void) {
626 register unsigned int i;
627 unsigned int flushed = 0;
628 time_t now, next_expiring = 0;
629
630 now = time(NULL);
631
632 /* We always scan the in-memory large session entry list. */
633 if (sesscache_sess_list != NULL) {
634 struct sesscache_large_entry *entries;
635
636 entries = sesscache_sess_list->elts;
637 for (i = 0; i < sesscache_sess_list->nelts; i++) {
638 struct sesscache_large_entry *entry;
639
640 entry = &(entries[i]);
641
642 if (entry->expires > now) {
643 /* This entry has expired; clear its slot. */
644 entry->expires = 0;
645 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
646 }
647 }
648 }
649
650 /* If now is earlier than the earliest expiring session in the cache,
651 * then a scan will be pointless.
652 */
653 if (now < sesscache_data->next_expiring) {
654 unsigned int secs;
655
656 secs = sesscache_data->next_expiring - now;
657 tls_log("shmcache: no expired sessions to flush; %u secs to next "
658 "expiration", secs);
659 return 0;
660 }
661
662 tls_log("shmcache: flushing session cache of expired sessions");
663
664 for (i = 0; i < sesscache_data->sd_listsz; i++) {
665 struct sesscache_entry *entry;
666
667 entry = &(sesscache_data->sd_entries[i]);
668 if (entry->expires > 0) {
669 if (entry->expires > now) {
670 if (entry->expires < next_expiring) {
671 next_expiring = entry->expires;
672 }
673
674 } else {
675 /* This entry has expired; clear its slot. */
676 entry->expires = 0;
677 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
678
679 /* Don't forget to update the stats. */
680 sesscache_data->nexpired++;
681
682 if (sesscache_data->sd_listlen > 0) {
683 sesscache_data->sd_listlen--;
684 }
685
686 flushed++;
687 }
688 }
689
690 sesscache_data->next_expiring = next_expiring;
691 }
692
693 tls_log("shmcache: flushed %u expired %s from session cache", flushed,
694 flushed != 1 ? "sessions" : "session");
695 return flushed;
696 }
697
sess_cache_open(tls_sess_cache_t * cache,char * info,long timeout)698 static int sess_cache_open(tls_sess_cache_t *cache, char *info, long timeout) {
699 int fd, xerrno;
700 char *ptr;
701 size_t requested_size;
702 struct stat st;
703
704 pr_trace_msg(trace_channel, 9, "opening shmcache session cache %p", cache);
705
706 /* The info string must be formatted like:
707 *
708 * /path=%s[&size=%u]
709 *
710 * where the optional size is in bytes. There is a minimum size; if the
711 * configured size is less than the minimum, it's an error. The default
712 * size (when no size is explicitly configured) is, of course, larger than
713 * the minimum size.
714 */
715
716 if (strncmp(info, "/file=", 6) != 0) {
717 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
718 ": badly formatted info '%s', unable to open shmcache", info);
719 errno = EINVAL;
720 return -1;
721 }
722
723 info += 6;
724
725 /* Check for the optional size parameter. */
726 ptr = strchr(info, '&');
727 if (ptr != NULL) {
728 if (strncmp(ptr + 1, "size=", 5) == 0) {
729 char *tmp = NULL;
730 long size;
731
732 size = strtol(ptr + 6, &tmp, 10);
733 if (tmp && *tmp) {
734 pr_trace_msg(trace_channel, 1,
735 "badly formatted size parameter '%s', ignoring", ptr + 1);
736
737 /* Default size of 1.5M. That should hold around 100 sessions. */
738 requested_size = 1538 * 1024;
739
740 } else {
741 size_t min_size;
742
743 /* The bare minimum size MUST be able to hold at least one session. */
744 min_size = sizeof(struct sesscache_data) +
745 sizeof(struct sesscache_entry);
746
747 if ((size_t) size < min_size) {
748 pr_trace_msg(trace_channel, 1,
749 "requested size (%lu bytes) smaller than minimum size "
750 "(%lu bytes), ignoring", (unsigned long) size,
751 (unsigned long) min_size);
752
753 /* Default size of 1.5M. That should hold around 100 sessions. */
754 requested_size = 1538 * 1024;
755
756 } else {
757 requested_size = size;
758 }
759 }
760
761 } else {
762 pr_trace_msg(trace_channel, 1,
763 "badly formatted size parameter '%s', ignoring", ptr + 1);
764
765 /* Default size of 1.5M. That should hold around 100 sessions. */
766 requested_size = 1538 * 1024;
767 }
768
769 *ptr = '\0';
770
771 } else {
772 /* Default size of 1.5M. That should hold around 100 sessions. */
773 requested_size = 1538 * 1024;
774 }
775
776 /* We could change the cache_mode flags here, based on the given
777 * info, if needs be.
778 */
779
780 if (pr_fs_valid_path(info) < 0) {
781 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
782 ": file '%s' not an absolute path", info);
783
784 errno = EINVAL;
785 return -1;
786 }
787
788 /* If sesscache_fh is not null, then we are a restarted server. And if
789 * the 'info' path does not match that previous fh, then the admin
790 * has changed the configuration.
791 *
792 * For now, we complain about this, and tell the admin to manually remove
793 * the old file/shm.
794 */
795 if (sesscache_fh != NULL &&
796 strcmp(sesscache_fh->fh_path, info) != 0) {
797 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
798 ": file '%s' does not match previously configured file '%s'",
799 info, sesscache_fh->fh_path);
800 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
801 ": remove existing shmcache using 'ftpdctl tls sesscache remove' "
802 "before using new file");
803
804 errno = EINVAL;
805 return -1;
806 }
807
808 PRIVS_ROOT
809 sesscache_fh = pr_fsio_open(info, O_RDWR|O_CREAT);
810 xerrno = errno;
811 PRIVS_RELINQUISH
812
813 if (sesscache_fh == NULL) {
814 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
815 ": error: unable to open file '%s': %s", info, strerror(xerrno));
816
817 errno = EINVAL;
818 return -1;
819 }
820
821 if (pr_fsio_fstat(sesscache_fh, &st) < 0) {
822 xerrno = errno;
823
824 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
825 ": error: unable to stat file '%s': %s", info, strerror(xerrno));
826
827 pr_fsio_close(sesscache_fh);
828 sesscache_fh = NULL;
829
830 errno = EINVAL;
831 return -1;
832 }
833
834 if (S_ISDIR(st.st_mode)) {
835 xerrno = EISDIR;
836
837 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
838 ": error: unable to use file '%s': %s", info, strerror(xerrno));
839
840 pr_fsio_close(sesscache_fh);
841 sesscache_fh = NULL;
842
843 errno = EINVAL;
844 return -1;
845 }
846
847 /* Make sure that we don't inadvertently get one of the Big Three file
848 * descriptors (stdin/stdout/stderr), as can happen especially if the
849 * server has restarted.
850 */
851 fd = PR_FH_FD(sesscache_fh);
852 if (fd <= STDERR_FILENO) {
853 int res;
854
855 res = pr_fs_get_usable_fd(fd);
856 if (res < 0) {
857 pr_log_debug(DEBUG0,
858 "warning: unable to find good fd for shmcache fd %d: %s",
859 fd, strerror(errno));
860
861 } else {
862 close(fd);
863 PR_FH_FD(sesscache_fh) = res;
864 }
865 }
866
867 pr_trace_msg(trace_channel, 9,
868 "requested session cache file: %s (fd %d)", sesscache_fh->fh_path,
869 PR_FH_FD(sesscache_fh));
870 pr_trace_msg(trace_channel, 9,
871 "requested session cache size: %lu bytes", (unsigned long) requested_size);
872
873 sesscache_data = sess_cache_get_shm(sesscache_fh, requested_size);
874 if (sesscache_data == NULL) {
875 xerrno = errno;
876
877 pr_trace_msg(trace_channel, 1,
878 "unable to allocate session shm: %s", strerror(xerrno));
879 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
880 ": unable to allocate session shm: %s", strerror(xerrno));
881
882 pr_fsio_close(sesscache_fh);
883 sesscache_fh = NULL;
884
885 errno = EINVAL;
886 return -1;
887 }
888
889 cache->cache_pool = make_sub_pool(permanent_pool);
890 pr_pool_tag(cache->cache_pool, MOD_TLS_SHMCACHE_VERSION);
891
892 cache->cache_timeout = timeout;
893 return 0;
894 }
895
sess_cache_close(tls_sess_cache_t * cache)896 static int sess_cache_close(tls_sess_cache_t *cache) {
897
898 if (cache != NULL) {
899 pr_trace_msg(trace_channel, 9, "closing shmcache session cache %p", cache);
900 }
901
902 if (cache != NULL &&
903 cache->cache_pool != NULL) {
904 destroy_pool(cache->cache_pool);
905
906 if (sesscache_sess_list != NULL) {
907 register unsigned int i;
908 struct sesscache_large_entry *entries;
909
910 entries = sesscache_sess_list->elts;
911 for (i = 0; i < sesscache_sess_list->nelts; i++) {
912 struct sesscache_large_entry *entry;
913
914 entry = &(entries[i]);
915 if (entry->expires > 0) {
916 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
917 }
918 }
919
920 sesscache_sess_list = NULL;
921 }
922 }
923
924 if (sesscache_shmid >= 0) {
925 int res, xerrno = 0;
926
927 PRIVS_ROOT
928 #if !defined(_POSIX_SOURCE)
929 res = shmdt((char *) sesscache_data);
930 #else
931 res = shmdt((const char *) sesscache_data);
932 #endif
933 xerrno = errno;
934 PRIVS_RELINQUISH
935
936 if (res < 0) {
937 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
938 ": error detaching session shm ID %d: %s", sesscache_shmid,
939 strerror(xerrno));
940 }
941
942 sesscache_data = NULL;
943 }
944
945 pr_fsio_close(sesscache_fh);
946 sesscache_fh = NULL;
947 return 0;
948 }
949
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)950 static int sess_cache_add_large_sess(tls_sess_cache_t *cache,
951 const unsigned char *sess_id, unsigned int sess_id_len, time_t expires,
952 SSL_SESSION *sess, int sess_len) {
953 struct sesscache_large_entry *entry = NULL;
954
955 if (sess_len > TLS_MAX_SSL_SESSION_SIZE) {
956 /* We may get sessions to add to the list which do not exceed the max
957 * size, but instead are here because we couldn't get the lock on the
958 * shmcache. Don't track these in the 'exceeded' stats'.
959 */
960
961 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) == 0) {
962 sesscache_data->nexceeded++;
963 if ((size_t) sess_len > sesscache_data->exceeded_maxsz) {
964 sesscache_data->exceeded_maxsz = sess_len;
965 }
966
967 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
968 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
969 }
970
971 } else {
972 tls_log("shmcache: error write-locking shmcache: %s", strerror(errno));
973 }
974 }
975
976 if (sesscache_sess_list != NULL) {
977 register unsigned int i;
978 struct sesscache_large_entry *entries;
979 time_t now;
980
981 /* Look for any expired sessions in the list to overwrite/reuse. */
982 entries = sesscache_sess_list->elts;
983 now = time(NULL);
984 for (i = 0; i < sesscache_sess_list->nelts; i++) {
985 entry = &(entries[i]);
986
987 if (entry->expires > now) {
988 /* This entry has expired; clear and reuse its slot. */
989 entry->expires = 0;
990 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
991
992 break;
993 }
994 }
995
996 } else {
997 sesscache_sess_list = make_array(cache->cache_pool, 1,
998 sizeof(struct sesscache_large_entry));
999 entry = push_array(sesscache_sess_list);
1000 }
1001
1002 /* Be defensive, and catch the case where entry might still be null here. */
1003 if (entry == NULL) {
1004 errno = EPERM;
1005 return -1;
1006 }
1007
1008 entry->expires = expires;
1009 entry->sess_id_len = sess_id_len;
1010 entry->sess_id = palloc(cache->cache_pool, sess_id_len);
1011 memcpy((char *) entry->sess_id, sess_id, sess_id_len);
1012 entry->sess_datalen = sess_len;
1013 entry->sess_data = palloc(cache->cache_pool, sess_len);
1014 i2d_SSL_SESSION(sess, (unsigned char **) &(entry->sess_data));
1015
1016 return 0;
1017 }
1018
sess_cache_add(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len,time_t expires,SSL_SESSION * sess)1019 static int sess_cache_add(tls_sess_cache_t *cache, const unsigned char *sess_id,
1020 unsigned int sess_id_len, time_t expires, SSL_SESSION *sess) {
1021 register unsigned int i;
1022 unsigned int h, idx, last;
1023 int found_slot = FALSE, need_lock = TRUE, res = 0, sess_len;
1024
1025 pr_trace_msg(trace_channel, 9, "adding session to shmcache session cache %p",
1026 cache);
1027
1028 /* First we need to find out how much space is needed for the serialized
1029 * session data. There is no known maximum size for SSL session data;
1030 * this module is currently designed to allow only up to a certain size.
1031 */
1032 sess_len = i2d_SSL_SESSION(sess, NULL);
1033 if (sess_len > TLS_MAX_SSL_SESSION_SIZE) {
1034 tls_log("shmcache: length of serialized SSL session data (%d) exceeds "
1035 "maximum size (%u), unable to add to shared shmcache, adding to list",
1036 sess_len, TLS_MAX_SSL_SESSION_SIZE);
1037
1038 /* Instead of rejecting the add here, we add the session to a "large
1039 * session" list. Thus the large session would still be cached per process
1040 * and will not be lost.
1041 *
1042 * XXX We should also track how often this happens, and possibly trigger
1043 * a shmcache resize (using a larger record size, vs larger cache size)
1044 * so that we can cache these large records in the shm segment.
1045 */
1046
1047 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
1048 sess, sess_len);
1049 }
1050
1051 if (sesscache_data->sd_listlen == sesscache_data->sd_listsz) {
1052 /* It appears that the cache is full. Try flushing any expired
1053 * sessions.
1054 */
1055
1056 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) == 0) {
1057 if (sess_cache_flush() > 0) {
1058 /* If we made room, then do NOT release the lock; we keep the lock
1059 * so that we can add the session.
1060 */
1061 need_lock = FALSE;
1062
1063 } else {
1064 /* Release the lock, and use the "large session" list fallback. */
1065 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1066 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1067 }
1068
1069 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
1070 sess, sess_len);
1071 }
1072
1073 } else {
1074 tls_log("shmcache: unable to flush shm cache: error write-locking "
1075 "shmcache: %s", strerror(errno));
1076
1077 /* Add this session to the "large session" list instead as a fallback. */
1078 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
1079 sess, sess_len);
1080 }
1081 }
1082
1083 /* Hash the key, start looking for an open slot. */
1084 h = shmcache_hash(sess_id, sess_id_len);
1085 idx = h % sesscache_data->sd_listsz;
1086
1087 if (need_lock) {
1088 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) < 0) {
1089 tls_log("shmcache: unable to add session to shm cache: error "
1090 "write-locking shmcache: %s", strerror(errno));
1091
1092 /* Add this session to the "large session" list instead as a fallback. */
1093 return sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires,
1094 sess, sess_len);
1095 }
1096 }
1097
1098 i = idx;
1099 last = idx > 0 ? (idx - 1) : 0;
1100
1101 do {
1102 struct sesscache_entry *entry;
1103
1104 pr_signals_handle();
1105
1106 /* Look for the first open slot (i.e. expires == 0). */
1107 entry = &(sesscache_data->sd_entries[i]);
1108 if (entry->expires == 0) {
1109 unsigned char *ptr;
1110
1111 entry->expires = expires;
1112 entry->sess_id_len = sess_id_len;
1113 memcpy(entry->sess_id, sess_id, sess_id_len);
1114 entry->sess_datalen = sess_len;
1115
1116 ptr = entry->sess_data;
1117 i2d_SSL_SESSION(sess, &ptr);
1118
1119 sesscache_data->sd_listlen++;
1120 sesscache_data->nstored++;
1121
1122 if (sesscache_data->next_expiring > 0) {
1123 if (expires < sesscache_data->next_expiring) {
1124 sesscache_data->next_expiring = expires;
1125 }
1126
1127 } else {
1128 sesscache_data->next_expiring = expires;
1129 }
1130
1131 found_slot = TRUE;
1132 break;
1133 }
1134
1135 if (i < sesscache_data->sd_listsz) {
1136 i++;
1137
1138 } else {
1139 i = 0;
1140 }
1141
1142 } while (i != last);
1143
1144 /* There is a race condition possible between the open slots check
1145 * above and the scan through the slots. So if we didn't actually find
1146 * an open slot at this point, add it to the "large session" list.
1147 */
1148 if (!found_slot) {
1149 res = sess_cache_add_large_sess(cache, sess_id, sess_id_len, expires, sess,
1150 sess_len);
1151 }
1152
1153 if (need_lock) {
1154 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1155 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1156 }
1157 }
1158
1159 return res;
1160 }
1161
sess_cache_get(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len)1162 static SSL_SESSION *sess_cache_get(tls_sess_cache_t *cache,
1163 const unsigned char *sess_id, unsigned int sess_id_len) {
1164 unsigned int h, idx;
1165 SSL_SESSION *sess = NULL;
1166
1167 pr_trace_msg(trace_channel, 9,
1168 "getting session from shmcache session cache %p", cache);
1169
1170 /* Look for the requested session in the "large session" list first. */
1171 if (sesscache_sess_list != NULL) {
1172 register unsigned int i;
1173 struct sesscache_large_entry *entries;
1174
1175 entries = sesscache_sess_list->elts;
1176 for (i = 0; i < sesscache_sess_list->nelts; i++) {
1177 struct sesscache_large_entry *entry;
1178
1179 entry = &(entries[i]);
1180 if (entry->expires > 0 &&
1181 entry->sess_id_len == sess_id_len &&
1182 memcmp(entry->sess_id, sess_id, entry->sess_id_len) == 0) {
1183 time_t now;
1184
1185 now = time(NULL);
1186 if (entry->expires <= now) {
1187 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
1188
1189 ptr = entry->sess_data;
1190 sess = d2i_SSL_SESSION(NULL, &ptr, entry->sess_datalen);
1191 if (sess == NULL) {
1192 tls_log("shmcache: error retrieving session from session cache: %s",
1193 shmcache_get_errors());
1194
1195 } else {
1196 break;
1197 }
1198 }
1199 }
1200 }
1201 }
1202
1203 if (sess) {
1204 return sess;
1205 }
1206
1207 h = shmcache_hash(sess_id, sess_id_len);
1208 idx = h % sesscache_data->sd_listsz;
1209
1210 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) == 0) {
1211 register unsigned int i;
1212 unsigned int last;
1213
1214 i = idx;
1215 last = idx > 0 ? (idx -1) : 0;
1216
1217 do {
1218 struct sesscache_entry *entry;
1219
1220 pr_signals_handle();
1221
1222 entry = &(sesscache_data->sd_entries[i]);
1223 if (entry->expires > 0 &&
1224 entry->sess_id_len == sess_id_len &&
1225 memcmp(entry->sess_id, sess_id, entry->sess_id_len) == 0) {
1226 time_t now;
1227
1228 /* Don't forget to update the stats. */
1229 now = time(NULL);
1230
1231 if (entry->expires > now) {
1232 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
1233
1234 ptr = entry->sess_data;
1235 sess = d2i_SSL_SESSION(NULL, &ptr, entry->sess_datalen);
1236 if (sess != NULL) {
1237 sesscache_data->nhits++;
1238
1239 } else {
1240 tls_log("shmcache: error retrieving session from session cache: %s",
1241 shmcache_get_errors());
1242 sesscache_data->nerrors++;
1243 }
1244 }
1245
1246 break;
1247 }
1248
1249 if (i < sesscache_data->sd_listsz) {
1250 i++;
1251
1252 } else {
1253 i = 0;
1254 }
1255
1256 } while (i != last);
1257
1258 if (sess == NULL) {
1259 sesscache_data->nmisses++;
1260 errno = ENOENT;
1261 }
1262
1263 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1264 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1265 }
1266
1267 } else {
1268 tls_log("shmcache: unable to retrieve session from session cache: error "
1269 "write-locking shmcache: %s", strerror(errno));
1270
1271 errno = EPERM;
1272 }
1273
1274 return sess;
1275 }
1276
sess_cache_delete(tls_sess_cache_t * cache,const unsigned char * sess_id,unsigned int sess_id_len)1277 static int sess_cache_delete(tls_sess_cache_t *cache,
1278 const unsigned char *sess_id, unsigned int sess_id_len) {
1279 unsigned int h, idx;
1280 int res;
1281
1282 pr_trace_msg(trace_channel, 9,
1283 "removing session from shmcache session cache %p", cache);
1284
1285 /* Look for the requested session in the "large session" list first. */
1286 if (sesscache_sess_list != NULL) {
1287 register unsigned int i;
1288 struct sesscache_large_entry *entries;
1289
1290 entries = sesscache_sess_list->elts;
1291 for (i = 0; i < sesscache_sess_list->nelts; i++) {
1292 struct sesscache_large_entry *entry;
1293
1294 entry = &(entries[i]);
1295 if (entry->sess_id_len == sess_id_len &&
1296 memcmp(entry->sess_id, sess_id, entry->sess_id_len) == 0) {
1297
1298 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1299 entry->expires = 0;
1300 return 0;
1301 }
1302 }
1303 }
1304
1305 h = shmcache_hash(sess_id, sess_id_len);
1306 idx = h % sesscache_data->sd_listsz;
1307
1308 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) == 0) {
1309 register unsigned int i;
1310 unsigned int last;
1311
1312 i = idx;
1313 last = idx > 0 ? (idx - 1) : 0;
1314
1315 do {
1316 struct sesscache_entry *entry;
1317
1318 pr_signals_handle();
1319
1320 entry = &(sesscache_data->sd_entries[i]);
1321 if (entry->sess_id_len == sess_id_len &&
1322 memcmp(entry->sess_id, sess_id, entry->sess_id_len) == 0) {
1323 time_t now;
1324
1325 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1326
1327 if (sesscache_data->sd_listlen > 0) {
1328 sesscache_data->sd_listlen--;
1329 }
1330
1331 /* Don't forget to update the stats. */
1332 now = time(NULL);
1333 if (entry->expires > now) {
1334 sesscache_data->ndeleted++;
1335
1336 } else {
1337 sesscache_data->nexpired++;
1338 }
1339
1340 entry->expires = 0;
1341 break;
1342 }
1343
1344 if (i < sesscache_data->sd_listsz) {
1345 i++;
1346
1347 } else {
1348 i = 0;
1349 }
1350
1351 } while (i != last);
1352
1353 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1354 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1355 }
1356
1357 res = 0;
1358
1359 } else {
1360 tls_log("shmcache: unable to delete session from session cache: error "
1361 "write-locking shmcache: %s", strerror(errno));
1362
1363 errno = EPERM;
1364 res = -1;
1365 }
1366
1367 return res;
1368 }
1369
sess_cache_clear(tls_sess_cache_t * cache)1370 static int sess_cache_clear(tls_sess_cache_t *cache) {
1371 register unsigned int i;
1372 int res;
1373
1374 pr_trace_msg(trace_channel, 9, "clearing shmcache session cache %p", cache);
1375
1376 if (sesscache_shmid < 0) {
1377 errno = EINVAL;
1378 return -1;
1379 }
1380
1381 if (sesscache_sess_list != NULL) {
1382 struct sesscache_large_entry *entries;
1383
1384 entries = sesscache_sess_list->elts;
1385 for (i = 0; i < sesscache_sess_list->nelts; i++) {
1386 struct sesscache_large_entry *entry;
1387
1388 entry = &(entries[i]);
1389 entry->expires = 0;
1390 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1391 }
1392 }
1393
1394 if (shmcache_lock_shm(sesscache_fh, F_WRLCK) < 0) {
1395 tls_log("shmcache: unable to clear cache: error write-locking shmcache: %s",
1396 strerror(errno));
1397 return -1;
1398 }
1399
1400 for (i = 0; i < sesscache_data->sd_listsz; i++) {
1401 struct sesscache_entry *entry;
1402
1403 entry = &(sesscache_data->sd_entries[i]);
1404
1405 entry->expires = 0;
1406 pr_memscrub((void *) entry->sess_data, entry->sess_datalen);
1407 }
1408
1409 res = sesscache_data->sd_listlen;
1410 sesscache_data->sd_listlen = 0;
1411
1412 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1413 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1414 }
1415
1416 return res;
1417 }
1418
sess_cache_remove(tls_sess_cache_t * cache)1419 static int sess_cache_remove(tls_sess_cache_t *cache) {
1420 int res;
1421 struct shmid_ds ds;
1422 const char *cache_file;
1423
1424 if (sesscache_fh == NULL) {
1425 return 0;
1426 }
1427
1428 if (cache != NULL) {
1429 pr_trace_msg(trace_channel, 9, "removing shmcache session cache %p",
1430 cache);
1431 }
1432
1433 cache_file = sesscache_fh->fh_path;
1434 (void) sess_cache_close(cache);
1435
1436 if (sesscache_shmid < 0) {
1437 errno = EINVAL;
1438 return -1;
1439 }
1440
1441 pr_log_debug(DEBUG9, MOD_TLS_SHMCACHE_VERSION
1442 ": attempting to remove session cache shm ID %d", sesscache_shmid);
1443
1444 PRIVS_ROOT
1445 res = shmctl(sesscache_shmid, IPC_RMID, &ds);
1446 PRIVS_RELINQUISH
1447
1448 if (res < 0) {
1449 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1450 ": error removing session cache shm ID %d: %s", sesscache_shmid,
1451 strerror(errno));
1452
1453 } else {
1454 pr_log_debug(DEBUG9, MOD_TLS_SHMCACHE_VERSION
1455 ": removed session cache shm ID %d", sesscache_shmid);
1456 sesscache_shmid = -1;
1457 }
1458
1459 /* Don't forget to remove the on-disk file as well. */
1460 unlink(cache_file);
1461
1462 return res;
1463 }
1464
sess_cache_status(tls_sess_cache_t * cache,void (* statusf)(void *,const char *,...),void * arg,int flags)1465 static int sess_cache_status(tls_sess_cache_t *cache,
1466 void (*statusf)(void *, const char *, ...), void *arg, int flags) {
1467 int res, xerrno = 0;
1468 struct shmid_ds ds;
1469 pool *tmp_pool;
1470
1471 pr_trace_msg(trace_channel, 9, "checking shmcache session cache %p", cache);
1472
1473 if (shmcache_lock_shm(sesscache_fh, F_RDLCK) < 0) {
1474 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1475 ": error read-locking shmcache: %s", strerror(errno));
1476 return -1;
1477 }
1478
1479 tmp_pool = make_sub_pool(permanent_pool);
1480
1481 statusf(arg, "%s", "Shared memory (shm) SSL session cache provided by "
1482 MOD_TLS_SHMCACHE_VERSION);
1483 statusf(arg, "%s", "");
1484 statusf(arg, "Shared memory segment ID: %d", sesscache_shmid);
1485
1486 PRIVS_ROOT
1487 res = shmctl(sesscache_shmid, IPC_STAT, &ds);
1488 xerrno = errno;
1489 PRIVS_RELINQUISH
1490
1491 if (res == 0) {
1492 statusf(arg, "Shared memory segment size: %u bytes",
1493 (unsigned int) ds.shm_segsz);
1494 statusf(arg, "Shared memory cache created on: %s",
1495 pr_strtime3(tmp_pool, ds.shm_ctime, FALSE));
1496 statusf(arg, "Shared memory attach count: %u",
1497 (unsigned int) ds.shm_nattch);
1498
1499 } else {
1500 statusf(arg, "Unable to stat shared memory segment ID %d: %s",
1501 sesscache_shmid, strerror(xerrno));
1502 }
1503
1504 statusf(arg, "%s", "");
1505 statusf(arg, "Max session cache size: %u", sesscache_data->sd_listsz);
1506 statusf(arg, "Current session cache size: %u", sesscache_data->sd_listlen);
1507 statusf(arg, "%s", "");
1508 statusf(arg, "Cache lifetime hits: %u", sesscache_data->nhits);
1509 statusf(arg, "Cache lifetime misses: %u", sesscache_data->nmisses);
1510 statusf(arg, "%s", "");
1511 statusf(arg, "Cache lifetime sessions stored: %u", sesscache_data->nstored);
1512 statusf(arg, "Cache lifetime sessions deleted: %u", sesscache_data->ndeleted);
1513 statusf(arg, "Cache lifetime sessions expired: %u", sesscache_data->nexpired);
1514 statusf(arg, "%s", "");
1515 statusf(arg, "Cache lifetime errors handling sessions in cache: %u",
1516 sesscache_data->nerrors);
1517 statusf(arg, "Cache lifetime sessions exceeding max entry size: %u",
1518 sesscache_data->nexceeded);
1519 if (sesscache_data->nexceeded > 0) {
1520 statusf(arg, " Largest session exceeding max entry size: %u",
1521 sesscache_data->exceeded_maxsz);
1522 }
1523
1524 if (flags & TLS_SESS_CACHE_STATUS_FL_SHOW_SESSIONS) {
1525 register unsigned int i;
1526
1527 statusf(arg, "%s", "");
1528 statusf(arg, "%s", "Cached sessions:");
1529
1530 if (sesscache_data->sd_listlen == 0) {
1531 statusf(arg, "%s", " (none)");
1532 }
1533
1534 /* We _could_ use SSL_SESSION_print(), which is what the sess_id
1535 * command-line tool does. The problem is that SSL_SESSION_print() shows
1536 * too much (particularly, it shows the master secret). And
1537 * SSL_SESSION_print() does not support a flags argument to use for
1538 * specifying which bits of the session we want to print.
1539 *
1540 * Instead, we get to do the more dangerous (compatibility-wise) approach
1541 * of rolling our own printing function.
1542 */
1543
1544 for (i = 0; i < sesscache_data->sd_listsz; i++) {
1545 struct sesscache_entry *entry;
1546
1547 pr_signals_handle();
1548
1549 entry = &(sesscache_data->sd_entries[i]);
1550 if (entry->expires > 0) {
1551 SSL_SESSION *sess;
1552 TLS_D2I_SSL_SESSION_CONST unsigned char *ptr;
1553 time_t ts;
1554 int ssl_version;
1555
1556 ptr = entry->sess_data;
1557 sess = d2i_SSL_SESSION(NULL, &ptr, entry->sess_datalen);
1558 if (sess == NULL) {
1559 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
1560 ": error retrieving session from session cache: %s",
1561 shmcache_get_errors());
1562 continue;
1563 }
1564
1565 statusf(arg, "%s", " -----BEGIN SSL SESSION PARAMETERS-----");
1566
1567 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1568 /* XXX Directly accessing these fields cannot be a Good Thing. */
1569 if (sess->session_id_length > 0) {
1570 char *sess_id_str;
1571
1572 sess_id_str = pr_str_bin2hex(tmp_pool, sess->session_id,
1573 sess->session_id_length, PR_STR_FL_HEX_USE_UC);
1574
1575 statusf(arg, " Session ID: %s", sess_id_str);
1576 }
1577
1578 if (sess->sid_ctx_length > 0) {
1579 char *sid_ctx_str;
1580
1581 sid_ctx_str = pr_str_bin2hex(tmp_pool, sess->sid_ctx,
1582 sess->sid_ctx_length, PR_STR_FL_HEX_USE_UC);
1583
1584 statusf(arg, " Session ID Context: %s", sid_ctx_str);
1585 }
1586
1587 ssl_version = sess->ssl_version;
1588 #else
1589 # if OPENSSL_VERSION_NUMBER >= 0x10100006L && \
1590 !defined(HAVE_LIBRESSL)
1591 ssl_version = SSL_SESSION_get_protocol_version(sess);
1592 # else
1593 ssl_version = 0;
1594 # endif /* prior to OpenSSL-1.1.0-pre5 */
1595 #endif /* prior to OpenSSL-1.1.x */
1596
1597 switch (ssl_version) {
1598 case SSL3_VERSION:
1599 statusf(arg, " Protocol: %s", "SSLv3");
1600 break;
1601
1602 case TLS1_VERSION:
1603 statusf(arg, " Protocol: %s", "TLSv1");
1604 break;
1605
1606 #if defined(TLS1_1_VERSION)
1607 case TLS1_1_VERSION:
1608 statusf(arg, " Protocol: %s", "TLSv1.1");
1609 break;
1610 #endif /* TLS1_1_VERSION */
1611
1612 #if defined(TLS1_2_VERSION)
1613 case TLS1_2_VERSION:
1614 statusf(arg, " Protocol: %s", "TLSv1.2");
1615 break;
1616 #endif /* TLS1_2_VERSION */
1617
1618 #ifdef TLS1_3_VERSION
1619 case TLS1_3_VERSION:
1620 statusf(arg, " Protocol: %s", "TLSv1.3");
1621 break;
1622 #endif /* TLS1_3_VERSION */
1623
1624 default:
1625 statusf(arg, " Protocol: %s", "unknown");
1626 }
1627
1628 ts = SSL_SESSION_get_time(sess);
1629 statusf(arg, " Started: %s", pr_strtime3(tmp_pool, ts, FALSE));
1630 ts = entry->expires;
1631 statusf(arg, " Expires: %s (%u secs)",
1632 pr_strtime3(tmp_pool, ts, FALSE), SSL_SESSION_get_timeout(sess));
1633
1634 SSL_SESSION_free(sess);
1635 statusf(arg, "%s", " -----END SSL SESSION PARAMETERS-----");
1636 statusf(arg, "%s", "");
1637 }
1638 }
1639 }
1640
1641 if (shmcache_lock_shm(sesscache_fh, F_UNLCK) < 0) {
1642 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1643 ": error unlocking shmcache: %s", strerror(errno));
1644 }
1645
1646 destroy_pool(tmp_pool);
1647 return 0;
1648 }
1649
1650 #if defined(PR_USE_OPENSSL_OCSP)
1651
1652 /* OCSP response cache implementation callbacks.
1653 */
1654
1655 /* Scan the entire list, and clear out the oldest response. Logs the number
1656 * of responses cleared and updates the header stat.
1657 *
1658 * NOTE: Callers are assumed to handle the locking of the shm before/after
1659 * calling this function!
1660 */
ocsp_cache_flush(void)1661 static unsigned int ocsp_cache_flush(void) {
1662 register unsigned int i;
1663 unsigned int flushed = 0;
1664 time_t now;
1665
1666 now = time(NULL);
1667
1668 /* We always scan the in-memory large response entry list. */
1669 if (ocspcache_resp_list != NULL) {
1670 struct ocspcache_large_entry *entries;
1671
1672 entries = ocspcache_resp_list->elts;
1673 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1674 struct ocspcache_large_entry *entry;
1675
1676 entry = &(entries[i]);
1677
1678 if (entry->age > (now - 3600)) {
1679 /* This entry has expired; clear its slot. */
1680 pr_memscrub(entry->resp_der, entry->resp_derlen);
1681 entry->resp_derlen = 0;
1682 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1683 entry->fingerprint_len = 0;
1684 }
1685 }
1686 }
1687
1688 tls_log("shmcache: flushing ocsp cache of oldest responses");
1689
1690 for (i = 0; i < ocspcache_data->od_listsz; i++) {
1691 struct ocspcache_entry *entry;
1692
1693 entry = &(ocspcache_data->od_entries[i]);
1694 if (entry->age > (now - 3600)) {
1695 /* This entry has expired; clear its slot. */
1696 pr_memscrub(entry->resp_der, entry->resp_derlen);
1697 entry->resp_derlen = 0;
1698 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
1699 entry->fingerprint_len = 0;
1700 entry->age = 0;
1701
1702 /* Don't forget to update the stats. */
1703 ocspcache_data->nexpired++;
1704
1705 if (ocspcache_data->od_listlen > 0) {
1706 ocspcache_data->od_listlen--;
1707 }
1708
1709 flushed++;
1710 }
1711 }
1712
1713 tls_log("shmcache: flushed %u old %s from ocsp cache", flushed,
1714 flushed != 1 ? "responses" : "response");
1715 return flushed;
1716 }
1717
ocsp_cache_open(tls_ocsp_cache_t * cache,char * info)1718 static int ocsp_cache_open(tls_ocsp_cache_t *cache, char *info) {
1719 int fd, xerrno;
1720 char *ptr;
1721 size_t requested_size;
1722 struct stat st;
1723
1724 pr_trace_msg(trace_channel, 9, "opening shmcache ocsp cache %p", cache);
1725
1726 /* The info string must be formatted like:
1727 *
1728 * /file=%s[&size=%u]
1729 *
1730 * where the optional size is in bytes. There is a minimum size; if the
1731 * configured size is less than the minimum, it's an error. The default
1732 * size (when no size is explicitly configured) is, of course, larger than
1733 * the minimum size.
1734 */
1735
1736 if (strncmp(info, "/file=", 6) != 0) {
1737 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
1738 ": badly formatted info '%s', unable to open shmcache", info);
1739 errno = EINVAL;
1740 return -1;
1741 }
1742
1743 info += 6;
1744
1745 /* Check for the optional size parameter. */
1746 ptr = strchr(info, '&');
1747 if (ptr != NULL) {
1748 if (strncmp(ptr + 1, "size=", 5) == 0) {
1749 char *tmp = NULL;
1750 long size;
1751
1752 size = strtol(ptr + 6, &tmp, 10);
1753 if (tmp && *tmp) {
1754 pr_trace_msg(trace_channel, 1,
1755 "badly formatted size parameter '%s', ignoring", ptr + 1);
1756
1757 /* Default size of 1.5M. */
1758 requested_size = 1538 * 1024;
1759
1760 } else {
1761 size_t min_size;
1762
1763 /* The bare minimum size MUST be able to hold at least one response. */
1764 min_size = sizeof(struct ocspcache_data) +
1765 sizeof(struct ocspcache_entry);
1766
1767 if ((size_t) size < min_size) {
1768 pr_trace_msg(trace_channel, 1,
1769 "requested size (%lu bytes) smaller than minimum size "
1770 "(%lu bytes), ignoring", (unsigned long) size,
1771 (unsigned long) min_size);
1772
1773 /* Default size of 1.5M. */
1774 requested_size = 1538 * 1024;
1775
1776 } else {
1777 requested_size = size;
1778 }
1779 }
1780
1781 } else {
1782 pr_trace_msg(trace_channel, 1,
1783 "badly formatted size parameter '%s', ignoring", ptr + 1);
1784
1785 /* Default size of 1.5M. */
1786 requested_size = 1538 * 1024;
1787 }
1788
1789 *ptr = '\0';
1790
1791 } else {
1792 /* Default size of 1.5M. */
1793 requested_size = 1538 * 1024;
1794 }
1795
1796 if (pr_fs_valid_path(info) < 0) {
1797 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
1798 ": file '%s' not an absolute path", info);
1799
1800 errno = EINVAL;
1801 return -1;
1802 }
1803
1804 /* If ocspcache_fh is not null, then we are a restarted server. And if
1805 * the 'info' path does not match that previous fh, then the admin
1806 * has changed the configuration.
1807 *
1808 * For now, we complain about this, and tell the admin to manually remove
1809 * the old file/shm.
1810 */
1811
1812 if (ocspcache_fh != NULL &&
1813 strcmp(ocspcache_fh->fh_path, info) != 0) {
1814 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
1815 ": file '%s' does not match previously configured file '%s'",
1816 info, ocspcache_fh->fh_path);
1817 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
1818 ": remove existing shmcache using 'ftpdctl tls ocspcache remove' "
1819 "before using new file");
1820
1821 errno = EINVAL;
1822 return -1;
1823 }
1824
1825 PRIVS_ROOT
1826 ocspcache_fh = pr_fsio_open(info, O_RDWR|O_CREAT);
1827 xerrno = errno;
1828 PRIVS_RELINQUISH
1829
1830 if (ocspcache_fh == NULL) {
1831 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1832 ": error: unable to open file '%s': %s", info, strerror(xerrno));
1833
1834 errno = EINVAL;
1835 return -1;
1836 }
1837
1838 if (pr_fsio_fstat(ocspcache_fh, &st) < 0) {
1839 xerrno = errno;
1840
1841 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1842 ": error: unable to stat file '%s': %s", info, strerror(xerrno));
1843
1844 pr_fsio_close(ocspcache_fh);
1845 ocspcache_fh = NULL;
1846
1847 errno = EINVAL;
1848 return -1;
1849 }
1850
1851 if (S_ISDIR(st.st_mode)) {
1852 xerrno = EISDIR;
1853
1854 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1855 ": error: unable to use file '%s': %s", info, strerror(xerrno));
1856
1857 pr_fsio_close(ocspcache_fh);
1858 ocspcache_fh = NULL;
1859
1860 errno = EINVAL;
1861 return -1;
1862 }
1863
1864 /* Make sure that we don't inadvertently get one of the Big Three file
1865 * descriptors (stdin/stdout/stderr), as can happen especially if the
1866 * server has restarted.
1867 */
1868 fd = PR_FH_FD(ocspcache_fh);
1869 if (fd <= STDERR_FILENO) {
1870 int res;
1871
1872 res = pr_fs_get_usable_fd(fd);
1873 if (res < 0) {
1874 pr_log_debug(DEBUG0,
1875 "warning: unable to find good fd for shmcache fd %d: %s", fd,
1876 strerror(errno));
1877
1878 } else {
1879 close(fd);
1880 PR_FH_FD(ocspcache_fh) = res;
1881 }
1882 }
1883
1884 pr_trace_msg(trace_channel, 9,
1885 "requested OCSP response cache file: %s (fd %d)", ocspcache_fh->fh_path,
1886 PR_FH_FD(ocspcache_fh));
1887 pr_trace_msg(trace_channel, 9,
1888 "requested OCSP cache size: %lu bytes", (unsigned long) requested_size);
1889 ocspcache_data = ocsp_cache_get_shm(ocspcache_fh, requested_size);
1890 if (ocspcache_data == NULL) {
1891 xerrno = errno;
1892
1893 pr_trace_msg(trace_channel, 1,
1894 "unable to allocate OCSP response shm: %s", strerror(xerrno));
1895 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1896 ": unable to allocate OCSP response shm: %s", strerror(xerrno));
1897
1898 pr_fsio_close(ocspcache_fh);
1899 ocspcache_fh = NULL;
1900
1901 errno = EINVAL;
1902 return -1;
1903 }
1904
1905 cache->cache_pool = make_sub_pool(permanent_pool);
1906 pr_pool_tag(cache->cache_pool, MOD_TLS_SHMCACHE_VERSION);
1907
1908 return 0;
1909 }
1910
ocsp_cache_close(tls_ocsp_cache_t * cache)1911 static int ocsp_cache_close(tls_ocsp_cache_t *cache) {
1912 if (cache != NULL) {
1913 pr_trace_msg(trace_channel, 9, "closing shmcache ocsp cache %p", cache);
1914 }
1915
1916 if (cache != NULL &&
1917 cache->cache_pool != NULL) {
1918 if (ocspcache_resp_list != NULL) {
1919 register unsigned int i;
1920 struct ocspcache_large_entry *entries;
1921
1922 entries = ocspcache_resp_list->elts;
1923 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
1924 struct ocspcache_large_entry *entry;
1925
1926 entry = &(entries[i]);
1927 pr_memscrub(entry->resp_der, entry->resp_derlen);
1928 }
1929
1930 ocspcache_resp_list = NULL;
1931 }
1932
1933 destroy_pool(cache->cache_pool);
1934 }
1935
1936 if (ocspcache_shmid >= 0) {
1937 int res, xerrno = 0;
1938
1939 PRIVS_ROOT
1940 #if !defined(_POSIX_SOURCE)
1941 res = shmdt((char *) ocspcache_data);
1942 #else
1943 res = shmdt((const char *) ocspcache_data);
1944 #endif
1945 xerrno = errno;
1946 PRIVS_RELINQUISH
1947
1948 if (res < 0) {
1949 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
1950 ": error detaching ocsp shm ID %d: %s", ocspcache_shmid,
1951 strerror(xerrno));
1952 }
1953
1954 ocspcache_data = NULL;
1955 }
1956
1957 pr_fsio_close(ocspcache_fh);
1958 ocspcache_fh = NULL;
1959 return 0;
1960 }
1961
ocsp_cache_add_large_resp(tls_ocsp_cache_t * cache,const char * fingerprint,OCSP_RESPONSE * resp,time_t resp_age)1962 static int ocsp_cache_add_large_resp(tls_ocsp_cache_t *cache,
1963 const char *fingerprint, OCSP_RESPONSE *resp, time_t resp_age) {
1964 struct ocspcache_large_entry *entry = NULL;
1965 int resp_derlen = 0;
1966 unsigned char *ptr;
1967
1968 resp_derlen = i2d_OCSP_RESPONSE(resp, NULL);
1969
1970 if (resp_derlen > TLS_MAX_OCSP_RESPONSE_SIZE) {
1971 /* We may get responses to add to the list which do not exceed the max
1972 * size, but instead are here because we couldn't get the lock on the
1973 * shmcache. Don't track these in the 'exceeded' stats'.
1974 */
1975
1976 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) == 0) {
1977 ocspcache_data->nexceeded++;
1978 if ((size_t) resp_derlen > ocspcache_data->exceeded_maxsz) {
1979 ocspcache_data->exceeded_maxsz = resp_derlen;
1980 }
1981
1982 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
1983 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
1984 }
1985
1986 } else {
1987 tls_log("shmcache: error write-locking shmcache: %s", strerror(errno));
1988 }
1989 }
1990
1991 if (ocspcache_resp_list != NULL) {
1992 register unsigned int i;
1993 struct ocspcache_large_entry *entries;
1994 time_t now;
1995
1996 /* Look for any expired responses in the list to overwrite/reuse. */
1997 entries = ocspcache_resp_list->elts;
1998 now = time(NULL);
1999 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
2000 entry = &(entries[i]);
2001
2002 if (entry->age > (now - 3600)) {
2003 /* This entry has expired; clear and reuse its slot. */
2004 entry->age = 0;
2005 pr_memscrub(entry->resp_der, entry->resp_derlen);
2006 entry->resp_derlen = 0;
2007 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
2008 entry->fingerprint_len = 0;
2009
2010 break;
2011 }
2012 }
2013
2014 } else {
2015 ocspcache_resp_list = make_array(cache->cache_pool, 1,
2016 sizeof(struct ocspcache_large_entry));
2017 entry = push_array(ocspcache_resp_list);
2018 }
2019
2020 /* Be defensive, and catch the case where entry might still be null here. */
2021 if (entry == NULL) {
2022 errno = EPERM;
2023 return -1;
2024 }
2025
2026 entry->age = resp_age;
2027 entry->fingerprint_len = strlen(fingerprint);
2028 entry->fingerprint = palloc(cache->cache_pool, entry->fingerprint_len);
2029 memcpy(entry->fingerprint, fingerprint, entry->fingerprint_len);
2030 entry->resp_derlen = resp_derlen;
2031 entry->resp_der = palloc(cache->cache_pool, resp_derlen);
2032
2033 ptr = entry->resp_der;
2034 i2d_OCSP_RESPONSE(resp, &ptr);
2035
2036 return 0;
2037 }
2038
ocsp_cache_add(tls_ocsp_cache_t * cache,const char * fingerprint,OCSP_RESPONSE * resp,time_t resp_age)2039 static int ocsp_cache_add(tls_ocsp_cache_t *cache, const char *fingerprint,
2040 OCSP_RESPONSE *resp, time_t resp_age) {
2041 register unsigned int i;
2042 unsigned int h, idx, last;
2043 int found_slot = FALSE, need_lock = TRUE, res = 0, resp_derlen;
2044 size_t fingerprint_len;
2045
2046 pr_trace_msg(trace_channel, 9, "adding response to shmcache ocsp cache %p",
2047 cache);
2048
2049 /* First we need to find out how much space is needed for the serialized
2050 * response data. There is no known maximum size for OCSP response data;
2051 * this module is currently designed to allow only up to a certain size.
2052 */
2053
2054 resp_derlen = i2d_OCSP_RESPONSE(resp, NULL);
2055 if (resp_derlen <= 0) {
2056 pr_trace_msg(trace_channel, 1,
2057 "error DER-encoding OCSP response: %s", shmcache_get_errors());
2058 errno = EINVAL;
2059 return -1;
2060 }
2061
2062 if (resp_derlen > TLS_MAX_OCSP_RESPONSE_SIZE) {
2063 tls_log("shmcache: length of serialized OCSP response data (%d) exceeds "
2064 "maximum size (%u), unable to add to shared shmcache", resp_derlen,
2065 TLS_MAX_OCSP_RESPONSE_SIZE);
2066
2067 /* Instead of rejecting the add here, we add the response to a "large
2068 * response" list. Thus the large response would still be cached per
2069 * process and will not be lost.
2070 *
2071 * XXX We should also track how often this happens, and possibly trigger
2072 * a shmcache resize (using a larger record size, vs larger cache size)
2073 * so that we can cache these large records in the shm segment.
2074 */
2075
2076 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
2077 }
2078
2079 if (ocspcache_data->od_listlen == ocspcache_data->od_listsz) {
2080 /* It appears that the cache is full. Flush the oldest response. */
2081 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) == 0) {
2082 if (ocsp_cache_flush() > 0) {
2083 /* If we made room, then do NOT release the lock; we keep the lock
2084 * so that we can add the response.
2085 */
2086 need_lock = FALSE;
2087
2088 } else {
2089 /* Release the lock, and use the "large response" list fallback. */
2090 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2091 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
2092 }
2093
2094 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
2095 }
2096
2097 } else {
2098 tls_log("shmcache: unable to flush ocsp shmcache: error write-locking "
2099 "shmcache: %s", strerror(errno));
2100
2101 /* Add this response to the "large response" list instead as a
2102 * fallback.
2103 */
2104 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
2105 }
2106 }
2107
2108 /* Hash the key, start looking for an open slot. */
2109 fingerprint_len = strlen(fingerprint);
2110 h = shmcache_hash((unsigned char *) fingerprint, fingerprint_len);
2111 idx = h % ocspcache_data->od_listsz;
2112
2113 if (need_lock) {
2114 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) < 0) {
2115 tls_log("shmcache: unable to add response to ocsp shmcache: error "
2116 "write-locking shmcache: %s", strerror(errno));
2117
2118 /* Add this response to the "large response" list instead as a
2119 * fallback.
2120 */
2121 return ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
2122 }
2123 }
2124
2125 i = idx;
2126 last = idx > 0 ? (idx - 1) : 0;
2127
2128 do {
2129 struct ocspcache_entry *entry;
2130
2131 pr_signals_handle();
2132
2133 /* Look for the first open slot (i.e. fingerprint_len == 0). */
2134 entry = &(ocspcache_data->od_entries[i]);
2135 if (entry->fingerprint_len == 0) {
2136 unsigned char *ptr;
2137
2138 entry->age = resp_age;
2139 entry->fingerprint_len = fingerprint_len;
2140 memcpy(entry->fingerprint, fingerprint, fingerprint_len);
2141 entry->resp_derlen = resp_derlen;
2142
2143 ptr = entry->resp_der;
2144 i2d_OCSP_RESPONSE(resp, &ptr);
2145
2146 ocspcache_data->od_listlen++;
2147 ocspcache_data->nstored++;
2148
2149 found_slot = TRUE;
2150 break;
2151 }
2152
2153 if (i < ocspcache_data->od_listsz) {
2154 i++;
2155
2156 } else {
2157 i = 0;
2158 }
2159 } while (i != last);
2160
2161 /* There is a race condition possible between the open slots check
2162 * above and the scan through the slots. So if we didn't actually find
2163 * an open slot at this point, add it to the "large response" list.
2164 */
2165 if (!found_slot) {
2166 res = ocsp_cache_add_large_resp(cache, fingerprint, resp, resp_age);
2167 }
2168
2169 if (need_lock) {
2170 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2171 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
2172 }
2173 }
2174
2175 return res;
2176 }
2177
ocsp_cache_get(tls_ocsp_cache_t * cache,const char * fingerprint,time_t * resp_age)2178 static OCSP_RESPONSE *ocsp_cache_get(tls_ocsp_cache_t *cache,
2179 const char *fingerprint, time_t *resp_age) {
2180 unsigned int h, idx;
2181 OCSP_RESPONSE *resp = NULL;
2182 size_t fingerprint_len = 0;
2183
2184 pr_trace_msg(trace_channel, 9,
2185 "getting response from shmcache ocsp cache %p", cache);
2186
2187 fingerprint_len = strlen(fingerprint);
2188
2189 /* Look for the requested response in the "large response" list first. */
2190 if (ocspcache_resp_list != NULL) {
2191 register unsigned int i;
2192 struct ocspcache_large_entry *entries;
2193
2194 entries = ocspcache_resp_list->elts;
2195 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
2196 struct ocspcache_large_entry *entry;
2197
2198 entry = &(entries[i]);
2199 if (entry->fingerprint_len > 0 &&
2200 entry->fingerprint_len == fingerprint_len &&
2201 memcmp(entry->fingerprint, fingerprint, fingerprint_len) == 0) {
2202 const unsigned char *ptr;
2203
2204 ptr = entry->resp_der;
2205 resp = d2i_OCSP_RESPONSE(NULL, &ptr, entry->resp_derlen);
2206 if (resp == NULL) {
2207 tls_log("shmcache: error retrieving response from ocsp cache: %s",
2208 shmcache_get_errors());
2209
2210 } else {
2211 *resp_age = entry->age;
2212 break;
2213 }
2214 }
2215 }
2216 }
2217
2218 if (resp) {
2219 return resp;
2220 }
2221
2222 h = shmcache_hash((unsigned char *) fingerprint, fingerprint_len);
2223 idx = h % ocspcache_data->od_listsz;
2224
2225 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) == 0) {
2226 register unsigned int i;
2227 unsigned int last;
2228
2229 i = idx;
2230 last = idx > 0 ? (idx -1) : 0;
2231
2232 do {
2233 struct ocspcache_entry *entry;
2234
2235 pr_signals_handle();
2236
2237 entry = &(ocspcache_data->od_entries[i]);
2238 if (entry->fingerprint_len > 0 &&
2239 entry->fingerprint_len == fingerprint_len &&
2240 memcmp(entry->fingerprint, fingerprint, fingerprint_len) == 0) {
2241 const unsigned char *ptr;
2242
2243 /* Don't forget to update the stats. */
2244
2245 ptr = entry->resp_der;
2246 resp = d2i_OCSP_RESPONSE(NULL, &ptr, entry->resp_derlen);
2247 if (resp != NULL) {
2248 *resp_age = entry->age;
2249 ocspcache_data->nhits++;
2250
2251 } else {
2252 tls_log("shmcache: error retrieving response from ocsp cache: %s",
2253 shmcache_get_errors());
2254 ocspcache_data->nerrors++;
2255 }
2256
2257 break;
2258 }
2259
2260 if (i < ocspcache_data->od_listsz) {
2261 i++;
2262
2263 } else {
2264 i = 0;
2265 }
2266 } while (i != last);
2267
2268 if (resp == NULL) {
2269 ocspcache_data->nmisses++;
2270 errno = ENOENT;
2271 }
2272
2273 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2274 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
2275 }
2276
2277 } else {
2278 tls_log("shmcache: unable to retrieve response from ocsp cache: error "
2279 "write-locking shmcache: %s", strerror(errno));
2280
2281 errno = EPERM;
2282 }
2283
2284 return resp;
2285 }
2286
ocsp_cache_delete(tls_ocsp_cache_t * cache,const char * fingerprint)2287 static int ocsp_cache_delete(tls_ocsp_cache_t *cache, const char *fingerprint) {
2288 unsigned int h, idx;
2289 int res;
2290 size_t fingerprint_len = 0;
2291
2292 pr_trace_msg(trace_channel, 9,
2293 "removing response from shmcache ocsp cache %p", cache);
2294
2295 fingerprint_len = strlen(fingerprint);
2296
2297 /* Look for the requested response in the "large response" list first. */
2298 if (ocspcache_resp_list != NULL) {
2299 register unsigned int i;
2300 struct ocspcache_large_entry *entries;
2301
2302 entries = ocspcache_resp_list->elts;
2303 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
2304 struct ocspcache_large_entry *entry;
2305
2306 entry = &(entries[i]);
2307 if (entry->fingerprint_len == fingerprint_len &&
2308 memcmp(entry->fingerprint, fingerprint, fingerprint_len) == 0) {
2309
2310 pr_memscrub(entry->resp_der, entry->resp_derlen);
2311 entry->resp_derlen = 0;
2312 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
2313 entry->fingerprint_len = 0;
2314 entry->age = 0;
2315 return 0;
2316 }
2317 }
2318 }
2319
2320 h = shmcache_hash((unsigned char *) fingerprint, fingerprint_len);
2321 idx = h % ocspcache_data->od_listsz;
2322
2323 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) == 0) {
2324 register unsigned int i;
2325 unsigned int last;
2326
2327 i = idx;
2328 last = idx > 0 ? (idx - 1) : 0;
2329
2330 do {
2331 struct ocspcache_entry *entry;
2332
2333 pr_signals_handle();
2334
2335 entry = &(ocspcache_data->od_entries[i]);
2336 if (entry->fingerprint_len == fingerprint_len &&
2337 memcmp(entry->fingerprint, fingerprint, fingerprint_len) == 0) {
2338 time_t now;
2339
2340 pr_memscrub(entry->resp_der, entry->resp_derlen);
2341 entry->resp_derlen = 0;
2342 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
2343 entry->fingerprint_len = 0;
2344
2345 if (ocspcache_data->od_listlen > 0) {
2346 ocspcache_data->od_listlen--;
2347 }
2348
2349 /* Don't forget to update the stats. */
2350 now = time(NULL);
2351 if (entry->age > (now - 3600)) {
2352 ocspcache_data->nexpired++;
2353
2354 } else {
2355 ocspcache_data->ndeleted++;
2356 }
2357
2358 entry->age = 0;
2359 break;
2360 }
2361
2362 if (i < ocspcache_data->od_listsz) {
2363 i++;
2364
2365 } else {
2366 i = 0;
2367 }
2368
2369 } while (i != last);
2370
2371 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2372 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
2373 }
2374
2375 res = 0;
2376
2377 } else {
2378 tls_log("shmcache: unable to delete response from ocsp cache: error "
2379 "write-locking shmcache: %s", strerror(errno));
2380
2381 errno = EPERM;
2382 res = -1;
2383 }
2384
2385 return res;
2386 }
2387
ocsp_cache_clear(tls_ocsp_cache_t * cache)2388 static int ocsp_cache_clear(tls_ocsp_cache_t *cache) {
2389 register unsigned int i;
2390 int res;
2391
2392 pr_trace_msg(trace_channel, 9, "clearing shmcache ocsp cache %p", cache);
2393
2394 if (ocspcache_shmid < 0) {
2395 errno = EINVAL;
2396 return -1;
2397 }
2398
2399 if (ocspcache_resp_list != NULL) {
2400 struct ocspcache_large_entry *entries;
2401
2402 entries = ocspcache_resp_list->elts;
2403 for (i = 0; i < ocspcache_resp_list->nelts; i++) {
2404 struct ocspcache_large_entry *entry;
2405
2406 entry = &(entries[i]);
2407 entry->age = 0;
2408 pr_memscrub(entry->resp_der, entry->resp_derlen);
2409 entry->resp_derlen = 0;
2410 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
2411 entry->fingerprint_len = 0;
2412 }
2413 }
2414
2415 if (shmcache_lock_shm(ocspcache_fh, F_WRLCK) < 0) {
2416 tls_log("shmcache: unable to clear cache: error write-locking shmcache: %s",
2417 strerror(errno));
2418 return -1;
2419 }
2420
2421 for (i = 0; i < ocspcache_data->od_listsz; i++) {
2422 struct ocspcache_entry *entry;
2423
2424 entry = &(ocspcache_data->od_entries[i]);
2425
2426 entry->age = 0;
2427 pr_memscrub(entry->resp_der, entry->resp_derlen);
2428 entry->resp_derlen = 0;
2429 pr_memscrub(entry->fingerprint, entry->fingerprint_len);
2430 entry->fingerprint_len = 0;
2431 }
2432
2433 res = ocspcache_data->od_listlen;
2434 ocspcache_data->od_listlen = 0;
2435
2436 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2437 tls_log("shmcache: error unlocking shmcache: %s", strerror(errno));
2438 }
2439
2440 return res;
2441 }
2442
ocsp_cache_remove(tls_ocsp_cache_t * cache)2443 static int ocsp_cache_remove(tls_ocsp_cache_t *cache) {
2444 int res;
2445 struct shmid_ds ds;
2446 const char *cache_file;
2447
2448 if (ocspcache_fh == NULL) {
2449 return 0;
2450 }
2451
2452 if (cache != NULL) {
2453 pr_trace_msg(trace_channel, 9, "removing shmcache ocsp cache %p", cache);
2454 }
2455
2456 cache_file = ocspcache_fh->fh_path;
2457 (void) ocsp_cache_close(cache);
2458
2459 if (ocspcache_shmid < 0) {
2460 errno = EINVAL;
2461 return -1;
2462 }
2463
2464 pr_log_debug(DEBUG9, MOD_TLS_SHMCACHE_VERSION
2465 ": attempting to remove OCSP response cache shm ID %d", ocspcache_shmid);
2466
2467 PRIVS_ROOT
2468 res = shmctl(ocspcache_shmid, IPC_RMID, &ds);
2469 PRIVS_RELINQUISH
2470
2471 if (res < 0) {
2472 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
2473 ": error removing OCSP response cache shm ID %d: %s", ocspcache_shmid,
2474 strerror(errno));
2475
2476 } else {
2477 pr_log_debug(DEBUG9, MOD_TLS_SHMCACHE_VERSION
2478 ": removed OCSP response cache shm ID %d", ocspcache_shmid);
2479 ocspcache_shmid = -1;
2480 }
2481
2482 /* Don't forget to remove the on-disk file as well. */
2483 unlink(cache_file);
2484
2485 return res;
2486 }
2487
ocsp_cache_status(tls_ocsp_cache_t * cache,void (* statusf)(void *,const char *,...),void * arg,int flags)2488 static int ocsp_cache_status(tls_ocsp_cache_t *cache,
2489 void (*statusf)(void *, const char *, ...), void *arg, int flags) {
2490 int res, xerrno = 0;
2491 struct shmid_ds ds;
2492 pool *tmp_pool;
2493
2494 pr_trace_msg(trace_channel, 9, "checking shmcache ocsp cache %p", cache);
2495
2496 if (shmcache_lock_shm(ocspcache_fh, F_RDLCK) < 0) {
2497 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
2498 ": error read-locking shmcache: %s", strerror(errno));
2499 return -1;
2500 }
2501
2502 tmp_pool = make_sub_pool(permanent_pool);
2503
2504 statusf(arg, "%s", "Shared memory (shm) OCSP response cache provided by "
2505 MOD_TLS_SHMCACHE_VERSION);
2506 statusf(arg, "%s", "");
2507 statusf(arg, "Shared memory segment ID: %d", ocspcache_shmid);
2508
2509 PRIVS_ROOT
2510 res = shmctl(ocspcache_shmid, IPC_STAT, &ds);
2511 xerrno = errno;
2512 PRIVS_RELINQUISH
2513
2514 if (res == 0) {
2515 statusf(arg, "Shared memory segment size: %u bytes",
2516 (unsigned int) ds.shm_segsz);
2517 statusf(arg, "Shared memory cache created on: %s",
2518 pr_strtime3(tmp_pool, ds.shm_ctime, FALSE));
2519 statusf(arg, "Shared memory attach count: %u",
2520 (unsigned int) ds.shm_nattch);
2521
2522 } else {
2523 statusf(arg, "Unable to stat shared memory segment ID %d: %s",
2524 ocspcache_shmid, strerror(xerrno));
2525 }
2526
2527 statusf(arg, "%s", "");
2528 statusf(arg, "Max response cache size: %u", ocspcache_data->od_listsz);
2529 statusf(arg, "Current response cache size: %u", ocspcache_data->od_listlen);
2530 statusf(arg, "%s", "");
2531 statusf(arg, "Cache lifetime hits: %u", ocspcache_data->nhits);
2532 statusf(arg, "Cache lifetime misses: %u", ocspcache_data->nmisses);
2533 statusf(arg, "%s", "");
2534 statusf(arg, "Cache lifetime responses stored: %u", ocspcache_data->nstored);
2535 statusf(arg, "Cache lifetime responses deleted: %u",
2536 ocspcache_data->ndeleted);
2537 statusf(arg, "Cache lifetime responses expired: %u",
2538 ocspcache_data->nexpired);
2539 statusf(arg, "%s", "");
2540 statusf(arg, "Cache lifetime errors handling responses in cache: %u",
2541 ocspcache_data->nerrors);
2542 statusf(arg, "Cache lifetime responses exceeding max entry size: %u",
2543 ocspcache_data->nexceeded);
2544 if (ocspcache_data->nexceeded > 0) {
2545 statusf(arg, " Largest response exceeding max entry size: %u",
2546 ocspcache_data->exceeded_maxsz);
2547 }
2548
2549 if (shmcache_lock_shm(ocspcache_fh, F_UNLCK) < 0) {
2550 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
2551 ": error unlocking shmcache: %s", strerror(errno));
2552 }
2553
2554 destroy_pool(tmp_pool);
2555 return 0;
2556 }
2557
2558 #endif /* PR_USE_OPENSSL_OCSP */
2559
2560 /* Event Handlers
2561 */
2562
2563 /* Daemon PID */
2564 extern pid_t mpid;
2565
shmcache_shutdown_ev(const void * event_data,void * user_data)2566 static void shmcache_shutdown_ev(const void *event_data, void *user_data) {
2567 if (mpid == getpid() &&
2568 ServerType == SERVER_STANDALONE) {
2569
2570 /* Remove external session caches on shutdown; the security policy/config
2571 * may have changed, e.g. becoming more strict, and allow clients to
2572 * resumed cached sessions from a more relaxed security config is not a
2573 * Good Thing at all.
2574 */
2575 sess_cache_remove(NULL);
2576 #if defined(PR_USE_OPENSSL_OCSP)
2577 ocsp_cache_remove(NULL);
2578 #endif /* PR_USE_OPENSSL_OCSP */
2579 }
2580 }
2581
2582 #if defined(PR_SHARED_MODULE)
shmcache_mod_unload_ev(const void * event_data,void * user_data)2583 static void shmcache_mod_unload_ev(const void *event_data, void *user_data) {
2584 if (strcmp("mod_tls_shmcache.c", (const char *) event_data) == 0) {
2585 pr_event_unregister(&tls_shmcache_module, NULL, NULL);
2586 tls_sess_cache_unregister("shm");
2587
2588 /* This clears our cache by detaching and destroying the shared memory
2589 * segment.
2590 */
2591 sess_cache_remove(NULL);
2592
2593 #if defined(PR_USE_OPENSSL_OCSP)
2594 tls_ocsp_cache_unregister("shm");
2595 #endif /* PR_USE_OPENSSL_OCSP */
2596 }
2597 }
2598 #endif /* !PR_SHARED_MODULE */
2599
shmcache_restart_ev(const void * event_data,void * user_data)2600 static void shmcache_restart_ev(const void *event_data, void *user_data) {
2601 /* Clear external session caches on shutdown; the security policy/config
2602 * may have changed, e.g. becoming more strict, and allow clients to
2603 * resumed cached sessions from a more relaxed security config is not a
2604 * Good Thing at all.
2605 */
2606 sess_cache_clear(NULL);
2607 }
2608
2609 /* Initialization functions
2610 */
2611
tls_shmcache_init(void)2612 static int tls_shmcache_init(void) {
2613 #if defined(PR_SHARED_MODULE)
2614 pr_event_register(&tls_shmcache_module, "core.module-unload",
2615 shmcache_mod_unload_ev, NULL);
2616 #endif /* !PR_SHARED_MODULE */
2617 pr_event_register(&tls_shmcache_module, "core.restart", shmcache_restart_ev,
2618 NULL);
2619 pr_event_register(&tls_shmcache_module, "core.shutdown", shmcache_shutdown_ev,
2620 NULL);
2621
2622 /* Prepare our SSL session cache handler. */
2623 memset(&sess_cache, 0, sizeof(sess_cache));
2624 sess_cache.open = sess_cache_open;
2625 sess_cache.close = sess_cache_close;
2626 sess_cache.add = sess_cache_add;
2627 sess_cache.get = sess_cache_get;
2628 sess_cache.delete = sess_cache_delete;
2629 sess_cache.clear = sess_cache_clear;
2630 sess_cache.remove = sess_cache_remove;
2631 sess_cache.status = sess_cache_status;
2632
2633 #ifdef SSL_SESS_CACHE_NO_INTERNAL_LOOKUP
2634 /* Take a chance, and inform OpenSSL that it does not need to use its own
2635 * internal session cache lookups; using the external session cache (i.e. us)
2636 * will be enough.
2637 */
2638 sess_cache.cache_mode = SSL_SESS_CACHE_NO_INTERNAL_LOOKUP;
2639 #endif
2640
2641 /* Register ourselves with mod_tls. */
2642 if (tls_sess_cache_register("shm", &sess_cache) < 0) {
2643 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
2644 ": notice: error registering 'shm' SSL session cache: %s",
2645 strerror(errno));
2646 return -1;
2647 }
2648
2649 #if defined(PR_USE_OPENSSL_OCSP)
2650 /* Prepare our OCSP response cache handler. */
2651 memset(&ocsp_cache, 0, sizeof(ocsp_cache));
2652 ocsp_cache.open = ocsp_cache_open;
2653 ocsp_cache.close = ocsp_cache_close;
2654 ocsp_cache.add = ocsp_cache_add;
2655 ocsp_cache.get = ocsp_cache_get;
2656 ocsp_cache.delete = ocsp_cache_delete;
2657 ocsp_cache.clear = ocsp_cache_clear;
2658 ocsp_cache.remove = ocsp_cache_remove;
2659 ocsp_cache.status = ocsp_cache_status;
2660
2661 /* Register ourselves with mod_tls. */
2662 if (tls_ocsp_cache_register("shm", &ocsp_cache) < 0) {
2663 pr_log_pri(PR_LOG_NOTICE, MOD_TLS_SHMCACHE_VERSION
2664 ": notice: error registering 'shm' OCSP response cache: %s",
2665 strerror(errno));
2666 return -1;
2667 }
2668 #endif /* PR_USE_OPENSSL_OCSP */
2669
2670 return 0;
2671 }
2672
tls_shmcache_sess_init(void)2673 static int tls_shmcache_sess_init(void) {
2674
2675 #ifdef HAVE_MLOCK
2676 if (sesscache_data != NULL) {
2677 int res, xerrno = 0;
2678
2679 /* Make sure the memory is pinned in RAM where possible.
2680 *
2681 * Since this is a session process, we do not need to worry about
2682 * explicitly unlocking the locked memory; that will happen automatically
2683 * when the session process exits.
2684 */
2685 PRIVS_ROOT
2686 res = mlock(sesscache_data, sesscache_datasz);
2687 xerrno = errno;
2688 PRIVS_RELINQUISH
2689
2690 if (res < 0) {
2691 pr_log_debug(DEBUG1, MOD_TLS_SHMCACHE_VERSION
2692 ": error locking 'shm' session cache (%lu bytes) into memory: %s",
2693 (unsigned long) sesscache_datasz, strerror(xerrno));
2694
2695 } else {
2696 pr_log_debug(DEBUG5, MOD_TLS_SHMCACHE_VERSION
2697 ": 'shm' session cache locked into memory (%lu bytes)",
2698 (unsigned long) sesscache_datasz);
2699 }
2700 }
2701 #endif
2702
2703 return 0;
2704 }
2705
2706 /* Module API tables
2707 */
2708
2709 module tls_shmcache_module = {
2710 NULL, NULL,
2711
2712 /* Module API version 2.0 */
2713 0x20,
2714
2715 /* Module name */
2716 "tls_shmcache",
2717
2718 /* Module configuration handler table */
2719 NULL,
2720
2721 /* Module command handler table */
2722 NULL,
2723
2724 /* Module authentication handler table */
2725 NULL,
2726
2727 /* Module initialization function */
2728 tls_shmcache_init,
2729
2730 /* Session initialization function */
2731 tls_shmcache_sess_init,
2732
2733 /* Module version */
2734 MOD_TLS_SHMCACHE_VERSION
2735 };
2736