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 wich 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  */
176typedef struct {
177    time_t  timestamp;			/* time when saved */
178    char    session[1];			/* actually a bunch of bytes */
179} TLS_SCACHE_ENTRY;
180
181static 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
191static 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((char *) entry);
228
229    return (hex_data);
230}
231
232/* tls_scache_decode - decode TLS session cache entry */
233
234static 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
294int     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
331int     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
366int     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     */
414    if (cp->flags & TLS_SCACHE_FLAG_DEL_SAVED_CURSOR) {
415	cp->flags &= ~TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
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)
440	    cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
441    }
442    return (found_entry);
443}
444
445/* tls_scache_delete - delete session from cache */
446
447int     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
467TLS_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#else
486#define DICT_FLAGS \
487	(DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE)
488#endif
489
490    dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS);
491
492    /*
493     * Sanity checks.
494     */
495    if (dict->update == 0)
496	msg_fatal("dictionary %s does not support update operations", dbname);
497    if (dict->delete == 0)
498	msg_fatal("dictionary %s does not support delete operations", dbname);
499    if (dict->sequence == 0)
500	msg_fatal("dictionary %s does not support sequence operations", dbname);
501
502    /*
503     * Create the TLS_SCACHE object.
504     */
505    cp = (TLS_SCACHE *) mymalloc(sizeof(*cp));
506    cp->flags = 0;
507    cp->db = dict;
508    cp->cache_label = mystrdup(cache_label);
509    cp->verbose = verbose;
510    cp->timeout = timeout;
511    cp->saved_cursor = 0;
512
513    return (cp);
514}
515
516/* tls_scache_close - close TLS session cache file */
517
518void    tls_scache_close(TLS_SCACHE *cp)
519{
520
521    /*
522     * Logging.
523     */
524    if (cp->verbose)
525	msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name);
526
527    /*
528     * Destroy the TLS_SCACHE object.
529     */
530    dict_close(cp->db);
531    myfree(cp->cache_label);
532    if (cp->saved_cursor)
533	myfree(cp->saved_cursor);
534    myfree((char *) cp);
535}
536
537/* tls_scache_key - find session ticket key for given key name */
538
539TLS_TICKET_KEY *tls_scache_key(unsigned char *keyname, time_t now, int timeout)
540{
541    int     i;
542
543    /*
544     * The keys array contains 2 elements, the current signing key and the
545     * previous key.
546     *
547     * When name == 0 we are issuing a ticket, otherwise decrypting an existing
548     * ticket with the given key name.  For new tickets we always use the
549     * current key if unexpired.  For existing tickets, we use either the
550     * current or previous key with a validation expiration that is timeout
551     * longer than the signing expiration.
552     */
553    if (keyname) {
554	for (i = 0; i < 2 && keys[i]; ++i) {
555	    if (memcmp(keyname, keys[i]->name, TLS_TICKET_NAMELEN) == 0) {
556		if (timecmp(keys[i]->tout + timeout, now) > 0)
557		    return (keys[i]);
558		break;
559	    }
560	}
561    } else if (keys[0]) {
562	if (timecmp(keys[0]->tout, now) > 0)
563	    return (keys[0]);
564    }
565    return (0);
566}
567
568/* tls_scache_key_rotate - rotate session ticket keys */
569
570TLS_TICKET_KEY *tls_scache_key_rotate(TLS_TICKET_KEY *newkey)
571{
572
573    /*
574     * Allocate or re-use storage of retired key, then overwrite it, since
575     * caller's key data is ephemeral.
576     */
577    if (keys[1] == 0)
578	keys[1] = (TLS_TICKET_KEY *) mymalloc(sizeof(*newkey));
579    *keys[1] = *newkey;
580    newkey = keys[1];
581
582    /*
583     * Rotate if required, ensuring that the keys are sorted by expiration
584     * time with keys[0] expiring last.
585     */
586    if (keys[0] == 0 || keys[0]->tout < keys[1]->tout) {
587	keys[1] = keys[0];
588	keys[0] = newkey;
589    }
590    return (newkey);
591}
592
593#endif
594