summaryrefslogtreecommitdiff
path: root/src/dkimverify.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/dkimverify.cpp')
-rw-r--r--src/dkimverify.cpp786
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);
}