#include <openssl/sha.h>
#include <string.h>
#include "smtpd.h"
static uint8_t base32[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
static int
minrange(uint16_t tref, uint16_t t2, int drift, int mod)
{
if (tref > drift) {
if (t2 <= tref && t2>= tref - drift)
return 1;
}
else {
if (t2 <= tref || t2 >= mod - (drift - tref))
return 1;
}
return 0;
}
static int
maxrange(uint16_t tref, uint16_t t2, int drift, int mod)
{
if (tref + drift < 1024) {
if (t2 >= tref && t2 <= tref + drift)
return 1;
}
else {
if (t2 >= tref || t2 <= (tref + drift) % 1024)
return 1;
}
return 0;
}
static int
timestamp_check_range(uint16_t tref, uint16_t t2)
{
if (! minrange(tref, t2, env->sc_srs_ttl, 1024) &&
! maxrange(tref, t2, 1, 1024))
return 0;
return 1;
}
static const unsigned char *
srs_hash(const char *key, const char *value)
{
SHA_CTX c;
static unsigned char md[SHA_DIGEST_LENGTH];
SHA1_Init(&c);
SHA1_Update(&c, key, strlen(key));
SHA1_Update(&c, value, strlen(value));
SHA1_Final(md, &c);
return md;
}
static const char *
srs0_encode(const char *sender, const char *rcpt_domain)
{
static char dest[SMTPD_MAXMAILADDRSIZE];
char tmp[SMTPD_MAXMAILADDRSIZE];
char md[SHA_DIGEST_LENGTH*4+1];
struct mailaddr maddr;
uint16_t timestamp;
int ret;
timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
if (! text_to_mailaddr(&maddr, sender))
return sender;
ret = snprintf(tmp, sizeof tmp, "%c%c=%s=%s@%s",
base32[(timestamp>>5) & 0x1F],
base32[timestamp & 0x1F],
maddr.domain, maddr.user, rcpt_domain);
if (ret == -1 || ret >= (int)sizeof tmp)
return sender;
base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
md, sizeof md);
ret = snprintf(dest, sizeof dest, "SRS0=%c%c%c%c=%s",
md[0], md[1], md[2], md[3], tmp);
if (ret == -1 || ret >= (int)sizeof dest)
return sender;
return dest;
}
static const char *
srs1_encode_srs0(const char *sender, const char *rcpt_domain)
{
static char dest[SMTPD_MAXMAILADDRSIZE];
char tmp[SMTPD_MAXMAILADDRSIZE];
char md[SHA_DIGEST_LENGTH*4+1];
struct mailaddr maddr;
int ret;
if (! text_to_mailaddr(&maddr, sender))
return sender;
ret = snprintf(tmp, sizeof tmp, "%s==%s@%s",
maddr.domain, maddr.user, rcpt_domain);
if (ret == -1 || ret >= (int)sizeof tmp)
return sender;
base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp), SHA_DIGEST_LENGTH,
md, sizeof md);
ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
md[0], md[1], md[2], md[3], tmp);
if (ret == -1 || ret >= (int)sizeof dest)
return sender;
return dest;
}
static const char *
srs1_encode_srs1(const char *sender, const char *rcpt_domain)
{
static char dest[SMTPD_MAXMAILADDRSIZE];
char tmp[SMTPD_MAXMAILADDRSIZE];
char md[SHA_DIGEST_LENGTH*4+1];
struct mailaddr maddr;
int ret;
if (! text_to_mailaddr(&maddr, sender))
return sender;
ret = snprintf(tmp, sizeof tmp, "%s@%s", maddr.user, rcpt_domain);
if (ret == -1 || ret >= (int)sizeof tmp)
return sender;
if (strlen(tmp) < 5)
return sender;
if (tmp[4] != '=' && tmp[4] != '+' && tmp[4] != '-')
return sender;
base64_encode_rfc3548(srs_hash(env->sc_srs_key, tmp + 5), SHA_DIGEST_LENGTH,
md, sizeof md);
ret = snprintf(dest, sizeof dest, "SRS1=%c%c%c%c=%s",
md[0], md[1], md[2], md[3], tmp + 5);
if (ret == -1 || ret >= (int)sizeof dest)
return sender;
return dest;
}
const char *
srs_encode(const char *sender, const char *rcpt_domain)
{
if (strncasecmp(sender, "SRS0=", 5) == 0)
return srs1_encode_srs0(sender+5, rcpt_domain);
if (strncasecmp(sender, "SRS1=", 5) == 0)
return srs1_encode_srs1(sender+5, rcpt_domain);
return srs0_encode(sender, rcpt_domain);
}
static const char *
srs0_decode(const char *rcpt)
{
static char dest[SMTPD_MAXMAILADDRSIZE];
char md[SHA_DIGEST_LENGTH*4+1];
struct mailaddr maddr;
char *p;
uint8_t *idx;
int ret;
uint16_t timestamp, srs_timestamp;
if (strlen(rcpt) < 5)
return NULL;
base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
md, sizeof md);
if (strncmp(md, rcpt, 4) != 0) {
if (env->sc_srs_key_backup == NULL)
return NULL;
base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
SHA_DIGEST_LENGTH, md, sizeof md);
if (strncmp(md, rcpt, 4) != 0)
return NULL;
}
rcpt += 5;
if (strlen(rcpt) < 3)
return NULL;
if ((idx = strchr(base32, rcpt[0])) == NULL)
return NULL;
srs_timestamp = ((idx - base32) << 5);
if ((idx = strchr(base32, rcpt[1])) == NULL)
return NULL;
srs_timestamp |= (idx - base32);
rcpt += 3;
timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
if (timestamp != srs_timestamp)
if (! timestamp_check_range(timestamp, srs_timestamp))
return NULL;
if (! text_to_mailaddr(&maddr, rcpt))
return NULL;
if ((p = strchr(maddr.user, '=')) == NULL)
return NULL;
*p++ = '\0';
ret = snprintf(dest, sizeof dest, "%s@%s", p, maddr.user);
if (ret == -1 || ret >= (int)sizeof dest)
return NULL;
return dest;
}
static const char *
srs1_decode(const char *rcpt)
{
static char dest[SMTPD_MAXMAILADDRSIZE];
char md[SHA_DIGEST_LENGTH*4+1];
struct mailaddr maddr;
char *p;
uint8_t *idx;
int ret;
uint16_t timestamp, srs_timestamp;
if (strlen(rcpt) < 5)
return NULL;
base64_encode_rfc3548(srs_hash(env->sc_srs_key, rcpt+5), SHA_DIGEST_LENGTH,
md, sizeof md);
if (strncmp(md, rcpt, 4) != 0) {
if (env->sc_srs_key_backup == NULL)
return NULL;
base64_encode_rfc3548(srs_hash(env->sc_srs_key_backup, rcpt+5),
SHA_DIGEST_LENGTH, md, sizeof md);
if (strncmp(md, rcpt, 4) != 0)
return NULL;
}
rcpt += 5;
if (! text_to_mailaddr(&maddr, rcpt))
return NULL;
if ((p = strchr(maddr.user, '=')) == NULL)
return NULL;
*p++ = '\0';
ret = snprintf(dest, sizeof dest, "SRS0%s@%s", p, maddr.user);
if (ret == -1 || ret >= (int)sizeof dest)
return NULL;
if (strlen(p) < 6)
return NULL;
p += 6;
if (strlen(p) < 3)
return NULL;
if (p[2] != '=' && p[2] != '+' && p[2] != '-')
return NULL;
p[2] = '\0';
if ((idx = strchr(base32, p[0])) == NULL)
return NULL;
srs_timestamp = ((idx - base32) << 5);
if ((idx = strchr(base32, p[1])) == NULL)
return NULL;
srs_timestamp |= (idx - base32);
timestamp = (time(NULL) / (60 * 60 * 24)) % 1024;
if (timestamp != srs_timestamp)
if (! timestamp_check_range(timestamp, srs_timestamp))
return NULL;
return dest;
}
const char *
srs_decode(const char *rcpt)
{
if (strncasecmp(rcpt, "SRS0=", 5) == 0)
return srs0_decode(rcpt + 5);
if (strncasecmp(rcpt, "SRS1=", 5) == 0)
return srs1_decode(rcpt + 5);
return NULL;
}