1 /*	$NetBSD: tls_scache.c,v 1.1.1.1 2009/06/23 10:08:57 tron Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	tls_scache 3
6 /* SUMMARY
7 /*	TLS session cache manager
8 /* SYNOPSIS
9 /*	#include <tls_scache.h>
10 /*
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;
16 /*
17 /*	void	tls_scache_close(cache)
18 /*	TLS_SCACHE *cache;
19 /*
20 /*	int	tls_scache_lookup(cache, cache_id, out_session)
21 /*	TLS_SCACHE *cache;
22 /*	const char *cache_id;
23 /*	VSTRING	*out_session;
24 /*
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;
30 /*
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;
37 /*
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).
45 /*
46 /*	tls_scache_open() opens the specified TLS session cache
47 /*	and returns a handle that must be used for subsequent
48 /*	access.
49 /*
50 /*	tls_scache_close() closes the specified TLS session cache
51 /*	and releases memory that was allocated by tls_scache_open().
52 /*
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.
56 /*
57 /*	tls_scache_update() updates the specified TLS session cache
58 /*	with the specified session information.
59 /*
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.
68 /*
69 /*	tls_scache_delete() removes the specified cache entry from
70 /*	the specified TLS session cache.
71 /*
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.
94 /*
95 /*	Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving
96 /*	the session cache ID of the cache entry.
97 /*
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.
105 /*
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 /*--*/
119 
120 /* System library. */
121 
122 #include <sys_defs.h>
123 
124 #ifdef USE_TLS
125 
126 #include <string.h>
127 #include <stddef.h>
128 
129 /* Utility library. */
130 
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>
138 
139 /* Global library. */
140 
141 /* TLS library. */
142 
143 #include <tls_scache.h>
144 
145 /* Application-specific. */
146 
147  /*
148   * Session cache entry format.
149   */
150 typedef struct {
151     time_t  timestamp;			/* time when saved */
152     char    session[1];			/* actually a bunch of bytes */
153 } TLS_SCACHE_ENTRY;
154 
155  /*
156   * SLMs.
157   */
158 #define STR(x)		vstring_str(x)
159 #define LEN(x)		VSTRING_LEN(x)
160 
161 /* tls_scache_encode - encode TLS session cache entry */
162 
163 static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
164 				          const char *session,
165 				          ssize_t session_len)
166 {
167     TLS_SCACHE_ENTRY *entry;
168     VSTRING *hex_data;
169     ssize_t binary_data_len;
170 
171     /*
172      * Assemble the TLS session cache entry.
173      *
174      * We could eliminate some copying by using incremental encoding, but
175      * sessions are so small that it really does not matter.
176      */
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);
181 
182     /*
183      * Encode the TLS session cache entry.
184      */
185     hex_data = vstring_alloc(2 * binary_data_len + 1);
186     hex_encode(hex_data, (char *) entry, binary_data_len);
187 
188     /*
189      * Logging.
190      */
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);
195 
196     /*
197      * Clean up.
198      */
199     myfree((char *) entry);
200 
201     return (hex_data);
202 }
203 
204 /* tls_scache_decode - decode TLS session cache entry */
205 
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)
209 {
210     TLS_SCACHE_ENTRY *entry;
211     VSTRING *bin_data;
212 
213     /*
214      * Sanity check.
215      */
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);
220     }
221 
222     /*
223      * Disassemble the TLS session cache entry.
224      *
225      * No early returns or we have a memory leak.
226      */
227 #define FREE_AND_RETURN(ptr, x) { vstring_free(ptr); return (x); }
228 
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);
234     }
235     entry = (TLS_SCACHE_ENTRY *) STR(bin_data);
236 
237     /*
238      * Logging.
239      */
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)));
244 
245     /*
246      * Other mandatory restrictions.
247      */
248     if (entry->timestamp + cp->timeout < time((time_t *) 0))
249 	FREE_AND_RETURN(bin_data, 0);
250 
251     /*
252      * Optional output.
253      */
254     if (out_session != 0)
255 	vstring_memcpy(out_session, entry->session,
256 		       LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
257 
258     /*
259      * Clean up.
260      */
261     FREE_AND_RETURN(bin_data, 1);
262 }
263 
264 /* tls_scache_lookup - load session from cache */
265 
266 int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
267 			          VSTRING *session)
268 {
269     const char *hex_data;
270 
271     /*
272      * Logging.
273      */
274     if (cp->verbose)
275 	msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
276 
277     /*
278      * Initialize. Don't leak data.
279      */
280     if (session)
281 	VSTRING_RESET(session);
282 
283     /*
284      * Search the cache database.
285      */
286     if ((hex_data = dict_get(cp->db, cache_id)) == 0)
287 	return (0);
288 
289     /*
290      * Decode entry and delete if expired or malformed.
291      */
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);
298     }
299 }
300 
301 /* tls_scache_update - save session to cache */
302 
303 int     tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
304 			          const char *buf, ssize_t len)
305 {
306     VSTRING *hex_data;
307 
308     /*
309      * Logging.
310      */
311     if (cp->verbose)
312 	msg_info("put %s session id=%s [data %ld bytes]",
313 		 cp->cache_label, cache_id, (long) len);
314 
315     /*
316      * Encode the cache entry.
317      */
318     hex_data = tls_scache_encode(cp, cache_id, buf, len);
319 
320     /*
321      * Store the cache entry.
322      *
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.
325      */
326     dict_put(cp->db, cache_id, STR(hex_data));
327 
328     /*
329      * Clean up.
330      */
331     vstring_free(hex_data);
332 
333     return (1);
334 }
335 
336 /* tls_scache_sequence - get first/next TLS session cache entry */
337 
338 int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
339 			            char **out_cache_id,
340 			            VSTRING *out_session)
341 {
342     const char *member;
343     const char *value;
344     char   *saved_cursor;
345     int     found_entry;
346     int     keep_entry;
347     char   *saved_member;
348 
349     /*
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.
358      */
359 
360     /*
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.
364      *
365      * Save the member (cache id) so that it will not be clobbered by the
366      * tls_scache_lookup() call below.
367      */
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);
375     }
376 
377     /*
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.
381      */
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);
388     }
389 
390     /*
391      * Otherwise, clean up if this is not the first iteration.
392      */
393     else {
394 	if (cp->saved_cursor)
395 	    myfree(cp->saved_cursor);
396 	cp->saved_cursor = 0;
397     }
398 
399     /*
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.
404      */
405     if (found_entry) {
406 	cp->saved_cursor = saved_member;
407 	if (keep_entry == 0)
408 	    cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
409     }
410     return (found_entry);
411 }
412 
413 /* tls_scache_delete - delete session from cache */
414 
415 int     tls_scache_delete(TLS_SCACHE *cp, const char *cache_id)
416 {
417 
418     /*
419      * Logging.
420      */
421     if (cp->verbose)
422 	msg_info("delete %s session id=%s", cp->cache_label, cache_id);
423 
424     /*
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.
428      */
429     return ((cp->saved_cursor != 0 && strcmp(cp->saved_cursor, cache_id) == 0)
430 	    || dict_del(cp->db, cache_id) == 0);
431 }
432 
433 /* tls_scache_open - open TLS session cache file */
434 
435 TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label,
436 			            int verbose, int timeout)
437 {
438     TLS_SCACHE *cp;
439     DICT   *dict;
440 
441     /*
442      * Logging.
443      */
444     if (verbose)
445 	msg_info("open %s TLS cache %s", cache_label, dbname);
446 
447     /*
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.
450      */
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
457 
458     dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS);
459 
460     /*
461      * Sanity checks.
462      */
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);
476 
477     /*
478      * Create the TLS_SCACHE object.
479      */
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;
487 
488     return (cp);
489 }
490 
491 /* tls_scache_close - close TLS session cache file */
492 
493 void    tls_scache_close(TLS_SCACHE *cp)
494 {
495 
496     /*
497      * Logging.
498      */
499     if (cp->verbose)
500 	msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
501 
502     /*
503      * Destroy the TLS_SCACHE object.
504      */
505     dict_close(cp->db);
506     myfree(cp->cache_label);
507     if (cp->saved_cursor)
508 	myfree(cp->saved_cursor);
509     myfree((char *) cp);
510 }
511 
512 #endif
513