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