diff options
Diffstat (limited to 'sqmail-4.3.07/src/dkimsign.cpp')
-rw-r--r-- | sqmail-4.3.07/src/dkimsign.cpp | 1106 |
1 files changed, 0 insertions, 1106 deletions
diff --git a/sqmail-4.3.07/src/dkimsign.cpp b/sqmail-4.3.07/src/dkimsign.cpp deleted file mode 100644 index 03b03e2..0000000 --- a/sqmail-4.3.07/src/dkimsign.cpp +++ /dev/null @@ -1,1106 +0,0 @@ -/***************************************************************************** -* Copyright 2005 Alt-N Technologies, Ltd. -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* This code incorporates intellectual property owned by Yahoo! and licensed -* pursuant to the Yahoo! DomainKeys Patent License Agreement. -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -* -* Changes done by ¢feh@fehcom.de obeying the above license -* -*****************************************************************************/ - -#define _strnicmp strncasecmp -#define _stricmp strcasecmp -#define LOWORD(l) ((unsigned)(l) & 0xffff) -#define HIWORD(l) ((unsigned)(l) >> 16) - -#include <string.h> -#include <map> - -#include "dkim.h" -#include "dkimsign.h" - -/***************************************************************************** -* -* Generating Ed25519 signed message: -* -* 1. RSA SHA1/SHA256 signatures are generated in streaming mode together with -* their hashes. Two different 'contexts' (ctx) are used here: -* m_Hdr_shaXctx => Used for signing (EVP_Sign...) -- covering the header only -* m_[B,E]dy_shaXctx => Used for hashing (EVP_Digest..) -- covering the body only -* -* 2. Private keys -* For hybrid signing we need two distinct keys: -* - RSAKey -* - ECCKey -* These private keys needs to be passed concurrently to the signature functions. -* Given those keys, the signature operation itself is executed in one step. -* -* 3. Public keys -* The 'public keys' need to be deployed in the DNS: -* - The RSA public key is DER-header enriched base64-encoded; thus is 9 byte larger -* than the 'naked' public key, which size depends on the given parameters. -* - The Ed25519 public key is also base64-encoded with a constant length of 60 byte. -* -* 4. DKIM message preparation scheme -* According to RFC 6376 Sec. 3.7, we have a conducted hash for -* - the previously available headers in the message; -* selected and given in order by h=..., -* - any existing DKIM signature fields b=..., -* - except for previous added 'X-Authentication ...' header fields, -* - and all (new) synthezised DKIM header tokens; except of course for the -* signature itself - treated as 'null string': b="". -* All this is subject of canonicalization (adding/removing CRLF, whitespaces ...). -+ As a result, the input for further calculations depends on this order given. -* -* Results following the 'preparation scheme': -* - The message body hash is included in the DKIM header => bh=[m_[B,E]dy_shaXctx]. -* - The message signature (including the result of bh=...) => b=[m_Hdr_shaXctx] -* -* We consider SHA256 as default hash function and SHA1 as exception (on demand). -* -* 5. Generating (ECC) signatures -* According to RFC 8032 Sect 4., we have two possible Ed25519 signature schemes: -* -* a) PureEd25519, as a one shot signature calculation swallowing the -* complete message and employing a shortened SHA-512 hash input. -* b) HashEd25519 working again in 'streaming mode' and permitting a choice -* for the hash function - which is in RFC 8463 - defined to be SHA-256. -* -* RFC 8463 in Sect 3 is a bit ambiguous about the signing function: -* Ed25519-256 vs. PureEd25519. -* In fact (after consulting John Levine), it is PureEd25519. -* -* In order to allow parallel RSA/Ed25519 processing, we need to generate: -* m_Hdr_sha256ctx => Used for RSA signatures -* m_Bdy_sha256ctx => The SHA256 hash of selected header parts and body (RSA) -* m_Edy_sha256ctx => The SHA256 hash of selected header parts and body (Ed25519) -* m_Hdr_ed25519ctx => The signature of the messsage header using PureEd25519 -* following the 'preparation' scheme -* -* Now, two cryptographic informations are provided in the header: -* bh=[m_Edy_sha256ctx] => The SHA256 digest of the message (BodyHash), -* b=[m_Hdr_ed25519ctx] => The PureED25519 signature. -* including the value of bh=... (EmailSignature) -* having a length of 512 bits => 64 bytes. -* -* 6. Hybrid signatures (RSA and Ed25519) -* They involve -* m_Hdr_sha256ctx => Used for RSA signatures -* m_Hdr_ed25519ctx => PureED25519 signature -* m_Bdy_sha256ctx => SHA256 digest of the message (BodyHash) for RSA -* m_Edy_sha256ctx => SHA256 digest of the message (BodyHash) for Ed25519 -* -* The EVP_DigestFinal routine has to be replaced by EVP_DigestFinal_ex. -* However; after the first call, its content seems to be garbeled. -* A common MD for both RSA and Ed2551 seems to be infeasible. -* -* ------ -* -* The particular function and variable names chosen here do not obviously match -* what they are intended to do. However, in order to keep traceablility of the -* changes, I left those untouched. -* -*****************************************************************************/ - -CDKIMSign::CDKIMSign() -{ - m_EmptyLineCount = 0; - m_pfnHdrCallback = NULL; - -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_SignInit(&m_Hdr_sha1ctx,EVP_sha1()); - EVP_SignInit(&m_Hdr_sha256ctx,EVP_sha256()); - EVP_DigestInit(&m_Bdy_sha1ctx,EVP_sha1()); - EVP_DigestInit(&m_Bdy_sha256ctx,EVP_sha256()); -#else - m_Hdr_sha1ctx = EVP_MD_CTX_create(); - EVP_SignInit_ex(m_Hdr_sha1ctx,EVP_sha1(),NULL); - - m_Hdr_sha256ctx = EVP_MD_CTX_create(); - EVP_SignInit_ex(m_Hdr_sha256ctx,EVP_sha256(),NULL); - - m_Bdy_sha1ctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(m_Bdy_sha1ctx,EVP_sha1(),NULL); - - m_Bdy_sha256ctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(m_Bdy_sha256ctx,EVP_sha256(),NULL); - - m_Hdr_ed25519ctx = EVP_MD_CTX_create(); - - m_Edy_sha256ctx = EVP_MD_CTX_create(); - EVP_DigestInit_ex(m_Edy_sha256ctx,EVP_sha256(),NULL); -#endif -} - -CDKIMSign::~CDKIMSign() -{ -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_MD_CTX_cleanup(&m_Hdr_sha1ctx); - EVP_MD_CTX_cleanup(&m_Hdr_sha256ctx); - EVP_MD_CTX_cleanup(&m_Bdy_sha1ctx); - EVP_MD_CTX_cleanup(&m_Bdy_sha256ctx); -#else - EVP_MD_CTX_free(m_Hdr_sha1ctx); - EVP_MD_CTX_free(m_Hdr_sha256ctx); - EVP_MD_CTX_free(m_Hdr_ed25519ctx); - EVP_MD_CTX_free(m_Bdy_sha1ctx); - EVP_MD_CTX_free(m_Bdy_sha256ctx); - EVP_MD_CTX_free(m_Edy_sha256ctx); -#endif -} - -//////////////////////////////////////////////////////////////////////////////// -// -// Init - save the options -// -//////////////////////////////////////////////////////////////////////////////// -int CDKIMSign::Init(DKIMSignOptions* pOptions) -{ - int nRet = CDKIMBase::Init(); - - m_Canon = pOptions->nCanon; - - // as of draft 01, these are the only allowed signing types: - if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && - (m_Canon != DKIM_SIGN_RELAXED) && - (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) { - m_Canon = DKIM_SIGN_SIMPLE; - } - - sSelector.assign(pOptions->szSelector); - eSelector.assign(pOptions->szSelectorE); - - m_pfnHdrCallback = pOptions->pfnHeaderCallback; - - sDomain.assign(pOptions->szDomain); - - m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0); - - m_nBodyLength = 0; - - m_ExpireTime = pOptions->expireTime; - - sIdentity.assign(pOptions->szIdentity); - - m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp; - m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod; - m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders; - - // NOTE: the following line is not backwards compatible with MD 8.0.3 - // because the szRequiredHeaders member was added after the release - //sRequiredHeaders.assign(pOptions->szRequiredHeaders); - - //make sure there is a colon after the last header in the list - if ((sRequiredHeaders.size() > 0) && - sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':') { - sRequiredHeaders.append(":"); - } - - m_nHash = pOptions->nHash; - m_bReturnedSigAssembled = false; - m_sCopiedHeaders.erase(); - - // Initializes ED25519 header fields SigHdrs -#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) - SigHdrs.assign(""); - m_SigHdrs = 0; -#endif - - return nRet; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// Hash - update the hash -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::Hash(const char *szBuffer,int nBufLength,bool bHdr) -{ - - /** START DEBUG CODE ** - if (nBufLength == 2 && szBuffer[0] == '\r' && szBuffer[1] == '\n') { - printf("[CRLF]\n"); - } else { - char *szDbg = new char[nBufLength+1]; - strncpy(szDbg,szBuffer,nBufLength); - szDbg[nBufLength] = '\0'; - printf("[%s]\n",szDbg); - } *** - - if (fpdebug == NULL) { - fpdebug = fopen("canon.msg", "wb"); - } - - fwrite(szBuffer,1,nBufLength,fpdebug); - - ** END DEBUG CODE **/ - - if (bHdr) { /* Generate signature: b=... */ - if ((m_nHash == DKIM_HASH_SHA1) || - (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_SignUpdate(&m_Hdr_sha1ctx,szBuffer,nBufLength); -#else - EVP_SignUpdate(m_Hdr_sha1ctx,szBuffer,nBufLength); -#endif - if ((m_nHash == DKIM_HASH_SHA256) || - (m_nHash == DKIM_HASH_SHA1_AND_SHA256) || - (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_SignUpdate(&m_Hdr_sha256ctx,szBuffer,nBufLength); -#else - EVP_SignUpdate(m_Hdr_sha256ctx,szBuffer,nBufLength); -#endif -#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) - if ((m_nHash == DKIM_HASH_ED25519) || - (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { - SigHdrs.append(szBuffer,nBufLength); - m_SigHdrs += nBufLength; - } -#endif - } else { /* lets go for body hash values: bh=... (either SHA1 or SHA256) */ - if ((m_nHash == DKIM_HASH_SHA1) || - (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_DigestUpdate(&m_Bdy_sha1ctx,szBuffer,nBufLength); -#else - EVP_DigestUpdate(m_Bdy_sha1ctx,szBuffer,nBufLength); -#endif - if (m_nHash != DKIM_HASH_SHA1) -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_DigestUpdate(&m_Bdy_sha256ctx,szBuffer,nBufLength); -#else - EVP_DigestUpdate(m_Bdy_sha256ctx,szBuffer,nBufLength); -#endif -#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) - if ((m_nHash == DKIM_HASH_ED25519) || - (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) - EVP_DigestUpdate(m_Edy_sha256ctx,szBuffer,nBufLength); -#endif - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -// SignThisTag - return boolean whether or not to sign this tag -// -//////////////////////////////////////////////////////////////////////////////// -bool CDKIMSign::SignThisTag(const string& sTag) -{ - bool bRet = true; - - if (_strnicmp(sTag.c_str(),"X-",2) == 0 || - _stricmp(sTag.c_str(),"Authentication-Results:") == 0 || - _stricmp(sTag.c_str(),"Return-Path:") == 0) { - bRet = false; - } - - return bRet; -} - -bool ConvertHeaderToQuotedPrintable(const char* source, char* dest) -{ - bool bConvert = false; - - // do quoted printable - static unsigned char hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - unsigned char *d = (unsigned char*)dest; - for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) - { - if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') { - *d++ = *s; - } else { - bConvert = true; - *d++ = '='; - *d++ = hexchars[*s >> 4]; - *d++ = hexchars[*s & 15]; - } - } - *d = '\0'; - - return bConvert; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// GetHeaderParams - Extract any needed header parameters -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::GetHeaderParams(const string &sHdr) -{ - if (_strnicmp(sHdr.c_str(),"X",1) == 0) return; - if (_strnicmp(sHdr.c_str(),"From:",5) == 0) { sFrom.assign(sHdr.c_str() + 5); } - if (_strnicmp(sHdr.c_str(),"Sender:",7) == 0) { sSender.assign(sHdr.c_str() + 7); } - - if (m_nIncludeCopiedHeaders) { - string::size_type pos = sHdr.find(':'); - - if (pos != string::npos) { - string sTag, sValue; - char *workBuffer = new char[sHdr.size() * 3 + 1]; - - sTag.assign(sHdr.substr(0,pos)); - sValue.assign(sHdr.substr(pos + 1,string::npos)); - - ConvertHeaderToQuotedPrintable(sTag.c_str(),workBuffer); - if (!m_sCopiedHeaders.empty()) { m_sCopiedHeaders.append("|"); } - m_sCopiedHeaders.append(workBuffer); m_sCopiedHeaders.append(":"); - ConvertHeaderToQuotedPrintable(sValue.c_str(),workBuffer); - m_sCopiedHeaders.append(workBuffer); - - delete[] workBuffer; - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -// ProcessHeaders - sign headers and save needed parameters (this is a lie) -// -//////////////////////////////////////////////////////////////////////////////// -int CDKIMSign::ProcessHeaders(void) -{ - map<string,list<string>::reverse_iterator> IterMap; - map<string,list<string>::reverse_iterator>::iterator IterMapIter; - list<string>::reverse_iterator riter; - list<string>::iterator iter; - string sTag; - bool bFromHeaderFound = false; - - // walk the header list - for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) { - sTag.assign(*iter); - - // look for a colon - string::size_type pos = sTag.find(':'); - - if (pos != string::npos) { - int nSignThisTag = 1; - - // hack off anything past the colon - sTag.erase(pos + 1,string::npos); - - // is this the From: header? - if (_stricmp(sTag.c_str(),"From:") == 0) { - bFromHeaderFound = true; - nSignThisTag = 1; - IsRequiredHeader(sTag); // remove from required header list - } - // is this in the list of headers that must be signed? - else if (IsRequiredHeader(sTag)) { - nSignThisTag = 1; - } - else { - if(m_pfnHdrCallback) { - nSignThisTag = m_pfnHdrCallback(iter->c_str()); - } else { - nSignThisTag = SignThisTag(sTag) ? 1 : 0; - } - } - - // save header parameters - GetHeaderParams(*iter); - - if (nSignThisTag > 0) { - // add this tag to h= - hParam.append(sTag); - - IterMapIter = IterMap.find(sTag); - - riter = (IterMapIter == IterMap.end()) ? HeaderList.rbegin() : IterMapIter->second; - - // walk the list in reverse looking for the last instance of this header - while (riter != HeaderList.rend()) { - if (_strnicmp(riter->c_str(),sTag.c_str(),sTag.size()) == 0) { - ProcessHeader(*riter); - - // save the reverse iterator position for this tag - riter++; - IterMap[sTag] = riter; - break; - } - riter++; - } - } - } - } - - if(!bFromHeaderFound) { - string sFrom("From:"); - hParam.append(sFrom); - IsRequiredHeader(sFrom); // remove from required header list -// Hash("\r\n",2); - } - - hParam.append(sRequiredHeaders); - -// string::size_type end = sRequiredHeaders.find(':'); -// while (end != string::npos) -// { -// Hash("\r\n",2); -// end = sRequiredHeaders.find(':', end+1); -// } - - // remove the last colon from h= - if (hParam.at(hParam.size() - 1) == ':') - hParam.erase(hParam.size() - 1,string::npos); - - return DKIM_SUCCESS; -} - -void CDKIMSign::ProcessHeader(const string &sHdr) -{ - switch (HIWORD(m_Canon)) { - case DKIM_CANON_SIMPLE: - Hash(sHdr.c_str(),sHdr.size(),true); - Hash("\r\n",2,true); - break; - - case DKIM_CANON_NOWSP: { - string sTemp = sHdr; - 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'; - } - - Hash(sTemp.c_str(),sTemp.size(),true); - Hash("\r\n",2,true); - } - break; - - case DKIM_CANON_RELAXED: { - string sTemp = RelaxHeader(sHdr); - Hash(sTemp.c_str(),sTemp.length(),true); - Hash("\r\n",2,true); - } - break; - } -} - -int CDKIMSign::ProcessBody(char *szBuffer,int nBufLength,bool bEOF) -{ - switch(LOWORD(m_Canon)) { - case DKIM_CANON_SIMPLE: - if (nBufLength > 0) { - while (m_EmptyLineCount > 0) { - Hash("\r\n",2,false); - m_nBodyLength += 2; - m_EmptyLineCount--; - } - Hash(szBuffer,nBufLength,false); - Hash("\r\n",2,false); - m_nBodyLength += nBufLength + 2; - } else { - m_EmptyLineCount++; - if (bEOF) { - Hash("\r\n",2,false); - m_nBodyLength += 2; - } - } - break; - case DKIM_CANON_NOWSP: - RemoveSWSP(szBuffer,nBufLength); - if (nBufLength > 0) { - Hash(szBuffer,nBufLength,false); - m_nBodyLength += nBufLength; - } - break; - case DKIM_CANON_RELAXED: - CompressSWSP(szBuffer,nBufLength); - if (nBufLength > 0) { - while (m_EmptyLineCount > 0) { - Hash("\r\n",2,false); - m_nBodyLength += 2; - m_EmptyLineCount--; - } - Hash(szBuffer,nBufLength,false); - m_nBodyLength += nBufLength; - if (!bEOF) { - Hash("\r\n",2,false); - m_nBodyLength += 2; - } - } else - m_EmptyLineCount++; - break; - } - - return DKIM_SUCCESS; -} - -bool CDKIMSign::ParseFromAddress(void) -{ - string::size_type pos; - string sAddress; - - if (!sFrom.empty()) { - sAddress.assign(sFrom); - } else if (!sSender.empty()) { - sAddress.assign(sSender); - } else { - return false; - } - - // simple for now, beef it up later - - // remove '<' and anything before it - pos = sAddress.find('<'); - if(pos != string::npos) - sAddress.erase(0,pos); - - // remove '>' and anything after it - pos = sAddress.find('>'); - if (pos != string::npos) - sAddress.erase(pos,string::npos); - - // look for '@' symbol - pos = sAddress.find('@'); - if (pos == string::npos) - return false; - - if (sDomain.empty()) { - sDomain.assign (sAddress.c_str() + pos + 1); - RemoveSWSP(sDomain); - } - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// InitSig - initialize signature folding algorithm -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::InitSig(void) -{ - m_sSig.reserve(1024); - m_sSig.assign("DKIM-Signature:"); - m_nSigPos = m_sSig.size(); -} - -//////////////////////////////////////////////////////////////////////////////// -// -// AddTagToSig - add tag and value to signature folding if necessary -// if bFold, fold at cbrk char -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::AddTagToSig(const char* const Tag,const string &sValue,char cbrk,bool bFold) -{ - int nTagLen = strlen(Tag); - - AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2); - - m_sSig.append(Tag); - m_sSig.append("="); - m_nSigPos += 1 + nTagLen; - - if (!bFold) { - m_sSig.append(sValue); - m_nSigPos += sValue.size(); - } else { - AddFoldedValueToSig(sValue,cbrk); - } - m_sSig.append(";"); - m_nSigPos++; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// AddTagToSig - add tag and numeric value to signature folding if necessary -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::AddTagToSig(const char* const Tag,unsigned long nValue) -{ - char szValue[64]; - sprintf(szValue,"%lu",nValue); - AddTagToSig(Tag,szValue,0,false); -} - -//////////////////////////////////////////////////////////////////////////////// -// -// AddInterTagSpace - add space or fold here -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag) -{ - if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) { -// m_sSig.append("\r\n\t"); - m_sSig.append("\r\n "); /* s/qmail style */ - m_nSigPos = 1; - } else { - m_sSig.append(" "); - m_nSigPos++; - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -// AddTagToSig - add value to signature folding if necessary -// if cbrk == 0 fold anywhere, otherwise fold only at cbrk -// -//////////////////////////////////////////////////////////////////////////////// -void CDKIMSign::AddFoldedValueToSig(const string &sValue,char cbrk) -{ - string::size_type pos = 0; - - if (cbrk == 0) { - // fold anywhere - while (pos < sValue.size()) { - string::size_type len = OptimalHeaderLineLength - m_nSigPos; - if (len > sValue.size() - pos) - len = sValue.size() - pos; - m_sSig.append(sValue.substr(pos,len)); - m_nSigPos += len; - pos += len; - - if (pos < sValue.size()) { -// m_sSig.append("\r\n\t"); - m_sSig.append("\r\n "); /* s/qmail style */ - m_nSigPos = 1; - } - } - } else { - // fold only at cbrk - while (pos < sValue.size()) { - string::size_type len = OptimalHeaderLineLength - m_nSigPos; - string::size_type brkpos; - - if (sValue.size() - pos < len) { - brkpos = sValue.size(); - } else { - brkpos = sValue.rfind(cbrk,pos + len); - } - - if (brkpos == string::npos || brkpos < pos) { - brkpos = sValue.find(cbrk,pos); - if (brkpos == string::npos) { - brkpos = sValue.size(); - } - } - - len = brkpos - pos + 1; - - m_sSig.append(sValue.substr(pos,len)); - - m_nSigPos += len; - pos += len; - - if (pos < sValue.size()) { -// m_sSig.append("\r\n\t"); - m_sSig.append("\r\n "); /* s/qmail style */ - m_nSigPos = 1; - } - } - } -} - -//////////////////////////////////////////////////////////////////////////////// -// -// GetSig - compute hash and return signature header in szSignature -// -//////////////////////////////////////////////////////////////////////////////// -int CDKIMSign::GetSig2(char* szRSAKey,char* szECCKey,char** pszSignature) -{ - if (szRSAKey == NULL && szECCKey == NULL) { - return DKIM_BAD_PRIVATE_KEY; - } - - if (pszSignature == NULL) { - return DKIM_BUFFER_TOO_SMALL; - } - - int nRet = AssembleReturnedSig(szRSAKey,szECCKey); - - if (nRet != DKIM_SUCCESS) - return nRet; - - *pszSignature = (char*)m_sReturnedSig.c_str(); - - return DKIM_SUCCESS; -} - - -//////////////////////////////////////////////////////////////////////////////// -// -// IsRequiredHeader - Check if header in required list. If so, delete -// header from list. -// -//////////////////////////////////////////////////////////////////////////////// -bool CDKIMSign::IsRequiredHeader(const string& sTag) -{ - string::size_type start = 0; - string::size_type end = sRequiredHeaders.find(':'); - - while (end != string::npos) { - // check for a zero-length header - if(start == end) { - sRequiredHeaders.erase(start,1); - } else { - if (_stricmp(sTag.c_str(),sRequiredHeaders.substr(start,end - start + 1).c_str()) == 0) { - sRequiredHeaders.erase(start,end - start + 1); - return true; - } else { - start = end + 1; - } - } - - end = sRequiredHeaders.find(':',start); - } - - return false; -} -//////////////////////////////////////////////////////////////////////////////// -// -// ConstructSignature -// -// Here, we don't construct the 'signature' but rather the DKIM header -// multiply and indidually crafted for each distinct nSigAlg method -// -// nSigAlg: DKIM_HASH_SHA1, DKIM_HASH_SHA256, DKIM_HASH_ED25519 -// -//////////////////////////////////////////////////////////////////////////////// -int CDKIMSign::ConstructSignature(char* szPrivKey,int nSigAlg) -{ - string sSignedSig; - unsigned char* sig; - EVP_PKEY *pkey = 0; - BIO *bio, *b64; - unsigned int siglen; - int size; - int len; - char* buf; - int nSignRet; - - /* construct the DKIM-Signature: header and add to hash */ - InitSig(); - - AddTagToSig("v","1",0,false); - - switch (nSigAlg) { - case DKIM_HASH_SHA1: - AddTagToSig("a","rsa-sha1",0,false); break; - case DKIM_HASH_SHA256: - AddTagToSig("a","rsa-sha256",0,false); break; - case DKIM_HASH_ED25519: - AddTagToSig("a","ed25519-sha256",0,false); break; - } - - switch (m_Canon) { - case DKIM_SIGN_SIMPLE: - AddTagToSig("c","simple/simple",0,false); break; - case DKIM_SIGN_SIMPLE_RELAXED: - AddTagToSig("c","simple/relaxed",0,false); break; - case DKIM_SIGN_RELAXED: - AddTagToSig("c","relaxed/relaxed",0,false); break; - case DKIM_SIGN_RELAXED_SIMPLE: - AddTagToSig("c","relaxed/simple",0,false); break; - } - - AddTagToSig("d",sDomain,0,false); - if (nSigAlg == DKIM_HASH_ED25519) - AddTagToSig("s",eSelector,0,false); - else - AddTagToSig("s",sSelector,0,false); - if (m_IncludeBodyLengthTag) { AddTagToSig("l",m_nBodyLength); } - if (m_nIncludeTimeStamp != 0) { time_t t; time(&t); AddTagToSig("t",t); } - if (m_ExpireTime != 0) { AddTagToSig("x",m_ExpireTime); } - if (!sIdentity.empty()) { AddTagToSig("i",sIdentity,0,false); } - if (m_nIncludeQueryMethod) { AddTagToSig("q","dns/txt",0,false); } - - AddTagToSig("h",hParam,':',true); // copied headers follow the ':' - if (m_nIncludeCopiedHeaders) { AddTagToSig("z",m_sCopiedHeaders,0,true); } - - /* Set up context for (body) hash */ - - unsigned char Hash[4096]; - unsigned int nHashLen = 0; - - switch (nSigAlg) { - case DKIM_HASH_SHA1: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_DigestFinal(&m_Bdy_sha1ctx,Hash,&nHashLen); break; -#else - EVP_DigestFinal_ex(m_Bdy_sha1ctx,Hash,&nHashLen); break; -#endif - case DKIM_HASH_SHA256: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_DigestFinal(&m_Bdy_sha256ctx,Hash,&nHashLen); break; -#else - EVP_DigestFinal_ex(m_Bdy_sha256ctx,Hash,&nHashLen); break; -#endif -#if (OPENSSL_VERSION_NUMBER > 0x10101000L) - case DKIM_HASH_ED25519: - EVP_DigestFinal_ex(m_Edy_sha256ctx,Hash,&nHashLen); break; -#endif - } - - bio = BIO_new(BIO_s_mem()); - if (!bio) return DKIM_OUT_OF_MEMORY; - - b64 = BIO_new(BIO_f_base64()); - if (!b64) { - BIO_free(bio); - return DKIM_OUT_OF_MEMORY; - } - BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); - BIO_push(b64,bio); - if (BIO_write(b64,Hash,nHashLen) < (int)nHashLen) { - BIO_free_all(b64); - return DKIM_OUT_OF_MEMORY; - } - BIO_flush(b64); - - len = nHashLen * 2; - buf = new char[len]; - - if (buf == NULL) { - BIO_free_all(b64); - return DKIM_OUT_OF_MEMORY; - } - - size = BIO_read(bio,buf,len); - BIO_free_all(b64); - - // this should never happen - if (size >= len) { - delete[] buf; - return DKIM_OUT_OF_MEMORY; - } - - buf[size] = '\0'; - AddTagToSig("bh",buf,0,true); - delete[] buf; - - AddInterTagSpace(3); - - m_sSig.append("b="); - m_nSigPos += 2; - - // Force a full copy - no reference copies please - sSignedSig.assign(m_sSig.c_str()); - - // note that since we're not calling hash here, need to dump this - // to the debug file if you want the full canonical form - - string sTemp; - - if (HIWORD(m_Canon) == DKIM_CANON_RELAXED) { - sTemp = RelaxHeader(sSignedSig); - } else { - sTemp = sSignedSig.c_str(); - } - - /* Update streaming signatures */ - - switch (nSigAlg) { - case DKIM_HASH_SHA1: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_SignUpdate(&m_Hdr_sha1ctx,sTemp.c_str(),sTemp.size()); break; -#else - EVP_SignUpdate(m_Hdr_sha1ctx,sTemp.c_str(),sTemp.size()); break; -#endif - case DKIM_HASH_SHA256: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - EVP_SignUpdate(&m_Hdr_sha256ctx,sTemp.c_str(),sTemp.size()); break; -#else - EVP_SignUpdate(m_Hdr_sha256ctx,sTemp.c_str(),sTemp.size()); break; -#endif -#if ((OPENSSL_VERSION_NUMBER > 0x10101000L)) - case DKIM_HASH_ED25519: - SigHdrs.append(sTemp.c_str(),sTemp.size()); - m_SigHdrs += sTemp.size(); break; -#endif - } - - bio = BIO_new_mem_buf(szPrivKey, -1); - if (bio == NULL) return DKIM_OUT_OF_MEMORY; - - pkey = PEM_read_bio_PrivateKey(bio,NULL,NULL,NULL); // FIXME - done - BIO_free(bio); - - if (!pkey) { return DKIM_BAD_PRIVATE_KEY; } - siglen = EVP_PKEY_size(pkey); - - sig = (unsigned char*) OPENSSL_malloc(siglen); - if (sig == NULL) { - EVP_PKEY_free(pkey); - return DKIM_OUT_OF_MEMORY; - } - - /* Finish streaming signature and potentially go for Ed25519 signatures */ - - size_t sig_len; - unsigned char* SignMsg; - - switch (nSigAlg) { - case DKIM_HASH_SHA1: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - nSignRet = EVP_SignFinal(&m_Hdr_sha1ctx,sig,&siglen,pkey); break; -#else - nSignRet = EVP_SignFinal(m_Hdr_sha1ctx,sig,&siglen,pkey); break; -#endif - case DKIM_HASH_SHA256: -#if ((OPENSSL_VERSION_NUMBER < 0x10100000L) || (LIBRESSL_VERSION_NUMBER > 0 && LIBRESSL_VERSION_NUMBER < 0x20700000L)) - nSignRet = EVP_SignFinal(&m_Hdr_sha256ctx,sig,&siglen,pkey); break; -#else - nSignRet = EVP_SignFinal(m_Hdr_sha256ctx,sig,&siglen,pkey); break; -#endif -#if (OPENSSL_VERSION_NUMBER > 0x10101000L) - case DKIM_HASH_ED25519: - EVP_DigestSignInit(m_Hdr_ed25519ctx,NULL,NULL,NULL,pkey); - SignMsg = (unsigned char*) SigHdrs.c_str(); - EVP_DigestSign(m_Hdr_ed25519ctx,NULL,&sig_len,SignMsg,m_SigHdrs); - sig = (unsigned char*) OPENSSL_malloc(sig_len); - nSignRet = EVP_DigestSign(m_Hdr_ed25519ctx,sig,&sig_len,SignMsg,m_SigHdrs); - siglen = (unsigned int) sig_len; break; -#endif - } - EVP_PKEY_free(pkey); - - if (!nSignRet) { - OPENSSL_free(sig); - return DKIM_BAD_PRIVATE_KEY; // key too small - } - - bio = BIO_new(BIO_s_mem()); - if (!bio) { - return DKIM_OUT_OF_MEMORY; - } - - b64 = BIO_new(BIO_f_base64()); - if (!b64) { - BIO_free(bio); - return DKIM_OUT_OF_MEMORY; - } - - BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL); - BIO_push(b64,bio); - - if (BIO_write(b64,sig,siglen) < (int) siglen) { - OPENSSL_free(sig); - BIO_free_all(b64); - return DKIM_OUT_OF_MEMORY; - } - BIO_flush(b64); - OPENSSL_free(sig); - - len = siglen * 2; - buf = new char[len]; - - if (buf == NULL) { - BIO_free_all(b64); - return DKIM_OUT_OF_MEMORY; - } - - size = BIO_read(bio,buf,len); - BIO_free_all(b64); - - // this should never happen - if (size >= len) { - delete[] buf; - return DKIM_OUT_OF_MEMORY; - } - - buf[size] = '\0'; - AddFoldedValueToSig(buf,0); - delete[] buf; - return DKIM_SUCCESS; -} - -//////////////////////////////////////////////////////////////////////////////// -// -// AssembleReturnSig -// -// calls ConstructSignature -// for all different hashes and signature key files -// -//////////////////////////////////////////////////////////////////////////////// -int CDKIMSign::AssembleReturnedSig(char* szRSAKey,char* szECCKey) -{ - int nRet; - - if (m_bReturnedSigAssembled) - return DKIM_SUCCESS; - - ProcessFinal(); - - if (ParseFromAddress() == false) { - return DKIM_NO_SENDER; - } - - string ed25519Sig, sha256Sig, sha1Sig; - - if ((m_nHash == DKIM_HASH_ED25519) || - (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { - nRet = ConstructSignature(szECCKey,DKIM_HASH_ED25519); - if (nRet == DKIM_SUCCESS) { - ed25519Sig.assign(m_sSig); - } else { - return nRet; - } - } - - if ((m_nHash == DKIM_HASH_SHA256) || - (m_nHash == DKIM_HASH_SHA1_AND_SHA256) || - (m_nHash == DKIM_HASH_RSA256_AND_ED25519)) { - nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA256); - if (nRet == DKIM_SUCCESS) { - sha256Sig.assign(m_sSig); - } else { - return nRet; - } - } - - if ((m_nHash == DKIM_HASH_SHA1) || - (m_nHash == DKIM_HASH_SHA1_AND_SHA256)) { - nRet = ConstructSignature(szRSAKey,DKIM_HASH_SHA1); - if (nRet == DKIM_SUCCESS) { - sha1Sig.assign(m_sSig); - } else { - return nRet; - } - } - -// fclose(fpdebug); -// fpdebug = NULL; - - if (!ed25519Sig.empty()) { -/* if (!m_sReturnedSig.empty()) { - m_sReturnedSig.append("\r\n"); - } - */ - m_sReturnedSig.assign(ed25519Sig); - } - - if (!sha1Sig.empty()) { - if (!m_sReturnedSig.empty()) { - m_sReturnedSig.append("\r\n"); - } - m_sReturnedSig.append(sha1Sig); - } - - if (!sha256Sig.empty()) { - if (!m_sReturnedSig.empty()) { - m_sReturnedSig.append("\r\n"); - } - m_sReturnedSig.append(sha256Sig); - } - - m_bReturnedSigAssembled = true; - return DKIM_SUCCESS; -} |