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