From 1001ed6c2ae98a5b9ed707b7710eb3ae87a0a7ce Mon Sep 17 00:00:00 2001 From: Jannis Hoffmann Date: Mon, 8 Jul 2024 17:55:05 +0200 Subject: Downgraded qmail-dkim from C++ to C. Made DKIMContext opaque. Added functions DKIMContextNew and DKIMContextFree. Changed fields of DKIMContext. --- src/dkim.cpp | 42 +++--- src/include/dkim.h | 28 ++-- src/meson.build | 2 +- src/qmail-dkim.c | 375 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/qmail-dkim.cpp | 377 ----------------------------------------------------- 5 files changed, 409 insertions(+), 415 deletions(-) create mode 100644 src/qmail-dkim.c delete mode 100644 src/qmail-dkim.cpp (limited to 'src') diff --git a/src/dkim.cpp b/src/dkim.cpp index 8f07519..ff21423 100644 --- a/src/dkim.cpp +++ b/src/dkim.cpp @@ -27,8 +27,6 @@ #include "dkimverify.h" -#define DKIMID ('D' | 'K' << 8 | 'I' << 16 | 'M' << 24) - /* taken from removed file "ressource.h" */ #ifdef VERSION #define VERSION_STRING VERSION @@ -36,21 +34,19 @@ #define VERSION_STRING "1.4.0" #endif +struct DKIMContext { + CDKIMSign *signObject; + CDKIMVerify *verifyObject; +}; -static void InitContext(DKIMContext *pContext, bool bSign, void *pObject) +DKIMContext *DKIMContextNew() { - pContext->reserved1 = DKIMID; - pContext->reserved2 = bSign ? 1 : 0; - pContext->reserved3 = pObject; + return new DKIMContext; } -static void *ValidateContext(DKIMContext *pContext, bool bSign) +void DKIMContextFree(DKIMContext *ctx) { - if (pContext->reserved1 != DKIMID) return NULL; - - if (pContext->reserved2 != (unsigned int)(bSign ? 1 : 0)) return NULL; - - return pContext->reserved3; + delete ctx; } int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions) @@ -64,13 +60,13 @@ int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions) if (nRet != DKIM_SUCCESS) delete pSign; } - if (nRet == DKIM_SUCCESS) InitContext(pSignContext, true, pSign); + if (nRet == DKIM_SUCCESS) pSignContext->signObject = pSign; return nRet; } int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength) { - CDKIMSign *pSign = (CDKIMSign *)ValidateContext(pSignContext, true); + CDKIMSign *pSign = pSignContext->signObject; if (pSign) return pSign->Process(szBuffer, nBufLength, false); return DKIM_INVALID_CONTEXT; @@ -79,7 +75,7 @@ int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBu int DKIM_CALL DKIMSignGetSig2( DKIMContext *pSignContext, char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature) { - CDKIMSign *pSign = (CDKIMSign *)ValidateContext(pSignContext, true); + CDKIMSign *pSign = pSignContext->signObject; if (pSign) return pSign->GetSig2(szRSAPrivKey, szECCPrivKey, pszSignature); return DKIM_INVALID_CONTEXT; @@ -87,11 +83,11 @@ int DKIM_CALL DKIMSignGetSig2( void DKIM_CALL DKIMSignFree(DKIMContext *pSignContext) { - CDKIMSign *pSign = (CDKIMSign *)ValidateContext(pSignContext, true); + CDKIMSign *pSign = pSignContext->signObject; if (pSign) { delete pSign; - pSignContext->reserved3 = NULL; + pSignContext->signObject = NULL; } } @@ -106,7 +102,7 @@ int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOp if (nRet != DKIM_SUCCESS) delete pVerify; } - if (nRet == DKIM_SUCCESS) InitContext(pVerifyContext, false, pVerify); + if (nRet == DKIM_SUCCESS) pVerifyContext->verifyObject = pVerify; return nRet; } @@ -114,7 +110,7 @@ int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOp int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, const char *const szBuffer, int nBufLength) { - CDKIMVerify *pVerify = (CDKIMVerify *)ValidateContext(pVerifyContext, false); + CDKIMVerify *pVerify = pVerifyContext->verifyObject; if (pVerify) return pVerify->Process(szBuffer, nBufLength, false); @@ -123,7 +119,7 @@ int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, const char *const s int DKIM_CALL DKIMVerifyResults(DKIMContext *pVerifyContext) { - CDKIMVerify *pVerify = (CDKIMVerify *)ValidateContext(pVerifyContext, false); + CDKIMVerify *pVerify = pVerifyContext->verifyObject; if (pVerify) return pVerify->GetResults(); return DKIM_INVALID_CONTEXT; @@ -134,7 +130,7 @@ int DKIM_CALL DKIMVerifyGetDetails( { szPractices[0] = '\0'; - CDKIMVerify *pVerify = (CDKIMVerify *)ValidateContext(pVerifyContext, false); + CDKIMVerify *pVerify = pVerifyContext->verifyObject; if (pVerify) { strcpy(szPractices, pVerify->GetPractices()); @@ -147,11 +143,11 @@ int DKIM_CALL DKIMVerifyGetDetails( void DKIM_CALL DKIMVerifyFree(DKIMContext *pVerifyContext) { - CDKIMVerify *pVerify = (CDKIMVerify *)ValidateContext(pVerifyContext, false); + CDKIMVerify *pVerify = pVerifyContext->verifyObject; if (pVerify) { delete pVerify; - pVerifyContext->reserved3 = NULL; + pVerifyContext->verifyObject = NULL; } } diff --git a/src/include/dkim.h b/src/include/dkim.h index a74b785..64e2c59 100644 --- a/src/include/dkim.h +++ b/src/include/dkim.h @@ -89,11 +89,7 @@ typedef int (*DKIMHEADERCALLBACK)(const char *szHeader); // This function is called to retrieve a TXT record from DNS typedef int (*DKIMDNSCALLBACK)(const char *szFQDN, char *szBuffer, int nBufLen); -typedef struct DKIMContext_t { - unsigned int reserved1; - unsigned int reserved2; - void *reserved3; -} DKIMContext; +struct DKIMContext; typedef struct DKIMSignOptions_t { int nCanon; // canonization @@ -132,18 +128,22 @@ typedef struct DKIMVerifyDetails_t { int nResult; } DKIMVerifyDetails; -int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions); -int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength); +struct DKIMContext *DKIMContextNew(); +void DKIMContextFree(struct DKIMContext *); + +int DKIM_CALL DKIMSignInit(struct DKIMContext *pSignContext, DKIMSignOptions *pOptions); +int DKIM_CALL DKIMSignProcess(struct DKIMContext *pSignContext, char *szBuffer, int nBufLength); int DKIM_CALL DKIMSignGetSig2( - DKIMContext *pSignContext, char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature); -void DKIM_CALL DKIMSignFree(DKIMContext *pSignContext); + struct DKIMContext *pSignContext, char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature); +void DKIM_CALL DKIMSignFree(struct DKIMContext *pSignContext); -int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions); -int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, const char *szBuffer, int nBufLength); -int DKIM_CALL DKIMVerifyResults(DKIMContext *pVerifyContext); +int DKIM_CALL DKIMVerifyInit(struct DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions); +int DKIM_CALL DKIMVerifyProcess( + struct DKIMContext *pVerifyContext, const char *szBuffer, int nBufLength); +int DKIM_CALL DKIMVerifyResults(struct DKIMContext *pVerifyContext); int DKIM_CALL DKIMVerifyGetDetails( - DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices); -void DKIM_CALL DKIMVerifyFree(DKIMContext *pVerifyContext); + struct DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices); +void DKIM_CALL DKIMVerifyFree(struct DKIMContext *pVerifyContext); // const char *DKIM_CALL DKIMVersion(); diff --git a/src/meson.build b/src/meson.build index 1f33050..6b40c42 100644 --- a/src/meson.build +++ b/src/meson.build @@ -298,7 +298,7 @@ if get_option('build-dkim') dependencies : [dnsresolv_dep, ssl_dep, crypto_dep], build_by_default : false) - executable('qmail-dkim', 'qmail-dkim.cpp', + executable('qmail-dkim', 'qmail-dkim.c', include_directories : inc, install : true, link_with : dkim_slib, diff --git a/src/qmail-dkim.c b/src/qmail-dkim.c new file mode 100644 index 0000000..8b28ab1 --- /dev/null +++ b/src/qmail-dkim.c @@ -0,0 +1,375 @@ +/***************************************************************************** +* 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 +* +* Comment: Awful mixture of C and C++ making use of the worst parts of it. +* Style: Partial Hungarian notation (see Torvalds comments) +* C++: Obsolete classes, allocators, virtual constructors w/o destructors +* C: Stdio interface routines +* OpenSSL: Brain demaged EVP_Digest calls with memory leaks. +* Network: Sigh, exchanged internal DNS routines by fehQlibs resolver +* +*****************************************************************************/ +#include + +#include +#include +#include +#include +#include + +#include "dkim.h" + +// change these to your selector name, domain name, etc +#define MYRSASELECTOR "default" +#define MYECCSELECTOR "eddy" +#define MYDOMAIN "" //"bardenhagen.com" +#define MYIDENTITY "" //"dkimtest@bardenhagen.com" + +#define strnicmp strncasecmp +#define FDLOG stderr /* writing to another FD requires a method */ + +static int DKIM_CALL SignThisHeader(const char *szHeader) +{ + if (strnicmp(szHeader, "X-", 2) == 0) { + return 0; + } + return 1; +} + +static int DKIM_CALL SelectorCallback(const char *szFQDN, char *szBuffer, int nBufLen) +{ + return 0; +} + +static void usage() +{ + char version[] = "1.4.0"; + fprintf(FDLOG, "qmail-dkim %s \n", version); + fprintf( + FDLOG, + "Usage: qmail-dkim [-h|-v|-s] [tags] [ ]\n\n"); + fprintf(FDLOG, "Options:\n\t-h show this help\n"); + fprintf(FDLOG, "\t-s sign the message \n"); + fprintf(FDLOG, "\t-v verify the message\n"); + fprintf(FDLOG, "\t-V verify the message and write result to output file (Pass/Fail)\n\n"); + fprintf(FDLOG, "These tags are available:\n"); + fprintf( + FDLOG, + "\t-c - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n"); + fprintf( + FDLOG, + "\t-d - Signing Domain Identifier (if not provided it will be determined from " + "the sender/from header)\n"); + fprintf( + FDLOG, + "\t-i - Agent User Identifier, usually the sender's email address " + "(optional)\n"); + fprintf(FDLOG, "\t-l - include body length tag (optional)\n"); + fprintf(FDLOG, "\t-q - include query method tag\n"); + fprintf(FDLOG, "\t-t - include a timestamp tag (optional)\n"); + fprintf( + FDLOG, + "\t-x - the expire time in seconds since epoch (optional, DEFAULT = current " + "time + 604800)\n"); + fprintf(FDLOG, "\t-y - set RSA selector (DEFAULT: default)\n"); + fprintf(FDLOG, "\t-Y - set Ed25519 selector (DEFAULT: default)\n"); + fprintf( + FDLOG, + "\t-z - set signature algorithm type (1=rsa-sha1, 2=rsa-sha256, 3=both, " + "4=ed25519, 5=hybrid)\n"); +} + +int main(int argc, char *argv[]) +{ + int n; + const char *RSAKeyFile = "rsa.pem"; + const char *ECCKeyFile = "ed25519.pem"; + const char *MsgFile = "test.msg"; + const char *OutFile = "signed.msg"; + int nKeyLen; + char RSAPrivKey[4196]; // storge for private key FILE including header and DER envelope + char ECCPrivKey[128]; + char Buffer[1000]; + int BufLen; + char szSignature[8192]; + time_t t; + struct DKIMContext *ctxt = DKIMContextNew(); + DKIMSignOptions opts = {0}; + + opts.nHash = DKIM_HASH_SHA256; // default + + time(&t); + + opts.nCanon = DKIM_SIGN_RELAXED; + opts.nIncludeBodyLengthTag = 0; + opts.nIncludeQueryMethod = 0; + opts.nIncludeTimeStamp = 0; + opts.expireTime = t + 604800; // expires in 1 week + strcpy(opts.szSelector, MYRSASELECTOR); + strcpy(opts.szSelectorE, MYECCSELECTOR); + strcpy(opts.szDomain, MYDOMAIN); + strcpy(opts.szIdentity, MYIDENTITY); + opts.pfnHeaderCallback = SignThisHeader; + strcpy(opts.szRequiredHeaders, "NonExistant"); + opts.nIncludeCopiedHeaders = 0; + + int nArgParseState = 0; + bool bSign = true; + bool bRes = false; + + if (argc < 2) { + usage(); + exit(1); + } + + for (n = 1; n < argc; n++) { + if (argv[n][0] == '-' && strlen(argv[n]) > 1) { + switch (argv[n][1]) { + case 'c': // canonicalization + if (argv[n][2] == 'r') { + opts.nCanon = DKIM_SIGN_RELAXED; + } else if (argv[n][2] == 's') { + opts.nCanon = DKIM_SIGN_SIMPLE; + } else if (argv[n][2] == 't') { + opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; + } else if (argv[n][2] == 'u') { + opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; + } + break; + case 'd': strncpy(opts.szDomain, (const char *)(argv[n] + 2), sizeof(opts.szDomain) - 1); break; + case 'l': // body length tag + opts.nIncludeBodyLengthTag = 1; + break; + case 'h': usage(); return 0; + case 'i': // identity + if (argv[n][2] == '-') { + opts.szIdentity[0] = '\0'; + } else { + strncpy(opts.szIdentity, argv[n] + 2, sizeof(opts.szIdentity) - 1); + } + break; + case 'q': // query method tag + opts.nIncludeQueryMethod = 1; + break; + case 's': // sign with and use potentially Ed25519 private key + bSign = true; + break; + case 't': // timestamp tag + opts.nIncludeTimeStamp = 1; + break; + case 'v': // verify + bSign = false; + break; + case 'V': // verify and write result to OutFile + bSign = false; + bRes = true; + break; + case 'x': // expire time + if (argv[n][2] == '-') { + opts.expireTime = 0; + } else { + opts.expireTime = t + atoi(argv[n] + 2); + } + break; + case 'y': strncpy(opts.szSelector, argv[n] + 2, sizeof(opts.szSelector) - 1); break; + case 'Y': strncpy(opts.szSelectorE, argv[n] + 2, sizeof(opts.szSelectorE) - 1); break; + case 'z': // sign w/ sha1, sha256, both, ed25519, hybrid + opts.nHash = atoi(&argv[n][2]); + } + } else { + switch (nArgParseState) { + case 0: MsgFile = argv[n]; break; + case 1: RSAKeyFile = argv[n]; break; + case 2: OutFile = argv[n]; break; + case 3: ECCKeyFile = argv[n]; break; + } + nArgParseState++; + } + } + + /** Go for DKIM signing ... **/ + + if (bSign) { + if (opts.nHash != 4) { + FILE *RSAPrivKeyFP = fopen(RSAKeyFile, "r"); + if (RSAPrivKeyFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: can't open private key file (%s) \n", RSAKeyFile); +#endif + exit(1); + } + nKeyLen = fread( + RSAPrivKey, + 1, + sizeof(RSAPrivKey), + RSAPrivKeyFP); // we read sizeof(RSAPrivKey) members with size of 1 byte each; sigh +#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: private key file (%s) - length %i \n", RSAKeyFile, nKeyLen); +#endif + if (nKeyLen >= sizeof(RSAPrivKey)) { /* (TC9) on return, we get the number of members read! */ +#ifdef SHOWLOG + fprintf( + FDLOG, + " qmail-dkim: private key buffer isn't big enough for private key length %i \n", + nKeyLen); +#endif + exit(1); + } + RSAPrivKey[nKeyLen] = '\0'; + fclose(RSAPrivKeyFP); + } + + /** Ed25519 signing **/ + + if (opts.nHash == 4 || opts.nHash == 5) { + FILE *ECCPrivKeyFP = fopen(ECCKeyFile, "r"); + if (ECCPrivKeyFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: can't open Ed25519 private key file (%s) \n", ECCKeyFile); +#endif + exit(1); + } + nKeyLen = fread(ECCPrivKey, 1, sizeof(ECCPrivKey), ECCPrivKeyFP); +#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: Ed25519 private key file (%s) - length %i \n", ECCKeyFile, nKeyLen); +#endif + if (nKeyLen >= sizeof(ECCPrivKey)) { +#ifdef SHOWLOG + fprintf( + FDLOG, + " qmail-dkim: ECC private key buffer isn't big enough for ECC private key length %i \n", + nKeyLen); +#endif + exit(1); + } + ECCPrivKey[nKeyLen] = '\0'; + fclose(ECCPrivKeyFP); + } + + /** Input message for signing **/ + + FILE *MsgFP = fopen(MsgFile, "rb"); + if (MsgFP == NULL) { +#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: can't open msg file (%s) \n", MsgFile); +#endif + exit(1); + } + + n = DKIMSignInit(ctxt, &opts); + + while (1) { + BufLen = fread(Buffer, 1, sizeof(Buffer), MsgFP); + if (BufLen > 0) { + DKIMSignProcess(ctxt, Buffer, BufLen); + } else { + break; + } + } + fclose(MsgFP); + + char *pSig = NULL; + + /** Do the actual signing **/ + + n = DKIMSignGetSig2(ctxt, RSAPrivKey, ECCPrivKey, &pSig); + + strcpy(szSignature, pSig); + + DKIMSignFree(ctxt); + + FILE *in = fopen(MsgFile, "rb"); + FILE *out = fopen(OutFile, "wb+"); + +#ifdef SHOWLOG + fprintf(FDLOG, " outfile written %s \n", OutFile); +#endif + + fwrite(szSignature, 1, strlen(szSignature), out); + fwrite("\r\n", 1, 2, out); + + while (1) { + BufLen = fread(Buffer, 1, sizeof(Buffer), in); + if (BufLen > 0) { + fwrite(Buffer, 1, BufLen, out); + } else { + break; + } + } + fclose(in); + + } + + /** Now go for verification **/ + + else + { + FILE *in = fopen(MsgFile, "rb"); + if (in == NULL) { + //#ifdef SHOWLOG + fprintf(FDLOG, " qmail-dkim: can't open input file\n"); + //#endif + return 0; // bad option -- no CTX set up yet + } + + DKIMVerifyOptions vopts = {0}; + vopts.pfnSelectorCallback = NULL; //SelectorCallback; + + n = DKIMVerifyInit(ctxt, &vopts); + + while (1) { + BufLen = fread(Buffer, 1, sizeof(Buffer), in); + if (BufLen > 0) { + DKIMVerifyProcess(ctxt, Buffer, BufLen); + } else { + break; + } + } + + n = DKIMVerifyResults(ctxt); + + int nSigCount = 0; + DKIMVerifyDetails *pDetails; + char szPolicy[512]; + + n = DKIMVerifyGetDetails(ctxt, &nSigCount, &pDetails, szPolicy); + + for (int i = 0; i < nSigCount; i++) { + const char s[] = "pass"; + const char f[] = "fail"; + const char *error = DKIM_ErrorResult(pDetails[i].nResult); + if (!bRes) fprintf(FDLOG, " Signature #%d: ", i + 1); + if (pDetails[i].nResult >= 0) { + if (bRes) { + _DKIM_ReportResult(OutFile, s, 0); + } else + printf(" Pass\n"); + } else { // fail + if (bRes) { + _DKIM_ReportResult(OutFile, f, error); + } else + printf(" Fail %s \n", error); + } + } + DKIMVerifyFree(ctxt); + } + DKIMContextFree(ctxt); + return 0; +} diff --git a/src/qmail-dkim.cpp b/src/qmail-dkim.cpp deleted file mode 100644 index 1a05e6c..0000000 --- a/src/qmail-dkim.cpp +++ /dev/null @@ -1,377 +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 -* -* Comment: Awful mixture of C and C++ making use of the worst parts of it. -* Style: Partial Hungarian notation (see Torvalds comments) -* C++: Obsolete classes, allocators, virtual constructors w/o destructors -* C: Stdio interface routines -* OpenSSL: Brain demaged EVP_Digest calls with memory leaks. -* Network: Sigh, exchanged internal DNS routines by fehQlibs resolver -* -*****************************************************************************/ -#include - -#include -#include -#include -#include - -#include "dkim.h" - -extern "C" { -#include "dns.h" -} - -// change these to your selector name, domain name, etc -#define MYRSASELECTOR "default" -#define MYECCSELECTOR "eddy" -#define MYDOMAIN "" //"bardenhagen.com" -#define MYIDENTITY "" //"dkimtest@bardenhagen.com" - -#define strnicmp strncasecmp -#define FDLOG stderr /* writing to another FD requires a method */ - -int DKIM_CALL SignThisHeader(const char *szHeader) -{ - if (strnicmp(szHeader, "X-", 2) == 0) { - return 0; - } - return 1; -} - -int DKIM_CALL SelectorCallback(const char *szFQDN, char *szBuffer, int nBufLen) -{ - return 0; -} - -void usage() -{ - char version[] = "1.4.0"; - fprintf(FDLOG, "qmail-dkim %s \n", version); - fprintf( - FDLOG, - "Usage: qmail-dkim [-h|-v|-s] [tags] [ ]\n\n"); - fprintf(FDLOG, "Options:\n\t-h show this help\n"); - fprintf(FDLOG, "\t-s sign the message \n"); - fprintf(FDLOG, "\t-v verify the message\n"); - fprintf(FDLOG, "\t-V verify the message and write result to output file (Pass/Fail)\n\n"); - fprintf(FDLOG, "These tags are available:\n"); - fprintf( - FDLOG, - "\t-c - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n"); - fprintf( - FDLOG, - "\t-d - Signing Domain Identifier (if not provided it will be determined from " - "the sender/from header)\n"); - fprintf( - FDLOG, - "\t-i - Agent User Identifier, usually the sender's email address " - "(optional)\n"); - fprintf(FDLOG, "\t-l - include body length tag (optional)\n"); - fprintf(FDLOG, "\t-q - include query method tag\n"); - fprintf(FDLOG, "\t-t - include a timestamp tag (optional)\n"); - fprintf( - FDLOG, - "\t-x - the expire time in seconds since epoch (optional, DEFAULT = current " - "time + 604800)\n"); - fprintf(FDLOG, "\t-y - set RSA selector (DEFAULT: default)\n"); - fprintf(FDLOG, "\t-Y - set Ed25519 selector (DEFAULT: default)\n"); - fprintf( - FDLOG, - "\t-z - set signature algorithm type (1=rsa-sha1, 2=rsa-sha256, 3=both, " - "4=ed25519, 5=hybrid)\n"); -} - -int main(int argc, char *argv[]) -{ - int n; - const char *RSAKeyFile = "rsa.pem"; - const char *ECCKeyFile = "ed25519.pem"; - const char *MsgFile = "test.msg"; - const char *OutFile = "signed.msg"; - int nKeyLen; - char RSAPrivKey[4196]; // storge for private key FILE including header and DER envelope - char ECCPrivKey[128]; - char Buffer[1000]; - int BufLen; - char szSignature[8192]; - time_t t; - DKIMContext ctxt; - DKIMSignOptions opts = {0}; - - opts.nHash = DKIM_HASH_SHA256; // default - - time(&t); - - opts.nCanon = DKIM_SIGN_RELAXED; - opts.nIncludeBodyLengthTag = 0; - opts.nIncludeQueryMethod = 0; - opts.nIncludeTimeStamp = 0; - opts.expireTime = t + 604800; // expires in 1 week - strcpy(opts.szSelector, MYRSASELECTOR); - strcpy(opts.szSelectorE, MYECCSELECTOR); - strcpy(opts.szDomain, MYDOMAIN); - strcpy(opts.szIdentity, MYIDENTITY); - opts.pfnHeaderCallback = SignThisHeader; - strcpy(opts.szRequiredHeaders, "NonExistant"); - opts.nIncludeCopiedHeaders = 0; - - int nArgParseState = 0; - bool bSign = true; - bool bRes = false; - - if (argc < 2) { - usage(); - exit(1); - } - - for (n = 1; n < argc; n++) { - if (argv[n][0] == '-' && strlen(argv[n]) > 1) { - switch (argv[n][1]) { - case 'c': // canonicalization - if (argv[n][2] == 'r') { - opts.nCanon = DKIM_SIGN_RELAXED; - } else if (argv[n][2] == 's') { - opts.nCanon = DKIM_SIGN_SIMPLE; - } else if (argv[n][2] == 't') { - opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; - } else if (argv[n][2] == 'u') { - opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; - } - break; - case 'd': strncpy(opts.szDomain, (const char *)(argv[n] + 2), sizeof(opts.szDomain) - 1); break; - case 'l': // body length tag - opts.nIncludeBodyLengthTag = 1; - break; - case 'h': usage(); return 0; - case 'i': // identity - if (argv[n][2] == '-') { - opts.szIdentity[0] = '\0'; - } else { - strncpy(opts.szIdentity, argv[n] + 2, sizeof(opts.szIdentity) - 1); - } - break; - case 'q': // query method tag - opts.nIncludeQueryMethod = 1; - break; - case 's': // sign with and use potentially Ed25519 private key - bSign = true; - break; - case 't': // timestamp tag - opts.nIncludeTimeStamp = 1; - break; - case 'v': // verify - bSign = false; - break; - case 'V': // verify and write result to OutFile - bSign = false; - bRes = true; - break; - case 'x': // expire time - if (argv[n][2] == '-') { - opts.expireTime = 0; - } else { - opts.expireTime = t + atoi(argv[n] + 2); - } - break; - case 'y': strncpy(opts.szSelector, argv[n] + 2, sizeof(opts.szSelector) - 1); break; - case 'Y': strncpy(opts.szSelectorE, argv[n] + 2, sizeof(opts.szSelectorE) - 1); break; - case 'z': // sign w/ sha1, sha256, both, ed25519, hybrid - opts.nHash = atoi(&argv[n][2]); - } - } else { - switch (nArgParseState) { - case 0: MsgFile = argv[n]; break; - case 1: RSAKeyFile = argv[n]; break; - case 2: OutFile = argv[n]; break; - case 3: ECCKeyFile = argv[n]; break; - } - nArgParseState++; - } - } - - /** Go for DKIM signing ... **/ - - if (bSign) { - if (opts.nHash != 4) { - FILE *RSAPrivKeyFP = fopen(RSAKeyFile, "r"); - if (RSAPrivKeyFP == NULL) { -#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: can't open private key file (%s) \n", RSAKeyFile); -#endif - exit(1); - } - nKeyLen = fread( - RSAPrivKey, - 1, - sizeof(RSAPrivKey), - RSAPrivKeyFP); // we read sizeof(RSAPrivKey) members with size of 1 byte each; sigh -#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: private key file (%s) - length %i \n", RSAKeyFile, nKeyLen); -#endif - if (nKeyLen >= sizeof(RSAPrivKey)) { /* (TC9) on return, we get the number of members read! */ -#ifdef SHOWLOG - fprintf( - FDLOG, - " qmail-dkim: private key buffer isn't big enough for private key length %i \n", - nKeyLen); -#endif - exit(1); - } - RSAPrivKey[nKeyLen] = '\0'; - fclose(RSAPrivKeyFP); - } - - /** Ed25519 signing **/ - - if (opts.nHash == 4 || opts.nHash == 5) { - FILE *ECCPrivKeyFP = fopen(ECCKeyFile, "r"); - if (ECCPrivKeyFP == NULL) { -#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: can't open Ed25519 private key file (%s) \n", ECCKeyFile); -#endif - exit(1); - } - nKeyLen = fread(ECCPrivKey, 1, sizeof(ECCPrivKey), ECCPrivKeyFP); -#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: Ed25519 private key file (%s) - length %i \n", ECCKeyFile, nKeyLen); -#endif - if (nKeyLen >= sizeof(ECCPrivKey)) { -#ifdef SHOWLOG - fprintf( - FDLOG, - " qmail-dkim: ECC private key buffer isn't big enough for ECC private key length %i \n", - nKeyLen); -#endif - exit(1); - } - ECCPrivKey[nKeyLen] = '\0'; - fclose(ECCPrivKeyFP); - } - - /** Input message for signing **/ - - FILE *MsgFP = fopen(MsgFile, "rb"); - if (MsgFP == NULL) { -#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: can't open msg file (%s) \n", MsgFile); -#endif - exit(1); - } - - n = DKIMSignInit(&ctxt, &opts); - - while (1) { - BufLen = fread(Buffer, 1, sizeof(Buffer), MsgFP); - if (BufLen > 0) { - DKIMSignProcess(&ctxt, Buffer, BufLen); - } else { - break; - } - } - fclose(MsgFP); - - char *pSig = NULL; - - /** Do the actual signing **/ - - n = DKIMSignGetSig2(&ctxt, RSAPrivKey, ECCPrivKey, &pSig); - - strcpy(szSignature, pSig); - - DKIMSignFree(&ctxt); - - FILE *in = fopen(MsgFile, "rb"); - FILE *out = fopen(OutFile, "wb+"); - -#ifdef SHOWLOG - fprintf(FDLOG, " outfile written %s \n", OutFile); -#endif - - fwrite(szSignature, 1, strlen(szSignature), out); - fwrite("\r\n", 1, 2, out); - - while (1) { - BufLen = fread(Buffer, 1, sizeof(Buffer), in); - if (BufLen > 0) { - fwrite(Buffer, 1, BufLen, out); - } else { - break; - } - } - fclose(in); - - } - - /** Now go for verification **/ - - else - { - FILE *in = fopen(MsgFile, "rb"); - if (in == NULL) { - //#ifdef SHOWLOG - fprintf(FDLOG, " qmail-dkim: can't open input file\n"); - //#endif - return 0; // bad option -- no CTX set up yet - } - - DKIMVerifyOptions vopts = {0}; - vopts.pfnSelectorCallback = NULL; //SelectorCallback; - - n = DKIMVerifyInit(&ctxt, &vopts); - - while (1) { - BufLen = fread(Buffer, 1, sizeof(Buffer), in); - if (BufLen > 0) { - DKIMVerifyProcess(&ctxt, Buffer, BufLen); - } else { - break; - } - } - - n = DKIMVerifyResults(&ctxt); - - int nSigCount = 0; - DKIMVerifyDetails *pDetails; - char szPolicy[512]; - - n = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy); - - for (int i = 0; i < nSigCount; i++) { - const char s[] = "pass"; - const char f[] = "fail"; - const char *error = DKIM_ErrorResult(pDetails[i].nResult); - if (!bRes) fprintf(FDLOG, " Signature #%d: ", i + 1); - if (pDetails[i].nResult >= 0) { - if (bRes) { - _DKIM_ReportResult(OutFile, s, 0); - } else - printf(" Pass\n"); - } else { // fail - if (bRes) { - _DKIM_ReportResult(OutFile, f, error); - } else - printf(" Fail %s \n", error); - } - } - DKIMVerifyFree(&ctxt); - } - return 0; -} -- cgit v1.2.3