aboutsummaryrefslogtreecommitdiffstats
path: root/src/libserver/dkim.h
blob: 57f761895d6782393f40c269c638aaaef7c62a51 (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
/*-
 * Copyright 2016 Vsevolod Stakhov
 *
 * 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
 *
 * 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.
 */
#ifndef DKIM_H_
#define DKIM_H_

#include "config.h"
#include "contrib/libev/ev.h"
#include "dns.h"
#include "ref.h"

/* Main types and definitions */

#define RSPAMD_DKIM_SIGNHEADER     "DKIM-Signature"
#define RSPAMD_DKIM_ARC_SIGNHEADER     "ARC-Message-Signature"
#define RSPAMD_DKIM_ARC_AUTHHEADER     "ARC-Authentication-Results"
#define RSPAMD_DKIM_ARC_SEALHEADER     "ARC-Seal"
/* DKIM signature header */


/* Errors (from OpenDKIM) */

#define DKIM_SIGERROR_UNKNOWN       (-1)    /* unknown error */
#define DKIM_SIGERROR_OK        0   /* no error */
#define DKIM_SIGERROR_VERSION       1   /* unsupported version */
#define DKIM_SIGERROR_DOMAIN        2   /* invalid domain (d=/i=) */
#define DKIM_SIGERROR_EXPIRED       3   /* signature expired */
#define DKIM_SIGERROR_FUTURE        4   /* signature in the future */
#define DKIM_SIGERROR_TIMESTAMPS    5   /* x= < t= */
#define DKIM_SIGERROR_NOREC         6   /* No record */
#define DKIM_SIGERROR_INVALID_HC    7   /* c= invalid (header) */
#define DKIM_SIGERROR_INVALID_BC    8   /* c= invalid (body) */
#define DKIM_SIGERROR_MISSING_A     9   /* a= missing */
#define DKIM_SIGERROR_INVALID_A     10  /* a= invalid */
#define DKIM_SIGERROR_MISSING_H     11  /* h= missing */
#define DKIM_SIGERROR_INVALID_L     12  /* l= invalid */
#define DKIM_SIGERROR_INVALID_Q     13  /* q= invalid */
#define DKIM_SIGERROR_INVALID_QO    14  /* q= option invalid */
#define DKIM_SIGERROR_MISSING_D     15  /* d= missing */
#define DKIM_SIGERROR_EMPTY_D       16  /* d= empty */
#define DKIM_SIGERROR_MISSING_S     17  /* s= missing */
#define DKIM_SIGERROR_EMPTY_S       18  /* s= empty */
#define DKIM_SIGERROR_MISSING_B     19  /* b= missing */
#define DKIM_SIGERROR_EMPTY_B       20  /* b= empty */
#define DKIM_SIGERROR_CORRUPT_B     21  /* b= corrupt */
#define DKIM_SIGERROR_NOKEY     22  /* no key found in DNS */
#define DKIM_SIGERROR_DNSSYNTAX     23  /* DNS reply corrupt */
#define DKIM_SIGERROR_KEYFAIL       24  /* DNS query failed */
#define DKIM_SIGERROR_MISSING_BH    25  /* bh= missing */
#define DKIM_SIGERROR_EMPTY_BH      26  /* bh= empty */
#define DKIM_SIGERROR_CORRUPT_BH    27  /* bh= corrupt */
#define DKIM_SIGERROR_BADSIG        28  /* signature mismatch */
#define DKIM_SIGERROR_SUBDOMAIN     29  /* unauthorized subdomain */
#define DKIM_SIGERROR_MULTIREPLY    30  /* multiple records returned */
#define DKIM_SIGERROR_EMPTY_H       31  /* h= empty */
#define DKIM_SIGERROR_INVALID_H     32  /* h= missing req'd entries */
#define DKIM_SIGERROR_TOOLARGE_L    33  /* l= value exceeds body size */
#define DKIM_SIGERROR_MBSFAILED     34  /* "must be signed" failure */
#define DKIM_SIGERROR_KEYVERSION    35  /* unknown key version */
#define DKIM_SIGERROR_KEYUNKNOWNHASH    36  /* unknown key hash */
#define DKIM_SIGERROR_KEYHASHMISMATCH   37  /* sig-key hash mismatch */
#define DKIM_SIGERROR_NOTEMAILKEY   38  /* not an e-mail key */
#define DKIM_SIGERROR_UNUSED2       39  /* OBSOLETE */
#define DKIM_SIGERROR_KEYTYPEMISSING    40  /* key type missing */
#define DKIM_SIGERROR_KEYTYPEUNKNOWN    41  /* key type unknown */
#define DKIM_SIGERROR_KEYREVOKED    42  /* key revoked */
#define DKIM_SIGERROR_KEYDECODE     43  /* key couldn't be decoded */
#define DKIM_SIGERROR_MISSING_V     44  /* v= tag missing */
#define DKIM_SIGERROR_EMPTY_V       45  /* v= tag empty */

/* Check results */
enum rspamd_dkim_check_rcode {
	DKIM_CONTINUE = 0,
	DKIM_REJECT,
	DKIM_TRYAGAIN,
	DKIM_NOTFOUND,
	DKIM_RECORD_ERROR,
	DKIM_PERM_ERROR,
};

#define DKIM_CANON_SIMPLE   0   /* as specified in DKIM spec */
#define DKIM_CANON_RELAXED  1   /* as specified in DKIM spec */

struct rspamd_dkim_context_s;
typedef struct rspamd_dkim_context_s rspamd_dkim_context_t;

struct rspamd_dkim_sign_context_s;
typedef struct rspamd_dkim_sign_context_s rspamd_dkim_sign_context_t;

struct rspamd_dkim_key_s;
typedef struct rspamd_dkim_key_s rspamd_dkim_key_t;
typedef struct rspamd_dkim_key_s rspamd_dkim_sign_key_t;

struct rspamd_task;

enum rspamd_dkim_key_format {
	RSPAMD_DKIM_KEY_FILE = 0,
	RSPAMD_DKIM_KEY_PEM,
	RSPAMD_DKIM_KEY_BASE64,
	RSPAMD_DKIM_KEY_RAW,
	RSPAMD_DKIM_KEY_UNKNOWN
};

enum rspamd_dkim_type {
	RSPAMD_DKIM_NORMAL,
	RSPAMD_DKIM_ARC_SIG,
	RSPAMD_DKIM_ARC_SEAL
};

/* Signature methods */
enum rspamd_sign_type {
	DKIM_SIGN_UNKNOWN = -2,
	DKIM_SIGN_RSASHA1 = 0,
	DKIM_SIGN_RSASHA256,
	DKIM_SIGN_RSASHA512,
	DKIM_SIGN_ECDSASHA256,
	DKIM_SIGN_ECDSASHA512,
	DKIM_SIGN_EDDSASHA256,
};

enum rspamd_dkim_key_type {
	RSPAMD_DKIM_KEY_RSA = 0,
	RSPAMD_DKIM_KEY_ECDSA,
	RSPAMD_DKIM_KEY_EDDSA
};

struct rspamd_dkim_check_result {
	enum rspamd_dkim_check_rcode rcode;
	rspamd_dkim_context_t *ctx;
	/* Processed parts */
	const gchar *selector;
	const gchar *domain;
	const gchar *short_b;
	const gchar *fail_reason;
};


/* Err MUST be freed if it is not NULL, key is allocated by slice allocator */
typedef void (*dkim_key_handler_f)(rspamd_dkim_key_t *key, gsize keylen,
	rspamd_dkim_context_t *ctx, gpointer ud, GError *err);

/**
 * Create new dkim context from signature
 * @param sig message's signature
 * @param pool pool to allocate memory from
 * @param time_jitter jitter in seconds to allow time diff while checking
 * @param err pointer to error object
 * @return new context or NULL
 */
rspamd_dkim_context_t * rspamd_create_dkim_context (const gchar *sig,
		rspamd_mempool_t *pool,
		guint time_jitter,
		enum rspamd_dkim_type type,
		GError **err);

/**
 * Create new dkim context for making a signature
 * @param task
 * @param priv_key
 * @param err
 * @return
 */
rspamd_dkim_sign_context_t * rspamd_create_dkim_sign_context (struct rspamd_task *task,
		rspamd_dkim_sign_key_t *priv_key,
		gint headers_canon,
		gint body_canon,
		const gchar *dkim_headers,
		enum rspamd_dkim_type type,
		GError **err);

/**
 * Load dkim key
 * @param path
 * @param err
 * @return
 */
rspamd_dkim_sign_key_t* rspamd_dkim_sign_key_load (const gchar *what, gsize len,
		enum rspamd_dkim_key_format type,
		GError **err);

/**
 * Invalidate modified sign key
 * @param key
 * @return
*/
gboolean rspamd_dkim_sign_key_maybe_invalidate (rspamd_dkim_sign_key_t *key,
		time_t mtime);

/**
 * Make DNS request for specified context and obtain and parse key
 * @param ctx dkim context from signature
 * @param resolver dns resolver object
 * @param s async session to make request
 * @return
 */
gboolean rspamd_get_dkim_key (rspamd_dkim_context_t *ctx,
	struct rspamd_task *task,
	dkim_key_handler_f handler,
	gpointer ud);

/**
 * Check task for dkim context using dkim key
 * @param ctx dkim verify context
 * @param key dkim key (from cache or from dns request)
 * @param task task to check
 * @return
 */
struct rspamd_dkim_check_result * rspamd_dkim_check (rspamd_dkim_context_t *ctx,
													 rspamd_dkim_key_t *key,
													 struct rspamd_task *task);

struct rspamd_dkim_check_result *
rspamd_dkim_create_result (rspamd_dkim_context_t *ctx,
						   enum rspamd_dkim_check_rcode rcode,
						   struct rspamd_task *task);

GString *rspamd_dkim_sign (struct rspamd_task *task,
						   const gchar *selector,
						   const gchar *domain,
						   time_t expire,
						   gsize len,
						   guint idx,
						   const gchar *arc_cv,
						   rspamd_dkim_sign_context_t *ctx);

rspamd_dkim_key_t * rspamd_dkim_key_ref (rspamd_dkim_key_t *k);
void rspamd_dkim_key_unref (rspamd_dkim_key_t *k);
rspamd_dkim_sign_key_t * rspamd_dkim_sign_key_ref (rspamd_dkim_sign_key_t *k);
void rspamd_dkim_sign_key_unref (rspamd_dkim_sign_key_t *k);
const gchar* rspamd_dkim_get_domain (rspamd_dkim_context_t *ctx);
const gchar* rspamd_dkim_get_selector (rspamd_dkim_context_t *ctx);
const gchar* rspamd_dkim_get_dns_key (rspamd_dkim_context_t *ctx);
guint rspamd_dkim_key_get_ttl (rspamd_dkim_key_t *k);

/**
 * Create DKIM public key from a raw data
 * @param keydata
 * @param keylen
 * @param type
 * @param err
 * @return
 */
rspamd_dkim_key_t * rspamd_dkim_make_key (const gchar *keydata, guint keylen,
										  enum rspamd_dkim_key_type type,
										  GError **err);

/**
 * Parse DKIM public key from a TXT record
 * @param txt
 * @param keylen
 * @param err
 * @return
 */
rspamd_dkim_key_t * rspamd_dkim_parse_key (const gchar *txt, gsize *keylen,
										   GError **err);

/**
 * Canonocalise header using relaxed algorithm
 * @param hname
 * @param hvalue
 * @param out
 * @param outlen
 * @return
 */
goffset rspamd_dkim_canonize_header_relaxed_str (const gchar *hname,
		const gchar *hvalue,
		gchar *out,
		gsize outlen);

/**
 * Checks public and private keys for match
 * @param pk
 * @param sk
 * @param err
 * @return
 */
gboolean rspamd_dkim_match_keys (rspamd_dkim_key_t *pk,
								 rspamd_dkim_sign_key_t *sk,
								 GError **err);

/**
 * Free DKIM key
 * @param key
 */
void rspamd_dkim_key_free (rspamd_dkim_key_t *key);

#endif /* DKIM_H_ */