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