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/* DESCRIPTION 40/* This module maintains Postfix TLS session cache files. 41/* each session is stored under a lookup key (hostname or 42/* session ID). 43/* 44/* tls_scache_open() opens the specified TLS session cache 45/* and returns a handle that must be used for subsequent 46/* access. 47/* 48/* tls_scache_close() closes the specified TLS session cache 49/* and releases memory that was allocated by tls_scache_open(). 50/* 51/* tls_scache_lookup() looks up the specified session in the 52/* specified cache, and applies session timeout restrictions. 53/* Entries that are too old are silently deleted. 54/* 55/* tls_scache_update() updates the specified TLS session cache 56/* with the specified session information. 57/* 58/* tls_scache_sequence() iterates over the specified TLS session 59/* cache and either returns the first or next entry that has not 60/* timed out, or returns no data. Entries that are too old are 61/* silently deleted. Specify TLS_SCACHE_SEQUENCE_NOTHING as the 62/* third and last argument to disable saving of cache entry 63/* content or cache entry ID information. This is useful when 64/* purging expired entries. A result value of zero means that 65/* the end of the cache was reached. 66/* 67/* tls_scache_delete() removes the specified cache entry from 68/* the specified TLS session cache. 69/* 70/* Arguments: 71/* .IP dbname 72/* The base name of the session cache file. 73/* .IP cache_label 74/* A string that is used in logging and error messages. 75/* .IP verbose 76/* Do verbose logging of cache operations? (zero == no) 77/* .IP timeout 78/* The time after wich a session cache entry is considered too old. 79/* .IP first_next 80/* One of DICT_SEQ_FUN_FIRST (first cache element) or DICT_SEQ_FUN_NEXT 81/* (next cache element). 82/* .IP cache_id 83/* Session cache lookup key. 84/* .IP session 85/* Storage for session information. 86/* .IP session_len 87/* The size of the session information in bytes. 88/* .IP out_cache_id 89/* .IP out_session 90/* Storage for saving the cache_id or session information of the 91/* current cache entry. 92/* 93/* Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving 94/* the session cache ID of the cache entry. 95/* 96/* Specify TLS_SCACHE_DONT_NEED_SESSION to avoid 97/* saving the session information in the cache entry. 98/* DIAGNOSTICS 99/* These routines terminate with a fatal run-time error 100/* for unrecoverable database errors. This allows the 101/* program to restart and reset the database to an 102/* empty initial state. 103/* 104/* tls_scache_open() never returns on failure. All other 105/* functions return non-zero on success, zero when the 106/* operation could not be completed. 107/* LICENSE 108/* .ad 109/* .fi 110/* The Secure Mailer license must be distributed with this software. 111/* AUTHOR(S) 112/* Wietse Venema 113/* IBM T.J. Watson Research 114/* P.O. Box 704 115/* Yorktown Heights, NY 10598, USA 116/*--*/ 117 118/* System library. */ 119 120#include <sys_defs.h> 121 122#ifdef USE_TLS 123 124#include <string.h> 125#include <stddef.h> 126 127/* Utility library. */ 128 129#include <msg.h> 130#include <dict.h> 131#include <stringops.h> 132#include <mymalloc.h> 133#include <hex_code.h> 134#include <myflock.h> 135#include <vstring.h> 136 137/* Global library. */ 138 139/* TLS library. */ 140 141#include <tls_scache.h> 142 143/* Application-specific. */ 144 145 /* 146 * Session cache entry format. 147 */ 148typedef struct { 149 time_t timestamp; /* time when saved */ 150 char session[1]; /* actually a bunch of bytes */ 151} TLS_SCACHE_ENTRY; 152 153 /* 154 * SLMs. 155 */ 156#define STR(x) vstring_str(x) 157#define LEN(x) VSTRING_LEN(x) 158 159/* tls_scache_encode - encode TLS session cache entry */ 160 161static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id, 162 const char *session, 163 ssize_t session_len) 164{ 165 TLS_SCACHE_ENTRY *entry; 166 VSTRING *hex_data; 167 ssize_t binary_data_len; 168 169 /* 170 * Assemble the TLS session cache entry. 171 * 172 * We could eliminate some copying by using incremental encoding, but 173 * sessions are so small that it really does not matter. 174 */ 175 binary_data_len = session_len + offsetof(TLS_SCACHE_ENTRY, session); 176 entry = (TLS_SCACHE_ENTRY *) mymalloc(binary_data_len); 177 entry->timestamp = time((time_t *) 0); 178 memcpy(entry->session, session, session_len); 179 180 /* 181 * Encode the TLS session cache entry. 182 */ 183 hex_data = vstring_alloc(2 * binary_data_len + 1); 184 hex_encode(hex_data, (char *) entry, binary_data_len); 185 186 /* 187 * Logging. 188 */ 189 if (cp->verbose) 190 msg_info("write %s TLS cache entry %s: time=%ld [data %ld bytes]", 191 cp->cache_label, cache_id, (long) entry->timestamp, 192 (long) session_len); 193 194 /* 195 * Clean up. 196 */ 197 myfree((char *) entry); 198 199 return (hex_data); 200} 201 202/* tls_scache_decode - decode TLS session cache entry */ 203 204static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id, 205 const char *hex_data, ssize_t hex_data_len, 206 VSTRING *out_session) 207{ 208 TLS_SCACHE_ENTRY *entry; 209 VSTRING *bin_data; 210 211 /* 212 * Sanity check. 213 */ 214 if (hex_data_len < 2 * (offsetof(TLS_SCACHE_ENTRY, session))) { 215 msg_warn("%s TLS cache: truncated entry for %s: %.100s", 216 cp->cache_label, cache_id, hex_data); 217 return (0); 218 } 219 220 /* 221 * Disassemble the TLS session cache entry. 222 * 223 * No early returns or we have a memory leak. 224 */ 225#define FREE_AND_RETURN(ptr, x) { vstring_free(ptr); return (x); } 226 227 bin_data = vstring_alloc(hex_data_len / 2 + 1); 228 if (hex_decode(bin_data, hex_data, hex_data_len) == 0) { 229 msg_warn("%s TLS cache: malformed entry for %s: %.100s", 230 cp->cache_label, cache_id, hex_data); 231 FREE_AND_RETURN(bin_data, 0); 232 } 233 entry = (TLS_SCACHE_ENTRY *) STR(bin_data); 234 235 /* 236 * Logging. 237 */ 238 if (cp->verbose) 239 msg_info("read %s TLS cache entry %s: time=%ld [data %ld bytes]", 240 cp->cache_label, cache_id, (long) entry->timestamp, 241 (long) (LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session))); 242 243 /* 244 * Other mandatory restrictions. 245 */ 246 if (entry->timestamp + cp->timeout < time((time_t *) 0)) 247 FREE_AND_RETURN(bin_data, 0); 248 249 /* 250 * Optional output. 251 */ 252 if (out_session != 0) 253 vstring_memcpy(out_session, entry->session, 254 LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session)); 255 256 /* 257 * Clean up. 258 */ 259 FREE_AND_RETURN(bin_data, 1); 260} 261 262/* tls_scache_lookup - load session from cache */ 263 264int tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id, 265 VSTRING *session) 266{ 267 const char *hex_data; 268 269 /* 270 * Logging. 271 */ 272 if (cp->verbose) 273 msg_info("lookup %s session id=%s", cp->cache_label, cache_id); 274 275 /* 276 * Initialize. Don't leak data. 277 */ 278 if (session) 279 VSTRING_RESET(session); 280 281 /* 282 * Search the cache database. 283 */ 284 if ((hex_data = dict_get(cp->db, cache_id)) == 0) 285 return (0); 286 287 /* 288 * Decode entry and delete if expired or malformed. 289 */ 290 if (tls_scache_decode(cp, cache_id, hex_data, strlen(hex_data), 291 session) == 0) { 292 tls_scache_delete(cp, cache_id); 293 return (0); 294 } else { 295 return (1); 296 } 297} 298 299/* tls_scache_update - save session to cache */ 300 301int tls_scache_update(TLS_SCACHE *cp, const char *cache_id, 302 const char *buf, ssize_t len) 303{ 304 VSTRING *hex_data; 305 306 /* 307 * Logging. 308 */ 309 if (cp->verbose) 310 msg_info("put %s session id=%s [data %ld bytes]", 311 cp->cache_label, cache_id, (long) len); 312 313 /* 314 * Encode the cache entry. 315 */ 316 hex_data = tls_scache_encode(cp, cache_id, buf, len); 317 318 /* 319 * Store the cache entry. 320 * 321 * XXX Berkeley DB supports huge database keys and values. SDBM seems to 322 * have a finite limit, and DBM simply can't be used at all. 323 */ 324 dict_put(cp->db, cache_id, STR(hex_data)); 325 326 /* 327 * Clean up. 328 */ 329 vstring_free(hex_data); 330 331 return (1); 332} 333 334/* tls_scache_sequence - get first/next TLS session cache entry */ 335 336int tls_scache_sequence(TLS_SCACHE *cp, int first_next, 337 char **out_cache_id, 338 VSTRING *out_session) 339{ 340 const char *member; 341 const char *value; 342 char *saved_cursor; 343 int found_entry; 344 int keep_entry; 345 char *saved_member; 346 347 /* 348 * XXX Deleting entries while enumerating a map can he tricky. Some map 349 * types have a concept of cursor and support a "delete the current 350 * element" operation. Some map types without cursors don't behave well 351 * when the current first/next entry is deleted (example: with Berkeley 352 * DB < 2, the "next" operation produces garbage). To avoid trouble, we 353 * delete an expired entry after advancing the current first/next 354 * position beyond it, and ignore client requests to delete the current 355 * entry. 356 */ 357 358 /* 359 * Find the first or next database entry. Activate the passivated entry 360 * and check the time stamp. Schedule the entry for deletion if it is too 361 * old. 362 * 363 * Save the member (cache id) so that it will not be clobbered by the 364 * tls_scache_lookup() call below. 365 */ 366 found_entry = (dict_seq(cp->db, first_next, &member, &value) == 0); 367 if (found_entry) { 368 keep_entry = tls_scache_decode(cp, member, value, strlen(value), 369 out_session); 370 if (keep_entry && out_cache_id) 371 *out_cache_id = mystrdup(member); 372 saved_member = mystrdup(member); 373 } 374 375 /* 376 * Delete behind. This is a no-op if an expired cache entry was updated 377 * in the mean time. Use the saved lookup criteria so that the "delete 378 * behind" operation works as promised. 379 */ 380 if (cp->flags & TLS_SCACHE_FLAG_DEL_SAVED_CURSOR) { 381 cp->flags &= ~TLS_SCACHE_FLAG_DEL_SAVED_CURSOR; 382 saved_cursor = cp->saved_cursor; 383 cp->saved_cursor = 0; 384 tls_scache_lookup(cp, saved_cursor, (VSTRING *) 0); 385 myfree(saved_cursor); 386 } 387 388 /* 389 * Otherwise, clean up if this is not the first iteration. 390 */ 391 else { 392 if (cp->saved_cursor) 393 myfree(cp->saved_cursor); 394 cp->saved_cursor = 0; 395 } 396 397 /* 398 * Protect the current first/next entry against explicit or implied 399 * client delete requests, and schedule a bad or expired entry for 400 * deletion. Save the lookup criteria so that the "delete behind" 401 * operation will work as promised. 402 */ 403 if (found_entry) { 404 cp->saved_cursor = saved_member; 405 if (keep_entry == 0) 406 cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR; 407 } 408 return (found_entry); 409} 410 411/* tls_scache_delete - delete session from cache */ 412 413int tls_scache_delete(TLS_SCACHE *cp, const char *cache_id) 414{ 415 416 /* 417 * Logging. 418 */ 419 if (cp->verbose) 420 msg_info("delete %s session id=%s", cp->cache_label, cache_id); 421 422 /* 423 * Do it, unless we would delete the current first/next entry. Some map 424 * types don't have cursors, and some of those don't behave when the 425 * "current" entry is deleted. 426 */ 427 return ((cp->saved_cursor != 0 && strcmp(cp->saved_cursor, cache_id) == 0) 428 || dict_del(cp->db, cache_id) == 0); 429} 430 431/* tls_scache_open - open TLS session cache file */ 432 433TLS_SCACHE *tls_scache_open(const char *dbname, const char *cache_label, 434 int verbose, int timeout) 435{ 436 TLS_SCACHE *cp; 437 DICT *dict; 438 439 /* 440 * Logging. 441 */ 442 if (verbose) 443 msg_info("open %s TLS cache %s", cache_label, dbname); 444 445 /* 446 * Open the dictionary with O_TRUNC, so that we never have to worry about 447 * opening a damaged file after some process terminated abnormally. 448 */ 449#ifdef SINGLE_UPDATER 450#define DICT_FLAGS (DICT_FLAG_DUP_REPLACE) 451#else 452#define DICT_FLAGS \ 453 (DICT_FLAG_DUP_REPLACE | DICT_FLAG_LOCK | DICT_FLAG_SYNC_UPDATE) 454#endif 455 456 dict = dict_open(dbname, O_RDWR | O_CREAT | O_TRUNC, DICT_FLAGS); 457 458 /* 459 * Sanity checks. 460 */ 461 if (dict->lock_fd < 0) 462 msg_fatal("dictionary %s is not a regular file", dbname); 463#ifdef SINGLE_UPDATER 464 if (myflock(dict->lock_fd, INTERNAL_LOCK, 465 MYFLOCK_OP_EXCLUSIVE | MYFLOCK_OP_NOWAIT) < 0) 466 msg_fatal("cannot lock dictionary %s for exclusive use: %m", dbname); 467#endif 468 if (dict->update == 0) 469 msg_fatal("dictionary %s does not support update operations", dbname); 470 if (dict->delete == 0) 471 msg_fatal("dictionary %s does not support delete operations", dbname); 472 if (dict->sequence == 0) 473 msg_fatal("dictionary %s does not support sequence operations", dbname); 474 475 /* 476 * Create the TLS_SCACHE object. 477 */ 478 cp = (TLS_SCACHE *) mymalloc(sizeof(*cp)); 479 cp->flags = 0; 480 cp->db = dict; 481 cp->cache_label = mystrdup(cache_label); 482 cp->verbose = verbose; 483 cp->timeout = timeout; 484 cp->saved_cursor = 0; 485 486 return (cp); 487} 488 489/* tls_scache_close - close TLS session cache file */ 490 491void tls_scache_close(TLS_SCACHE *cp) 492{ 493 494 /* 495 * Logging. 496 */ 497 if (cp->verbose) 498 msg_info("close %s TLS cache %s", cp->cache_label, cp->db->name); 499 500 /* 501 * Destroy the TLS_SCACHE object. 502 */ 503 dict_close(cp->db); 504 myfree(cp->cache_label); 505 if (cp->saved_cursor) 506 myfree(cp->saved_cursor); 507 myfree((char *) cp); 508} 509 510#endif 511