Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: verify_sender_addr.c,v 1.4 2022/10/08 16:12:45 christos 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 /* Utility library. */
     64 
     65 #include <msg.h>
     66 #include <vstring.h>
     67 #include <events.h>
     68 #include <stringops.h>
     69 
     70 /* Global library */
     71 
     72 #include <mail_params.h>
     73 #include <rewrite_clnt.h>
     74 #include <safe_ultostr.h>
     75 #include <verify_sender_addr.h>
     76 
     77 /* Application-specific. */
     78 
     79  /*
     80   * We convert the time-dependent portion to a safe string (no vowels) in a
     81   * reversible manner, so that we can check an incoming address against the
     82   * current and +/-1 TTL time slot. This allows for some time slippage
     83   * between multiple MTAs that handle mail for the same site. We use base 31
     84   * so that the time stamp contains B-Z0-9. This simplifies regression tests.
     85   */
     86 #define VERIFY_BASE		31
     87 
     88  /*
     89   * We append the time-dependent portion to the localpart of the address
     90   * verification probe sender address, so that the result has the form
     91   * ``fixed1variable@fixed2''. There is no delimiter between ``fixed1'' and
     92   * ``variable'', because that could make "old" time stamps valid depending
     93   * on how the recipient_delimiter feature is configured. The fixed text is
     94   * taken from var_verify_sender with perhaps domain information appended
     95   * during address canonicalization. The variable part of the address changes
     96   * every var_verify_sender_ttl seconds.
     97   */
     98 char   *var_verify_sender;		/* "bare" probe sender address */
     99 int     var_verify_sender_ttl;		/* time between address changes */
    100 
    101  /*
    102   * Scaffolding for stand-alone testing.
    103   */
    104 #ifdef TEST
    105 #undef event_time
    106 #define event_time() verify_time
    107 static unsigned long verify_time;
    108 
    109 #endif
    110 
    111 #define VERIFY_SENDER_ADDR_EPOCH() (event_time() / var_verify_sender_ttl)
    112 
    113  /*
    114   * SLMs.
    115   */
    116 #define STR(x) vstring_str(x)
    117 #define LEN(x) VSTRING_LEN(x)
    118 
    119 /* make_verify_sender_addr - generate address_verify_sender address */
    120 
    121 const char *make_verify_sender_addr(void)
    122 {
    123     static VSTRING *verify_sender_buf;	/* the complete sender address */
    124     static VSTRING *my_epoch_buf;	/* scratch space */
    125     char   *my_at_domain;
    126 
    127     /*
    128      * The null sender is always time-independent.
    129      */
    130     if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0)
    131 	return ("");
    132 
    133     /*
    134      * Sanity check.
    135      */
    136     if (*var_verify_sender == '@')
    137 	msg_fatal("parameter %s: value \"%s\" must not start with '@'",
    138 		  VAR_VERIFY_SENDER, var_verify_sender);
    139     if ((my_at_domain = strchr(var_verify_sender, '@')) != 0 && my_at_domain[1] == 0)
    140 	msg_fatal("parameter %s: value \"%s\" must not end with '@'",
    141 		  VAR_VERIFY_SENDER, var_verify_sender);
    142 
    143     /*
    144      * One-time initialization.
    145      */
    146     if (verify_sender_buf == 0) {
    147 	verify_sender_buf = vstring_alloc(10);
    148 	my_epoch_buf = vstring_alloc(10);
    149     }
    150 
    151     /*
    152      * Start with the bare sender address.
    153      */
    154     vstring_strcpy(verify_sender_buf, var_verify_sender);
    155 
    156     /*
    157      * Append the time stamp to the address localpart, encoded in some
    158      * non-decimal form for obscurity.
    159      *
    160      * XXX It would be nice to have safe_ultostr() append-only support.
    161      */
    162     if (var_verify_sender_ttl > 0) {
    163 	/* Strip the @domain portion, if applicable. */
    164 	if (my_at_domain != 0)
    165 	    vstring_truncate(verify_sender_buf,
    166 			     (ssize_t) (my_at_domain - var_verify_sender));
    167 	/* Append the time stamp to the address localpart. */
    168 	vstring_sprintf_append(verify_sender_buf, "%s",
    169 			       safe_ultostr(my_epoch_buf,
    170 					    VERIFY_SENDER_ADDR_EPOCH(),
    171 					    VERIFY_BASE, 0, 0));
    172 	/* Add back the @domain, if applicable. */
    173 	if (my_at_domain != 0)
    174 	    vstring_sprintf_append(verify_sender_buf, "%s", my_at_domain);
    175     }
    176 
    177     /*
    178      * Rewrite the address to canonical form.
    179      */
    180     rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(verify_sender_buf),
    181 			  verify_sender_buf);
    182 
    183     return (STR(verify_sender_buf));
    184 }
    185 
    186 /* valid_verify_sender_addr - decide if address matches time window +/-1 */
    187 
    188 const char *valid_verify_sender_addr(const char *their_addr)
    189 {
    190     static VSTRING *time_indep_sender_buf;	/* sender without time stamp */
    191     ssize_t base_len;
    192     unsigned long my_epoch;
    193     unsigned long their_epoch;
    194     char   *my_at_domain;
    195     char   *their_at_domain;
    196     char   *cp;
    197 
    198     /*
    199      * The null address is always time-independent.
    200      */
    201     if (*var_verify_sender == 0 || strcmp(var_verify_sender, "<>") == 0)
    202 	return (*their_addr ? 0 : "");
    203 
    204     /*
    205      * One-time initialization. Generate the time-independent address that we
    206      * will return if the match is successful. This address is also used as a
    207      * matching template.
    208      */
    209     if (time_indep_sender_buf == 0) {
    210 	time_indep_sender_buf = vstring_alloc(10);
    211 	vstring_strcpy(time_indep_sender_buf, var_verify_sender);
    212 	rewrite_clnt_internal(MAIL_ATTR_RWR_LOCAL, STR(time_indep_sender_buf),
    213 			      time_indep_sender_buf);
    214     }
    215 
    216     /*
    217      * Check the time-independent sender localpart.
    218      */
    219     if ((my_at_domain = strchr(STR(time_indep_sender_buf), '@')) != 0)
    220 	base_len = my_at_domain - STR(time_indep_sender_buf);
    221     else
    222 	base_len = LEN(time_indep_sender_buf);
    223     if (strncasecmp_utf8(STR(time_indep_sender_buf), their_addr, base_len) != 0)
    224 	return (0);				/* sender localpart mis-match */
    225 
    226     /*
    227      * Check the time-independent domain.
    228      */
    229     if ((their_at_domain = strchr(their_addr, '@')) == 0 && my_at_domain != 0)
    230 	return (0);				/* sender domain mis-match */
    231     if (their_at_domain != 0
    232 	&& (my_at_domain == 0
    233 	    || strcasecmp_utf8(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 
    273 int     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 address.
    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