xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/verify_sender_addr.c (revision 230b95665bbd3a9d1a53658a36b1053f8382a519)
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