diff options
Diffstat (limited to 'src/dkimverify.cpp')
-rw-r--r-- | src/dkimverify.cpp | 786 |
1 files changed, 380 insertions, 406 deletions
diff --git a/src/dkimverify.cpp b/src/dkimverify.cpp index c9f1003..6693d8e 100644 --- a/src/dkimverify.cpp +++ b/src/dkimverify.cpp @@ -18,17 +18,21 @@ * Changes done by ¢feh@fehcom.de obeying the above license * *****************************************************************************/ -#include <string.h> -#include <ctype.h> +#include "dkimverify.h" + #include <assert.h> -#include <vector> +#include <ctype.h> +#include <string.h> + #include <algorithm> +#include <vector> + #include "dkim.h" -#include "dkimverify.h" #include "dnsgettxt.h" -extern "C" { -#include "dns.h" +extern "C" { #include "stralloc.h" + +#include "dns.h" } /***************************************************************************** @@ -49,17 +53,17 @@ extern "C" { * *****************************************************************************/ -#define _strnicmp strncasecmp -#define _stricmp strcasecmp -#define MAX_SIGNATURES 10 // maximum number of DKIM signatures to process/message -#define FDLOG stderr /* writing to another FD requires a method */ +#define _strnicmp strncasecmp +#define _stricmp strcasecmp +#define MAX_SIGNATURES 10 // maximum number of DKIM signatures to process/message +#define FDLOG stderr /* writing to another FD requires a method */ string SigHdr; size_t m_SigHdr; -extern "C" int stralloc_copys(stralloc *,char const *); +extern "C" int stralloc_copys(stralloc *, const char *); -int dig_ascii(char *digascii,unsigned const char *digest,const int len) +int dig_ascii(char *digascii, const unsigned char *digest, const int len) { static const char hextab[] = "0123456789abcdef"; int j; @@ -74,81 +78,70 @@ int dig_ascii(char *digascii,unsigned const char *digest,const int len) } -int _DNSGetTXT(const char *szFQDN,char *Buffer,int nBufLen) +int _DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen) { - stralloc out = {0}; - stralloc sa = {0}; - Buffer[0] = '\0'; // need to be initialized - - if (!stralloc_copys(&sa,szFQDN)) return -1; - - DNS_INIT - - switch (dns_txt(&out,&sa)) { - case -1: return -1; - case 0: return 0; - } - - if (nBufLen < out.len) - return -2; - - if (!stralloc_0(&out)) return -1; - memcpy(Buffer,out.s,out.len); // Return-by-value; sigh - - return out.len; + stralloc out = {0}; + stralloc sa = {0}; + Buffer[0] = '\0'; // need to be initialized + + if (!stralloc_copys(&sa, szFQDN)) return -1; + + DNS_INIT + + switch (dns_txt(&out, &sa)) { + case -1: return -1; + case 0: return 0; + } + + if (nBufLen < out.len) return -2; + + if (!stralloc_0(&out)) return -1; + memcpy(Buffer, out.s, out.len); // Return-by-value; sigh + + return out.len; } -int _DKIM_ReportResult(const char* ResFile,const char* result,const char* reason) +int _DKIM_ReportResult(const char *ResFile, const char *result, const char *reason) { int len = 0; - FILE* out = fopen(ResFile,"wb+"); + FILE *out = fopen(ResFile, "wb+"); if (out == NULL) return -1; if (result) { len = strlen(result); - fwrite(result,1,len,out); - fwrite("\r",1,1,out); + fwrite(result, 1, len, out); + fwrite("\r", 1, 1, out); } if (reason) { - fwrite(reason,1,strlen(reason),out); - fwrite("\r",1,1,out); + fwrite(reason, 1, strlen(reason), out); + fwrite("\r", 1, 1, out); } fclose(out); return len; } -const char* DKIM_ErrorResult(const int res) +const char *DKIM_ErrorResult(const int res) { - const char* errormsg = ""; + const char *errormsg = ""; switch (res) { - case DKIM_FAIL: - errormsg = " (verify error: message is suspicious)"; - break; - case DKIM_BAD_SYNTAX: - errormsg = " (signature error: could not parse or has bad tags/values)"; - break; - case DKIM_SIGNATURE_BAD: - errormsg = " (signature error: RSA/ED25519 verify failed)"; - break; + case DKIM_FAIL: errormsg = " (verify error: message is suspicious)"; break; + case DKIM_BAD_SYNTAX: errormsg = " (signature error: could not parse or has bad tags/values)"; break; + case DKIM_SIGNATURE_BAD: errormsg = " (signature error: RSA/ED25519 verify failed)"; break; case DKIM_SIGNATURE_BAD_BUT_TESTING: errormsg = " (signature error: RSA/ED25519 verify failed but testing)"; break; - case DKIM_SIGNATURE_EXPIRED: - errormsg = " (signature error: signature x= value expired)"; - break; + case DKIM_SIGNATURE_EXPIRED: errormsg = " (signature error: signature x= value expired)"; break; case DKIM_SELECTOR_INVALID: errormsg = " (signature error: selector doesn't parse or contains invalid values)"; break; case DKIM_SELECTOR_GRANULARITY_MISMATCH: errormsg = " (signature error: selector g= doesn't match i=)"; break; - case DKIM_SELECTOR_KEY_REVOKED: - errormsg = " (signature error: revoked p= empty)"; - break; + case DKIM_SELECTOR_KEY_REVOKED: errormsg = " (signature error: revoked p= empty)"; break; case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG: errormsg = " (dns error: selector domain name too long to request)"; break; @@ -161,36 +154,26 @@ const char* DKIM_ErrorResult(const int res) case DKIM_SELECTOR_PUBLIC_KEY_INVALID: errormsg = " (signature error: selector p= value invalid or wrong format)"; break; - case DKIM_NO_SIGNATURES: - errormsg = " (process error: no signatures)"; - break; - case DKIM_NO_VALID_SIGNATURES: - errormsg = " (process error: no valid signatures)"; - break; + case DKIM_NO_SIGNATURES: errormsg = " (process error: no signatures)"; break; + case DKIM_NO_VALID_SIGNATURES: errormsg = " (process error: no valid signatures)"; break; case DKIM_BODY_HASH_MISMATCH: errormsg = " (signature verify error: message body does not hash to bh= value)"; break; case DKIM_SELECTOR_ALGORITHM_MISMATCH: errormsg = " (signature error: selector h= doesn't match signature a=)"; break; - case DKIM_STAT_INCOMPAT: - errormsg = " (signature error: incompatible v= value)"; - break; + case DKIM_STAT_INCOMPAT: errormsg = " (signature error: incompatible v= value)"; break; case DKIM_UNSIGNED_FROM: errormsg = " (signature error: not all message's From headers in signature)"; break; - case DKIM_OUT_OF_MEMORY: - errormsg = " (internal error: memory allocation failed)"; - break; + case DKIM_OUT_OF_MEMORY: errormsg = " (internal error: memory allocation failed)"; break; case DKIM_INVALID_CONTEXT: errormsg = " (internal error: DKIMContext structure invalid for this operation)"; break; case DKIM_NO_SENDER: errormsg = " (signing error: Could not find From: or Sender: header in message)"; break; - case DKIM_BAD_PRIVATE_KEY: - errormsg = " (signing error: Could not parse private key)"; - break; + case DKIM_BAD_PRIVATE_KEY: errormsg = " (signing error: Could not parse private key)"; break; case DKIM_BUFFER_TOO_SMALL: errormsg = " (signing error: Buffer passed in is not large enough)"; break; @@ -204,7 +187,9 @@ SignatureInfo::SignatureInfo(bool s) VerifiedBodyCount = 0; UnverifiedBodyCount = 0; -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) EVP_MD_CTX_init(&m_Hdr_ctx); EVP_MD_CTX_init(&m_Bdy_ctx); #else @@ -223,12 +208,14 @@ SignatureInfo::SignatureInfo(bool s) SignatureInfo::~SignatureInfo() { -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) EVP_MD_CTX_cleanup(&m_Hdr_ctx); EVP_MD_CTX_cleanup(&m_Bdy_ctx); #else /** FIXME: No free but reset ! **/ - EVP_MD_CTX_reset(m_Hdr_ctx); + EVP_MD_CTX_reset(m_Hdr_ctx); EVP_MD_CTX_reset(m_Bdy_ctx); #endif #if (OPENSSL_VERSION_NUMBER > 0x10101000L) @@ -242,26 +229,23 @@ inline bool isswsp(char ch) } //////////////////////////////////////////////////////////////////////////////// -// +// // Parse a DKIM tag-list. Returns true for success // //////////////////////////////////////////////////////////////////////////////// -bool ParseTagValueList(char *tagvaluelist,const char *wanted[],char *values[]) +bool ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[]) { char *s = tagvaluelist; for (;;) { // skip whitespace - while (isswsp(*s)) - s++; + while (isswsp(*s)) s++; // if at the end of the string, return success. Note: this allows a list with no entries - if (*s == '\0') - return true; + if (*s == '\0') return true; // get tag name - if (!isalpha(*s)) - return false; + if (!isalpha(*s)) return false; char *tag = s; do { @@ -271,26 +255,22 @@ bool ParseTagValueList(char *tagvaluelist,const char *wanted[],char *values[]) char *endtag = s; // skip whitespace before equals - while (isswsp(*s)) - s++; + while (isswsp(*s)) s++; // next character must be equals - if (*s != '=') - return false; + if (*s != '=') return false; s++; // null-terminate tag name *endtag = '\0'; // skip whitespace after equals - while (isswsp(*s)) - s++; + while (isswsp(*s)) s++; // get tag value char *value = s; - while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~'))) - s++; + while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~'))) s++; char *e = s; @@ -299,31 +279,27 @@ bool ParseTagValueList(char *tagvaluelist,const char *wanted[],char *values[]) if (*s == '\0') done = true; else { - if (*s != ';') - return false; + if (*s != ';') return false; s++; } // skip backwards past any trailing whitespace - while (e > value && isswsp(e[-1])) - e--; + while (e > value && isswsp(e[-1])) e--; // null-terminate tag value *e = '\0'; // check to see if we want this tag for (unsigned i = 0; wanted[i] != NULL; i++) { - if (strcmp(wanted[i],tag) == 0) { + if (strcmp(wanted[i], tag) == 0) { // return failure if we already have a value for this tag (duplicates not allowed) - if (values[i] != NULL) - return false; + if (values[i] != NULL) return false; values[i] = value; break; } } - if (done) - return true; + if (done) return true; } } //////////////////////////////////////////////////////////////////////////////// @@ -345,18 +321,16 @@ char Tohex(char ch) } } //////////////////////////////////////////////////////////////////////////////// -// +// // Decode quoted printable string in-place // //////////////////////////////////////////////////////////////////////////////// -void DecodeQuotedPrintable(char* ptr) +void DecodeQuotedPrintable(char *ptr) { char *s = ptr; - while (*s != '\0' && *s != '=') - s++; + while (*s != '\0' && *s != '=') s++; - if (*s == '\0') - return; + if (*s == '\0') return; char *d = s; do { @@ -370,30 +344,33 @@ void DecodeQuotedPrintable(char* ptr) *d = '\0'; } //////////////////////////////////////////////////////////////////////////////// -// +// // Decode base64 string in-place, returns number of bytes output // //////////////////////////////////////////////////////////////////////////////// unsigned DecodeBase64(char *ptr) { static const char base64_table[256] = { - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, - -1, 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,-1,-1,-1,-1,-1, - -1,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,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, - -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; - - unsigned char* s = (unsigned char* )ptr; - unsigned char* d = (unsigned char* )ptr; + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 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, -1, -1, -1, -1, -1, + -1, 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, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; + + unsigned char *s = (unsigned char *)ptr; + unsigned char *d = (unsigned char *)ptr; unsigned b64accum = 0; unsigned char b64shift = 0; while (*s != '\0') { unsigned char value = base64_table[*s++]; - if ((signed char) value >= 0) { + if ((signed char)value >= 0) { b64accum = (b64accum << 6) | value; b64shift += 6; if (b64shift >= 8) { @@ -403,11 +380,11 @@ unsigned DecodeBase64(char *ptr) } } - return (char* )d-ptr; + return (char *)d - ptr; } //////////////////////////////////////////////////////////////////////////////// -// +// // Match a string with a pattern (used for g= value) // Supports a single, optional "*" wildcard character. // @@ -415,35 +392,34 @@ unsigned DecodeBase64(char *ptr) bool WildcardMatch(const char *p, const char *s) { // special case: An empty "g=" value never matches any addresses - if (*p == '\0') - return false; + if (*p == '\0') return false; - const char* wildcard = strchr(p,'*'); + const char *wildcard = strchr(p, '*'); if (wildcard == NULL) { return strcmp(s, p) == 0; } else { unsigned beforewildcardlen = wildcard - p; unsigned afterwildcardlen = strlen(wildcard + 1); unsigned slen = strlen(s); - return (slen >= beforewildcardlen + afterwildcardlen) && - (strncmp(s,p,beforewildcardlen) == 0) && strcmp(s + slen - afterwildcardlen,wildcard + 1) == 0; + return (slen >= beforewildcardlen + afterwildcardlen) && (strncmp(s, p, beforewildcardlen) == 0) + && strcmp(s + slen - afterwildcardlen, wildcard + 1) == 0; } } //////////////////////////////////////////////////////////////////////////////// -// +// // Parse addresses from a string. Returns true if at least one address found // //////////////////////////////////////////////////////////////////////////////// -bool ParseAddresses(string str,vector<string> &Addresses) +bool ParseAddresses(string str, vector<string>& Addresses) { - char* s = (char* )str.c_str(); + char *s = (char *)str.c_str(); while (*s != '\0') { - char* start = s; - char* from = s; - char* to = s; - char* lt = NULL; // pointer to less than character (<) which starts the address if found + char *start = s; + char *from = s; + char *to = s; + char *lt = NULL; // pointer to less than character (<) which starts the address if found while (*from != '\0') { if (*from == '(') { @@ -459,16 +435,14 @@ bool ParseAddresses(string str,vector<string> &Addresses) else if (*from == '\\' && from[1] != '\0') from++; } - } - else if (*from == ')') { + } else if (*from == ')') { // ignore closing parenthesis outside of comment from++; } else if (*from == ',' || *from == ';') { // comma/semicolon ends the address from++; break; - } - else if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') { + } else if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') { // ignore whitespace from++; } else if (*from == '"') { @@ -490,8 +464,7 @@ bool ParseAddresses(string str,vector<string> &Addresses) // copy any other char *to = *from++; // save pointer to '<' for later... - if (*to == '<') - lt = to; + if (*to == '<') lt = to; to++; } } @@ -500,22 +473,20 @@ bool ParseAddresses(string str,vector<string> &Addresses) // if there's < > get what's inside if (lt != NULL) { - start = lt+1; + start = lt + 1; char *gt = strchr(start, '>'); - if (gt != NULL) - *gt = '\0'; + if (gt != NULL) *gt = '\0'; } else { // look for and strip group name char *colon = strchr(start, ':'); if (colon != NULL) { char *at = strchr(start, '@'); - if (at == NULL || colon < at) - start = colon+1; + if (at == NULL || colon < at) start = colon + 1; } } if (*start != '\0' && strchr(start, '@') != NULL) { - Addresses.push_back(start); // save address + Addresses.push_back(start); // save address } s = from; @@ -529,24 +500,24 @@ bool ParseAddresses(string str,vector<string> &Addresses) CDKIMVerify::CDKIMVerify() { m_pfnSelectorCallback = NULL; -// m_pfnPracticesCallback = NULL; + // m_pfnPracticesCallback = NULL; m_HonorBodyLengthTag = false; m_CheckPractices = false; -// Kai: -// m_SubjectIsRequired = true; + // Kai: + // m_SubjectIsRequired = true; m_SubjectIsRequired = false; m_SaveCanonicalizedData = false; m_AllowUnsignedFromHeaders = false; } -CDKIMVerify::~CDKIMVerify() {} // Destructor +CDKIMVerify::~CDKIMVerify() {} // Destructor //////////////////////////////////////////////////////////////////////////////// -// +// // Init - save the options // //////////////////////////////////////////////////////////////////////////////// -int CDKIMVerify::Init(DKIMVerifyOptions* pOptions) +int CDKIMVerify::Init(DKIMVerifyOptions *pOptions) { int nRet = CDKIMBase::Init(); @@ -555,8 +526,8 @@ int CDKIMVerify::Init(DKIMVerifyOptions* pOptions) m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0; m_CheckPractices = pOptions->nCheckPractices != 0; -// Kai: -// m_SubjectIsRequired = pOptions->nSubjectRequired == 0; + // Kai: + // m_SubjectIsRequired = pOptions->nSubjectRequired == 0; m_SubjectIsRequired = pOptions->nSubjectRequired == 1; m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0; m_AllowUnsignedFromHeaders = pOptions->nAllowUnsignedFromHeaders != 0; @@ -565,7 +536,7 @@ int CDKIMVerify::Init(DKIMVerifyOptions* pOptions) } //////////////////////////////////////////////////////////////////////////////// -// +// // GetResults - return the pass/fail/neutral verification result // //////////////////////////////////////////////////////////////////////////////// @@ -573,7 +544,7 @@ int CDKIMVerify::GetResults(void) { // char mdi[128]; // char digi[128]; - + ProcessFinal(); unsigned char *SignMsg; @@ -590,16 +561,18 @@ int CDKIMVerify::GetResults(void) unsigned char md[EVP_MAX_MD_SIZE]; unsigned len = 0; -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - res = EVP_DigestFinal(&i->m_Bdy_ctx,md,&len); +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + res = EVP_DigestFinal(&i->m_Bdy_ctx, md, &len); #else - res = EVP_DigestFinal_ex(i->m_Bdy_ctx,md,&len); + res = EVP_DigestFinal_ex(i->m_Bdy_ctx, md, &len); EVP_MD_CTX_reset(i->m_Bdy_ctx); #endif // dig_ascii(digi,md,32); // dig_ascii(mdi,(unsigned const char *)i->BodyHashData.data(),32); - if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(),md,len) != 0) { + if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(), md, len) != 0) { // body hash mismatch // if the selector is in testing mode... @@ -614,7 +587,7 @@ int CDKIMVerify::GetResults(void) } } else { // hash CRLF separating the body from the signature - i->Hash("\r\n",2); + i->Hash("\r\n", 2); } // SECOND: Fetch the signature @@ -622,39 +595,53 @@ int CDKIMVerify::GetResults(void) string sSignedSig = i->Header; string sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1); - static const char* tags[] = {"b",NULL}; - char* values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + static const char *tags[] = {"b", NULL}; + char *values[sizeof(tags) / sizeof(tags[0])] = {NULL}; - char* pSigValue = (char* ) sSigValue.c_str(); // our signature - if (ParseTagValueList(pSigValue,tags,values) && values[0] != NULL) { - sSignedSig.erase(15 + values[0] - pSigValue,strlen(values[0])); + char *pSigValue = (char *)sSigValue.c_str(); // our signature + if (ParseTagValueList(pSigValue, tags, values) && values[0] != NULL) { + sSignedSig.erase(15 + values[0] - pSigValue, strlen(values[0])); } if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) { sSignedSig = RelaxHeader(sSignedSig); - } - else if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) { + } else if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) { RemoveSWSP(sSignedSig); // convert "DKIM-Signature" to lower case - sSignedSig.replace(0,14,"dkim-signature",14); + sSignedSig.replace(0, 14, "dkim-signature", 14); } - i->Hash(sSignedSig.c_str(),sSignedSig.length()); // include generated DKIM signature header + i->Hash(sSignedSig.c_str(), sSignedSig.length()); // include generated DKIM signature header assert(i->m_pSelector != NULL); - if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) != EVP_PKEY_ED25519) -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - res = EVP_VerifyFinal(&i->m_Hdr_ctx,(unsigned char *)i->SignatureData.data(),i->SignatureData.length(),i->m_pSelector->PublicKey); + if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) != EVP_PKEY_ED25519) +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + res = EVP_VerifyFinal( + &i->m_Hdr_ctx, + (unsigned char *)i->SignatureData.data(), + i->SignatureData.length(), + i->m_pSelector->PublicKey); #else - res = EVP_VerifyFinal(i->m_Hdr_ctx,(unsigned char *)i->SignatureData.data(),i->SignatureData.length(),i->m_pSelector->PublicKey); + res = EVP_VerifyFinal( + i->m_Hdr_ctx, + (unsigned char *)i->SignatureData.data(), + i->SignatureData.length(), + i->m_pSelector->PublicKey); #endif #if (OPENSSL_VERSION_NUMBER > 0x10101000L) else if (EVP_PKEY_base_id(i->m_pSelector->PublicKey) == EVP_PKEY_ED25519) { - EVP_DigestVerifyInit(i->m_Msg_ctx,NULL,NULL,NULL,i->m_pSelector->PublicKey); // late initialization - - SignMsg = (unsigned char *) SigHdr.data(); - res = EVP_DigestVerify(i->m_Msg_ctx,(unsigned char *)i->SignatureData.data(),(size_t)i->SignatureData.length(), - SignMsg,m_SigHdr); + EVP_DigestVerifyInit( + i->m_Msg_ctx, NULL, NULL, NULL, i->m_pSelector->PublicKey); // late initialization + + SignMsg = (unsigned char *)SigHdr.data(); + res = EVP_DigestVerify( + i->m_Msg_ctx, + (unsigned char *)i->SignatureData.data(), + (size_t)i->SignatureData.length(), + SignMsg, + m_SigHdr); } #endif @@ -675,25 +662,25 @@ int CDKIMVerify::GetResults(void) RealFailures++; } } - } else if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH || - i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH || - i->Status == DKIM_SELECTOR_KEY_REVOKED) { + } else if ( + i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH || i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH + || i->Status == DKIM_SELECTOR_KEY_REVOKED) + { // treat these as failures // todo: maybe see if the selector is in testing mode? RealFailures++; } - } // loop over signature infos done + } // loop over signature infos done // get the From address's domain if we might need it string sFromDomain; if (SuccessCount > 0 || m_CheckPractices) { for (list<string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { - if (_strnicmp(i->c_str(),"From",4) == 0) { + if (_strnicmp(i->c_str(), "From", 4) == 0) { // skip over whitespace between the header name and : - const char* s = i->c_str() + 4; - while (*s == ' ' || *s == '\t') - s++; + const char *s = i->c_str() + 4; + while (*s == ' ' || *s == '\t') s++; if (*s == ':') { vector<string> Addresses; if (ParseAddresses(s + 1, Addresses)) { @@ -705,17 +692,17 @@ int CDKIMVerify::GetResults(void) } } } - + // if a signature from the From domain verified successfully, return success now // without checking the author domain signing practices if (SuccessCount > 0 && !sFromDomain.empty()) { for (list<string>::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) { // see if the successful domain is the same as or a parent of the From domain - if (i->length() > sFromDomain.length()) - continue; - if (_stricmp(i->c_str(),sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0) - continue; - if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.') { + if (i->length() > sFromDomain.length()) continue; + if (_stricmp(i->c_str(), sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0) continue; + if (i->length() == sFromDomain.length() + || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.') + { return SuccessCount == Signatures.size() ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS; } } @@ -728,11 +715,11 @@ int CDKIMVerify::GetResults(void) } //////////////////////////////////////////////////////////////////////////////// -// +// // Hash - update the hash or update the Ed25519 signature input // //////////////////////////////////////////////////////////////////////////////// -void SignatureInfo::Hash(const char* szBuffer,unsigned nBufLength,bool IsBody) +void SignatureInfo::Hash(const char *szBuffer, unsigned nBufLength, bool IsBody) { #if 0 /** START DEBUG CODE **/ @@ -748,7 +735,7 @@ void SignatureInfo::Hash(const char* szBuffer,unsigned nBufLength,bool IsBody) /** END DEBUG CODE **/ #endif - if (IsBody && BodyLength != (unsigned) -1) { // trick: 2's complement + if (IsBody && BodyLength != (unsigned)-1) { // trick: 2's complement VerifiedBodyCount += nBufLength; if (VerifiedBodyCount > BodyLength) { nBufLength = BodyLength - (VerifiedBodyCount - nBufLength); @@ -759,29 +746,31 @@ void SignatureInfo::Hash(const char* szBuffer,unsigned nBufLength,bool IsBody) } if (IsBody && !BodyHashData.empty()) { -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_DigestUpdate(&m_Bdy_ctx,szBuffer,nBufLength); +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_DigestUpdate(&m_Bdy_ctx, szBuffer, nBufLength); } else { - EVP_VerifyUpdate(&m_Hdr_ctx,szBuffer,nBufLength); + EVP_VerifyUpdate(&m_Hdr_ctx, szBuffer, nBufLength); #else - EVP_DigestUpdate(m_Bdy_ctx,szBuffer,nBufLength); + EVP_DigestUpdate(m_Bdy_ctx, szBuffer, nBufLength); } else { - EVP_VerifyUpdate(m_Hdr_ctx,szBuffer,nBufLength); + EVP_VerifyUpdate(m_Hdr_ctx, szBuffer, nBufLength); #endif #if (OPENSSL_VERSION_NUMBER > 0x10101000L) - SigHdr.append(szBuffer,nBufLength); + SigHdr.append(szBuffer, nBufLength); m_SigHdr += nBufLength; #endif } if (m_SaveCanonicalizedData) { - CanonicalizedData.append(szBuffer,nBufLength); + CanonicalizedData.append(szBuffer, nBufLength); } } //////////////////////////////////////////////////////////////////////////////// -// +// // ProcessHeaders - Look for DKIM-Signatures and start processing them // look for DKIM-Signature header(s) // @@ -789,49 +778,46 @@ void SignatureInfo::Hash(const char* szBuffer,unsigned nBufLength,bool IsBody) int CDKIMVerify::ProcessHeaders(void) { for (list<string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) { - if (strlen(i->c_str()) < 14) continue; // too short - if (_strnicmp(i->c_str(),"DKIM-Signature",14) == 0) { + if (strlen(i->c_str()) < 14) continue; // too short + if (_strnicmp(i->c_str(), "DKIM-Signature", 14) == 0) { // skip over whitespace between the header name and : const char *s = i->c_str() + 14; - while (*s == ' ' || *s == '\t') - s++; + while (*s == ' ' || *s == '\t') s++; if (*s == ':') { // found SignatureInfo sig(m_SaveCanonicalizedData); - sig.Status = ParseDKIMSignature(*i,sig); - Signatures.push_back(sig); // save signature + sig.Status = ParseDKIMSignature(*i, sig); + Signatures.push_back(sig); // save signature - if (Signatures.size() >= MAX_SIGNATURES) - break; + if (Signatures.size() >= MAX_SIGNATURES) break; } } } - if (Signatures.empty()) - return DKIM_NO_SIGNATURES; + if (Signatures.empty()) return DKIM_NO_SIGNATURES; bool ValidSigFound = false; for (list<SignatureInfo>::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { - SignatureInfo &sig = *s; + SignatureInfo& sig = *s; if (sig.Status != DKIM_SUCCESS) continue; - SelectorInfo &sel = GetSelector(sig.Selector,sig.Domain); + SelectorInfo& sel = GetSelector(sig.Selector, sig.Domain); sig.m_pSelector = &sel; if (sel.Status != DKIM_SUCCESS) { sig.Status = sel.Status; } else { // check the granularity - if (!WildcardMatch(sel.Granularity.c_str(),sig.IdentityLocalPart.c_str())) - sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH; // this error causes the signature to fail + if (!WildcardMatch(sel.Granularity.c_str(), sig.IdentityLocalPart.c_str())) + sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH; // this error causes the signature to fail // check the hash algorithm - if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || - (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256)) - sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH; // causes signature to fail + if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) + || (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256)) + sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH; // causes signature to fail // check for same domain - if (sel.SameDomain && _stricmp(sig.Domain.c_str(),sig.IdentityDomain.c_str()) != 0) + if (sel.SameDomain && _stricmp(sig.Domain.c_str(), sig.IdentityDomain.c_str()) != 0) sig.Status = DKIM_BAD_SYNTAX; } @@ -839,23 +825,27 @@ int CDKIMVerify::ProcessHeaders(void) // initialize the hashes if (sig.m_nHash == DKIM_HASH_SHA1) { -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_VerifyInit(&sig.m_Hdr_ctx,EVP_sha1()); - EVP_DigestInit(&sig.m_Bdy_ctx,EVP_sha1()); +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1()); + EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1()); #else - EVP_VerifyInit_ex(sig.m_Hdr_ctx,EVP_sha1(),NULL); - EVP_DigestInit_ex(sig.m_Bdy_ctx,EVP_sha1(),NULL); + EVP_VerifyInit_ex(sig.m_Hdr_ctx, EVP_sha1(), NULL); + EVP_DigestInit_ex(sig.m_Bdy_ctx, EVP_sha1(), NULL); #endif } if (sig.m_nHash == DKIM_HASH_SHA256) { -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_VerifyInit(&sig.m_Hdr_ctx,EVP_sha256()); - EVP_DigestInit(&sig.m_Bdy_ctx,EVP_sha256()); +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10100000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha256()); + EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha256()); #else - EVP_VerifyInit_ex(sig.m_Hdr_ctx,EVP_sha256(),NULL); - EVP_DigestInit_ex(sig.m_Bdy_ctx,EVP_sha256(),NULL); + EVP_VerifyInit_ex(sig.m_Hdr_ctx, EVP_sha256(), NULL); + EVP_DigestInit_ex(sig.m_Bdy_ctx, EVP_sha256(), NULL); #endif -#if (OPENSSL_VERSION_NUMBER > 0x10101000L) +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) SigHdr.assign(""); m_SigHdr = 0; } @@ -867,13 +857,11 @@ int CDKIMVerify::ProcessHeaders(void) for (vector<string>::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) { list<string>::reverse_iterator i; for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) { - if (_strnicmp(i->c_str(),x->c_str(),x->length()) == 0) { + if (_strnicmp(i->c_str(), x->c_str(), x->length()) == 0) { // skip over whitespace between the header name and : - const char* s = i->c_str()+x->length(); - while (*s == ' ' || *s == '\t') - s++; - if (*s == ':' && find(used.begin(),used.end(),i) == used.end()) - break; + const char *s = i->c_str() + x->length(); + while (*s == ' ' || *s == '\t') s++; + if (*s == ':' && find(used.begin(), used.end(), i) == used.end()) break; } } @@ -882,41 +870,39 @@ int CDKIMVerify::ProcessHeaders(void) // hash this header if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE) { - sig.Hash(i->c_str(),i->length()); + sig.Hash(i->c_str(), i->length()); } else if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) { string sTemp = RelaxHeader(*i); - sig.Hash(sTemp.c_str(),sTemp.length()); + sig.Hash(sTemp.c_str(), sTemp.length()); } else if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) { string sTemp = *i; RemoveSWSP(sTemp); // convert characters before ':' to lower case - for (char* s = (char*)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { - if (*s >= 'A' && *s <= 'Z') - *s += 'a' - 'A'; + for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) { + if (*s >= 'A' && *s <= 'Z') *s += 'a' - 'A'; } - sig.Hash(sTemp.c_str(),sTemp.length()); + sig.Hash(sTemp.c_str(), sTemp.length()); } - sig.Hash("\r\n",2); + sig.Hash("\r\n", 2); } } if (sig.BodyHashData.empty()) { // hash CRLF separating headers from body - sig.Hash("\r\n",2); + sig.Hash("\r\n", 2); } if (!m_AllowUnsignedFromHeaders) { // make sure the message has no unsigned From headers list<string>::reverse_iterator i; for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) { - if (_strnicmp(i->c_str(),"From",4) == 0) { + if (_strnicmp(i->c_str(), "From", 4) == 0) { // skip over whitespace between the header name and : const char *s = i->c_str() + 4; - while (*s == ' ' || *s == '\t') - s++; + while (*s == ' ' || *s == '\t') s++; if (*s == ':') { - if (find(used.begin(),used.end(),i) == used.end()) { + if (find(used.begin(), used.end(), i) == used.end()) { // this From header was not signed break; } @@ -933,8 +919,7 @@ int CDKIMVerify::ProcessHeaders(void) ValidSigFound = true; } - if (!ValidSigFound) - return DKIM_NO_VALID_SIGNATURES; + if (!ValidSigFound) return DKIM_NO_VALID_SIGNATURES; return DKIM_SUCCESS; } @@ -952,12 +937,10 @@ bool ParseUnsigned(const char *s, unsigned *result) bool overflowed = false; do { - if (*s < '0' || *s > '9') - return false; // returns false for an initial '\0' + if (*s < '0' || *s > '9') return false; // returns false for an initial '\0' temp = temp * 10 + (*s - '0'); - if (temp < last) - overflowed = true; + if (temp < last) overflowed = true; last = temp; s++; @@ -969,11 +952,11 @@ bool ParseUnsigned(const char *s, unsigned *result) //////////////////////////////////////////////////////////////////////////////// -// +// // ParseDKIMSignature - Parse a DKIM-Signature header field // //////////////////////////////////////////////////////////////////////////////// -int CDKIMVerify::ParseDKIMSignature(const string& sHeader,SignatureInfo &sig) +int CDKIMVerify::ParseDKIMSignature(const string& sHeader, SignatureInfo& sig) { // for strtok_r() char *saveptr; @@ -983,77 +966,72 @@ int CDKIMVerify::ParseDKIMSignature(const string& sHeader,SignatureInfo &sig) string sValue = sHeader.substr(sHeader.find(':') + 1); - static const char *tags[] = {"v","a","b","d","h","s","c","i","l","q","t","x","bh",NULL}; - char *values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + static const char *tags[] = {"v", "a", "b", "d", "h", "s", "c", "i", "l", "q", "t", "x", "bh", NULL}; + char *values[sizeof(tags) / sizeof(tags[0])] = {NULL}; - if (!ParseTagValueList((char*) sValue.c_str(),tags,values)) - return DKIM_BAD_SYNTAX; + if (!ParseTagValueList((char *)sValue.c_str(), tags, values)) return DKIM_BAD_SYNTAX; // check signature version if (values[0] == NULL) return DKIM_BAD_SYNTAX; - + // signature MUST have a=, b=, d=, h=, s= - if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL) + if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL + || values[5] == NULL) return DKIM_BAD_SYNTAX; // algorithm ('a=') can be "rsa-sha1" or "rsa-sha256" or "ed25519" - if (strcmp(values[1],"rsa-sha1") == 0) { + if (strcmp(values[1], "rsa-sha1") == 0) { sig.m_nHash = DKIM_HASH_SHA1; - } else if (strcmp(values[1],"rsa-sha256") == 0) { + } else if (strcmp(values[1], "rsa-sha256") == 0) { sig.m_nHash = DKIM_HASH_SHA256; -#if (OPENSSL_VERSION_NUMBER > 0x10101000L) - } else if (strcmp(values[1],"ed25519-sha256") == 0) { +#if (OPENSSL_VERSION_NUMBER > 0x10101000L) + } else if (strcmp(values[1], "ed25519-sha256") == 0) { sig.m_nHash = DKIM_HASH_SHA256; #endif } else { - return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown algorithm + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown algorithm } // make sure the signature data is not empty: b=[...] unsigned SigDataLen = DecodeBase64(values[2]); - if (SigDataLen == 0) - return DKIM_BAD_SYNTAX; + if (SigDataLen == 0) return DKIM_BAD_SYNTAX; - sig.SignatureData.assign(values[2],SigDataLen); + sig.SignatureData.assign(values[2], SigDataLen); // check for body hash in DKIM header: bh=[...]; unsigned BodyHashLen = DecodeBase64(values[12]); if (BodyHashLen == 0) return DKIM_BAD_SYNTAX; - sig.BodyHashData.assign(values[12],BodyHashLen); + sig.BodyHashData.assign(values[12], BodyHashLen); // domain must not be empty - if (*values[3] == '\0') - return DKIM_BAD_SYNTAX; + if (*values[3] == '\0') return DKIM_BAD_SYNTAX; sig.Domain = values[3]; // signed headers must not be empty (more verification is done later) - if (*values[4] == '\0') - return DKIM_BAD_SYNTAX; + if (*values[4] == '\0') return DKIM_BAD_SYNTAX; // selector must not be empty - if (*values[5] == '\0') - return DKIM_BAD_SYNTAX; + if (*values[5] == '\0') return DKIM_BAD_SYNTAX; sig.Selector = values[5]; // canonicalization if (values[6] == NULL) { sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE; } else { - char* slash = strchr(values[6],'/'); - if (slash != NULL) - *slash = '\0'; + char *slash = strchr(values[6], '/'); + if (slash != NULL) *slash = '\0'; - if (strcmp(values[6],"simple") == 0) + if (strcmp(values[6], "simple") == 0) sig.HeaderCanonicalization = DKIM_CANON_SIMPLE; - else if (strcmp(values[6],"relaxed") == 0) + else if (strcmp(values[6], "relaxed") == 0) sig.HeaderCanonicalization = DKIM_CANON_RELAXED; else return DKIM_BAD_SYNTAX; - if (slash == NULL || strcmp(slash + 1,"simple") == 0) + if (slash == NULL || strcmp(slash + 1, "simple") == 0) sig.BodyCanonicalization = DKIM_CANON_SIMPLE; - else if (strcmp(slash + 1,"relaxed") == 0) + else if (strcmp(slash + 1, "relaxed") == 0) sig.BodyCanonicalization = DKIM_CANON_RELAXED; else return DKIM_BAD_SYNTAX; @@ -1068,25 +1046,21 @@ int CDKIMVerify::ParseDKIMSignature(const string& sHeader,SignatureInfo &sig) DecodeQuotedPrintable(values[7]); // must have a '@' separating the local part from the domain - char* at = strchr(values[7],'@'); - if (at == NULL) - return DKIM_BAD_SYNTAX; + char *at = strchr(values[7], '@'); + if (at == NULL) return DKIM_BAD_SYNTAX; *at = '\0'; - char* ilocalpart = values[7]; - char* idomain = at + 1; + char *ilocalpart = values[7]; + char *idomain = at + 1; // i= domain must be the same as or a subdomain of the d= domain int idomainlen = strlen(idomain); int ddomainlen = strlen(values[3]); // todo: maybe create a new error code for invalid identity domain - if (idomainlen < ddomainlen) - return DKIM_BAD_SYNTAX; - if (_stricmp(idomain + idomainlen - ddomainlen,values[3]) != 0) - return DKIM_BAD_SYNTAX; - if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.') - return DKIM_BAD_SYNTAX; + if (idomainlen < ddomainlen) return DKIM_BAD_SYNTAX; + if (_stricmp(idomain + idomainlen - ddomainlen, values[3]) != 0) return DKIM_BAD_SYNTAX; + if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.') return DKIM_BAD_SYNTAX; sig.IdentityLocalPart = ilocalpart; sig.IdentityDomain = idomain; @@ -1094,83 +1068,76 @@ int CDKIMVerify::ParseDKIMSignature(const string& sHeader,SignatureInfo &sig) // body count if (values[8] == NULL || !m_HonorBodyLengthTag) { - sig.BodyLength = (unsigned) -1; + sig.BodyLength = (unsigned)-1; } else { - if (!ParseUnsigned(values[8],&sig.BodyLength)) - return DKIM_BAD_SYNTAX; + if (!ParseUnsigned(values[8], &sig.BodyLength)) return DKIM_BAD_SYNTAX; } // query methods if (values[9] != NULL) { // make sure "dns" is in the list bool HasDNS = false; - char* s = strtok_r(values[9],":",&saveptr); + char *s = strtok_r(values[9], ":", &saveptr); while (s != NULL) { - if (strncmp(s,"dns",3) == 0 && (s[3] == '\0' || s[3] == '/')) { + if (strncmp(s, "dns", 3) == 0 && (s[3] == '\0' || s[3] == '/')) { HasDNS = true; break; } - s = strtok_r(NULL,": \t",&saveptr); /* FIXME */ -// s = strtok_r(NULL,": ",&saveptr); /* FIXME */ + s = strtok_r(NULL, ": \t", &saveptr); /* FIXME */ + // s = strtok_r(NULL,": ",&saveptr); /* FIXME */ } - if (!HasDNS) - return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method + if (!HasDNS) return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method } // signature time unsigned SignedTime = -1; if (values[10] != NULL) { - if (!ParseUnsigned(values[10],&SignedTime)) - return DKIM_BAD_SYNTAX; + if (!ParseUnsigned(values[10], &SignedTime)) return DKIM_BAD_SYNTAX; } // expiration time if (values[11] == NULL) { - sig.ExpireTime = (unsigned) -1; // common trick; feh + sig.ExpireTime = (unsigned)-1; // common trick; feh } else { - if (!ParseUnsigned(values[11],&sig.ExpireTime)) - return DKIM_BAD_SYNTAX; + if (!ParseUnsigned(values[11], &sig.ExpireTime)) return DKIM_BAD_SYNTAX; - if (sig.ExpireTime != (unsigned) -1) { + if (sig.ExpireTime != (unsigned)-1) { // the value of x= MUST be greater than the value of t= if both are present - if (SignedTime != (unsigned) -1 && sig.ExpireTime <= SignedTime) - return DKIM_BAD_SYNTAX; + if (SignedTime != (unsigned)-1 && sig.ExpireTime <= SignedTime) return DKIM_BAD_SYNTAX; // todo: if possible, use the received date/time instead of the current time unsigned curtime = time(NULL); - if (curtime > sig.ExpireTime) - return DKIM_SIGNATURE_EXPIRED; + if (curtime > sig.ExpireTime) return DKIM_SIGNATURE_EXPIRED; } } // parse the signed headers list bool HasFrom = false, HasSubject = false; - RemoveSWSP(values[4]); // header names shouldn't have spaces in them so this should be ok... - char* s = strtok_r(values[4],":",&saveptr); + RemoveSWSP(values[4]); // header names shouldn't have spaces in them so this should be ok... + char *s = strtok_r(values[4], ":", &saveptr); while (s != NULL) { - if (_stricmp(s,"From") == 0) + if (_stricmp(s, "From") == 0) HasFrom = true; - else if (_stricmp(s,"Subject") == 0) + else if (_stricmp(s, "Subject") == 0) HasSubject = true; sig.SignedHeaders.push_back(s); - s = strtok_r(NULL,":",&saveptr); + s = strtok_r(NULL, ":", &saveptr); } - if (!HasFrom) - return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing From + if (!HasFrom) return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing From if (m_SubjectIsRequired && !HasSubject) - return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing Subject + return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing Subject return DKIM_SUCCESS; } //////////////////////////////////////////////////////////////////////////////// -// +// // ProcessBody - Process message body data // //////////////////////////////////////////////////////////////////////////////// -int CDKIMVerify::ProcessBody(char* szBuffer,int nBufLength,bool bEOF) +int CDKIMVerify::ProcessBody(char *szBuffer, int nBufLength, bool bEOF) { bool MoreBodyNeeded = false; @@ -1179,44 +1146,43 @@ int CDKIMVerify::ProcessBody(char* szBuffer,int nBufLength,bool bEOF) if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) { if (nBufLength > 0) { while (i->EmptyLineCount > 0) { - i->Hash("\r\n",2,true); + i->Hash("\r\n", 2, true); i->EmptyLineCount--; } - i->Hash(szBuffer,nBufLength,true); - i->Hash("\r\n",2,true); + i->Hash(szBuffer, nBufLength, true); + i->Hash("\r\n", 2, true); } else { i->EmptyLineCount++; - if (bEOF) - i->Hash("\r\n",2,true); + if (bEOF) i->Hash("\r\n", 2, true); } } else if (i->BodyCanonicalization == DKIM_CANON_RELAXED) { CompressSWSP(szBuffer, nBufLength); if (nBufLength > 0) { while (i->EmptyLineCount > 0) { - i->Hash("\r\n",2,true); + i->Hash("\r\n", 2, true); i->EmptyLineCount--; } - i->Hash(szBuffer,nBufLength,true); - if (!bEOF) - i->Hash("\r\n",2,true); - } else i->EmptyLineCount++; + i->Hash(szBuffer, nBufLength, true); + if (!bEOF) i->Hash("\r\n", 2, true); + } else + i->EmptyLineCount++; } else if (i->BodyCanonicalization == DKIM_CANON_NOWSP) { - RemoveSWSP(szBuffer,nBufLength); - i->Hash(szBuffer,nBufLength,true); + RemoveSWSP(szBuffer, nBufLength); + i->Hash(szBuffer, nBufLength, true); } - if (i->UnverifiedBodyCount == 0) - MoreBodyNeeded = true; + if (i->UnverifiedBodyCount == 0) MoreBodyNeeded = true; } } - if (!MoreBodyNeeded) - return DKIM_FINISHED_BODY; + if (!MoreBodyNeeded) return DKIM_FINISHED_BODY; return DKIM_SUCCESS; } -SelectorInfo::SelectorInfo(const string &sSelector,const string &sDomain) : Domain(sDomain),Selector(sSelector) +SelectorInfo::SelectorInfo(const string& sSelector, const string& sDomain) : + Domain(sDomain), + Selector(sSelector) { AllowSHA1 = true; AllowSHA256 = true; @@ -1234,30 +1200,30 @@ SelectorInfo::~SelectorInfo() } //////////////////////////////////////////////////////////////////////////////// -// +// // Parse - Parse a DKIM selector from DNS data // //////////////////////////////////////////////////////////////////////////////// -int SelectorInfo::Parse(char* Buffer) +int SelectorInfo::Parse(char *Buffer) { // for strtok_r() char *saveptr; char *PubKeyBase64; /*- public key Base64 encoded */ char ed25519PubKey[61]; - static const char *tags[] = {"v","g","h","k","p","s","t","n",NULL}; // 0, 1, 2, 3, 4 - char *values[sizeof(tags)/sizeof(tags[0])] = {NULL}; + static const char *tags[] = {"v", "g", "h", "k", "p", "s", "t", "n", NULL}; // 0, 1, 2, 3, 4 + char *values[sizeof(tags) / sizeof(tags[0])] = {NULL}; + + ParseTagValueList(Buffer, tags, values); - ParseTagValueList(Buffer,tags,values); - // return DKIM_SELECTOR_INVALID; if (values[0] != NULL) { // make sure the version is "DKIM1" - if (strcmp(values[0],"DKIM1") != 0) - return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported selector version + if (strcmp(values[0], "DKIM1") != 0) + return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported selector version // make sure v= is the first tag in the response // todo: maybe don't enforce this, it seems unnecessary - for (unsigned j = 1; j < sizeof(values)/sizeof(values[0]); j++) { + for (unsigned j = 1; j < sizeof(values) / sizeof(values[0]); j++) { if (values[j] != NULL && values[j] < values[0]) { return DKIM_SELECTOR_INVALID; } @@ -1265,10 +1231,9 @@ int SelectorInfo::Parse(char* Buffer) } // selector MUST have p= tag - if (values[4] == NULL) - return DKIM_SELECTOR_INVALID; + if (values[4] == NULL) return DKIM_SELECTOR_INVALID; - PubKeyBase64 = values[4]; // gotcha + PubKeyBase64 = values[4]; // gotcha // granularity -- [g= ... ] if (values[1] == NULL) @@ -1282,13 +1247,16 @@ int SelectorInfo::Parse(char* Buffer) AllowSHA256 = true; } else { // MUST include "sha1" or "sha256" - char* s = strtok_r(values[2],":",&saveptr); + char *s = strtok_r(values[2], ":", &saveptr); while (s != NULL) { - if (strcmp(s,"sha1") == 0) - { AllowSHA1 = true; AllowSHA256 = false; } - else if (strcmp(s,"sha256") == 0) - { AllowSHA256 = true; AllowSHA1 = false; } - s = strtok_r(NULL,":",&saveptr); + if (strcmp(s, "sha1") == 0) { + AllowSHA1 = true; + AllowSHA256 = false; + } else if (strcmp(s, "sha256") == 0) { + AllowSHA256 = true; + AllowSHA1 = false; + } + s = strtok_r(NULL, ":", &saveptr); } if (!(AllowSHA1 || AllowSHA256)) return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported hash algorithm @@ -1297,50 +1265,48 @@ int SelectorInfo::Parse(char* Buffer) // key type -- [k=rsa|ed25519] (not required) if (values[3] != NULL) { // key type MUST be "rsa" or "ed25519" - if (strcmp(values[3],"rsa") != 0 && strcmp(values[3],"ed25519") != 0) // none of either + if (strcmp(values[3], "rsa") != 0 && strcmp(values[3], "ed25519") != 0) // none of either return DKIM_SELECTOR_INVALID; - if (strcmp(values[3],"ed25519") == 0) { - AllowSHA1 = false; - AllowSHA256 = true; - strcpy(ed25519PubKey,"MCowBQYDK2VwAyEA"); + if (strcmp(values[3], "ed25519") == 0) { + AllowSHA1 = false; + AllowSHA256 = true; + strcpy(ed25519PubKey, "MCowBQYDK2VwAyEA"); /* * rfc8463 * since Ed25519 public keys are 256 bits long, * the base64-encoded key is only 44 octets */ - if (strlen(values[4]) > 44) - return DKIM_SELECTOR_PUBLIC_KEY_INVALID; - strcat(ed25519PubKey,values[4]); + if (strlen(values[4]) > 44) return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + strcat(ed25519PubKey, values[4]); PubKeyBase64 = ed25519PubKey; - } + } } // service type -- [s= ...] (not required) if (values[5] != NULL) { // make sure "*" or "email" is in the list bool ServiceTypeMatch = false; - char* s = strtok_r(values[5],":",&saveptr); + char *s = strtok_r(values[5], ":", &saveptr); while (s != NULL) { - if (strcmp(s, "*") == 0 || strcmp(s,"email") == 0) { + if (strcmp(s, "*") == 0 || strcmp(s, "email") == 0) { ServiceTypeMatch = true; break; } - s = strtok_r(NULL,":",&saveptr); + s = strtok_r(NULL, ":", &saveptr); } - if (!ServiceTypeMatch) - return DKIM_SELECTOR_INVALID; + if (!ServiceTypeMatch) return DKIM_SELECTOR_INVALID; } // flags -- [t= ...] (not required) if (values[6] != NULL) { - char *s = strtok_r(values[6],":",&saveptr); + char *s = strtok_r(values[6], ":", &saveptr); while (s != NULL) { - if (strcmp(s,"y") == 0) { + if (strcmp(s, "y") == 0) { Testing = true; - } else if (strcmp(s,"s") == 0) { + } else if (strcmp(s, "s") == 0) { SameDomain = true; } - s = strtok_r(NULL,":",&saveptr); + s = strtok_r(NULL, ":", &saveptr); } } @@ -1350,20 +1316,22 @@ int SelectorInfo::Parse(char* Buffer) if (PublicKeyLen == 0) { return DKIM_SELECTOR_KEY_REVOKED; // this error causes the signature to fail } else { - const unsigned char *PublicKeyData = (unsigned char* )PubKeyBase64; // 0-terminated + const unsigned char *PublicKeyData = (unsigned char *)PubKeyBase64; // 0-terminated - EVP_PKEY *pkey = d2i_PUBKEY(NULL,&PublicKeyData,PublicKeyLen); /* retrieve and return PubKey from data */ + EVP_PKEY *pkey = + d2i_PUBKEY(NULL, &PublicKeyData, PublicKeyLen); /* retrieve and return PubKey from data */ - if (pkey == NULL) - return DKIM_SELECTOR_PUBLIC_KEY_INVALID; + if (pkey == NULL) return DKIM_SELECTOR_PUBLIC_KEY_INVALID; - // make sure public key is the correct type (we only support rsa & ed25519) -#if ((OPENSSL_VERSION_NUMBER < 0x10101000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) + // make sure public key is the correct type (we only support rsa & ed25519) +#if ( \ + (OPENSSL_VERSION_NUMBER < 0x10101000L) \ + || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) { #else - if ((EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) || - (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA2) || - (EVP_PKEY_base_id(pkey) == EVP_PKEY_ED25519)) { + if ((EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) || (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA2) + || (EVP_PKEY_base_id(pkey) == EVP_PKEY_ED25519)) + { #endif PublicKey = pkey; } else { @@ -1376,62 +1344,68 @@ int SelectorInfo::Parse(char* Buffer) } //////////////////////////////////////////////////////////////////////////////// -// +// // GetSelector - Get a DKIM selector for a domain // //////////////////////////////////////////////////////////////////////////////// -SelectorInfo& CDKIMVerify::GetSelector(const string &sSelector,const string &sDomain) +SelectorInfo& CDKIMVerify::GetSelector(const string& sSelector, const string& sDomain) { // see if we already have this selector for (list<SelectorInfo>::iterator i = Selectors.begin(); i != Selectors.end(); ++i) { - if (_stricmp(i->Selector.c_str(),sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(),sDomain.c_str()) == 0) { + if (_stricmp(i->Selector.c_str(), sSelector.c_str()) == 0 + && _stricmp(i->Domain.c_str(), sDomain.c_str()) == 0) + { return *i; } } - Selectors.push_back(SelectorInfo(sSelector,sDomain)); + Selectors.push_back(SelectorInfo(sSelector, sDomain)); SelectorInfo& sel = Selectors.back(); string sFQDN = sSelector; sFQDN += "._domainkey."; sFQDN += sDomain; - int BufLen = 1024; + int BufLen = 1024; char Buffer[BufLen]; int DNSResult; - if (m_pfnSelectorCallback) { - DNSResult = m_pfnSelectorCallback(sFQDN.c_str(),Buffer,BufLen); + if (m_pfnSelectorCallback) { + DNSResult = m_pfnSelectorCallback(sFQDN.c_str(), Buffer, BufLen); } else - DNSResult = _DNSGetTXT(sFQDN.c_str(),Buffer,BufLen); + DNSResult = _DNSGetTXT(sFQDN.c_str(), Buffer, BufLen); -// Buffer++; BufLen--; + // Buffer++; BufLen--; switch (DNSResult) { - case -1: case -2: case -3: case -5: sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE; break; - case 0: case -6: sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE; break; + case -1: + case -2: + case -3: + case -5: sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE; break; + case 0: + case -6: sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE; break; default: sel.Status = sel.Parse(Buffer); - } + } return sel; } //////////////////////////////////////////////////////////////////////////////// -// +// // GetDetails - Get DKIM verification details (per signature) // //////////////////////////////////////////////////////////////////////////////// -int CDKIMVerify::GetDetails(int* nSigCount,DKIMVerifyDetails** pDetails) +int CDKIMVerify::GetDetails(int *nSigCount, DKIMVerifyDetails **pDetails) { Details.clear(); - for (list < SignatureInfo>::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { + for (list<SignatureInfo>::iterator i = Signatures.begin(); i != Signatures.end(); ++i) { DKIMVerifyDetails d; - d.szSignature = (char* )i->Header.c_str(); - d.szSignatureDomain = (char* )i->Domain.c_str(); - d.szIdentityDomain = (char* )i->IdentityDomain.c_str(); - d.szCanonicalizedData = (char* )i->CanonicalizedData.c_str(); + d.szSignature = (char *)i->Header.c_str(); + d.szSignatureDomain = (char *)i->Domain.c_str(); + d.szIdentityDomain = (char *)i->IdentityDomain.c_str(); + d.szCanonicalizedData = (char *)i->CanonicalizedData.c_str(); d.nResult = i->Status; Details.push_back(d); } |