52#define _strnicmp strncasecmp
53#define _stricmp strcasecmp
54#define MAX_SIGNATURES 10
62int dig_ascii(
char *digascii,
unsigned const char *digest,
const int len)
64 static const char hextab[] =
"0123456789abcdef";
67 for (
j = 0;
j < len;
j++) {
68 digascii[2 *
j] = hextab[(
unsigned char)digest[
j] >> 4];
69 digascii[2 *
j + 1] = hextab[(
unsigned char)digest[
j] & 0x0f];
71 digascii[2 * len] =
'\0';
77int _DNSGetTXT(
const char *szFQDN,
char *Buffer,
int nBufLen)
83 if (!szFQDN)
return -1;
88 switch (dns_txt(&
out,&
sa)) {
93 if (nBufLen <
out.len)
96 if (!stralloc_0(&
out))
return -1;
97 memcpy(Buffer,
out.s,
out.len);
106 FILE*
out = fopen(ResFile,
"wb+");
107 if (
out == NULL)
return -1;
110 len = strlen(result);
111 fwrite(result,1,len,
out);
112 fwrite(
"\r",1,1,
out);
116 fwrite(reason,1,strlen(reason),
out);
117 fwrite(
"\r",1,1,
out);
126 const char* errormsg =
"";
130 errormsg =
" (verify error: message is suspicious)";
133 errormsg =
" (signature error: could not parse or has bad tags/values)";
136 errormsg =
" (signature error: RSA/ED25519 verify failed)";
139 errormsg =
" (signature error: RSA/ED25519 verify failed but testing)";
142 errormsg =
" (signature error: signature x= value expired)";
145 errormsg =
" (signature error: selector doesn't parse or contains invalid values)";
148 errormsg =
" (signature error: selector g= doesn't match i=)";
151 errormsg =
" (signature error: revoked p= empty)";
154 errormsg =
" (dns error: selector domain name too long to request)";
157 errormsg =
" (dns error: temporary dns failure requesting selector)";
160 errormsg =
" (dns error: permanent dns failure requesting selector)";
163 errormsg =
" (signature error: selector p= value invalid or wrong format)";
166 errormsg =
" (process error: no signatures)";
169 errormsg =
" (process error: no valid signatures)";
172 errormsg =
" (signature verify error: message body does not hash to bh= value)";
175 errormsg =
" (signature error: selector h= doesn't match signature a=)";
178 errormsg =
" (signature error: incompatible v= value)";
181 errormsg =
" (signature error: not all message's From headers in signature)";
184 errormsg =
" (internal error: memory allocation failed)";
187 errormsg =
" (internal error: DKIMContext structure invalid for this operation)";
190 errormsg =
" (signing error: Could not find From: or Sender: header in message)";
193 errormsg =
" (signing error: Could not parse private key)";
196 errormsg =
" (signing error: Buffer passed in is not large enough)";
228 return (ch ==
' ' || ch ==
'\t' || ch ==
'\r' || ch ==
'\n');
238 char *s = tagvaluelist;
256 }
while (isalnum(*s) || *s ==
'-');
279 while (*s !=
';' && ((*s ==
'\t' || *s ==
'\r' || *s ==
'\n') || (*s >=
' ' && *s <=
'~')))
295 while (e > value &&
isswsp(e[-1]))
302 for (
unsigned i = 0; wanted[i] != NULL; i++) {
303 if (strcmp(wanted[i],tag) == 0) {
305 if (values[i] != NULL)
323 if (ch >=
'0' && ch <=
'9')
325 else if (ch >=
'A' && ch <=
'F')
326 return (ch -
'A' + 10);
327 else if (ch >=
'a' && ch <=
'f')
328 return (ch -
'a' + 10);
342 while (*s !=
'\0' && *s !=
'=')
350 if (*s ==
'=' && isxdigit(s[1]) && isxdigit(s[2])) {
356 }
while (*s !=
'\0');
366 static const unsigned char base64_table[256] = {
367 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
368 255,255,255,255,255,255,255,255,255,255,255, 62,255,255,255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,255,255,255,255,255,255,
369 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,255,255,255,255,255,
370 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,255,255,255,255,255,
371 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
372 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
373 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
374 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 };
376 unsigned char* s = (
unsigned char* )ptr;
377 unsigned char*
d = (
unsigned char* )ptr;
378 unsigned b64accum = 0;
379 unsigned char b64shift = 0;
382 unsigned char value = base64_table[*s++];
383 if ((
signed char) value >= 0) {
384 b64accum = (b64accum << 6) | value;
388 *
d++ = (b64accum >> b64shift);
393 return (
char* )
d-ptr;
408 const char* wildcard = strchr(
p,
'*');
409 if (wildcard == NULL) {
410 return strcmp(s,
p) == 0;
412 unsigned beforewildcardlen = wildcard -
p;
413 unsigned afterwildcardlen = strlen(wildcard + 1);
414 unsigned slen = strlen(s);
415 return (slen >= beforewildcardlen + afterwildcardlen) &&
416 (strncmp(s,
p,beforewildcardlen) == 0) && strcmp(s + slen - afterwildcardlen,wildcard + 1) == 0;
427 char* s = (
char* )str.c_str();
435 while (*from !=
'\0') {
439 for (
int depth = 1; depth != 0; from++) {
442 else if (*from ==
'(')
444 else if (*from ==
')')
446 else if (*from ==
'\\' && from[1] !=
'\0')
450 else if (*from ==
')') {
453 }
else if (*from ==
',' || *from ==
';') {
458 else if (*from ==
' ' || *from ==
'\t' || *from ==
'\r' || *from ==
'\n') {
461 }
else if (*from ==
'"') {
464 while (*from !=
'\0') {
468 }
else if (*from ==
'\\' && from[1] !=
'\0')
472 }
else if (*from ==
'\\' && from[1] !=
'\0') {
491 char *gt = strchr(start,
'>');
496 char *colon = strchr(start,
':');
498 char *at = strchr(start,
'@');
499 if (at == NULL || colon < at)
504 if (*start !=
'\0' && strchr(start,
'@') != NULL) {
505 Addresses.push_back(start);
511 return !Addresses.empty();
566 unsigned char *SignMsg;
567 unsigned SuccessCount = 0;
568 int TestingFailures = 0;
569 int RealFailures = 0;
572 list<string> SuccessfulDomains;
576 if (!i->BodyHashData.empty()) {
577 unsigned char md[EVP_MAX_MD_SIZE];
580 res = EVP_DigestFinal_ex(i->m_Bdy_ctx,md,&len);
581 EVP_MD_CTX_reset(i->m_Bdy_ctx);
585 if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(),md,len) != 0) {
589 if (i->m_pSelector->Testing) {
605 string sSignedSig = i->Header;
606 string sSigValue = sSignedSig.substr(sSignedSig.find(
':') + 1);
608 static const char* tags[] = {
"b",NULL};
609 char* values[
sizeof(tags)/
sizeof(tags[0])] = {NULL};
611 char* pSigValue = (
char* ) sSigValue.c_str();
613 sSignedSig.erase(15 + values[0] - pSigValue,strlen(values[0]));
622 sSignedSig.replace(0,14,
"dkim-signature",14);
625 i->Hash(sSignedSig.c_str(),sSignedSig.length());
626 assert(i->m_pSelector != NULL);
628 if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) != EVP_PKEY_ED25519)
629 res = EVP_VerifyFinal(i->m_Hdr_ctx,(
unsigned char *)i->SignatureData.data(),i->SignatureData.length(),i->m_pSelector->PublicKey);
630 else if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) == EVP_PKEY_ED25519) {
631 EVP_DigestVerifyInit(i->m_Msg_ctx,NULL,NULL,NULL,i->m_pSelector->PublicKey);
633 SignMsg = (
unsigned char *)
SigHdr.data();
634 res = EVP_DigestVerify(i->m_Msg_ctx,(
unsigned char *)i->SignatureData.data(),(
size_t)i->SignatureData.length(),
639 if (i->UnverifiedBodyCount == 0)
644 SuccessfulDomains.push_back(i->Domain);
647 if (i->m_pSelector->Testing) {
669 if (
_strnicmp(i->c_str(),
"From",4) == 0) {
671 const char* s = i->c_str() + 4;
672 while (*s ==
' ' || *s ==
'\t')
675 vector<string> Addresses;
677 unsigned atpos = Addresses[0].find(
'@');
678 sFromDomain = Addresses[0].substr(atpos + 1);
688 if (SuccessCount > 0 && !sFromDomain.empty()) {
689 for (list<string>::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) {
691 if (i->length() > sFromDomain.length())
693 if (
_stricmp(i->c_str(),sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0)
695 if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] ==
'.') {
716 if(nBufLength == 2 && szBuffer[0] ==
'\r' && szBuffer[1] ==
'\n')
720 char* szDbg =
new char[nBufLength+1];
721 strncpy(szDbg, szBuffer, nBufLength);
722 szDbg[nBufLength] =
'\0';
723 printf(
"[%s]\n", szDbg);
734 if (nBufLength == 0)
return;
739 EVP_DigestUpdate(
m_Bdy_ctx,szBuffer,nBufLength);
741 EVP_VerifyUpdate(
m_Hdr_ctx,szBuffer,nBufLength);
742 SigHdr.append(szBuffer,nBufLength);
761 if (strlen(i->c_str()) < 14)
continue;
762 if (
_strnicmp(i->c_str(),
"DKIM-Signature",14) == 0) {
764 const char *s = i->c_str() + 14;
765 while (*s ==
' ' || *s ==
'\t')
782 bool ValidSigFound =
false;
811 EVP_VerifyInit_ex(sig.
m_Hdr_ctx,EVP_sha1(),NULL);
812 EVP_DigestInit_ex(sig.
m_Bdy_ctx,EVP_sha1(),NULL);
815 EVP_VerifyInit_ex(sig.
m_Hdr_ctx,EVP_sha256(),NULL);
816 EVP_DigestInit_ex(sig.
m_Bdy_ctx,EVP_sha256(),NULL);
822 vector<list<string>::reverse_iterator> used;
825 list<string>::reverse_iterator i;
827 if (
_strnicmp(i->c_str(),x->c_str(),x->length()) == 0) {
829 const char* s = i->c_str()+x->length();
830 while (*s ==
' ' || *s ==
'\t')
832 if (*s ==
':' && find(used.begin(),used.end(),i) == used.end())
842 sig.
Hash(i->c_str(),i->length());
845 sig.
Hash(sTemp.c_str(),sTemp.length());
851 for (
char* s = (
char*)sTemp.c_str(); *s !=
'\0' && *s !=
':'; s++) {
852 if (*s >=
'A' && *s <=
'Z')
855 sig.
Hash(sTemp.c_str(),sTemp.length());
868 list<string>::reverse_iterator i;
870 if (
_strnicmp(i->c_str(),
"From",4) == 0) {
872 const char *s = i->c_str() + 4;
873 while (*s ==
' ' || *s ==
'\t')
876 if (find(used.begin(),used.end(),i) == used.end()) {
890 ValidSigFound =
true;
908 unsigned temp = 0,
last = 0;
909 bool overflowed =
false;
912 if (*s <
'0' || *s >
'9')
915 temp = temp * 10 + (*s -
'0');
921 }
while (*s !=
'\0');
923 *result = overflowed ? -1 : temp;
941 string sValue = sHeader.substr(sHeader.find(
':') + 1);
943 static const char *tags[] = {
"v",
"a",
"b",
"d",
"h",
"s",
"c",
"i",
"l",
"q",
"t",
"x",
"bh",NULL};
944 char *values[
sizeof(tags)/
sizeof(tags[0])] = {NULL};
953 if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL)
957 if (strcmp(values[1],
"rsa-sha1") == 0) {
959 }
else if (strcmp(values[1],
"rsa-sha256") == 0) {
961 }
else if (strcmp(values[1],
"ed25519-sha256") == 0) {
981 if (*values[3] ==
'\0')
986 if (*values[4] ==
'\0')
990 if (*values[5] ==
'\0')
995 if (values[6] == NULL) {
998 char* slash = strchr(values[6],
'/');
1002 if (strcmp(values[6],
"simple") == 0)
1004 else if (strcmp(values[6],
"relaxed") == 0)
1009 if (slash == NULL || strcmp(slash + 1,
"simple") == 0)
1011 else if (strcmp(slash + 1,
"relaxed") == 0)
1018 if (values[7] == NULL) {
1026 char* at = strchr(values[7],
'@');
1031 char* ilocalpart = values[7];
1032 char* idomain = at + 1;
1035 int idomainlen = strlen(idomain);
1036 int ddomainlen = strlen(values[3]);
1039 if (idomainlen < ddomainlen)
1041 if (
_stricmp(idomain + idomainlen - ddomainlen,values[3]) != 0)
1043 if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] !=
'.')
1059 if (values[9] != NULL) {
1061 bool HasDNS =
false;
1062 char* s = strtok_r(values[9],
":",&saveptr);
1064 if (strncmp(s,
"dns",3) == 0 && (s[3] ==
'\0' || s[3] ==
'/')) {
1068 s = strtok_r(NULL,
": \t",&saveptr);
1076 unsigned SignedTime = -1;
1077 if (values[10] != NULL) {
1083 if (values[11] == NULL) {
1091 if (SignedTime != (
unsigned) -1 && sig.
ExpireTime <= SignedTime)
1095 unsigned curtime = time(NULL);
1102 bool HasFrom =
false, HasSubject =
false;
1104 char* s = strtok_r(values[4],
":",&saveptr);
1108 else if (
_stricmp(s,
"Subject") == 0)
1112 s = strtok_r(NULL,
":",&saveptr);
1130 bool MoreBodyNeeded =
false;
1135 if (nBufLength > 0) {
1136 while (i->EmptyLineCount > 0) {
1137 i->Hash(
"\r\n",2,
true);
1138 i->EmptyLineCount--;
1140 i->Hash(szBuffer,nBufLength,
true);
1141 i->Hash(
"\r\n",2,
true);
1143 i->EmptyLineCount++;
1145 i->Hash(
"\r\n",2,
true);
1149 if (nBufLength > 0) {
1150 while (i->EmptyLineCount > 0) {
1151 i->Hash(
"\r\n",2,
true);
1152 i->EmptyLineCount--;
1154 i->Hash(szBuffer,nBufLength,
true);
1156 i->Hash(
"\r\n",2,
true);
1157 }
else i->EmptyLineCount++;
1160 i->Hash(szBuffer,nBufLength,
true);
1163 if (i->UnverifiedBodyCount == 0)
1164 MoreBodyNeeded =
true;
1168 if (!MoreBodyNeeded)
1201 char ed25519PubKey[61];
1203 static const char *tags[] = {
"v",
"g",
"h",
"k",
"p",
"s",
"t",
"n",NULL};
1204 char *values[
sizeof(tags)/
sizeof(tags[0])] = {NULL};
1209 if (values[0] != NULL) {
1211 if (strcmp(values[0],
"DKIM1") != 0)
1215 for (
unsigned j = 1;
j <
sizeof(values)/
sizeof(values[0]);
j++) {
1216 if (values[
j] != NULL && values[
j] < values[0]) {
1223 if (values[4] == NULL)
1226 PubKeyBase64 = values[4];
1229 if (values[1] == NULL)
1235 if (values[2] == NULL) {
1240 char* s = strtok_r(values[2],
":",&saveptr);
1242 if (strcmp(s,
"sha1") == 0)
1244 else if (strcmp(s,
"sha256") == 0)
1246 s = strtok_r(NULL,
":",&saveptr);
1253 if (values[3] != NULL) {
1255 if (strcmp(values[3],
"rsa") != 0 && strcmp(values[3],
"ed25519") != 0)
1257 if (strcmp(values[3],
"ed25519") == 0) {
1260 strcpy(ed25519PubKey,
"MCowBQYDK2VwAyEA");
1266 if (strlen(values[4]) > 44)
1268 strcat(ed25519PubKey,values[4]);
1269 PubKeyBase64 = ed25519PubKey;
1274 if (values[5] != NULL) {
1276 bool ServiceTypeMatch =
false;
1277 char* s = strtok_r(values[5],
":",&saveptr);
1279 if (strcmp(s,
"*") == 0 || strcmp(s,
"email") == 0) {
1280 ServiceTypeMatch =
true;
1283 s = strtok_r(NULL,
":",&saveptr);
1285 if (!ServiceTypeMatch)
1290 if (values[6] != NULL) {
1291 char *s = strtok_r(values[6],
":",&saveptr);
1293 if (strcmp(s,
"y") == 0) {
1295 }
else if (strcmp(s,
"s") == 0) {
1298 s = strtok_r(NULL,
":",&saveptr);
1305 if (PublicKeyLen == 0) {
1308 const unsigned char *PublicKeyData = (
unsigned char* )PubKeyBase64;
1310 EVP_PKEY *pkey = d2i_PUBKEY(NULL,&PublicKeyData,PublicKeyLen);
1316 if ((EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) ||
1317 (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA2) ||
1318 (EVP_PKEY_base_id(pkey) == EVP_PKEY_ED25519)) {
1321 EVP_PKEY_free(pkey);
1337 for (list<SelectorInfo>::iterator i =
Selectors.begin(); i !=
Selectors.end(); ++i) {
1338 if (
_stricmp(i->Selector.c_str(),sSelector.c_str()) == 0 &&
_stricmp(i->Domain.c_str(),sDomain.c_str()) == 0) {
1346 string sFQDN = sSelector;
1347 sFQDN +=
"._domainkey.";
1350 const int BufLen = 1024;
1351 char Buffer[BufLen];
1358 DNSResult =
_DNSGetTXT(sFQDN.c_str(),Buffer,BufLen);
1362 switch (DNSResult) {
1382 d.szSignature = (
char* )i->Header.c_str();
1383 d.szSignatureDomain = (
char* )i->Domain.c_str();
1384 d.szIdentityDomain = (
char* )i->IdentityDomain.c_str();
1385 d.szCanonicalizedData = (
char* )i->CanonicalizedData.c_str();
1386 d.nResult = i->Status;
1391 *pDetails = (*nSigCount != 0) ? &
Details[0] : NULL;
static string RelaxHeader(const string &sHeader)
static void CompressSWSP(char *pBuffer, int &nBufLength)
static void RemoveSWSP(char *szBuffer)
list< string > HeaderList
DKIMDNSCALLBACK m_pfnSelectorCallback
SelectorInfo & GetSelector(const string &sSelector, const string &sDomain)
vector< DKIMVerifyDetails > Details
bool m_AllowUnsignedFromHeaders
bool m_SaveCanonicalizedData
int GetDetails(int *nSigCount, DKIMVerifyDetails **pDetails)
int ParseDKIMSignature(const string &sHeader, SignatureInfo &sig)
list< SelectorInfo > Selectors
virtual int ProcessHeaders(void)
virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
bool m_HonorBodyLengthTag
list< SignatureInfo > Signatures
SelectorInfo(const string &sSelector, const string &sDomain)
vector< string > SignedHeaders
unsigned HeaderCanonicalization
bool m_SaveCanonicalizedData
unsigned UnverifiedBodyCount
unsigned BodyCanonicalization
SignatureInfo(bool SaveCanonicalizedData)
void Hash(const char *szBuffer, unsigned nBufLength, bool IsBody=false)
unsigned VerifiedBodyCount
SelectorInfo * m_pSelector
#define DKIM_SUCCESS_BUT_EXTRA
#define DKIM_BUFFER_TOO_SMALL
#define DKIM_PARTIAL_SUCCESS
#define DKIM_SELECTOR_PUBLIC_KEY_INVALID
#define DKIM_BODY_HASH_MISMATCH
#define DKIM_SELECTOR_KEY_REVOKED
#define DKIM_CANON_SIMPLE
#define DKIM_SIGNATURE_BAD_BUT_TESTING
#define DKIM_STAT_INCOMPAT
#define DKIM_NO_VALID_SIGNATURES
#define DKIM_SELECTOR_ALGORITHM_MISMATCH
#define DKIM_INVALID_CONTEXT
#define DKIM_SELECTOR_DNS_TEMP_FAILURE
#define DKIM_OUT_OF_MEMORY
#define DKIM_SELECTOR_GRANULARITY_MISMATCH
#define DKIM_SIGNATURE_BAD
#define DKIM_CANON_RELAXED
#define DKIM_SIGNATURE_EXPIRED
#define DKIM_BAD_PRIVATE_KEY
#define DKIM_UNSIGNED_FROM
#define DKIM_FINISHED_BODY
#define DKIM_NO_SIGNATURES
#define DKIM_SELECTOR_INVALID
#define DKIM_SELECTOR_DNS_PERM_FAILURE
#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG
int stralloc_copys(stralloc *, char const *)
int _DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen)
int _DKIM_ReportResult(const char *ResFile, const char *result, const char *reason)
bool WildcardMatch(const char *p, const char *s)
unsigned DecodeBase64(char *ptr)
bool ParseAddresses(string str, vector< string > &Addresses)
bool ParseUnsigned(const char *s, unsigned *result)
bool ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[])
const char * DKIM_ErrorResult(const int res)
void DecodeQuotedPrintable(char *ptr)
int dig_ascii(char *digascii, unsigned const char *digest, const int len)
void p(char *, char *, int, int, int)
DKIMDNSCALLBACK pfnSelectorCallback
int nAllowUnsignedFromHeaders
int nSaveCanonicalizedData