diff options
Diffstat (limited to '')
-rw-r--r-- | src/tls/tls_mgr.c | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/src/tls/tls_mgr.c b/src/tls/tls_mgr.c new file mode 100644 index 0000000..b69ddf6 --- /dev/null +++ b/src/tls/tls_mgr.c @@ -0,0 +1,486 @@ +/*++ +/* NAME +/* tls_mgr 3 +/* SUMMARY +/* tlsmgr client interface +/* SYNOPSIS +/* #include <tls_mgr.h> +/* +/* int tls_mgr_seed(buf, len) +/* VSTRING *buf; +/* int len; +/* +/* int tls_mgr_policy(cache_type, cachable, timeout) +/* const char *cache_type; +/* int *cachable; +/* int *timeout; +/* +/* int tls_mgr_update(cache_type, cache_id, buf, len) +/* const char *cache_type; +/* const char *cache_id; +/* const char *buf; +/* ssize_t len; +/* +/* int tls_mgr_lookup(cache_type, cache_id, buf) +/* const char *cache_type; +/* const char *cache_id; +/* VSTRING *buf; +/* +/* int tls_mgr_delete(cache_type, cache_id) +/* const char *cache_type; +/* const char *cache_id; +/* +/* TLS_TICKET_KEY *tls_mgr_key(keyname, timeout) +/* unsigned char *keyname; +/* int timeout; +/* DESCRIPTION +/* These routines communicate with the tlsmgr(8) server for +/* entropy and session cache management. Since these are +/* non-critical services, requests are allowed to fail without +/* disrupting Postfix. +/* +/* tls_mgr_seed() requests entropy from the tlsmgr(8) +/* Pseudo Random Number Generator (PRNG) pool. +/* +/* tls_mgr_policy() requests the session caching policy. +/* +/* tls_mgr_lookup() loads the specified session from +/* the specified session cache. +/* +/* tls_mgr_update() saves the specified session to +/* the specified session cache. +/* +/* tls_mgr_delete() removes specified session from +/* the specified session cache. +/* +/* tls_mgr_key() is used to retrieve the current TLS session ticket +/* encryption or decryption keys. +/* +/* Arguments: +/* .IP cache_type +/* One of TLS_MGR_SCACHE_SMTPD, TLS_MGR_SCACHE_SMTP or +/* TLS_MGR_SCACHE_LMTP. +/* .IP cachable +/* Pointer to int, set non-zero if the requested cache_type +/* is enabled. +/* .IP timeout +/* Pointer to int, returns the cache entry timeout. +/* .IP cache_id +/* The session cache lookup key. +/* .IP buf +/* The result or input buffer. +/* .IP len +/* The length of the input buffer, or the amount of data requested. +/* .IP keyname +/* Is null when requesting the current encryption keys. Otherwise, +/* keyname is a pointer to an array of TLS_TICKET_NAMELEN unsigned +/* chars (not NUL terminated) that is an identifier for a key +/* previously used to encrypt a session ticket. When encrypting +/* a null result indicates that session tickets are not supported, when +/* decrypting it indicates that no matching keys were found. +/* .IP timeout +/* The encryption key timeout. Once a key has been active for this many +/* seconds it is retired and used only for decrypting previously issued +/* session tickets for another timeout seconds, and is then destroyed. +/* The timeout must not be longer than half the SSL session lifetime. +/* DIAGNOSTICS +/* All client functions return one of the following status codes: +/* .IP TLS_MGR_STAT_OK +/* The request completed, and the requested operation was +/* successful (for example, the requested session was found, +/* or the specified session was saved or removed). +/* .IP TLS_MGR_STAT_ERR +/* The request completed, but the requested operation failed +/* (for example, the requested object was not found or the +/* specified session was not saved or removed). +/* .IP TLS_MGR_STAT_FAIL +/* The request could not complete (the client could not +/* communicate with the tlsmgr(8) server). +/* SEE ALSO +/* tlsmgr(8) TLS session and PRNG management +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/* +/* Wietse Venema +/* Google, Inc. +/* 111 8th Avenue +/* New York, NY 10011, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> + +#ifdef USE_TLS + +#ifdef STRCASECMP_IN_STRINGS_H +#include <strings.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <vstream.h> +#include <vstring.h> +#include <attr.h> +#include <attr_clnt.h> +#include <mymalloc.h> +#include <stringops.h> + +/* Global library. */ + +#include <mail_params.h> +#include <mail_proto.h> + +/* TLS library. */ +#include <tls_mgr.h> + +/* Application-specific. */ + +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +static ATTR_CLNT *tls_mgr; + +/* tls_mgr_handshake - receive server protocol announcement */ + +static int tls_mgr_handshake(VSTREAM *stream) +{ + return (attr_scan(stream, ATTR_FLAG_STRICT, + RECV_ATTR_STREQ(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TLSMGR), + ATTR_TYPE_END)); +} + +/* tls_mgr_open - create client handle */ + +static void tls_mgr_open(void) +{ + char *service; + + /* + * Sanity check. + */ + if (tls_mgr != 0) + msg_panic("tls_mgr_open: multiple initialization"); + + /* + * Use whatever IPC is preferred for internal use: UNIX-domain sockets or + * Solaris streams. + */ + service = concatenate("local:" TLS_MGR_CLASS "/", var_tls_mgr_service, + (char *) 0); + tls_mgr = attr_clnt_create(service, var_ipc_timeout, + var_ipc_idle_limit, var_ipc_ttl_limit); + myfree(service); + + attr_clnt_control(tls_mgr, + ATTR_CLNT_CTL_PROTO, attr_vprint, attr_vscan, + ATTR_CLNT_CTL_HANDSHAKE, tls_mgr_handshake, + ATTR_CLNT_CTL_END); +} + +/* tls_mgr_seed - request PRNG seed */ + +int tls_mgr_seed(VSTRING *buf, int len) +{ + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + /* + * Request seed. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request attributes */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_SEED), + SEND_ATTR_INT(TLS_MGR_ATTR_SIZE, len), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply attributes */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + RECV_ATTR_DATA(TLS_MGR_ATTR_SEED, buf), + ATTR_TYPE_END) != 2) + status = TLS_MGR_STAT_FAIL; + return (status); +} + +/* tls_mgr_policy - request caching policy */ + +int tls_mgr_policy(const char *cache_type, int *cachable, int *timeout) +{ + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + /* + * Request policy. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request attributes */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_POLICY), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply attributes */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + RECV_ATTR_INT(TLS_MGR_ATTR_CACHABLE, cachable), + RECV_ATTR_INT(TLS_MGR_ATTR_SESSTOUT, timeout), + ATTR_TYPE_END) != 3) + status = TLS_MGR_STAT_FAIL; + return (status); +} + +/* tls_mgr_lookup - request cached session */ + +int tls_mgr_lookup(const char *cache_type, const char *cache_id, + VSTRING *buf) +{ + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + /* + * Send the request and receive the reply. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + RECV_ATTR_DATA(TLS_MGR_ATTR_SESSION, buf), + ATTR_TYPE_END) != 2) + status = TLS_MGR_STAT_FAIL; + return (status); +} + +/* tls_mgr_update - save session to cache */ + +int tls_mgr_update(const char *cache_type, const char *cache_id, + const char *buf, ssize_t len) +{ + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + /* + * Send the request and receive the reply. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_UPDATE), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), + SEND_ATTR_DATA(TLS_MGR_ATTR_SESSION, len, buf), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + ATTR_TYPE_END) != 1) + status = TLS_MGR_STAT_FAIL; + return (status); +} + +/* tls_mgr_delete - remove cached session */ + +int tls_mgr_delete(const char *cache_type, const char *cache_id) +{ + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + /* + * Send the request and receive the reply. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_DELETE), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_TYPE, cache_type), + SEND_ATTR_STR(TLS_MGR_ATTR_CACHE_ID, cache_id), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + ATTR_TYPE_END) != 1) + status = TLS_MGR_STAT_FAIL; + return (status); +} + +/* request_scache_key - ask tlsmgr(8) for matching key */ + +static TLS_TICKET_KEY *request_scache_key(unsigned char *keyname) +{ + TLS_TICKET_KEY tmp; + static VSTRING *keybuf; + char *name; + size_t len; + int status; + + /* + * Create the tlsmgr client handle. + */ + if (tls_mgr == 0) + tls_mgr_open(); + + if (keybuf == 0) + keybuf = vstring_alloc(sizeof(tmp)); + + /* In tlsmgr requests we encode null key names as empty strings. */ + name = keyname ? (char *) keyname : ""; + len = keyname ? TLS_TICKET_NAMELEN : 0; + + /* + * Send the request and receive the reply. + */ + if (attr_clnt_request(tls_mgr, + ATTR_FLAG_NONE, /* Request */ + SEND_ATTR_STR(TLS_MGR_ATTR_REQ, TLS_MGR_REQ_TKTKEY), + SEND_ATTR_DATA(TLS_MGR_ATTR_KEYNAME, len, name), + ATTR_TYPE_END, + ATTR_FLAG_MISSING, /* Reply */ + RECV_ATTR_INT(TLS_MGR_ATTR_STATUS, &status), + RECV_ATTR_DATA(TLS_MGR_ATTR_KEYBUF, keybuf), + ATTR_TYPE_END) != 2 + || status != TLS_MGR_STAT_OK + || LEN(keybuf) != sizeof(tmp)) + return (0); + + memcpy((void *) &tmp, STR(keybuf), sizeof(tmp)); + return (tls_scache_key_rotate(&tmp)); +} + +/* tls_mgr_key - session ticket key lookup, local cache, then tlsmgr(8) */ + +TLS_TICKET_KEY *tls_mgr_key(unsigned char *keyname, int timeout) +{ + TLS_TICKET_KEY *key = 0; + time_t now = time((time_t *) 0); + + /* A zero timeout disables session tickets. */ + if (timeout <= 0) + return (0); + + if ((key = tls_scache_key(keyname, now, timeout)) == 0) + key = request_scache_key(keyname); + return (key); +} + +#ifdef TEST + +/* System library. */ + +#include <stdlib.h> + +/* Utility library. */ + +#include <argv.h> +#include <msg_vstream.h> +#include <vstring_vstream.h> +#include <hex_code.h> + +/* Global library. */ + +#include <config.h> + +/* Application-specific. */ + +int main(int unused_ac, char **av) +{ + VSTRING *inbuf = vstring_alloc(10); + int status; + ARGV *argv = 0; + + msg_vstream_init(av[0], VSTREAM_ERR); + + msg_verbose = 3; + + mail_conf_read(); + msg_info("using config files in %s", var_config_dir); + + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + + while (vstring_fgets_nonl(inbuf, VSTREAM_IN)) { + argv = argv_split(STR(inbuf), CHARS_SPACE); + if (argv->argc == 0) { + argv_free(argv); + continue; + } +#define COMMAND(argv, str, len) \ + (strcasecmp(argv->argv[0], str) == 0 && argv->argc == len) + + if (COMMAND(argv, "policy", 2)) { + int cachable; + int timeout; + + status = tls_mgr_policy(argv->argv[1], &cachable, &timeout); + vstream_printf("status=%d cachable=%d timeout=%d\n", + status, cachable, timeout); + } else if (COMMAND(argv, "seed", 2)) { + VSTRING *buf = vstring_alloc(10); + VSTRING *hex = vstring_alloc(10); + int len = atoi(argv->argv[1]); + + status = tls_mgr_seed(buf, len); + hex_encode(hex, STR(buf), LEN(buf)); + vstream_printf("status=%d seed=%s\n", status, STR(hex)); + vstring_free(hex); + vstring_free(buf); + } else if (COMMAND(argv, "lookup", 3)) { + VSTRING *buf = vstring_alloc(10); + + status = tls_mgr_lookup(argv->argv[1], argv->argv[2], buf); + vstream_printf("status=%d session=%.*s\n", + status, (int) LEN(buf), STR(buf)); + vstring_free(buf); + } else if (COMMAND(argv, "update", 4)) { + status = tls_mgr_update(argv->argv[1], argv->argv[2], + argv->argv[3], strlen(argv->argv[3])); + vstream_printf("status=%d\n", status); + } else if (COMMAND(argv, "delete", 3)) { + status = tls_mgr_delete(argv->argv[1], argv->argv[2]); + vstream_printf("status=%d\n", status); + } else { + vstream_printf("usage:\n" + "seed byte_count\n" + "policy smtpd|smtp|lmtp\n" + "lookup smtpd|smtp|lmtp cache_id\n" + "update smtpd|smtp|lmtp cache_id session\n" + "delete smtpd|smtp|lmtp cache_id\n"); + } + vstream_fflush(VSTREAM_OUT); + argv_free(argv); + } + + vstring_free(inbuf); + return (0); +} + +#endif /* TEST */ + +#endif /* USE_TLS */ |