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