Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / tls / tls_scache.c
blob9e9f972635bfb596f54920e57df9b52626204e0a
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* tls_scache 3
6 /* SUMMARY
7 /* TLS session cache manager
8 /* SYNOPSIS
9 /* #include <tls_scache.h>
11 /* TLS_SCACHE *tls_scache_open(dbname, cache_label, verbose, timeout)
12 /* const char *dbname
13 /* const char *cache_label;
14 /* int verbose;
15 /* int timeout;
17 /* void tls_scache_close(cache)
18 /* TLS_SCACHE *cache;
20 /* int tls_scache_lookup(cache, cache_id, out_session)
21 /* TLS_SCACHE *cache;
22 /* const char *cache_id;
23 /* VSTRING *out_session;
25 /* int tls_scache_update(cache, cache_id, session, session_len)
26 /* TLS_SCACHE *cache;
27 /* const char *cache_id;
28 /* const char *session;
29 /* ssize_t session_len;
31 /* int tls_scache_sequence(cache, first_next, out_cache_id,
32 /* VSTRING *out_session)
33 /* TLS_SCACHE *cache;
34 /* int first_next;
35 /* char **out_cache_id;
36 /* VSTRING *out_session;
38 /* int tls_scache_delete(cache, cache_id)
39 /* TLS_SCACHE *cache;
40 /* const char *cache_id;
41 /* DESCRIPTION
42 /* This module maintains Postfix TLS session cache files.
43 /* each session is stored under a lookup key (hostname or
44 /* session ID).
46 /* tls_scache_open() opens the specified TLS session cache
47 /* and returns a handle that must be used for subsequent
48 /* access.
50 /* tls_scache_close() closes the specified TLS session cache
51 /* and releases memory that was allocated by tls_scache_open().
53 /* tls_scache_lookup() looks up the specified session in the
54 /* specified cache, and applies session timeout restrictions.
55 /* Entries that are too old are silently deleted.
57 /* tls_scache_update() updates the specified TLS session cache
58 /* with the specified session information.
60 /* tls_scache_sequence() iterates over the specified TLS session
61 /* cache and either returns the first or next entry that has not
62 /* timed out, or returns no data. Entries that are too old are
63 /* silently deleted. Specify TLS_SCACHE_SEQUENCE_NOTHING as the
64 /* third and last argument to disable saving of cache entry
65 /* content or cache entry ID information. This is useful when
66 /* purging expired entries. A result value of zero means that
67 /* the end of the cache was reached.
69 /* tls_scache_delete() removes the specified cache entry from
70 /* the specified TLS session cache.
72 /* Arguments:
73 /* .IP dbname
74 /* The base name of the session cache file.
75 /* .IP cache_label
76 /* A string that is used in logging and error messages.
77 /* .IP verbose
78 /* Do verbose logging of cache operations? (zero == no)
79 /* .IP timeout
80 /* The time after wich a session cache entry is considered too old.
81 /* .IP first_next
82 /* One of DICT_SEQ_FUN_FIRST (first cache element) or DICT_SEQ_FUN_NEXT
83 /* (next cache element).
84 /* .IP cache_id
85 /* Session cache lookup key.
86 /* .IP session
87 /* Storage for session information.
88 /* .IP session_len
89 /* The size of the session information in bytes.
90 /* .IP out_cache_id
91 /* .IP out_session
92 /* Storage for saving the cache_id or session information of the
93 /* current cache entry.
95 /* Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving
96 /* the session cache ID of the cache entry.
98 /* Specify TLS_SCACHE_DONT_NEED_SESSION to avoid
99 /* saving the session information in the cache entry.
100 /* DIAGNOSTICS
101 /* These routines terminate with a fatal run-time error
102 /* for unrecoverable database errors. This allows the
103 /* program to restart and reset the database to an
104 /* empty initial state.
106 /* tls_scache_open() never returns on failure. All other
107 /* functions return non-zero on success, zero when the
108 /* operation could not be completed.
109 /* LICENSE
110 /* .ad
111 /* .fi
112 /* The Secure Mailer license must be distributed with this software.
113 /* AUTHOR(S)
114 /* Wietse Venema
115 /* IBM T.J. Watson Research
116 /* P.O. Box 704
117 /* Yorktown Heights, NY 10598, USA
118 /*--*/
120 /* System library. */
122 #include <sys_defs.h>
124 #ifdef USE_TLS
126 #include <string.h>
127 #include <stddef.h>
129 /* Utility library. */
131 #include <msg.h>
132 #include <dict.h>
133 #include <stringops.h>
134 #include <mymalloc.h>
135 #include <hex_code.h>
136 #include <myflock.h>
137 #include <vstring.h>
139 /* Global library. */
141 /* TLS library. */
143 #include <tls_scache.h>
145 /* Application-specific. */
148 * Session cache entry format.
150 typedef struct {
151 time_t timestamp; /* time when saved */
152 char session[1]; /* actually a bunch of bytes */
153 } TLS_SCACHE_ENTRY;
156 * SLMs.
158 #define STR(x) vstring_str(x)
159 #define LEN(x) VSTRING_LEN(x)
161 /* tls_scache_encode - encode TLS session cache entry */
163 static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
164 const char *session,
165 ssize_t session_len)
167 TLS_SCACHE_ENTRY *entry;
168 VSTRING *hex_data;
169 ssize_t binary_data_len;
172 * Assemble the TLS session cache entry.
174 * We could eliminate some copying by using incremental encoding, but
175 * sessions are so small that it really does not matter.
177 binary_data_len = session_len + offsetof(TLS_SCACHE_ENTRY, session);
178 entry = (TLS_SCACHE_ENTRY *) mymalloc(binary_data_len);
179 entry->timestamp = time((time_t *) 0);
180 memcpy(entry->session, session, session_len);
183 * Encode the TLS session cache entry.
185 hex_data = vstring_alloc(2 * binary_data_len + 1);
186 hex_encode(hex_data, (char *) entry, binary_data_len);
189 * Logging.
191 if (cp->verbose)
192 msg_info("write %s TLS cache entry %s: time=%ld [data %ld bytes]",
193 cp->cache_label, cache_id, (long) entry->timestamp,
194 (long) session_len);
197 * Clean up.
199 myfree((char *) entry);
201 return (hex_data);
204 /* tls_scache_decode - decode TLS session cache entry */
206 static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
207 const char *hex_data, ssize_t hex_data_len,
208 VSTRING *out_session)
210 TLS_SCACHE_ENTRY *entry;
211 VSTRING *bin_data;
214 * Sanity check.
216 if (hex_data_len < 2 * (offsetof(TLS_SCACHE_ENTRY, session))) {
217 msg_warn("%s TLS cache: truncated entry for %s: %.100s",
218 cp->cache_label, cache_id, hex_data);
219 return (0);
223 * Disassemble the TLS session cache entry.
225 * No early returns or we have a memory leak.
227 #define FREE_AND_RETURN(ptr, x) { vstring_free(ptr); return (x); }
229 bin_data = vstring_alloc(hex_data_len / 2 + 1);
230 if (hex_decode(bin_data, hex_data, hex_data_len) == 0) {
231 msg_warn("%s TLS cache: malformed entry for %s: %.100s",
232 cp->cache_label, cache_id, hex_data);
233 FREE_AND_RETURN(bin_data, 0);
235 entry = (TLS_SCACHE_ENTRY *) STR(bin_data);
238 * Logging.
240 if (cp->verbose)
241 msg_info("read %s TLS cache entry %s: time=%ld [data %ld bytes]",
242 cp->cache_label, cache_id, (long) entry->timestamp,
243 (long) (LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session)));
246 * Other mandatory restrictions.
248 if (entry->timestamp + cp->timeout < time((time_t *) 0))
249 FREE_AND_RETURN(bin_data, 0);
252 * Optional output.
254 if (out_session != 0)
255 vstring_memcpy(out_session, entry->session,
256 LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
259 * Clean up.
261 FREE_AND_RETURN(bin_data, 1);
264 /* tls_scache_lookup - load session from cache */
266 int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
267 VSTRING *session)
269 const char *hex_data;
272 * Logging.
274 if (cp->verbose)
275 msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
278 * Initialize. Don't leak data.
280 if (session)
281 VSTRING_RESET(session);
284 * Search the cache database.
286 if ((hex_data = dict_get(cp->db, cache_id)) == 0)
287 return (0);
290 * Decode entry and delete if expired or malformed.
292 if (tls_scache_decode(cp, cache_id, hex_data, strlen(hex_data),
293 session) == 0) {
294 tls_scache_delete(cp, cache_id);
295 return (0);
296 } else {
297 return (1);
301 /* tls_scache_update - save session to cache */
303 int tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
304 const char *buf, ssize_t len)
306 VSTRING *hex_data;
309 * Logging.
311 if (cp->verbose)
312 msg_info("put %s session id=%s [data %ld bytes]",
313 cp->cache_label, cache_id, (long) len);
316 * Encode the cache entry.
318 hex_data = tls_scache_encode(cp, cache_id, buf, len);
321 * Store the cache entry.
323 * XXX Berkeley DB supports huge database keys and values. SDBM seems to
324 * have a finite limit, and DBM simply can't be used at all.
326 dict_put(cp->db, cache_id, STR(hex_data));
329 * Clean up.
331 vstring_free(hex_data);
333 return (1);
336 /* tls_scache_sequence - get first/next TLS session cache entry */
338 int tls_scache_sequence(TLS_SCACHE *cp, int first_next,
339 char **out_cache_id,
340 VSTRING *out_session)
342 const char *member;
343 const char *value;
344 char *saved_cursor;
345 int found_entry;
346 int keep_entry;
347 char *saved_member;
350 * XXX Deleting entries while enumerating a map can he tricky. Some map
351 * types have a concept of cursor and support a "delete the current
352 * element" operation. Some map types without cursors don't behave well
353 * when the current first/next entry is deleted (example: with Berkeley
354 * DB < 2, the "next" operation produces garbage). To avoid trouble, we
355 * delete an expired entry after advancing the current first/next
356 * position beyond it, and ignore client requests to delete the current
357 * entry.
361 * Find the first or next database entry. Activate the passivated entry
362 * and check the time stamp. Schedule the entry for deletion if it is too
363 * old.
365 * Save the member (cache id) so that it will not be clobbered by the
366 * tls_scache_lookup() call below.
368 found_entry = (dict_seq(cp->db, first_next, &member, &value) == 0);
369 if (found_entry) {
370 keep_entry = tls_scache_decode(cp, member, value, strlen(value),
371 out_session);
372 if (keep_entry && out_cache_id)
373 *out_cache_id = mystrdup(member);
374 saved_member = mystrdup(member);
378 * Delete behind. This is a no-op if an expired cache entry was updated
379 * in the mean time. Use the saved lookup criteria so that the "delete
380 * behind" operation works as promised.
382 if (cp->flags & TLS_SCACHE_FLAG_DEL_SAVED_CURSOR) {
383 cp->flags &= ~TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
384 saved_cursor = cp->saved_cursor;
385 cp->saved_cursor = 0;
386 tls_scache_lookup(cp, saved_cursor, (VSTRING *) 0);
387 myfree(saved_cursor);
391 * Otherwise, clean up if this is not the first iteration.
393 else {
394 if (cp->saved_cursor)
395 myfree(cp->saved_cursor);
396 cp->saved_cursor = 0;
400 * Protect the current first/next entry against explicit or implied
401 * client delete requests, and schedule a bad or expired entry for
402 * deletion. Save the lookup criteria so that the "delete behind"
403 * operation will work as promised.
405 if (found_entry) {
406 cp->saved_cursor = saved_member;
407 if (keep_entry == 0)
408 cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
410 return (found_entry);
413 /* tls_scache_delete - delete session from cache */
415 int tls_scache_delete(TLS_SCACHE *cp, const char *cache_id)
419 * Logging.
421 if (cp->verbose)
422 msg_info("delete %s session id=%s", cp->cache_label, cache_id);
425 * Do it, unless we would delete the current first/next entry. Some map
426 * types don't have cursors, and some of those don't behave when the
427 * "current" entry is deleted.
429 return ((cp->saved_cursor != 0 && strcmp(cp->saved_cursor, cache_id) == 0)
430 || dict_del(cp->db, cache_id) == 0);
433 /* tls_scache_open - open TLS session cache file */
435 TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
436 int verbose, int timeout)
438 TLS_SCACHE *cp;
439 DICT *dict;
442 * Logging.
444 if (verbose)
445 msg_info("open %s TLS cache %s", cache_label, dbname);
448 * Open the dictionary with O_TRUNC, so that we never have to worry about
449 * opening a damaged file after some process terminated abnormally.
451 #ifdef SINGLE_UPDATER
452 #define DICT_FLAGS (DICT_FLAG_DUP_REPLACE)
453 #else
454 #define DICT_FLAGS \
455 (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE)
456 #endif
458 dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS);
461 * Sanity checks.
463 if (dict->lock_fd < 0)
464 msg_fatal("dictionary %s is not a regular file", dbname);
465 #ifdef SINGLE_UPDATER
466 if (myflock(dict->lock_fd, INTERNAL_LOCK,
467 MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0)
468 msg_fatal("cannot lock dictionary %s for exclusive use: %m", dbname);
469 #endif
470 if (dict->update == 0)
471 msg_fatal("dictionary %s does not support update operations", dbname);
472 if (dict->delete == 0)
473 msg_fatal("dictionary %s does not support delete operations", dbname);
474 if (dict->sequence == 0)
475 msg_fatal("dictionary %s does not support sequence operations", dbname);
478 * Create the TLS_SCACHE object.
480 cp = (TLS_SCACHE *) mymalloc(sizeof(*cp));
481 cp->flags = 0;
482 cp->db = dict;
483 cp->cache_label = mystrdup(cache_label);
484 cp->verbose = verbose;
485 cp->timeout = timeout;
486 cp->saved_cursor = 0;
488 return (cp);
491 /* tls_scache_close - close TLS session cache file */
493 void tls_scache_close(TLS_SCACHE *cp)
497 * Logging.
499 if (cp->verbose)
500 msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
503 * Destroy the TLS_SCACHE object.
505 dict_close(cp->db);
506 myfree(cp->cache_label);
507 if (cp->saved_cursor)
508 myfree(cp->saved_cursor);
509 myfree((char *) cp);
512 #endif