diff options
Diffstat (limited to 'src/qmail-dkim.cpp')
-rw-r--r-- | src/qmail-dkim.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/src/qmail-dkim.cpp b/src/qmail-dkim.cpp new file mode 100644 index 0000000..fba94fe --- /dev/null +++ b/src/qmail-dkim.cpp @@ -0,0 +1,343 @@ +/***************************************************************************** +* 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 <stdio.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#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] <msgfile> [<RSAkeyfile> <outfile> <Ed25519keyfile>]\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<canonicalization> - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n"); + fprintf(FDLOG, "\t-d<sdid> - Signing Domain Identifier (if not provided it will be determined from the sender/from header)\n"); + fprintf(FDLOG, "\t-i<auid> - 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<expire_time> - the expire time in seconds since epoch (optional, DEFAULT = current time + 604800)\n"); + fprintf(FDLOG, "\t-y<selector> - set RSA selector (DEFAULT: default)\n"); + fprintf(FDLOG, "\t-Y<selector> - set Ed25519 selector (DEFAULT: default)\n"); + fprintf(FDLOG, "\t-z<hash> - 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; +} |