diff options
Diffstat (limited to 'src/global/verify_sender_addr.c')
-rw-r--r-- | src/global/verify_sender_addr.c | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/src/global/verify_sender_addr.c b/src/global/verify_sender_addr.c new file mode 100644 index 0000000..e818f90 --- /dev/null +++ b/src/global/verify_sender_addr.c @@ -0,0 +1,341 @@ +/*++ +/* NAME +/* verify_sender_addr 3 +/* SUMMARY +/* time-dependent probe sender addresses +/* SYNOPSIS +/* #include <verify_sender_addr.h> +/* +/* char *var_verify_sender; +/* int var_verify_sender_ttl; +/* +/* const char *make_verify_sender_addr() +/* +/* const char *valid_verify_sender_addr(addr) +/* const char *addr; +/* DESCRIPTION +/* This module computes or verifies a constant or time-dependent +/* sender address for an address verification probe. The +/* time-dependent portion is appended to the address localpart +/* specified with the address_verify_sender parameter. +/* +/* When the address_verify_sender parameter is empty or <>, +/* the sender address is always the empty address (i.e. always +/* time-independent). +/* +/* The caller must initialize the address_verify_sender and +/* address_verify_sender_ttl parameter values. +/* +/* make_verify_sender_addr() generates an envelope sender +/* address for an address verification probe. +/* +/* valid_verify_sender_addr() verifies that the given address +/* is a valid sender address for address verification probes. +/* When probe sender addresses are configured to be time-dependent, +/* the given address is allowed to differ by +/-1 TTL unit +/* from the expected address. The result is a null pointer +/* when no match is found. Otherwise, the result is the sender +/* address without the time-dependent portion; this is the +/* address that should be used for further delivery. +/* DIAGNOSTICS +/* Fatal errors: malformed address_verify_sender value; out +/* of memory. +/* 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 +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> + +/* Utility library. */ + +#include <msg.h> +#include <vstring.h> +#include <events.h> +#include <stringops.h> + +/* Global library */ + +#include <mail_params.h> +#include <rewrite_clnt.h> +#include <safe_ultostr.h> +#include <verify_sender_addr.h> + +/* Application-specific. */ + + /* + * We convert the time-dependent portion to a safe string (no vowels) in a + * reversible manner, so that we can check an incoming address against the + * current and +/-1 TTL time slot. This allows for some time slippage + * between multiple MTAs that handle mail for the same site. We use base 31 + * so that the time stamp contains B-Z0-9. This simplifies regression tests. + */ +#define VERIFY_BASE 31 + + /* + * We append the time-dependent portion to the localpart of the the address + * verification probe sender address, so that the result has the form + * ``fixed1variable@fixed2''. There is no delimiter between ``fixed1'' and + * ``variable'', because that could make "old" time stamps valid depending + * on how the recipient_delimiter feature is configured. The fixed text is + * taken from var_verify_sender with perhaps domain information appended + * during address canonicalization. The variable part of the address changes + * every var_verify_sender_ttl seconds. + */ +char *var_verify_sender; /* "bare" probe sender address */ +int var_verify_sender_ttl; /* time between address changes */ + + /* + * Scaffolding for stand-alone testing. + */ +#ifdef TEST +#undef event_time +#define event_time() verify_time +static unsigned long verify_time; + +#endif + +#define VERIFY_SENDER_ADDR_EPOCH() (event_time() / var_verify_sender_ttl) + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) +#define LEN(x) VSTRING_LEN(x) + +/* make_verify_sender_addr - generate address_verify_sender address */ + +const char *make_verify_sender_addr(void) +{ + static VSTRING *verify_sender_buf; /* the complete sender address */ + static VSTRING *my_epoch_buf; /* scratch space */ + char *my_at_domain; + + /* + * The null sender is always time-independent. + */ + if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0) + return (""); + + /* + * Sanity check. + */ + if (*var_verify_sender == '@') + msg_fatal("parameter %s: value \"%s\" must not start with '@'", + VAR_VERIFY_SENDER, var_verify_sender); + if ((my_at_domain = strchr(var_verify_sender, '@')) != 0 && my_at_domain[1] == 0) + msg_fatal("parameter %s: value \"%s\" must not end with '@'", + VAR_VERIFY_SENDER, var_verify_sender); + + /* + * One-time initialization. + */ + if (verify_sender_buf == 0) { + verify_sender_buf = vstring_alloc(10); + my_epoch_buf = vstring_alloc(10); + } + + /* + * Start with the bare sender address. + */ + vstring_strcpy(verify_sender_buf, var_verify_sender); + + /* + * Append the time stamp to the address localpart, encoded in some + * non-decimal form for obscurity. + * + * XXX It would be nice to have safe_ultostr() append-only support. + */ + if (var_verify_sender_ttl > 0) { + /* Strip the @domain portion, if applicable. */ + if (my_at_domain != 0) + vstring_truncate(verify_sender_buf, + (ssize_t) (my_at_domain - var_verify_sender)); + /* Append the time stamp to the address localpart. */ + vstring_sprintf_append(verify_sender_buf, "%s", + safe_ultostr(my_epoch_buf, + VERIFY_SENDER_ADDR_EPOCH(), + VERIFY_BASE, 0, 0)); + /* Add back the @domain, if applicable. */ + if (my_at_domain != 0) + vstring_sprintf_append(verify_sender_buf, "%s", my_at_domain); + } + + /* + * Rewrite the address to canonical form. + */ + rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(verify_sender_buf), + verify_sender_buf); + + return (STR(verify_sender_buf)); +} + +/* valid_verify_sender_addr - decide if address matches time window +/-1 */ + +const char *valid_verify_sender_addr(const char *their_addr) +{ + static VSTRING *time_indep_sender_buf; /* sender without time stamp */ + ssize_t base_len; + unsigned long my_epoch; + unsigned long their_epoch; + char *my_at_domain; + char *their_at_domain; + char *cp; + + /* + * The null address is always time-independent. + */ + if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0) + return (*their_addr ? 0 : ""); + + /* + * One-time initialization. Generate the time-independent address that we + * will return if the match is successful. This address is also used as a + * matching template. + */ + if (time_indep_sender_buf == 0) { + time_indep_sender_buf = vstring_alloc(10); + vstring_strcpy(time_indep_sender_buf, var_verify_sender); + rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(time_indep_sender_buf), + time_indep_sender_buf); + } + + /* + * Check the time-independent sender localpart. + */ + if ((my_at_domain = strchr(STR(time_indep_sender_buf), '@')) != 0) + base_len = my_at_domain - STR(time_indep_sender_buf); + else + base_len = LEN(time_indep_sender_buf); + if (strncasecmp_utf8(STR(time_indep_sender_buf), their_addr, base_len) != 0) + return (0); /* sender localpart mis-match */ + + /* + * Check the time-independent domain. + */ + if ((their_at_domain = strchr(their_addr, '@')) == 0 && my_at_domain != 0) + return (0); /* sender domain mis-match */ + if (their_at_domain != 0 + && (my_at_domain == 0 + || strcasecmp_utf8(their_at_domain, my_at_domain) != 0)) + return (0); /* sender domain mis-match */ + + /* + * Check the time-dependent portion. + */ + if (var_verify_sender_ttl > 0) { + their_epoch = safe_strtoul(their_addr + base_len, &cp, VERIFY_BASE); + if ((*cp != '@' && *cp != 0) + || (their_epoch == ULONG_MAX && errno == ERANGE)) + return (0); /* malformed time stamp */ + my_epoch = VERIFY_SENDER_ADDR_EPOCH(); + if (their_epoch < my_epoch - 1 || their_epoch > my_epoch + 1) + return (0); /* outside time window */ + } + + /* + * No time-dependent portion. + */ + else { + if (their_addr[base_len] != '@' && their_addr[base_len] != 0) + return (0); /* garbage after sender base */ + } + return (STR(time_indep_sender_buf)); +} + + /* + * Proof-of-concept test program. Read test address_verify_sender and + * address_verify_sender_ttl values from stdin, and report results that we + * would get on stdout. + */ +#ifdef TEST + +#include <stdlib.h> +#include <vstream.h> +#include <msg_vstream.h> +#include <vstring_vstream.h> +#include <mail_conf.h> +#include <conv_time.h> + +int main(int argc, char **argv) +{ + const char *verify_sender; + const char *valid_sender; + + msg_vstream_init(argv[0], VSTREAM_ERR); + + /* + * Prepare to talk to the address rewriting service. + */ + mail_conf_read(); + vstream_printf("using config files in %s\n", var_config_dir); + if (chdir(var_queue_dir) < 0) + msg_fatal("chdir %s: %m", var_queue_dir); + + /* + * Parse JCL. + */ + if (argc != 3) + msg_fatal("usage: %s address_verify_sender address_verify_sender_ttl", + argv[0]); + var_verify_sender = argv[1]; + if (conv_time(argv[2], &var_verify_sender_ttl, 's') == 0) + msg_fatal("bad time value: %s", argv[2]); + verify_time = time((time_t *) 0); + + /* + * Compute the current probe sender address. + */ + verify_sender = make_verify_sender_addr(); + + /* + * Check two past time slots. + */ + if (var_verify_sender_ttl > 0) { + verify_time -= 2 * var_verify_sender_ttl; + vstream_printf("\"%s\" matches prev2: \"%s\"\n", verify_sender, + (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? + valid_sender : "nope"); + verify_time += var_verify_sender_ttl; + vstream_printf("\"%s\" matches prev1: \"%s\"\n", verify_sender, + (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? + valid_sender : "nope"); + verify_time += var_verify_sender_ttl; + } + + /* + * Check the current time slot. + */ + vstream_printf("\"%s\" matches self: \"%s\"\n", verify_sender, + (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? + valid_sender : "nope"); + + /* + * Check two future time slots. + */ + if (var_verify_sender_ttl > 0) { + verify_time += var_verify_sender_ttl; + vstream_printf("\"%s\" matches next1: \"%s\"\n", verify_sender, + (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? + valid_sender : "nope"); + verify_time += var_verify_sender_ttl; + vstream_printf("\"%s\" matches next2: \"%s\"\n", verify_sender, + (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? + valid_sender : "nope"); + } + vstream_fflush(VSTREAM_OUT); + exit(0); +} + +#endif |