1/*++ 2/* NAME 3/* verify_sender_addr 3 4/* SUMMARY 5/* time-dependent probe sender addresses 6/* SYNOPSIS 7/* #include <verify_sender_addr.h> 8/* 9/* char *var_verify_sender; 10/* int var_verify_sender_ttl; 11/* 12/* const char *make_verify_sender_addr() 13/* 14/* const char *valid_verify_sender_addr(addr) 15/* const char *addr; 16/* DESCRIPTION 17/* This module computes or verifies a constant or time-dependent 18/* sender address for an address verification probe. The 19/* time-dependent portion is appended to the address localpart 20/* specified with the address_verify_sender parameter. 21/* 22/* When the address_verify_sender parameter is empty or <>, 23/* the sender address is always the empty address (i.e. always 24/* time-independent). 25/* 26/* The caller must initialize the address_verify_sender and 27/* address_verify_sender_ttl parameter values. 28/* 29/* make_verify_sender_addr() generates an envelope sender 30/* address for an address verification probe. 31/* 32/* valid_verify_sender_addr() verifies that the given address 33/* is a valid sender address for address verification probes. 34/* When probe sender addresses are configured to be time-dependent, 35/* the given address is allowed to differ by +/-1 TTL unit 36/* from the expected address. The result is a null pointer 37/* when no match is found. Otherwise, the result is the sender 38/* address without the time-dependent portion; this is the 39/* address that should be used for further delivery. 40/* DIAGNOSTICS 41/* Fatal errors: malformed address_verify_sender value; out 42/* of memory. 43/* LICENSE 44/* .ad 45/* .fi 46/* The Secure Mailer license must be distributed with this software. 47/* AUTHOR(S) 48/* Wietse Venema 49/* IBM T.J. Watson Research 50/* P.O. Box 704 51/* Yorktown Heights, NY 10598, USA 52/*--*/ 53 54/* System library. */ 55 56#include <sys_defs.h> 57#include <errno.h> 58#include <string.h> 59#include <stdlib.h> 60 61#ifdef STRCASECMP_IN_STRINGS_H 62#include <strings.h> 63#endif 64 65/* Utility library. */ 66 67#include <msg.h> 68#include <vstring.h> 69#include <events.h> 70 71/* Global library */ 72 73#include <mail_params.h> 74#include <rewrite_clnt.h> 75#include <safe_ultostr.h> 76#include <verify_sender_addr.h> 77 78/* Application-specific. */ 79 80 /* 81 * We convert the time-dependent portion to a safe string (no vowels) in a 82 * reversible manner, so that we can check an incoming address against the 83 * current and +/-1 TTL time slot. This allows for some time slippage 84 * between multiple MTAs that handle mail for the same site. We use base 31 85 * so that the time stamp contains B-Z0-9. This simplifies regression tests. 86 */ 87#define VERIFY_BASE 31 88 89 /* 90 * We append the time-dependent portion to the localpart of the the address 91 * verification probe sender address, so that the result has the form 92 * ``fixed1variable@fixed2''. There is no delimiter between ``fixed1'' and 93 * ``variable'', because that could make "old" time stamps valid depending 94 * on how the recipient_delimiter feature is configured. The fixed text is 95 * taken from var_verify_sender with perhaps domain information appended 96 * during address canonicalization. The variable part of the address changes 97 * every var_verify_sender_ttl seconds. 98 */ 99char *var_verify_sender; /* "bare" probe sender address */ 100int var_verify_sender_ttl; /* time between address changes */ 101 102 /* 103 * Scaffolding for stand-alone testing. 104 */ 105#ifdef TEST 106#undef event_time 107#define event_time() verify_time 108static unsigned long verify_time; 109 110#endif 111 112#define VERIFY_SENDER_ADDR_EPOCH() (event_time() / var_verify_sender_ttl) 113 114 /* 115 * SLMs. 116 */ 117#define STR(x) vstring_str(x) 118#define LEN(x) VSTRING_LEN(x) 119 120/* make_verify_sender_addr - generate address_verify_sender address */ 121 122const char *make_verify_sender_addr(void) 123{ 124 static VSTRING *verify_sender_buf; /* the complete sender address */ 125 static VSTRING *my_epoch_buf; /* scratch space */ 126 char *my_at_domain; 127 128 /* 129 * The null sender is always time-independent. 130 */ 131 if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0) 132 return (""); 133 134 /* 135 * Sanity check. 136 */ 137 if (*var_verify_sender == '@') 138 msg_fatal("parameter %s: value \"%s\" must not start with '@'", 139 VAR_VERIFY_SENDER, var_verify_sender); 140 if ((my_at_domain = strchr(var_verify_sender, '@')) != 0 && my_at_domain[1] == 0) 141 msg_fatal("parameter %s: value \"%s\" must not end with '@'", 142 VAR_VERIFY_SENDER, var_verify_sender); 143 144 /* 145 * One-time initialization. 146 */ 147 if (verify_sender_buf == 0) { 148 verify_sender_buf = vstring_alloc(10); 149 my_epoch_buf = vstring_alloc(10); 150 } 151 152 /* 153 * Start with the bare sender address. 154 */ 155 vstring_strcpy(verify_sender_buf, var_verify_sender); 156 157 /* 158 * Append the time stamp to the address localpart, encoded in some 159 * non-decimal form for obscurity. 160 * 161 * XXX It would be nice to have safe_ultostr() append-only support. 162 */ 163 if (var_verify_sender_ttl > 0) { 164 /* Strip the @domain portion, if applicable. */ 165 if (my_at_domain != 0) 166 vstring_truncate(verify_sender_buf, 167 (ssize_t) (my_at_domain - var_verify_sender)); 168 /* Append the time stamp to the address localpart. */ 169 vstring_sprintf_append(verify_sender_buf, "%s", 170 safe_ultostr(my_epoch_buf, 171 VERIFY_SENDER_ADDR_EPOCH(), 172 VERIFY_BASE, 0, 0)); 173 /* Add back the @domain, if applicable. */ 174 if (my_at_domain != 0) 175 vstring_sprintf_append(verify_sender_buf, "%s", my_at_domain); 176 } 177 178 /* 179 * Rewrite the address to canonical form. 180 */ 181 rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(verify_sender_buf), 182 verify_sender_buf); 183 184 return (STR(verify_sender_buf)); 185} 186 187/* valid_verify_sender_addr - decide if address matches time window +/-1 */ 188 189const char *valid_verify_sender_addr(const char *their_addr) 190{ 191 static VSTRING *time_indep_sender_buf; /* sender without time stamp */ 192 ssize_t base_len; 193 unsigned long my_epoch; 194 unsigned long their_epoch; 195 char *my_at_domain; 196 char *their_at_domain; 197 char *cp; 198 199 /* 200 * The null address is always time-independent. 201 */ 202 if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0) 203 return (*their_addr ? 0 : ""); 204 205 /* 206 * One-time initialization. Generate the time-independent address that we 207 * will return if the match is successful. This address is also used as a 208 * matching template. 209 */ 210 if (time_indep_sender_buf == 0) { 211 time_indep_sender_buf = vstring_alloc(10); 212 vstring_strcpy(time_indep_sender_buf, var_verify_sender); 213 rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(time_indep_sender_buf), 214 time_indep_sender_buf); 215 } 216 217 /* 218 * Check the time-independent sender localpart. 219 */ 220 if ((my_at_domain = strchr(STR(time_indep_sender_buf), '@')) != 0) 221 base_len = my_at_domain - STR(time_indep_sender_buf); 222 else 223 base_len = LEN(time_indep_sender_buf); 224 if (strncasecmp(STR(time_indep_sender_buf), their_addr, base_len) != 0) 225 return (0); /* sender localpart mis-match */ 226 227 /* 228 * Check the time-independent domain. 229 */ 230 if ((their_at_domain = strchr(their_addr, '@')) == 0 && my_at_domain != 0) 231 return (0); /* sender domain mis-match */ 232 if (their_at_domain != 0 233 && (my_at_domain == 0 || strcasecmp(their_at_domain, my_at_domain) != 0)) 234 return (0); /* sender domain mis-match */ 235 236 /* 237 * Check the time-dependent portion. 238 */ 239 if (var_verify_sender_ttl > 0) { 240 their_epoch = safe_strtoul(their_addr + base_len, &cp, VERIFY_BASE); 241 if ((*cp != '@' && *cp != 0) 242 || (their_epoch == ULONG_MAX && errno == ERANGE)) 243 return (0); /* malformed time stamp */ 244 my_epoch = VERIFY_SENDER_ADDR_EPOCH(); 245 if (their_epoch < my_epoch - 1 || their_epoch > my_epoch + 1) 246 return (0); /* outside time window */ 247 } 248 249 /* 250 * No time-dependent portion. 251 */ 252 else { 253 if (their_addr[base_len] != '@' && their_addr[base_len] != 0) 254 return (0); /* garbage after sender base */ 255 } 256 return (STR(time_indep_sender_buf)); 257} 258 259 /* 260 * Proof-of-concept test program. Read test address_verify_sender and 261 * address_verify_sender_ttl values from stdin, and report results that we 262 * would get on stdout. 263 */ 264#ifdef TEST 265 266#include <stdlib.h> 267#include <vstream.h> 268#include <msg_vstream.h> 269#include <vstring_vstream.h> 270#include <mail_conf.h> 271#include <conv_time.h> 272 273int main(int argc, char **argv) 274{ 275 const char *verify_sender; 276 const char *valid_sender; 277 278 msg_vstream_init(argv[0], VSTREAM_ERR); 279 280 /* 281 * Prepare to talk to the address rewriting service. 282 */ 283 mail_conf_read(); 284 vstream_printf("using config files in %s\n", var_config_dir); 285 if (chdir(var_queue_dir) < 0) 286 msg_fatal("chdir %s: %m", var_queue_dir); 287 288 /* 289 * Parse JCL. 290 */ 291 if (argc != 3) 292 msg_fatal("usage: %s address_verify_sender address_verify_sender_ttl", 293 argv[0]); 294 var_verify_sender = argv[1]; 295 if (conv_time(argv[2], &var_verify_sender_ttl, 's') == 0) 296 msg_fatal("bad time value: %s", argv[2]); 297 verify_time = time((time_t *) 0); 298 299 /* 300 * Compute the current probe sender addres. 301 */ 302 verify_sender = make_verify_sender_addr(); 303 304 /* 305 * Check two past time slots. 306 */ 307 if (var_verify_sender_ttl > 0) { 308 verify_time -= 2 * var_verify_sender_ttl; 309 vstream_printf("\"%s\" matches prev2: \"%s\"\n", verify_sender, 310 (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? 311 valid_sender : "nope"); 312 verify_time += var_verify_sender_ttl; 313 vstream_printf("\"%s\" matches prev1: \"%s\"\n", verify_sender, 314 (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? 315 valid_sender : "nope"); 316 verify_time += var_verify_sender_ttl; 317 } 318 319 /* 320 * Check the current time slot. 321 */ 322 vstream_printf("\"%s\" matches self: \"%s\"\n", verify_sender, 323 (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? 324 valid_sender : "nope"); 325 326 /* 327 * Check two future time slots. 328 */ 329 if (var_verify_sender_ttl > 0) { 330 verify_time += var_verify_sender_ttl; 331 vstream_printf("\"%s\" matches next1: \"%s\"\n", verify_sender, 332 (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? 333 valid_sender : "nope"); 334 verify_time += var_verify_sender_ttl; 335 vstream_printf("\"%s\" matches next2: \"%s\"\n", verify_sender, 336 (valid_sender = valid_verify_sender_addr(verify_sender)) != 0 ? 337 valid_sender : "nope"); 338 } 339 vstream_fflush(VSTREAM_OUT); 340 exit(0); 341} 342 343#endif 344