s/qmail 4.3.20
Next generation secure email transport
Loading...
Searching...
No Matches
qmail-dkim.cpp
Go to the documentation of this file.
1/*****************************************************************************
2* Copyright 2005 Alt-N Technologies, Ltd.
3*
4* Licensed under the Apache License, Version 2.0 (the "License");
5* you may not use this file except in compliance with the License.
6* You may obtain a copy of the License at
7*
8* http://www.apache.org/licenses/LICENSE-2.0
9*
10* This code incorporates intellectual property owned by Yahoo! and licensed
11* pursuant to the Yahoo! DomainKeys Patent License Agreement.
12*
13* Unless required by applicable law or agreed to in writing, software
14* distributed under the License is distributed on an "AS IS" BASIS,
15* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16* See the License for the specific language governing permissions and
17* limitations under the License.
18*
19* Changes done by ¢feh@fehcom.de obeying the above license
20*
21* Comment: Awful mixture of C and C++ making use of the worst parts of it.
22* Style: Partial Hungarian notation (see Torvalds comments)
23* C++: Obsolete classes, allocators, virtual constructors w/o destructors
24* C: Stdio interface routines
25* OpenSSL: Brain demaged EVP_Digest calls with memory leaks.
26* Network: Sigh, exchanged internal DNS routines by fehQlibs resolver
27*
28*****************************************************************************/
29
30#include <stdio.h>
31#include <string.h>
32#include <time.h>
33#include <stdlib.h>
34#include <unistd.h>
35#include "dkim.h"
36extern "C" {
37#include "dns.h"
38}
39
40// change these to your selector name, domain name, etc
41#define MYRSASELECTOR "default"
42#define MYECCSELECTOR "eddy"
43#define MYDOMAIN "" //"bardenhagen.com"
44#define MYIDENTITY "" //"dkimtest@bardenhagen.com"
45
46#define strnicmp strncasecmp
47#define FDLOG stderr /* writing to another FD requires a method */
48
49int SignThisHeader(const char* szHeader)
50{
51 if (strnicmp(szHeader,"X-",2) == 0 ) { return 0; }
52 return 1;
53}
54
55int SelectorCallback(const char* szFQDN,char* szBuffer,int nBufLen)
56{
57 return 0;
58}
59
60void usage()
61{
62 char version[] = "1.4.1";
63 fprintf(FDLOG,"qmail-dkim %s \n",version);
64 fprintf(FDLOG,"Usage: qmail-dkim [-h|-v|-s] [tags] <msgfile> [<RSAkeyfile> <outfile> <Ed25519keyfile>]\n\n");
65 fprintf(FDLOG, "Options:\n\t-h show this help\n");
66 fprintf(FDLOG, "\t-s sign the message \n");
67 fprintf(FDLOG, "\t-v verify the message\n");
68 fprintf(FDLOG, "\t-V verify the message and write result to output file (Pass/Fail)\n\n");
69 fprintf(FDLOG, "These tags are available:\n");
70 fprintf(FDLOG, "\t-c<canonicalization> - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n");
71 fprintf(FDLOG, "\t-d<sdid> - Signing Domain Identifier (if not provided it will be determined from the sender/from header)\n");
72 fprintf(FDLOG, "\t-i<auid> - Agent User Identifier, usually the sender's email address (optional)\n");
73 fprintf(FDLOG, "\t-l - include body length tag (optional)\n");
74 fprintf(FDLOG, "\t-q - include query method tag\n");
75 fprintf(FDLOG, "\t-t - include a timestamp tag (optional)\n");
76 fprintf(FDLOG, "\t-x<expire_time> - the expire time in seconds since epoch (optional, DEFAULT = current time + 604800)\n");
77 fprintf(FDLOG, "\t-y<selector> - set RSA selector (DEFAULT: default)\n");
78 fprintf(FDLOG, "\t-Y<selector> - set Ed25519 selector (DEFAULT: default)\n");
79 fprintf(FDLOG, "\t-z<hash> - set signature algorithm type (1=rsa-sha1, 2=rsa-sha256, 3=both, 4=ed25519, 5=hybrid)\n");
80}
81
82int main(int argc, char* argv[])
83{
84 int n;
85 const char* RSAKeyFile = "rsa.pem";
86 const char* ECCKeyFile = "ed25519.pem";
87 const char* MsgFile = "test.msg";
88 const char* OutFile = "signed.msg";
89 int nKeyLen;
90 char RSAPrivKey[4196]; // storge for private key FILE including header and DER envelope
91 char ECCPrivKey[128];
92 char Buffer[1000];
93 int BufLen;
94 char szSignature[8192];
95 time_t t;
96 DKIMContext ctxt;
97 DKIMSignOptions opts = {0};
98
99 opts.nHash = DKIM_HASH_SHA256; // default
100
101 time(&t);
102
104 opts.nIncludeBodyLengthTag = 0;
105 opts.nIncludeQueryMethod = 0;
106 opts.nIncludeTimeStamp = 0;
107 opts.expireTime = t + 604800; // expires in 1 week
108 strcpy(opts.szSelector,MYRSASELECTOR);
109 strcpy(opts.szSelectorE,MYECCSELECTOR);
110 strcpy(opts.szDomain,MYDOMAIN);
111 strcpy(opts.szIdentity,MYIDENTITY);
113 strcpy(opts.szRequiredHeaders,"NonExistant");
114 opts.nIncludeCopiedHeaders = 0;
115
116 int nArgParseState = 0;
117 bool bSign = true;
118 bool bRes = false;
119
120 if (argc < 2){
121 usage();
122 exit(1);
123 }
124
125 for (n = 1; n < argc; n++) {
126 if (argv[n][0] == '-' && strlen(argv[n]) > 1) {
127 switch (argv[n][1]) {
128 case 'c': // canonicalization
129 if (argv[n][2] == 'r') { opts.nCanon = DKIM_SIGN_RELAXED; }
130 else if (argv[n][2] == 's') { opts.nCanon = DKIM_SIGN_SIMPLE; }
131 else if (argv[n][2] == 't') { opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; }
132 else if (argv[n][2] == 'u') { opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; }
133 break;
134 case 'd':
135 strncpy(opts.szDomain,(const char*)(argv[n] + 2),sizeof(opts.szDomain) - 1);
136 break;
137 case 'l': // body length tag
138 opts.nIncludeBodyLengthTag = 1;
139 break;
140 case 'h':
141 usage();
142 return 0;
143 case 'i': // identity
144 if (argv[n][2] == '-') { opts.szIdentity[0] = '\0'; }
145 else { strncpy(opts.szIdentity, argv[n] + 2,sizeof(opts.szIdentity) - 1); }
146 break;
147 case 'q': // query method tag
148 opts.nIncludeQueryMethod = 1;
149 break;
150 case 's': // sign with and use potentially Ed25519 private key
151 bSign = true;
152 break;
153 case 't': // timestamp tag
154 opts.nIncludeTimeStamp = 1;
155 break;
156 case 'v': // verify
157 bSign = false;
158 break;
159 case 'V': // verify and write result to OutFile
160 bSign = false;
161 bRes = true;
162 break;
163 case 'x': // expire time
164 if (argv[n][2] == '-') { opts.expireTime = 0; }
165 else { opts.expireTime = t + atoi(argv[n] + 2); }
166 break;
167 case 'y':
168 strncpy(opts.szSelector,argv[n] + 2,sizeof(opts.szSelector) - 1);
169 break;
170 case 'Y':
171 strncpy(opts.szSelectorE,argv[n] + 2,sizeof(opts.szSelectorE) - 1);
172 break;
173 case 'z': // sign w/ sha1, sha256, both, ed25519, hybrid
174 opts.nHash = atoi(&argv[n][2]);
175 }
176 }
177 else {
178 switch (nArgParseState) {
179 case 0:
180 MsgFile = argv[n];
181 break;
182 case 1:
183 RSAKeyFile = argv[n];
184 break;
185 case 2:
186 OutFile = argv[n];
187 break;
188 case 3:
189 ECCKeyFile = argv[n];
190 break;
191 }
192 nArgParseState++;
193 }
194 }
195
198 if (bSign) {
199 if (opts.nHash != 4) {
200 FILE* RSAPrivKeyFP = fopen(RSAKeyFile,"r");
201 if (RSAPrivKeyFP == NULL) {
202#ifdef SHOWLOG
203 fprintf(FDLOG," qmail-dkim: can't open private key file (%s) \n",RSAKeyFile);
204#endif
205 exit(1);
206 }
207 nKeyLen = fread(RSAPrivKey,1,sizeof(RSAPrivKey),RSAPrivKeyFP); // we read sizeof(RSAPrivKey) members with size of 1 byte each; sigh
208#ifdef SHOWLOG
209 fprintf(FDLOG," qmail-dkim: private key file (%s) - length %i \n",RSAKeyFile,nKeyLen);
210#endif
211 if (nKeyLen >= sizeof(RSAPrivKey)) { /* (TC9) on return, we get the number of members read! */
212#ifdef SHOWLOG
213 fprintf(FDLOG," qmail-dkim: private key buffer isn't big enough for private key length %i \n",nKeyLen);
214#endif
215 exit(1);
216 }
217 RSAPrivKey[nKeyLen] = '\0';
218 fclose(RSAPrivKeyFP);
219 }
220
223 if (opts.nHash == 4 || opts.nHash == 5) {
224 FILE* ECCPrivKeyFP = fopen(ECCKeyFile,"r");
225 if (ECCPrivKeyFP == NULL) {
226#ifdef SHOWLOG
227 fprintf(FDLOG," qmail-dkim: can't open Ed25519 private key file (%s) \n",ECCKeyFile);
228#endif
229 exit(1);
230 }
231 nKeyLen = fread(ECCPrivKey,1,sizeof(ECCPrivKey),ECCPrivKeyFP);
232#ifdef SHOWLOG
233 fprintf(FDLOG," qmail-dkim: Ed25519 private key file (%s) - length %i \n",ECCKeyFile,nKeyLen);
234#endif
235 if (nKeyLen >= sizeof(ECCPrivKey)) {
236#ifdef SHOWLOG
237 fprintf(FDLOG," qmail-dkim: ECC private key buffer isn't big enough for ECC private key length %i \n",nKeyLen);
238#endif
239 exit(1);
240 }
241 ECCPrivKey[nKeyLen] = '\0';
242 fclose(ECCPrivKeyFP);
243 }
244
247 FILE* in = fopen(MsgFile,"rb");
248 if (in == NULL) {
249#ifdef SHOWLOG
250 fprintf(FDLOG," qmail-dkim: can't open msg file (%s) \n",MsgFile);
251#endif
252 exit(1);
253 }
254
255 n = DKIMSignInit(&ctxt,&opts);
256
257 while (1) {
258 BufLen = fread(Buffer,1,sizeof(Buffer),in);
259 if (BufLen > 0) { DKIMSignProcess(&ctxt,Buffer,BufLen); }
260 else { break; }
261 }
262
263 char* pSig = NULL;
264
267 n = DKIMSignGetSig2(&ctxt,RSAPrivKey,ECCPrivKey,&pSig);
268
269 if (n == DKIM_SUCCESS) strcpy(szSignature,pSig);
270
271 DKIMSignFree(&ctxt);
272
273 fseek(in,0L,SEEK_SET); /* Go to initial state */
274 FILE* out = fopen(OutFile,"wb+");
275
276#ifdef SHOWLOG
277 fprintf(FDLOG," outfile written %s \n",OutFile);
278#endif
279
280 if (n == DKIM_SUCCESS) {
281 fwrite(szSignature,1,strlen(szSignature),out);
282 fwrite("\r\n",1,2,out);
283 }
284
285 while (1) {
286 BufLen = fread(Buffer,1,sizeof(Buffer),in);
287 if (BufLen > 0) { fwrite(Buffer,1,BufLen,out); }
288 else { break; }
289 }
290 fclose(in);
291
292 }
293
296 else {
297 FILE* in = fopen(MsgFile,"rb");
298 if (in == NULL) {
299//#ifdef SHOWLOG
300 fprintf(FDLOG," qmail-dkim: can't open input file\n");
301//#endif
302 return 0; // bad option -- no CTX set up yet
303 }
304
305 DKIMVerifyOptions vopts = {0};
306 vopts.pfnSelectorCallback = NULL; //SelectorCallback;
307
308 n = DKIMVerifyInit(&ctxt,&vopts);
309
310 while (1) {
311 BufLen = fread(Buffer,1,sizeof(Buffer),in);
312 if (BufLen > 0) { DKIMVerifyProcess(&ctxt,Buffer,BufLen); }
313 else { break; }
314 }
315
316 n = DKIMVerifyResults(&ctxt);
317
318 int nSigCount = 0;
319 DKIMVerifyDetails* pDetails;
320 char szPolicy[512];
321
322 n = DKIMVerifyGetDetails(&ctxt,&nSigCount,&pDetails,szPolicy);
323
324 for (int i = 0; i < nSigCount; i++) {
325 const char s[] = "pass";
326 const char f[] = "fail";
327 const char* error = DKIM_ErrorResult(pDetails[i].nResult);
328 if (!bRes)
329 fprintf(FDLOG," Signature #%d: ",i + 1);
330 if (pDetails[i].nResult >= 0 ) {
331 if (bRes) {
332 _DKIM_ReportResult(OutFile,s,0);
333 } else
334 printf(" Pass\n");
335 } else { // fail
336 if (bRes) {
337 _DKIM_ReportResult(OutFile,f,error);
338 } else
339 printf(" Fail %s \n",error);
340 }
341 }
342 DKIMVerifyFree(&ctxt);
343 }
344 return 0;
345}
int main()
Definition: chkshsgr.c:6
int DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions)
Definition: dkim.cpp:53
int DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions)
Definition: dkim.cpp:95
int DKIMSignGetSig2(DKIMContext *pSignContext, char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature)
Definition: dkim.cpp:77
void DKIMSignFree(DKIMContext *pSignContext)
Definition: dkim.cpp:85
int DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength)
Definition: dkim.cpp:69
void DKIMVerifyFree(DKIMContext *pVerifyContext)
Definition: dkim.cpp:151
int DKIMVerifyResults(DKIMContext *pVerifyContext)
Definition: dkim.cpp:126
int DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices)
Definition: dkim.cpp:136
int DKIMVerifyProcess(DKIMContext *pVerifyContext, const char *const szBuffer, int nBufLength)
Definition: dkim.cpp:115
#define DKIM_SIGN_RELAXED
Definition: dkim.h:45
#define DKIM_SIGN_SIMPLE
Definition: dkim.h:43
#define DKIM_SIGN_RELAXED_SIMPLE
Definition: dkim.h:46
#define DKIM_SIGN_SIMPLE_RELAXED
Definition: dkim.h:44
#define DKIM_HASH_SHA256
Definition: dkim.h:33
#define DKIM_SUCCESS
Definition: dkim.h:49
int _DKIM_ReportResult(const char *ResFile, const char *result, const char *reason)
Definition: dkimverify.cpp:102
const char * DKIM_ErrorResult(const int res)
Definition: dkimverify.cpp:124
stralloc out
Definition: dnscname.c:12
int SignThisHeader(const char *szHeader)
Definition: qmail-dkim.cpp:49
#define strnicmp
Definition: qmail-dkim.cpp:46
void usage()
Definition: qmail-dkim.cpp:60
#define MYECCSELECTOR
Definition: qmail-dkim.cpp:42
int SelectorCallback(const char *szFQDN, char *szBuffer, int nBufLen)
Definition: qmail-dkim.cpp:55
#define MYDOMAIN
Definition: qmail-dkim.cpp:43
#define FDLOG
Definition: qmail-dkim.cpp:47
#define MYIDENTITY
Definition: qmail-dkim.cpp:44
#define MYRSASELECTOR
Definition: qmail-dkim.cpp:41
void exit(int fail)
Definition: qmail-ldapam.c:70
buffer in
Definition: qmail-pw2u.c:240
int nIncludeBodyLengthTag
Definition: dkim.h:101
char szRequiredHeaders[256]
Definition: dkim.h:110
DKIMHEADERCALLBACK pfnHeaderCallback
Definition: dkim.h:109
int nIncludeCopiedHeaders
Definition: dkim.h:113
unsigned long expireTime
Definition: dkim.h:108
char szDomain[256]
Definition: dkim.h:106
char szIdentity[256]
Definition: dkim.h:107
int nIncludeTimeStamp
Definition: dkim.h:102
char szSelector[64]
Definition: dkim.h:104
int nIncludeQueryMethod
Definition: dkim.h:103
char szSelectorE[64]
Definition: dkim.h:105
DKIMDNSCALLBACK pfnSelectorCallback
Definition: dkim.h:118