xref: /openbsd/usr.sbin/smtpd/srs.c (revision 274d7c50)
1 /*	$OpenBSD: srs.c,v 1.3 2019/09/29 10:03:49 gilles Exp $	*/
2 
3 /*
4  * Copyright (c) 2019 Gilles Chehade <gilles@poolp.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/tree.h>
22 #include <sys/socket.h>
23 
24 #include <ctype.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <event.h>
28 #include <imsg.h>
29 #include <inttypes.h>
30 #include <netdb.h>
31 #include <limits.h>
32 #include <pwd.h>
33 #include <signal.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 #include <openssl/sha.h>
41 
42 #include "smtpd.h"
43 #include "log.h"
44 
45 static uint8_t	base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
46 
47 static int
48 minrange(uint16_t tref, uint16_t t2, int drift, int mod)
49 {
50 	if (tref > drift) {
51 		/* t2 must fall in between tref and tref - drift */
52 		if (t2 <= tref && t2>= tref - drift)
53 			return 1;
54 	}
55 	else {
56 		/* t2 must fall in between 0 and tref, or wrap */
57 		if (t2 <= tref || t2 >= mod - (drift - tref))
58 			return 1;
59 	}
60 	return 0;
61 }
62 
63 static int
64 maxrange(uint16_t tref, uint16_t t2, int drift, int mod)
65 {
66 	if (tref + drift < 1024) {
67 		/* t2 must fall in between tref and tref + drift */
68 		if (t2 >= tref && t2 <= tref + drift)
69 			return 1;
70 	}
71 	else {
72 		/* t2 must fall in between tref + drift, or wrap */
73 		if (t2 >= tref || t2 <= (tref + drift) % 1024)
74 			return 1;
75 	}
76 	return 0;
77 }
78 
79 static int
80 timestamp_check_range(uint16_t tref, uint16_t t2)
81 {
82 	if (! minrange(tref, t2, env->sc_srs_ttl, 1024) &&
83 	    ! maxrange(tref, t2, 1, 1024))
84 		return 0;
85 
86 	return 1;
87 }
88 
89 static const unsigned char *
90 srs_hash(const char *key, const char *value)
91 {
92 	SHA_CTX	c;
93 	static unsigned char md[SHA_DIGEST_LENGTH];
94 
95 	SHA1_Init(&c);
96 	SHA1_Update(&c, key, strlen(key));
97 	SHA1_Update(&c, value, strlen(value));
98 	SHA1_Final(md, &c);
99 	return md;
100 }
101 
102 static const char *
103 srs0_encode(const char *sender, const char *rcpt_domain)
104 {
105 	static char dest[SMTPD_MAXMAILADDRSIZE];
106 	char tmp[SMTPD_MAXMAILADDRSIZE];
107 	char md[SHA_DIGEST_LENGTH*4+1];
108 	struct mailaddr maddr;
109 	uint16_t timestamp;
110 	int ret;
111 
112 	/* compute 10 bits timestamp according to spec */
113 	timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
114 
115 	/* parse sender into user and domain */
116 	if (! text_to_mailaddr(&maddr, sender))
117 		return sender;
118 
119 	/* TT=<orig_domainpart>=<orig_userpart>@<new_domainpart> */
120 	ret = snprintf(tmp, sizeof tmp, "%c%c=%s=%s@%s",
121 	    base32[(timestamp>>5) & 0x1F],
122 	    base32[timestamp & 0x1F],
123 	    maddr.domain, maddr.user, rcpt_domain);
124 	if (ret == -1 || ret >= (int)sizeof tmp)
125 		return sender;
126 
127 	/* compute HHHH */
128 	base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
129 	    md, sizeof md);
130 
131 	/* prepend SRS0=HHHH= prefix */
132 	ret = snprintf(dest, sizeof dest, "SRS0=%c%c%c%c=%s",
133 	    md[0], md[1], md[2], md[3], tmp);
134 	if (ret == -1 || ret >= (int)sizeof dest)
135 		return sender;
136 
137 	return dest;
138 }
139 
140 static const char *
141 srs1_encode_srs0(const char *sender, const char *rcpt_domain)
142 {
143 	static char dest[SMTPD_MAXMAILADDRSIZE];
144 	char tmp[SMTPD_MAXMAILADDRSIZE];
145 	char md[SHA_DIGEST_LENGTH*4+1];
146 	struct mailaddr maddr;
147 	int ret;
148 
149 	/* parse sender into user and domain */
150 	if (! text_to_mailaddr(&maddr, sender))
151 		return sender;
152 
153 	/* <last_domainpart>==<SRS0_userpart>@<new_domainpart> */
154 	ret = snprintf(tmp, sizeof tmp, "%s==%s@%s",
155 	    maddr.domain, maddr.user, rcpt_domain);
156 	if (ret == -1 || ret >= (int)sizeof tmp)
157 		return sender;
158 
159 	/* compute HHHH */
160 	base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
161 		md, sizeof md);
162 
163 	/* prepend SRS1=HHHH= prefix */
164 	ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
165 	    md[0], md[1], md[2], md[3], tmp);
166 	if (ret == -1 || ret >= (int)sizeof dest)
167 		return sender;
168 
169 	return dest;
170 }
171 
172 static const char *
173 srs1_encode_srs1(const char *sender, const char *rcpt_domain)
174 {
175 	static char dest[SMTPD_MAXMAILADDRSIZE];
176 	char tmp[SMTPD_MAXMAILADDRSIZE];
177 	char md[SHA_DIGEST_LENGTH*4+1];
178 	struct mailaddr maddr;
179 	int ret;
180 
181 	/* parse sender into user and domain */
182 	if (! text_to_mailaddr(&maddr, sender))
183 		return sender;
184 
185 	/* <SRS1_userpart>@<new_domainpart> */
186 	ret = snprintf(tmp, sizeof tmp, "%s@%s", maddr.user, rcpt_domain);
187 	if (ret == -1 || ret >= (int)sizeof tmp)
188 		return sender;
189 
190 	/* sanity check: there's at least room for a checksum
191 	 * with allowed delimiter =, + or -
192 	 */
193 	if (strlen(tmp) < 5)
194 		return sender;
195 	if (tmp[4] != '=' && tmp[4] != '+' && tmp[4] != '-')
196 		return sender;
197 
198 	/* compute HHHH */
199 	base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp + 5), SHA_DIGEST_LENGTH,
200 		md, sizeof md);
201 
202 	/* prepend SRS1=HHHH= prefix skipping previous hops' HHHH */
203 	ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
204 	    md[0], md[1], md[2], md[3], tmp + 5);
205 	if (ret == -1 || ret >= (int)sizeof dest)
206 		return sender;
207 
208 	return dest;
209 }
210 
211 const char *
212 srs_encode(const char *sender, const char *rcpt_domain)
213 {
214 	if (strncasecmp(sender, "SRS0=", 5) == 0)
215 		return srs1_encode_srs0(sender+5, rcpt_domain);
216 	if (strncasecmp(sender, "SRS1=", 5) == 0)
217 		return srs1_encode_srs1(sender+5, rcpt_domain);
218 	return srs0_encode(sender, rcpt_domain);
219 }
220 
221 static const char *
222 srs0_decode(const char *rcpt)
223 {
224 	static char dest[SMTPD_MAXMAILADDRSIZE];
225 	char md[SHA_DIGEST_LENGTH*4+1];
226 	struct mailaddr maddr;
227 	char *p;
228 	uint8_t *idx;
229 	int ret;
230 	uint16_t timestamp, srs_timestamp;
231 
232 	/* sanity check: we have room for a checksum and delimiter */
233 	if (strlen(rcpt) < 5)
234 		return NULL;
235 
236 	/* compute checksum */
237 	base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
238 	    md, sizeof md);
239 
240 	/* compare prefix checksum with computed checksum */
241 	if (strncmp(md, rcpt, 4) != 0) {
242 		if (env->sc_srs_key_backup == NULL)
243 			return NULL;
244 		base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
245 		    SHA_DIGEST_LENGTH, md, sizeof md);
246 		if (strncmp(md, rcpt, 4) != 0)
247 			return NULL;
248 	}
249 	rcpt += 5;
250 
251 	/* sanity check: we have room for a timestamp and delimiter */
252 	if (strlen(rcpt) < 3)
253 		return NULL;
254 
255 	/* decode timestamp */
256 	if ((idx = strchr(base32, rcpt[0])) == NULL)
257 		return NULL;
258 	srs_timestamp = ((idx - base32) << 5);
259 
260 	if ((idx = strchr(base32, rcpt[1])) == NULL)
261 		return NULL;
262 	srs_timestamp |= (idx - base32);
263 	rcpt += 3;
264 
265 	/* compute current 10 bits timestamp */
266 	timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
267 
268 	/* check that SRS timestamp isn't too far from current */
269 	if (timestamp != srs_timestamp)
270 		if (! timestamp_check_range(timestamp, srs_timestamp))
271 			return NULL;
272 
273 	if (! text_to_mailaddr(&maddr, rcpt))
274 		return NULL;
275 
276 	/* sanity check: we have at least one SRS separator */
277 	if ((p = strchr(maddr.user, '=')) == NULL)
278 		return NULL;
279 	*p++ = '\0';
280 
281 	/* maddr.user holds "domain\0user", with p pointing at user */
282 	ret = snprintf(dest, sizeof dest, "%s@%s", p, maddr.user);
283 	if (ret == -1 || ret >= (int)sizeof dest)
284 		return NULL;
285 
286 	return dest;
287 }
288 
289 static const char *
290 srs1_decode(const char *rcpt)
291 {
292 	static char dest[SMTPD_MAXMAILADDRSIZE];
293 	char md[SHA_DIGEST_LENGTH*4+1];
294 	struct mailaddr maddr;
295 	char *p;
296 	uint8_t *idx;
297 	int ret;
298 	uint16_t timestamp, srs_timestamp;
299 
300 	/* sanity check: we have room for a checksum and delimiter */
301 	if (strlen(rcpt) < 5)
302 		return NULL;
303 
304 	/* compute checksum */
305 	base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
306 	    md, sizeof md);
307 
308 	/* compare prefix checksum with computed checksum */
309 	if (strncmp(md, rcpt, 4) != 0) {
310 		if (env->sc_srs_key_backup == NULL)
311 			return NULL;
312 		base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
313 		    SHA_DIGEST_LENGTH, md, sizeof md);
314 		if (strncmp(md, rcpt, 4) != 0)
315 			return NULL;
316 	}
317 	rcpt += 5;
318 
319 	if (! text_to_mailaddr(&maddr, rcpt))
320 		return NULL;
321 
322 	/* sanity check: we have at least one SRS separator */
323 	if ((p = strchr(maddr.user, '=')) == NULL)
324 		return NULL;
325 	*p++ = '\0';
326 
327 	/* maddr.user holds "domain\0user", with p pointing at user */
328 	ret = snprintf(dest, sizeof dest, "SRS0%s@%s", p, maddr.user);
329 	if (ret == -1 || ret >= (int)sizeof dest)
330 		return NULL;
331 
332 
333 	/* we're ready to return decoded address, but let's check if
334 	 * SRS0 timestamp is valid.
335 	 */
336 
337 	/* first, get rid of SRS0 checksum (=HHHH=), we can't check it */
338 	if (strlen(p) < 6)
339 		return NULL;
340 	p += 6;
341 
342 	/* we should be pointing to a timestamp, check that we're indeed */
343 	if (strlen(p) < 3)
344 		return NULL;
345 	if (p[2] != '=' && p[2] != '+' && p[2] != '-')
346 		return NULL;
347 	p[2] = '\0';
348 
349 	if ((idx = strchr(base32, p[0])) == NULL)
350 		return NULL;
351 	srs_timestamp = ((idx - base32) << 5);
352 
353 	if ((idx = strchr(base32, p[1])) == NULL)
354 		return NULL;
355 	srs_timestamp |= (idx - base32);
356 
357 	/* compute current 10 bits timestamp */
358 	timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
359 
360 	/* check that SRS timestamp isn't too far from current */
361 	if (timestamp != srs_timestamp)
362 		if (! timestamp_check_range(timestamp, srs_timestamp))
363 			return NULL;
364 
365 	return dest;
366 }
367 
368 const char *
369 srs_decode(const char *rcpt)
370 {
371 	if (strncasecmp(rcpt, "SRS0=", 5) == 0)
372 		return srs0_decode(rcpt + 5);
373 	if (strncasecmp(rcpt, "SRS1=", 5) == 0)
374 		return srs1_decode(rcpt + 5);
375 
376 	return NULL;
377 }
378