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