Postfix3.3.1
tls_scache.c
[詳解]
1 /*++
2 /* NAME
3 /* tls_scache 3
4 /* SUMMARY
5 /* TLS session cache manager
6 /* SYNOPSIS
7 /* #include <tls_scache.h>
8 /*
9 /* TLS_SCACHE *tls_scache_open(dbname, cache_label, verbose, timeout)
10 /* const char *dbname
11 /* const char *cache_label;
12 /* int verbose;
13 /* int timeout;
14 /*
15 /* void tls_scache_close(cache)
16 /* TLS_SCACHE *cache;
17 /*
18 /* int tls_scache_lookup(cache, cache_id, out_session)
19 /* TLS_SCACHE *cache;
20 /* const char *cache_id;
21 /* VSTRING *out_session;
22 /*
23 /* int tls_scache_update(cache, cache_id, session, session_len)
24 /* TLS_SCACHE *cache;
25 /* const char *cache_id;
26 /* const char *session;
27 /* ssize_t session_len;
28 /*
29 /* int tls_scache_sequence(cache, first_next, out_cache_id,
30 /* VSTRING *out_session)
31 /* TLS_SCACHE *cache;
32 /* int first_next;
33 /* char **out_cache_id;
34 /* VSTRING *out_session;
35 /*
36 /* int tls_scache_delete(cache, cache_id)
37 /* TLS_SCACHE *cache;
38 /* const char *cache_id;
39 /*
40 /* TLS_TICKET_KEY *tls_scache_key(keyname, now, timeout)
41 /* unsigned char *keyname;
42 /* time_t now;
43 /* int timeout;
44 /*
45 /* TLS_TICKET_KEY *tls_scache_key_rotate(newkey)
46 /* TLS_TICKET_KEY *newkey;
47 /* DESCRIPTION
48 /* This module maintains Postfix TLS session cache files.
49 /* each session is stored under a lookup key (hostname or
50 /* session ID).
51 /*
52 /* tls_scache_open() opens the specified TLS session cache
53 /* and returns a handle that must be used for subsequent
54 /* access.
55 /*
56 /* tls_scache_close() closes the specified TLS session cache
57 /* and releases memory that was allocated by tls_scache_open().
58 /*
59 /* tls_scache_lookup() looks up the specified session in the
60 /* specified cache, and applies session timeout restrictions.
61 /* Entries that are too old are silently deleted.
62 /*
63 /* tls_scache_update() updates the specified TLS session cache
64 /* with the specified session information.
65 /*
66 /* tls_scache_sequence() iterates over the specified TLS session
67 /* cache and either returns the first or next entry that has not
68 /* timed out, or returns no data. Entries that are too old are
69 /* silently deleted. Specify TLS_SCACHE_SEQUENCE_NOTHING as the
70 /* third and last argument to disable saving of cache entry
71 /* content or cache entry ID information. This is useful when
72 /* purging expired entries. A result value of zero means that
73 /* the end of the cache was reached.
74 /*
75 /* tls_scache_delete() removes the specified cache entry from
76 /* the specified TLS session cache.
77 /*
78 /* tls_scache_key() locates a TLS session ticket key in a 2-element
79 /* in-memory cache. A null result is returned if no unexpired matching
80 /* key is found.
81 /*
82 /* tls_scache_key_rotate() saves a TLS session tickets key in the
83 /* in-memory cache.
84 /*
85 /* Arguments:
86 /* .IP dbname
87 /* The base name of the session cache file.
88 /* .IP cache_label
89 /* A string that is used in logging and error messages.
90 /* .IP verbose
91 /* Do verbose logging of cache operations? (zero == no)
92 /* .IP timeout
93 /* The time after which a session cache entry is considered too old.
94 /* .IP first_next
95 /* One of DICT_SEQ_FUN_FIRST (first cache element) or DICT_SEQ_FUN_NEXT
96 /* (next cache element).
97 /* .IP cache_id
98 /* Session cache lookup key.
99 /* .IP session
100 /* Storage for session information.
101 /* .IP session_len
102 /* The size of the session information in bytes.
103 /* .IP out_cache_id
104 /* .IP out_session
105 /* Storage for saving the cache_id or session information of the
106 /* current cache entry.
107 /*
108 /* Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving
109 /* the session cache ID of the cache entry.
110 /*
111 /* Specify TLS_SCACHE_DONT_NEED_SESSION to avoid
112 /* saving the session information in the cache entry.
113 /* .IP keyname
114 /* Is null when requesting the current encryption keys. Otherwise,
115 /* keyname is a pointer to an array of TLS_TICKET_NAMELEN unsigned
116 /* chars (not NUL terminated) that is an identifier for a key
117 /* previously used to encrypt a session ticket.
118 /* .IP now
119 /* Current epoch time passed by caller.
120 /* .IP timeout
121 /* TLS session ticket encryption lifetime.
122 /* .IP newkey
123 /* TLS session ticket key obtained from tlsmgr(8) to be added to
124  * internal cache.
125 /* DIAGNOSTICS
126 /* These routines terminate with a fatal run-time error
127 /* for unrecoverable database errors. This allows the
128 /* program to restart and reset the database to an
129 /* empty initial state.
130 /*
131 /* tls_scache_open() never returns on failure. All other
132 /* functions return non-zero on success, zero when the
133 /* operation could not be completed.
134 /* LICENSE
135 /* .ad
136 /* .fi
137 /* The Secure Mailer license must be distributed with this software.
138 /* AUTHOR(S)
139 /* Wietse Venema
140 /* IBM T.J. Watson Research
141 /* P.O. Box 704
142 /* Yorktown Heights, NY 10598, USA
143 /*--*/
144 
145 /* System library. */
146 
147 #include <sys_defs.h>
148 
149 #ifdef USE_TLS
150 
151 #include <string.h>
152 #include <stddef.h>
153 
154 /* Utility library. */
155 
156 #include <msg.h>
157 #include <dict.h>
158 #include <stringops.h>
159 #include <mymalloc.h>
160 #include <hex_code.h>
161 #include <myflock.h>
162 #include <vstring.h>
163 #include <timecmp.h>
164 
165 /* Global library. */
166 
167 /* TLS library. */
168 
169 #include <tls_scache.h>
170 
171 /* Application-specific. */
172 
173  /*
174  * Session cache entry format.
175  */
176 typedef struct {
177  time_t timestamp; /* time when saved */
178  char session[1]; /* actually a bunch of bytes */
179 } TLS_SCACHE_ENTRY;
180 
181 static TLS_TICKET_KEY *keys[2];
182 
183  /*
184  * SLMs.
185  */
186 #define STR(x) vstring_str(x)
187 #define LEN(x) VSTRING_LEN(x)
188 
189 /* tls_scache_encode - encode TLS session cache entry */
190 
191 static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
192  const char *session,
193  ssize_t session_len)
194 {
195  TLS_SCACHE_ENTRY *entry;
196  VSTRING *hex_data;
197  ssize_t binary_data_len;
198 
199  /*
200  * Assemble the TLS session cache entry.
201  *
202  * We could eliminate some copying by using incremental encoding, but
203  * sessions are so small that it really does not matter.
204  */
205  binary_data_len = session_len + offsetof(TLS_SCACHE_ENTRY, session);
206  entry = (TLS_SCACHE_ENTRY *) mymalloc(binary_data_len);
207  entry->timestamp = time((time_t *) 0);
208  memcpy(entry->session, session, session_len);
209 
210  /*
211  * Encode the TLS session cache entry.
212  */
213  hex_data = vstring_alloc(2 * binary_data_len + 1);
214  hex_encode(hex_data, (char *) entry, binary_data_len);
215 
216  /*
217  * Logging.
218  */
219  if (cp->verbose)
220  msg_info("write %s TLS cache entry %s: time=%ld [data %ld bytes]",
221  cp->cache_label, cache_id, (long) entry->timestamp,
222  (long) session_len);
223 
224  /*
225  * Clean up.
226  */
227  myfree((void *) entry);
228 
229  return (hex_data);
230 }
231 
232 /* tls_scache_decode - decode TLS session cache entry */
233 
234 static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
235  const char *hex_data, ssize_t hex_data_len,
236  VSTRING *out_session)
237 {
238  TLS_SCACHE_ENTRY *entry;
239  VSTRING *bin_data;
240 
241  /*
242  * Sanity check.
243  */
244  if (hex_data_len < 2 * (offsetof(TLS_SCACHE_ENTRY, session))) {
245  msg_warn("%s TLS cache: truncated entry for %s: %.100s",
246  cp->cache_label, cache_id, hex_data);
247  return (0);
248  }
249 
250  /*
251  * Disassemble the TLS session cache entry.
252  *
253  * No early returns or we have a memory leak.
254  */
255 #define FREE_AND_RETURN(ptr, x) { vstring_free(ptr); return (x); }
256 
257  bin_data = vstring_alloc(hex_data_len / 2 + 1);
258  if (hex_decode(bin_data, hex_data, hex_data_len) == 0) {
259  msg_warn("%s TLS cache: malformed entry for %s: %.100s",
260  cp->cache_label, cache_id, hex_data);
261  FREE_AND_RETURN(bin_data, 0);
262  }
263  entry = (TLS_SCACHE_ENTRY *) STR(bin_data);
264 
265  /*
266  * Logging.
267  */
268  if (cp->verbose)
269  msg_info("read %s TLS cache entry %s: time=%ld [data %ld bytes]",
270  cp->cache_label, cache_id, (long) entry->timestamp,
271  (long) (LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session)));
272 
273  /*
274  * Other mandatory restrictions.
275  */
276  if (entry->timestamp + cp->timeout < time((time_t *) 0))
277  FREE_AND_RETURN(bin_data, 0);
278 
279  /*
280  * Optional output.
281  */
282  if (out_session != 0)
283  vstring_memcpy(out_session, entry->session,
284  LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
285 
286  /*
287  * Clean up.
288  */
289  FREE_AND_RETURN(bin_data, 1);
290 }
291 
292 /* tls_scache_lookup - load session from cache */
293 
294 int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
295  VSTRING *session)
296 {
297  const char *hex_data;
298 
299  /*
300  * Logging.
301  */
302  if (cp->verbose)
303  msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
304 
305  /*
306  * Initialize. Don't leak data.
307  */
308  if (session)
309  VSTRING_RESET(session);
310 
311  /*
312  * Search the cache database.
313  */
314  if ((hex_data = dict_get(cp->db, cache_id)) == 0)
315  return (0);
316 
317  /*
318  * Decode entry and delete if expired or malformed.
319  */
320  if (tls_scache_decode(cp, cache_id, hex_data, strlen(hex_data),
321  session) == 0) {
322  tls_scache_delete(cp, cache_id);
323  return (0);
324  } else {
325  return (1);
326  }
327 }
328 
329 /* tls_scache_update - save session to cache */
330 
331 int tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
332  const char *buf, ssize_t len)
333 {
334  VSTRING *hex_data;
335 
336  /*
337  * Logging.
338  */
339  if (cp->verbose)
340  msg_info("put %s session id=%s [data %ld bytes]",
341  cp->cache_label, cache_id, (long) len);
342 
343  /*
344  * Encode the cache entry.
345  */
346  hex_data = tls_scache_encode(cp, cache_id, buf, len);
347 
348  /*
349  * Store the cache entry.
350  *
351  * XXX Berkeley DB supports huge database keys and values. SDBM seems to
352  * have a finite limit, and DBM simply can't be used at all.
353  */
354  dict_put(cp->db, cache_id, STR(hex_data));
355 
356  /*
357  * Clean up.
358  */
359  vstring_free(hex_data);
360 
361  return (1);
362 }
363 
364 /* tls_scache_sequence - get first/next TLS session cache entry */
365 
366 int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
367  char **out_cache_id,
368  VSTRING *out_session)
369 {
370  const char *member;
371  const char *value;
372  char *saved_cursor;
373  int found_entry;
374  int keep_entry;
375  char *saved_member;
376 
377  /*
378  * XXX Deleting entries while enumerating a map can he tricky. Some map
379  * types have a concept of cursor and support a "delete the current
380  * element" operation. Some map types without cursors don't behave well
381  * when the current first/next entry is deleted (example: with Berkeley
382  * DB < 2, the "next" operation produces garbage). To avoid trouble, we
383  * delete an expired entry after advancing the current first/next
384  * position beyond it, and ignore client requests to delete the current
385  * entry.
386  */
387 
388  /*
389  * Find the first or next database entry. Activate the passivated entry
390  * and check the time stamp. Schedule the entry for deletion if it is too
391  * old.
392  *
393  * Save the member (cache id) so that it will not be clobbered by the
394  * tls_scache_lookup() call below.
395  */
396  found_entry = (dict_seq(cp->db, first_next, &member, &value) == 0);
397  if (found_entry) {
398  keep_entry = tls_scache_decode(cp, member, value, strlen(value),
399  out_session);
400  if (keep_entry && out_cache_id)
401  *out_cache_id = mystrdup(member);
402  saved_member = mystrdup(member);
403  }
404 
405  /*
406  * Delete behind. This is a no-op if an expired cache entry was updated
407  * in the mean time. Use the saved lookup criteria so that the "delete
408  * behind" operation works as promised.
409  *
410  * The delete-behind strategy assumes that all updates are made by a single
411  * process. Otherwise, delete-behind may remove an entry that was updated
412  * after it was scheduled for deletion.
413  */
416  saved_cursor = cp->saved_cursor;
417  cp->saved_cursor = 0;
418  tls_scache_lookup(cp, saved_cursor, (VSTRING *) 0);
419  myfree(saved_cursor);
420  }
421 
422  /*
423  * Otherwise, clean up if this is not the first iteration.
424  */
425  else {
426  if (cp->saved_cursor)
427  myfree(cp->saved_cursor);
428  cp->saved_cursor = 0;
429  }
430 
431  /*
432  * Protect the current first/next entry against explicit or implied
433  * client delete requests, and schedule a bad or expired entry for
434  * deletion. Save the lookup criteria so that the "delete behind"
435  * operation will work as promised.
436  */
437  if (found_entry) {
438  cp->saved_cursor = saved_member;
439  if (keep_entry == 0)
441  }
442  return (found_entry);
443 }
444 
445 /* tls_scache_delete - delete session from cache */
446 
447 int tls_scache_delete(TLS_SCACHE *cp, const char *cache_id)
448 {
449 
450  /*
451  * Logging.
452  */
453  if (cp->verbose)
454  msg_info("delete %s session id=%s", cp->cache_label, cache_id);
455 
456  /*
457  * Do it, unless we would delete the current first/next entry. Some map
458  * types don't have cursors, and some of those don't behave when the
459  * "current" entry is deleted.
460  */
461  return ((cp->saved_cursor != 0 && strcmp(cp->saved_cursor, cache_id) == 0)
462  || dict_del(cp->db, cache_id) == 0);
463 }
464 
465 /* tls_scache_open - open TLS session cache file */
466 
467 TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
468  int verbose, int timeout)
469 {
470  TLS_SCACHE *cp;
471  DICT *dict;
472 
473  /*
474  * Logging.
475  */
476  if (verbose)
477  msg_info("open %s TLS cache %s", cache_label, dbname);
478 
479  /*
480  * Open the dictionary with O_TRUNC, so that we never have to worry about
481  * opening a damaged file after some process terminated abnormally.
482  */
483 #ifdef SINGLE_UPDATER
484 #define DICT_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_OPEN_LOCK \
485  | DICT_FLAG_UTF8_REQUEST)
486 #else
487 #define DICT_FLAGS \
488  (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE \
489  | DICT_FLAG_UTF8_REQUEST)
490 #endif
491 
492  dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS);
493 
494  /*
495  * Sanity checks.
496  */
497  if (dict->update == 0)
498  msg_fatal("dictionary %s does not support update operations", dbname);
499  if (dict->delete == 0)
500  msg_fatal("dictionary %s does not support delete operations", dbname);
501  if (dict->sequence == 0)
502  msg_fatal("dictionary %s does not support sequence operations", dbname);
503 
504  /*
505  * Create the TLS_SCACHE object.
506  */
507  cp = (TLS_SCACHE *) mymalloc(sizeof(*cp));
508  cp->flags = 0;
509  cp->db = dict;
510  cp->cache_label = mystrdup(cache_label);
511  cp->verbose = verbose;
512  cp->timeout = timeout;
513  cp->saved_cursor = 0;
514 
515  return (cp);
516 }
517 
518 /* tls_scache_close - close TLS session cache file */
519 
521 {
522 
523  /*
524  * Logging.
525  */
526  if (cp->verbose)
527  msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
528 
529  /*
530  * Destroy the TLS_SCACHE object.
531  */
532  dict_close(cp->db);
533  myfree(cp->cache_label);
534  if (cp->saved_cursor)
535  myfree(cp->saved_cursor);
536  myfree((void *) cp);
537 }
538 
539 /* tls_scache_key - find session ticket key for given key name */
540 
541 TLS_TICKET_KEY *tls_scache_key(unsigned char *keyname, time_t now, int timeout)
542 {
543  int i;
544 
545  /*
546  * The keys array contains 2 elements, the current signing key and the
547  * previous key.
548  *
549  * When name == 0 we are issuing a ticket, otherwise decrypting an existing
550  * ticket with the given key name. For new tickets we always use the
551  * current key if unexpired. For existing tickets, we use either the
552  * current or previous key with a validation expiration that is timeout
553  * longer than the signing expiration.
554  */
555  if (keyname) {
556  for (i = 0; i < 2 && keys[i]; ++i) {
557  if (memcmp(keyname, keys[i]->name, TLS_TICKET_NAMELEN) == 0) {
558  if (timecmp(keys[i]->tout + timeout, now) > 0)
559  return (keys[i]);
560  break;
561  }
562  }
563  } else if (keys[0]) {
564  if (timecmp(keys[0]->tout, now) > 0)
565  return (keys[0]);
566  }
567  return (0);
568 }
569 
570 /* tls_scache_key_rotate - rotate session ticket keys */
571 
573 {
574 
575  /*
576  * Allocate or re-use storage of retired key, then overwrite it, since
577  * caller's key data is ephemeral.
578  */
579  if (keys[1] == 0)
580  keys[1] = (TLS_TICKET_KEY *) mymalloc(sizeof(*newkey));
581  *keys[1] = *newkey;
582  newkey = keys[1];
583 
584  /*
585  * Rotate if required, ensuring that the keys are sorted by expiration
586  * time with keys[0] expiring last.
587  */
588  if (keys[0] == 0 || keys[0]->tout < keys[1]->tout) {
589  keys[1] = keys[0];
590  keys[0] = newkey;
591  }
592  return (newkey);
593 }
594 
595 #endif
void myfree(void *ptr)
Definition: mymalloc.c:207
DICT * db
Definition: tls_scache.h:25
void tls_scache_close(TLS_SCACHE *)
char * mystrdup(const char *str)
Definition: mymalloc.c:225
#define dict_put(dp, key, val)
Definition: dict.h:237
#define TLS_TICKET_NAMELEN
Definition: tls_scache.h:32
int(* delete)(struct DICT *, const char *)
Definition: dict.h:84
char * saved_cursor
Definition: tls_scache.h:29
char * name
Definition: dict.h:80
char * cache_label
Definition: tls_scache.h:26
TLS_TICKET_KEY * tls_scache_key_rotate(TLS_TICKET_KEY *)
int tls_scache_sequence(TLS_SCACHE *, int, char **, VSTRING *)
#define LEN
Definition: cleanup_addr.c:106
DICT * dict_open(const char *, int, int)
Definition: dict_open.c:421
int timecmp(time_t t1, time_t t2)
Definition: timecmp.c:36
TLS_SCACHE * tls_scache_open(const char *, const char *, int, int)
Definition: dict.h:78
int tls_scache_lookup(TLS_SCACHE *, const char *, VSTRING *)
int(* update)(struct DICT *, const char *, const char *)
Definition: dict.h:83
#define dict_get(dp, key)
Definition: dict.h:236
VSTRING * hex_encode(VSTRING *result, const char *in, ssize_t len)
Definition: hex_code.c:62
TLS_TICKET_KEY * tls_scache_key(unsigned char *, time_t, int)
int tls_scache_update(TLS_SCACHE *, const char *, const char *, ssize_t)
#define dict_seq(dp, f, key, val)
Definition: dict.h:239
#define VSTRING_RESET(vp)
Definition: vstring.h:77
#define STR(x)
Definition: anvil.c:518
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
int timeout
Definition: tls_scache.h:28
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
#define DICT_FLAGS
int verbose
Definition: tls_scache.h:27
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
int tls_scache_delete(TLS_SCACHE *, const char *)
VSTRING * vstring_memcpy(VSTRING *vp, const char *src, ssize_t len)
Definition: vstring.c:483
int(* sequence)(struct DICT *, int, const char **, const char **)
Definition: dict.h:85
VSTRING * hex_decode(VSTRING *result, const char *in, ssize_t len)
Definition: hex_code.c:80
#define dict_del(dp, key)
Definition: dict.h:238
#define TLS_SCACHE_FLAG_DEL_SAVED_CURSOR
Definition: tls_scache.h:45
#define dict_close(dp)
Definition: dict.h:240
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
void msg_info(const char *fmt,...)
Definition: msg.c:199