xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/verify_sender_addr.c (revision 67b9b338a7386232ac596b5fd0cd5a9cc8a03c71)
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 
make_verify_sender_addr(void)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 
valid_verify_sender_addr(const char * their_addr)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 
main(int argc,char ** argv)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