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