1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
|
/*
* PDKIM - a RFC4871 (DKIM) implementation
*
* Copyright (C) 2009 - 2012 Tom Kistner <tom@duncanthrax.net>
* Copyright (c) 2016 - 2020 Jeremy Harris
*
* http://duncanthrax.net/pdkim/
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef PDKIM_H
#define PDKIM_H
#include "../blob.h"
#include "../hash.h"
#define PDKIM_DEFAULT_SIGN_HEADERS "From:Sender:Reply-To:Subject:Date:"\
"Message-ID:To:Cc:MIME-Version:Content-Type:"\
"Content-Transfer-Encoding:Content-ID:"\
"Content-Description:Resent-Date:Resent-From:"\
"Resent-Sender:Resent-To:Resent-Cc:"\
"Resent-Message-ID:In-Reply-To:References:"\
"List-Id:List-Help:List-Unsubscribe:"\
"List-Subscribe:List-Post:List-Owner:List-Archive"
#define PDKIM_OVERSIGN_HEADERS "+From:+Sender:+Reply-To:+Subject:+Date:"\
"+Message-ID:+To:+Cc:+MIME-Version:+Content-Type:"\
"+Content-Transfer-Encoding:+Content-ID:"\
"+Content-Description:+Resent-Date:+Resent-From:"\
"+Resent-Sender:+Resent-To:+Resent-Cc:"\
"+Resent-Message-ID:+In-Reply-To:+References:"\
"+List-Id:+List-Help:+List-Unsubscribe:"\
"+List-Subscribe:+List-Post:+List-Owner:+List-Archive"
/* -------------------------------------------------------------------------- */
/* Length of the preallocated buffer for the "answer" from the dns/txt
callback function. This should match the maximum RDLENGTH from DNS. */
#define PDKIM_DNS_TXT_MAX_RECLEN (1 << 16)
/* -------------------------------------------------------------------------- */
/* Function success / error codes */
#define PDKIM_OK 0
#define PDKIM_FAIL -1
#define PDKIM_ERR_RSA_PRIVKEY -101
#define PDKIM_ERR_RSA_SIGNING -102
#define PDKIM_ERR_LONG_LINE -103
#define PDKIM_ERR_BUFFER_TOO_SMALL -104
#define PDKIM_ERR_EXCESS_SIGS -105
#define PDKIM_SIGN_PRIVKEY_WRAP -106
#define PDKIM_SIGN_PRIVKEY_B64D -107
/* -------------------------------------------------------------------------- */
/* Main/Extended verification status */
#define PDKIM_VERIFY_NONE 0
#define PDKIM_VERIFY_INVALID 1
#define PDKIM_VERIFY_FAIL 2
#define PDKIM_VERIFY_PASS 3
#define PDKIM_VERIFY_POLICY BIT(31)
#define PDKIM_VERIFY_FAIL_BODY 1
#define PDKIM_VERIFY_FAIL_MESSAGE 2
#define PDKIM_VERIFY_FAIL_SIG_ALGO_MISMATCH 3
#define PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE 4
#define PDKIM_VERIFY_INVALID_BUFFER_SIZE 5
#define PDKIM_VERIFY_INVALID_PUBKEY_DNSRECORD 6
#define PDKIM_VERIFY_INVALID_PUBKEY_IMPORT 7
#define PDKIM_VERIFY_INVALID_PUBKEY_KEYSIZE 8
#define PDKIM_VERIFY_INVALID_SIGNATURE_ERROR 9
#define PDKIM_VERIFY_INVALID_DKIM_VERSION 10
/* -------------------------------------------------------------------------- */
/* Some parameter values */
#define PDKIM_QUERYMETHOD_DNS_TXT 0
#define PDKIM_CANON_SIMPLE 0
#define PDKIM_CANON_RELAXED 1
/* -------------------------------------------------------------------------- */
/* Some required forward declarations, please ignore */
typedef struct pdkim_stringlist pdkim_stringlist;
typedef struct pdkim_str pdkim_str;
typedef struct sha1_context sha1_context;
typedef struct sha2_context sha2_context;
#define HAVE_SHA1_CONTEXT
#define HAVE_SHA2_CONTEXT
/* -------------------------------------------------------------------------- */
/* Some concessions towards Redmond */
#ifdef WINDOWS
#define snprintf _snprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
#define DLLEXPORT __declspec(dllexport)
#else
#define DLLEXPORT
#endif
/* -------------------------------------------------------------------------- */
/* Public key as (usually) fetched from DNS */
typedef struct pdkim_pubkey {
const uschar * version; /* v= */
const uschar *granularity; /* g= */
const uschar * hashes; /* h= */
const uschar * keytype; /* k= */
const uschar * srvtype; /* s= */
uschar *notes; /* n= */
blob key; /* p= */
int testing; /* t=y */
int no_subdomaining; /* t=s */
} pdkim_pubkey;
/* -------------------------------------------------------------------------- */
/* Body-hash to be calculated */
typedef struct pdkim_bodyhash {
struct pdkim_bodyhash * next;
int hashtype;
int canon_method;
long bodylength;
hctx body_hash_ctx;
unsigned long signed_body_bytes; /* done so far */
int num_buffered_blanklines;
blob bh; /* completed hash */
} pdkim_bodyhash;
/* -------------------------------------------------------------------------- */
/* Signature as it appears in a DKIM-Signature header */
typedef struct pdkim_signature {
struct pdkim_signature * next;
/* Bits stored in a DKIM signature header --------------------------- */
/* (v=) The version, as an integer. Currently, always "1" */
int version;
/* (a=) The signature algorithm. */
int keytype; /* pdkim_keytypes index */
unsigned keybits; /* size of the key */
int hashtype; /* pdkim_hashes index */
/* (c=x/) Header canonicalization method. Either PDKIM_CANON_SIMPLE
or PDKIM_CANON_RELAXED */
int canon_headers;
/* (c=/x) Body canonicalization method. Either PDKIM_CANON_SIMPLE
or PDKIM_CANON_RELAXED */
int canon_body;
/* (q=) Query Method. Currently, only PDKIM_QUERYMETHOD_DNS_TXT
is specified */
int querymethod;
/* (s=) The selector string as given in the signature */
uschar *selector;
/* (d=) The domain as given in the signature */
uschar *domain;
/* (i=) The identity as given in the signature */
uschar *identity;
/* (t=) Timestamp of signature creation */
unsigned long created;
/* (x=) Timestamp of expiry of signature */
unsigned long expires;
/* (l=) Amount of hashed body bytes (after canonicalization). Default
is -1. Note: a value of 0 means that the body is unsigned! */
long bodylength;
/* (h=) Colon-separated list of header names that are included in the
signature */
uschar *headernames;
/* (z=) */
uschar *copiedheaders;
/* (b=) Raw signature data, along with its length in bytes */
blob sighash;
/* (bh=) Raw body hash data, along with its length in bytes */
blob bodyhash;
/* Folded DKIM-Signature: header. Signing only, NULL for verifying.
Ready for insertion into the message. Note: Folded using CRLFTB,
but final line terminator is NOT included. Note2: This buffer is
free()d when you call pdkim_free_ctx(). */
uschar *signature_header;
/* The main verification status. Verification only. One of:
PDKIM_VERIFY_NONE Verification was not attempted. This status
should not appear.
PDKIM_VERIFY_INVALID There was an error while trying to verify
the signature. A more precise description
is available in verify_ext_status.
PDKIM_VERIFY_FAIL Verification failed because either the body
hash did not match, or the signature verification
failed. This means the message was modified.
Check verify_ext_status for the exact reason.
PDKIM_VERIFY_PASS Verification succeeded.
*/
int verify_status;
/* Extended verification status. Verification only. Depending on the value
of verify_status, it can contain:
For verify_status == PDKIM_VERIFY_INVALID:
PDKIM_VERIFY_INVALID_PUBKEY_UNAVAILABLE
Unable to retrieve a public key container.
PDKIM_VERIFY_INVALID_BUFFER_SIZE
Either the DNS name constructed to retrieve the public key record
does not fit into PDKIM_DNS_TXT_MAX_NAMELEN bytes, or the retrieved
record is longer than PDKIM_DNS_TXT_MAX_RECLEN bytes.
PDKIM_VERIFY_INVALID_PUBKEY_PARSING
(Syntax) error while parsing the retrieved public key record.
For verify_status == PDKIM_VERIFY_FAIL:
PDKIM_VERIFY_FAIL_BODY
The calculated body hash does not match the advertised body hash
from the bh= tag of the signature.
PDKIM_VERIFY_FAIL_MESSAGE
RSA verification of the signature (b= tag) failed.
*/
int verify_ext_status;
/* Pointer to a public key record that was used to verify the signature.
See pdkim_pubkey declaration above for more information.
Caution: is NULL if signing or if no record was retrieved. */
pdkim_pubkey *pubkey;
/* Properties below this point are used internally only ------------- */
/* Per-signature helper variables ----------------------------------- */
pdkim_bodyhash *calc_body_hash; /* hash to be / being calculated */
pdkim_stringlist *headers; /* Raw headers included in the sig */
/* Signing specific ------------------------------------------------- */
uschar * privkey; /* Private key */
uschar * sign_headers; /* To-be-signed header names */
uschar * rawsig_no_b_val; /* Original signature header w/o b= tag value. */
} pdkim_signature;
/* -------------------------------------------------------------------------- */
/* Context to keep state between all operations. */
typedef struct pdkim_ctx {
#define PDKIM_MODE_SIGN BIT(0) /* if unset, mode==verify */
#define PDKIM_DOT_TERM BIT(1) /* dot termination and unstuffing */
#define PDKIM_SEEN_CR BIT(2)
#define PDKIM_SEEN_LF BIT(3)
#define PDKIM_PAST_HDRS BIT(4)
#define PDKIM_SEEN_EOD BIT(5)
unsigned flags;
/* One (signing) or several chained (verification) signatures */
pdkim_signature *sig;
/* One (signing) or several chained (verification) bodyhashes */
pdkim_bodyhash *bodyhash;
/* Callback for dns/txt query method (verification only) */
uschar * (*dns_txt_callback)(const uschar *);
/* Coder's little helpers */
gstring *cur_header;
uschar *linebuf;
int linebuf_offset;
int num_headers;
pdkim_stringlist *headers; /* Raw headers for verification */
} pdkim_ctx;
/******************************************************************************/
typedef struct {
const uschar * dkim_hashname;
hashmethod exim_hashmethod;
} pdkim_hashtype;
extern const pdkim_hashtype pdkim_hashes[];
/******************************************************************************/
/* -------------------------------------------------------------------------- */
/* API functions. Please see the sample code in sample/test_sign.c and
sample/test_verify.c for documentation.
*/
#ifdef __cplusplus
extern "C" {
#endif
void pdkim_init (void);
void pdkim_init_context (pdkim_ctx *, BOOL, uschar * (*)(const uschar *));
DLLEXPORT
pdkim_signature *pdkim_init_sign (pdkim_ctx *,
uschar *, uschar *, uschar *, uschar *,
const uschar **);
DLLEXPORT
pdkim_ctx *pdkim_init_verify (uschar * (*)(const uschar *), BOOL);
DLLEXPORT
void pdkim_set_optional (pdkim_signature *, char *, char *,int, int,
long,
unsigned long,
unsigned long);
int pdkim_hashname_to_hashtype(const uschar *, unsigned);
void pdkim_cstring_to_canons(const uschar *, unsigned, int *, int *);
pdkim_bodyhash *pdkim_set_bodyhash(pdkim_ctx *, int, int, long);
pdkim_bodyhash *pdkim_set_sig_bodyhash(pdkim_ctx *, pdkim_signature *);
DLLEXPORT
int pdkim_feed (pdkim_ctx *, uschar *, int);
DLLEXPORT
int pdkim_feed_finish (pdkim_ctx *, pdkim_signature **, const uschar **);
DLLEXPORT
void pdkim_free_ctx (pdkim_ctx *);
const uschar * pdkim_errstr(int);
extern uschar * pdkim_encode_base64(blob *);
extern void pdkim_decode_base64(const uschar *, blob *);
extern void pdkim_hexprint(const uschar *, int);
extern void pdkim_quoteprint(const uschar *, int);
extern pdkim_pubkey * pdkim_parse_pubkey_record(const uschar *);
extern uschar * pdkim_relax_header_n(const uschar *, int, BOOL);
extern uschar * pdkim_relax_header(const uschar *, BOOL);
extern uschar * dkim_sig_to_a_tag(const pdkim_signature *);
#ifdef __cplusplus
}
#endif
#endif
|