s/qmail 4.2.29a
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#include <stdio.h>
30#include <string.h>
31#include <time.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include "dkim.h"
35extern "C" {
36#include "dns.h"
37}
38
39// change these to your selector name, domain name, etc
40#define MYRSASELECTOR "default"
41#define MYECCSELECTOR "eddy"
42#define MYDOMAIN "" //"bardenhagen.com"
43#define MYIDENTITY "" //"dkimtest@bardenhagen.com"
44
45#define strnicmp strncasecmp
46#define FDLOG stderr /* writing to another FD requires a method */
47
48int DKIM_CALL SignThisHeader(const char* szHeader)
49{
50 if (strnicmp(szHeader,"X-",2) == 0 ) { return 0; }
51 return 1;
52}
53
54int DKIM_CALL SelectorCallback(const char* szFQDN,char* szBuffer,int nBufLen)
55{
56 return 0;
57}
58
59void usage()
60{
61 char version[] = "1.4.0";
62 fprintf(FDLOG,"qmail-dkim %s \n",version);
63 fprintf(FDLOG,"Usage: qmail-dkim [-h|-v|-s] [tags] <msgfile> [<RSAkeyfile> <outfile> <Ed25519keyfile>]\n\n");
64 fprintf(FDLOG, "Options:\n\t-h show this help\n");
65 fprintf(FDLOG, "\t-s sign the message \n");
66 fprintf(FDLOG, "\t-v verify the message\n");
67 fprintf(FDLOG, "\t-V verify the message and write result to output file (Pass/Fail)\n\n");
68 fprintf(FDLOG, "These tags are available:\n");
69 fprintf(FDLOG, "\t-c<canonicalization> - r=relaxed [DEFAULT], s=simple, t=relaxed/simple, u=simple/relaxed\n");
70 fprintf(FDLOG, "\t-d<sdid> - Signing Domain Identifier (if not provided it will be determined from the sender/from header)\n");
71 fprintf(FDLOG, "\t-i<auid> - Agent User Identifier, usually the sender's email address (optional)\n");
72 fprintf(FDLOG, "\t-l - include body length tag (optional)\n");
73 fprintf(FDLOG, "\t-q - include query method tag\n");
74 fprintf(FDLOG, "\t-t - include a timestamp tag (optional)\n");
75 fprintf(FDLOG, "\t-x<expire_time> - the expire time in seconds since epoch (optional, DEFAULT = current time + 604800)\n");
76 fprintf(FDLOG, "\t-y<selector> - set RSA selector (DEFAULT: default)\n");
77 fprintf(FDLOG, "\t-Y<selector> - set Ed25519 selector (DEFAULT: default)\n");
78 fprintf(FDLOG, "\t-z<hash> - set signature algorithm type (1=rsa-sha1, 2=rsa-sha256, 3=both, 4=ed25519, 5=hybrid)\n");
79}
80
81int main(int argc, char* argv[])
82{
83 int n;
84 const char* RSAKeyFile = "rsa.pem";
85 const char* ECCKeyFile = "ed25519.pem";
86 const char* MsgFile = "test.msg";
87 const char* OutFile = "signed.msg";
88 int nKeyLen;
89 char RSAPrivKey[4196]; // storge for private key FILE including header and DER envelope
90 char ECCPrivKey[128];
91 char Buffer[1000];
92 int BufLen;
93 char szSignature[8192];
94 time_t t;
95 DKIMContext ctxt;
96 DKIMSignOptions opts = {0};
97
98 opts.nHash = DKIM_HASH_SHA256; // default
99
100 time(&t);
101
103 opts.nIncludeBodyLengthTag = 0;
104 opts.nIncludeQueryMethod = 0;
105 opts.nIncludeTimeStamp = 0;
106 opts.expireTime = t + 604800; // expires in 1 week
107 strcpy(opts.szSelector,MYRSASELECTOR);
108 strcpy(opts.szSelectorE,MYECCSELECTOR);
109 strcpy(opts.szDomain,MYDOMAIN);
110 strcpy(opts.szIdentity,MYIDENTITY);
112 strcpy(opts.szRequiredHeaders,"NonExistant");
113 opts.nIncludeCopiedHeaders = 0;
114
115 int nArgParseState = 0;
116 bool bSign = true;
117 bool bRes = false;
118
119 if (argc < 2){
120 usage();
121 exit(1);
122 }
123
124 for (n = 1; n < argc; n++) {
125 if (argv[n][0] == '-' && strlen(argv[n]) > 1) {
126 switch (argv[n][1]) {
127 case 'c': // canonicalization
128 if (argv[n][2] == 'r') { opts.nCanon = DKIM_SIGN_RELAXED; }
129 else if (argv[n][2] == 's') { opts.nCanon = DKIM_SIGN_SIMPLE; }
130 else if (argv[n][2] == 't') { opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE; }
131 else if (argv[n][2] == 'u') { opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED; }
132 break;
133 case 'd':
134 strncpy(opts.szDomain,(const char*)(argv[n] + 2),sizeof(opts.szDomain) - 1);
135 break;
136 case 'l': // body length tag
137 opts.nIncludeBodyLengthTag = 1;
138 break;
139 case 'h':
140 usage();
141 return 0;
142 case 'i': // identity
143 if (argv[n][2] == '-') { opts.szIdentity[0] = '\0'; }
144 else { strncpy(opts.szIdentity, argv[n] + 2,sizeof(opts.szIdentity) - 1); }
145 break;
146 case 'q': // query method tag
147 opts.nIncludeQueryMethod = 1;
148 break;
149 case 's': // sign with and use potentially Ed25519 private key
150 bSign = true;
151 break;
152 case 't': // timestamp tag
153 opts.nIncludeTimeStamp = 1;
154 break;
155 case 'v': // verify
156 bSign = false;
157 break;
158 case 'V': // verify and write result to OutFile
159 bSign = false;
160 bRes = true;
161 break;
162 case 'x': // expire time
163 if (argv[n][2] == '-') { opts.expireTime = 0; }
164 else { opts.expireTime = t + atoi(argv[n] + 2); }
165 break;
166 case 'y':
167 strncpy(opts.szSelector,argv[n] + 2,sizeof(opts.szSelector) - 1);
168 break;
169 case 'Y':
170 strncpy(opts.szSelectorE,argv[n] + 2,sizeof(opts.szSelectorE) - 1);
171 break;
172 case 'z': // sign w/ sha1, sha256, both, ed25519, hybrid
173 opts.nHash = atoi(&argv[n][2]);
174 }
175 }
176 else {
177 switch (nArgParseState) {
178 case 0:
179 MsgFile = argv[n];
180 break;
181 case 1:
182 RSAKeyFile = argv[n];
183 break;
184 case 2:
185 OutFile = argv[n];
186 break;
187 case 3:
188 ECCKeyFile = argv[n];
189 break;
190 }
191 nArgParseState++;
192 }
193 }
194
197 if (bSign) {
198 if (opts.nHash != 4) {
199 FILE* RSAPrivKeyFP = fopen(RSAKeyFile,"r");
200 if (RSAPrivKeyFP == NULL) {
201#ifdef SHOWLOG
202 fprintf(FDLOG," qmail-dkim: can't open private key file (%s) \n",RSAKeyFile);
203#endif
204 exit(1);
205 }
206 nKeyLen = fread(RSAPrivKey,1,sizeof(RSAPrivKey),RSAPrivKeyFP); // we read sizeof(RSAPrivKey) members with size of 1 byte each; sigh
207#ifdef SHOWLOG
208 fprintf(FDLOG," qmail-dkim: private key file (%s) - length %i \n",RSAKeyFile,nKeyLen);
209#endif
210 if (nKeyLen >= sizeof(RSAPrivKey)) { /* (TC9) on return, we get the number of members read! */
211#ifdef SHOWLOG
212 fprintf(FDLOG," qmail-dkim: private key buffer isn't big enough for private key length %i \n",nKeyLen);
213#endif
214 exit(1);
215 }
216 RSAPrivKey[nKeyLen] = '\0';
217 fclose(RSAPrivKeyFP);
218 }
219
222 if (opts.nHash == 4 || opts.nHash == 5) {
223 FILE* ECCPrivKeyFP = fopen(ECCKeyFile,"r");
224 if (ECCPrivKeyFP == NULL) {
225#ifdef SHOWLOG
226 fprintf(FDLOG," qmail-dkim: can't open Ed25519 private key file (%s) \n",ECCKeyFile);
227#endif
228 exit(1);
229 }
230 nKeyLen = fread(ECCPrivKey,1,sizeof(ECCPrivKey),ECCPrivKeyFP);
231#ifdef SHOWLOG
232 fprintf(FDLOG," qmail-dkim: Ed25519 private key file (%s) - length %i \n",ECCKeyFile,nKeyLen);
233#endif
234 if (nKeyLen >= sizeof(ECCPrivKey)) {
235#ifdef SHOWLOG
236 fprintf(FDLOG," qmail-dkim: ECC private key buffer isn't big enough for ECC private key length %i \n",nKeyLen);
237#endif
238 exit(1);
239 }
240 ECCPrivKey[nKeyLen] = '\0';
241 fclose(ECCPrivKeyFP);
242 }
243
246 FILE* MsgFP = fopen(MsgFile,"rb");
247 if (MsgFP == NULL) {
248#ifdef SHOWLOG
249 fprintf(FDLOG," qmail-dkim: can't open msg file (%s) \n",MsgFile);
250#endif
251 exit(1);
252 }
253
254 n = DKIMSignInit(&ctxt,&opts);
255
256 while (1) {
257 BufLen = fread(Buffer,1,sizeof(Buffer),MsgFP);
258 if (BufLen > 0) { DKIMSignProcess(&ctxt,Buffer,BufLen); }
259 else { break; }
260 }
261 fclose(MsgFP);
262
263 char* pSig = NULL;
264
267 n = DKIMSignGetSig2(&ctxt,RSAPrivKey,ECCPrivKey,&pSig);
268
269 strcpy(szSignature,pSig);
270
271 DKIMSignFree(&ctxt);
272
273 FILE* in = fopen(MsgFile,"rb");
274 FILE* out = fopen(OutFile,"wb+");
275
276#ifdef SHOWLOG
277 fprintf(FDLOG," outfile written %s \n",OutFile);
278#endif
279
280 fwrite(szSignature,1,strlen(szSignature),out);
281 fwrite("\r\n",1,2,out);
282
283 while (1) {
284 BufLen = fread(Buffer,1,sizeof(Buffer),in);
285 if (BufLen > 0) { fwrite(Buffer,1,BufLen,out); }
286 else { break; }
287 }
288 fclose(in);
289
290 }
291
294 else {
295 FILE* in = fopen(MsgFile,"rb");
296 if (in == NULL) {
297//#ifdef SHOWLOG
298 fprintf(FDLOG," qmail-dkim: can't open input file\n");
299//#endif
300 return 0; // bad option -- no CTX set up yet
301 }
302
303 DKIMVerifyOptions vopts = {0};
304 vopts.pfnSelectorCallback = NULL; //SelectorCallback;
305
306 n = DKIMVerifyInit(&ctxt,&vopts);
307
308 while (1) {
309 BufLen = fread(Buffer,1,sizeof(Buffer),in);
310 if (BufLen > 0) { DKIMVerifyProcess(&ctxt,Buffer,BufLen); }
311 else { break; }
312 }
313
314 n = DKIMVerifyResults(&ctxt);
315
316 int nSigCount = 0;
317 DKIMVerifyDetails* pDetails;
318 char szPolicy[512];
319
320 n = DKIMVerifyGetDetails(&ctxt,&nSigCount,&pDetails,szPolicy);
321
322 for (int i = 0; i < nSigCount; i++) {
323 const char s[] = "pass";
324 const char f[] = "fail";
325 const char* error = DKIM_ErrorResult(pDetails[i].nResult);
326 if (!bRes)
327 fprintf(FDLOG," Signature #%d: ",i + 1);
328 if (pDetails[i].nResult >= 0 ) {
329 if (bRes) {
330 _DKIM_ReportResult(OutFile,s,0);
331 } else
332 printf(" Pass\n");
333 } else { // fail
334 if (bRes) {
335 _DKIM_ReportResult(OutFile,f,error);
336 } else
337 printf(" Fail %s \n",error);
338 }
339 }
340 DKIMVerifyFree(&ctxt);
341 }
342 return 0;
343}
int main()
Definition: chkshsgr.c:6
void DKIM_CALL DKIMSignFree(DKIMContext *pSignContext)
Definition: dkim.cpp:85
int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions *pOptions)
Definition: dkim.cpp:53
int DKIM_CALL DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices)
Definition: dkim.cpp:136
void DKIM_CALL DKIMVerifyFree(DKIMContext *pVerifyContext)
Definition: dkim.cpp:151
int DKIM_CALL DKIMSignGetSig2(DKIMContext *pSignContext, char *szRSAPrivKey, char *szECCPrivKey, char **pszSignature)
Definition: dkim.cpp:77
int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength)
Definition: dkim.cpp:69
int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, const char *const szBuffer, int nBufLength)
Definition: dkim.cpp:115
int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions *pOptions)
Definition: dkim.cpp:95
int DKIM_CALL DKIMVerifyResults(DKIMContext *pVerifyContext)
Definition: dkim.cpp:126
#define DKIM_CALL
Definition: dkim.h:22
#define DKIM_SIGN_RELAXED
Definition: dkim.h:43
#define DKIM_SIGN_SIMPLE
Definition: dkim.h:41
#define DKIM_SIGN_RELAXED_SIMPLE
Definition: dkim.h:44
#define DKIM_SIGN_SIMPLE_RELAXED
Definition: dkim.h:42
#define DKIM_HASH_SHA256
Definition: dkim.h:31
int _DKIM_ReportResult(const char *ResFile, const char *result, const char *reason)
Definition: dkimverify.cpp:101
const char * DKIM_ErrorResult(const int res)
Definition: dkimverify.cpp:123
stralloc out
Definition: dnscname.c:12
void exit(int fail)
Supported storage methods: (1) authuser:[=]plainpasswd, (2) authuser:hashpasswd, (3) authuser:?...
#define strnicmp
Definition: qmail-dkim.cpp:45
void usage()
Definition: qmail-dkim.cpp:59
int DKIM_CALL SignThisHeader(const char *szHeader)
Definition: qmail-dkim.cpp:48
#define MYECCSELECTOR
Definition: qmail-dkim.cpp:41
#define MYDOMAIN
Definition: qmail-dkim.cpp:42
int DKIM_CALL SelectorCallback(const char *szFQDN, char *szBuffer, int nBufLen)
Definition: qmail-dkim.cpp:54
#define FDLOG
Definition: qmail-dkim.cpp:46
#define MYIDENTITY
Definition: qmail-dkim.cpp:43
#define MYRSASELECTOR
Definition: qmail-dkim.cpp:40
buffer in
Definition: qmail-pw2u.c:239
int nIncludeBodyLengthTag
Definition: dkim.h:99
char szRequiredHeaders[256]
Definition: dkim.h:108
DKIMHEADERCALLBACK pfnHeaderCallback
Definition: dkim.h:107
int nIncludeCopiedHeaders
Definition: dkim.h:111
unsigned long expireTime
Definition: dkim.h:106
char szDomain[256]
Definition: dkim.h:104
char szIdentity[256]
Definition: dkim.h:105
int nIncludeTimeStamp
Definition: dkim.h:100
char szSelector[64]
Definition: dkim.h:102
int nIncludeQueryMethod
Definition: dkim.h:101
char szSelectorE[64]
Definition: dkim.h:103
DKIMDNSCALLBACK pfnSelectorCallback
Definition: dkim.h:116