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