summaryrefslogtreecommitdiffstats
path: root/src/pdkim/pdkim.h
blob: f6ff78251dd8801cd5b2c9825799d86a0435059b (plain)
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