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