1 /*
2 ** Copyright (c) 2010-2012, 2015, The Trusted Domain Project.
3 ** All rights reserved.
4 */
5
6 #include "build-config.h"
7
8 /* system includes */
9 #include <sys/types.h>
10 #include <sys/param.h>
11 #include <netinet/in.h>
12 #include <arpa/nameser.h>
13 #include <resolv.h>
14 #include <assert.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <ctype.h>
18 #include <netdb.h>
19
20 /* libopendkim includes */
21 #include "dkim.h"
22 #include "dkim-internal.h"
23 #include "dkim-types.h"
24 #include "dkim-tables.h"
25 #include "util.h"
26
27 #ifdef USE_GNUTLS
28 /* GnuTLS includes */
29 # include <gnutls/gnutls.h>
30 # include <gnutls/crypto.h>
31 # ifndef SHA_DIGEST_LENGTH
32 # define SHA_DIGEST_LENGTH 20
33 # endif /* ! SHA_DIGEST_LENGTH */
34 # ifndef SHA256_DIGEST_LENGTH
35 # define SHA256_DIGEST_LENGTH 32
36 # endif /* ! SHA256_DIGEST_LENGTH */
37 #else /* USE_GNUTLS */
38 /* openssl includes */
39 # include <openssl/sha.h>
40 #endif /* USE_GNUTLS */
41
42 /* prototypes */
43 extern void dkim_error __P((DKIM *, const char *, ...));
44
45 /* local definitions needed for DNS queries */
46 #define MAXPACKET 8192
47 #if defined(__RES) && (__RES >= 19940415)
48 # define RES_UNC_T char *
49 #else /* __RES && __RES >= 19940415 */
50 # define RES_UNC_T unsigned char *
51 #endif /* __RES && __RES >= 19940415 */
52 #ifndef T_RRSIG
53 # define T_RRSIG 46
54 #endif /* ! T_RRSIG */
55 #ifndef MAX
56 # define MAX(x,y) ((x) > (y) ? (x) : (y))
57 #endif /* ! MAX */
58
59 #define DKIM_ATPS_QUERYLENGTH 64
60 #define DKIM_ATPS_VALID "v=ATPS1"
61
62 #ifdef SHA256_DIGEST_LENGTH
63 # define MAXDIGEST MAX(SHA_DIGEST_LENGTH, SHA256_DIGEST_LENGTH)
64 #else /* SHA256_DIGEST_LENGTH */
65 # define MAXDIGEST SHA_DIGEST_LENGTH
66 #endif /* SHA256_DIGEST_LENGTH */
67
68 /*
69 ** DKIM_ATPS_CHECK -- check for Authorized Third Party Signing
70 **
71 ** Parameters:
72 ** dkim -- DKIM message handle
73 ** sig -- signature information handle
74 ** timeout -- timeout (can be NULL)
75 ** res -- ATPS result code
76 **
77 ** Return value:
78 ** A DKIM_STAT_* constant.
79 */
80
81 DKIM_STAT
dkim_atps_check(DKIM * dkim,DKIM_SIGINFO * sig,struct timeval * timeout,dkim_atps_t * res)82 dkim_atps_check(DKIM *dkim, DKIM_SIGINFO *sig, struct timeval *timeout,
83 dkim_atps_t *res)
84 {
85 #ifdef _FFR_ATPS
86 int status;
87 int qdcount;
88 int ancount;
89 int class;
90 int type;
91 int error;
92 int n;
93 int hash = DKIM_HASHTYPE_UNKNOWN;
94 int diglen;
95 #ifdef USE_GNUTLS
96 int ghash;
97 #endif /* USE_GNUTLS */
98 unsigned int c;
99 #ifdef QUERY_CACHE
100 uint32_t ttl;
101 #endif /* QUERY_CACHE */
102 size_t buflen;
103 size_t anslen;
104 DKIM_LIB *lib;
105 u_char *fdomain;
106 u_char *sdomain;
107 u_char *adomain;
108 u_char *txtfound = NULL;
109 u_char *ahash = NULL;
110 void *qh;
111 u_char *p;
112 u_char *cp;
113 u_char *eom;
114 #ifdef USE_GNUTLS
115 gnutls_hash_hd_t ctx;
116 #else /* USE_GNUTLS */
117 SHA_CTX ctx;
118 # ifdef HAVE_SHA256
119 SHA256_CTX ctx2;
120 # endif /* HAVE_SHA256 */
121 #endif /* USE_GNUTLS */
122 struct timeval to;
123 HEADER hdr;
124 u_char ansbuf[MAXPACKET];
125 u_char digest[MAXDIGEST];
126 u_char b32[DKIM_ATPS_QUERYLENGTH + 1];
127 u_char query[DKIM_MAXHOSTNAMELEN + 1];
128 u_char buf[BUFRSZ + 1];
129 #endif /* _FFR_ATPS */
130
131 assert(dkim != NULL);
132 assert(sig != NULL);
133 assert(res != NULL);
134
135 #ifdef _FFR_ATPS
136 lib = dkim->dkim_libhandle;
137 sdomain = dkim_sig_getdomain(sig);
138 fdomain = dkim_getdomain(dkim);
139 adomain = dkim_sig_gettagvalue(sig, FALSE, "atps");
140 ahash = dkim_sig_gettagvalue(sig, FALSE, "atpsh");
141
142 if (sdomain == NULL || fdomain == NULL || adomain == NULL ||
143 ahash == NULL || strcasecmp(adomain, fdomain) != 0)
144 return DKIM_STAT_INVALID;
145
146 /* confirm it requested a hash we know how to do */
147 if (strcasecmp(ahash, "none") != 0)
148 {
149 hash = dkim_name_to_code(hashes, ahash);
150 if (hash == -1)
151 return DKIM_STAT_INVALID;
152 }
153
154 switch (hash)
155 {
156 case DKIM_HASHTYPE_SHA1:
157 diglen = SHA_DIGEST_LENGTH;
158 break;
159
160 # ifdef HAVE_SHA256
161 case DKIM_HASHTYPE_SHA256:
162 diglen = SHA256_DIGEST_LENGTH;
163 break;
164 # endif /* HAVE_SHA256 */
165
166 case DKIM_HASHTYPE_UNKNOWN:
167 break;
168
169 default:
170 assert(0);
171 break;
172 }
173
174 if (hash != DKIM_HASHTYPE_UNKNOWN)
175 {
176 /* construct a hash of the signing domain */
177 # ifdef USE_GNUTLS
178 switch (hash)
179 {
180 case DKIM_HASHTYPE_SHA1:
181 ghash = GNUTLS_DIG_SHA1;
182 break;
183
184 case DKIM_HASHTYPE_SHA256:
185 ghash = GNUTLS_DIG_SHA256;
186 break;
187
188 default:
189 assert(0);
190 break;
191 }
192
193 if (gnutls_hash_init(&ctx, ghash) != 0 ||
194 gnutls_hash(ctx, sdomain, strlen(sdomain)) != 0)
195 return DKIM_STAT_INTERNAL;
196 gnutls_hash_deinit(ctx, digest);
197 # else /* USE_GNUTLS */
198 switch (hash)
199 {
200 case DKIM_HASHTYPE_SHA1:
201 SHA1_Init(&ctx);
202 SHA1_Update(&ctx, sdomain, strlen(sdomain));
203 SHA1_Final(digest, &ctx);
204 break;
205
206 # ifdef HAVE_SHA256
207 case DKIM_HASHTYPE_SHA256:
208 SHA256_Init(&ctx2);
209 SHA256_Update(&ctx2, sdomain, strlen(sdomain));
210 SHA256_Final(digest, &ctx2);
211 break;
212 # endif /* HAVE_SHA256 */
213
214 default:
215 assert(0);
216 break;
217 }
218 # endif /* USE_GNUTLS */
219
220 /* base32-encode the hash */
221 memset(b32, '\0', sizeof b32);
222 buflen = sizeof b32;
223 if (dkim_base32_encode(b32, &buflen,
224 digest,
225 diglen) >= DKIM_ATPS_QUERYLENGTH)
226 return DKIM_STAT_INTERNAL;
227
228 /* form the query */
229 snprintf(query, sizeof query, "%s._atps.%s", b32, fdomain);
230 }
231 else
232 {
233 /* form the query */
234 snprintf(query, sizeof query, "%s._atps.%s", sdomain, fdomain);
235 }
236
237 /* XXX -- add QUERY_CACHE support here */
238
239 if (lib->dkiml_dns_service == NULL &&
240 lib->dkiml_dns_init != NULL &&
241 lib->dkiml_dns_init(&lib->dkiml_dns_service) != 0)
242 {
243 *res = DKIM_ATPS_UNKNOWN;
244 return DKIM_STAT_CANTVRFY;
245 }
246
247 /* send it */
248 anslen = sizeof ansbuf;
249 status = lib->dkiml_dns_start(lib->dkiml_dns_service, T_TXT,
250 query, ansbuf, anslen, &qh);
251 if (status != DKIM_DNS_SUCCESS)
252 {
253 *res = DKIM_ATPS_UNKNOWN;
254 return DKIM_STAT_CANTVRFY;
255 }
256
257 /* wait for the reply */
258 to.tv_sec = dkim->dkim_timeout;
259 to.tv_usec = 0;
260 status = lib->dkiml_dns_waitreply(lib->dkiml_dns_service, qh,
261 timeout == NULL ? &to : timeout,
262 &anslen, &error, NULL);
263 (void) lib->dkiml_dns_cancel(lib->dkiml_dns_service, qh);
264
265 if (status != DKIM_DNS_SUCCESS)
266 {
267 *res = DKIM_ATPS_UNKNOWN;
268 return DKIM_STAT_CANTVRFY;
269 }
270
271 /* decode the reply */
272 memcpy(&hdr, ansbuf, sizeof hdr);
273 cp = (u_char *) &ansbuf + HFIXEDSZ;
274 eom = (u_char *) &ansbuf + anslen;
275
276 /* skip over the name at the front of the answer */
277 for (qdcount = ntohs((unsigned short) hdr.qdcount);
278 qdcount > 0;
279 qdcount--)
280 {
281 /* copy it first */
282 (void) dn_expand((unsigned char *) &ansbuf, eom, cp,
283 (char *) query, sizeof query);
284
285 if ((n = dn_skipname(cp, eom)) < 0)
286 {
287 dkim_error(dkim, "'%s' reply corrupt", query);
288 *res = DKIM_ATPS_UNKNOWN;
289 return DKIM_STAT_CANTVRFY;
290 }
291 cp += n;
292
293 /* extract the type and class */
294 if (cp + INT16SZ + INT16SZ > eom)
295 {
296 dkim_error(dkim, "'%s' reply corrupt", query);
297 *res = DKIM_ATPS_UNKNOWN;
298 return DKIM_STAT_CANTVRFY;
299 }
300 GETSHORT(type, cp);
301 GETSHORT(class, cp);
302 }
303
304 if (type != T_TXT || class != C_IN)
305 {
306 dkim_error(dkim, "'%s' unexpected reply type/class", query);
307 *res = DKIM_ATPS_UNKNOWN;
308 return DKIM_STAT_CANTVRFY;
309 }
310
311 if (hdr.rcode == NXDOMAIN)
312 {
313 *res = DKIM_ATPS_NOTFOUND;
314 return DKIM_STAT_OK;
315 }
316
317 /* if truncated, we can't do it */
318 if (dkim_check_dns_reply(ansbuf, anslen, C_IN, T_TXT) == 1)
319 {
320 dkim_error(dkim, "'%s' reply truncated", query);
321 *res = DKIM_ATPS_UNKNOWN;
322 return DKIM_STAT_CANTVRFY;
323 }
324
325 /* get the answer count */
326 ancount = ntohs((unsigned short) hdr.ancount);
327 if (ancount == 0)
328 {
329 *res = DKIM_ATPS_NOTFOUND;
330 return DKIM_STAT_OK;
331 }
332
333 /*
334 ** Extract the data from the first TXT answer.
335 */
336
337 while (--ancount >= 0 && cp < eom)
338 {
339 /* grab the label, even though we know what we asked... */
340 if ((n = dn_expand((unsigned char *) &ansbuf, eom, cp,
341 (RES_UNC_T) query, sizeof query)) < 0)
342 {
343 dkim_error(dkim, "'%s' reply corrupt", query);
344 *res = DKIM_ATPS_UNKNOWN;
345 return DKIM_STAT_CANTVRFY;
346 }
347 /* ...and move past it */
348 cp += n;
349
350 /* extract the type and class */
351 if (cp + INT16SZ + INT16SZ > eom)
352 {
353 dkim_error(dkim, "'%s' reply corrupt", query);
354 *res = DKIM_ATPS_UNKNOWN;
355 return DKIM_STAT_CANTVRFY;
356 }
357
358 GETSHORT(type, cp);
359 GETSHORT(class, cp);
360
361 #ifdef QUERY_CACHE
362 /* get the TTL */
363 GETLONG(ttl, cp);
364 #else /* QUERY_CACHE */
365 /* skip the TTL */
366 cp += INT32SZ;
367 #endif /* QUERY_CACHE */
368
369 /* skip CNAME if found; assume it was resolved */
370 if (type == T_CNAME)
371 {
372 char chost[DKIM_MAXHOSTNAMELEN + 1];
373
374 n = dn_expand((u_char *) &ansbuf, eom, cp,
375 chost, DKIM_MAXHOSTNAMELEN);
376 cp += n;
377 continue;
378 }
379 else if (type == T_RRSIG)
380 {
381 /* get payload length */
382 if (cp + INT16SZ > eom)
383 {
384 dkim_error(dkim, "'%s' reply corrupt", query);
385 *res = DKIM_ATPS_UNKNOWN;
386 return DKIM_STAT_CANTVRFY;
387 }
388 GETSHORT(n, cp);
389
390 cp += n;
391
392 continue;
393 }
394 else if (type != T_TXT)
395 {
396 dkim_error(dkim, "'%s' reply was unexpected type %d",
397 query, type);
398 *res = DKIM_ATPS_UNKNOWN;
399 return DKIM_STAT_CANTVRFY;
400 }
401
402 if (txtfound != NULL)
403 {
404 dkim_error(dkim, "multiple DNS replies for '%s'",
405 query);
406 *res = DKIM_ATPS_UNKNOWN;
407 return DKIM_STAT_MULTIDNSREPLY;
408 }
409
410 /* remember where this one started */
411 txtfound = cp;
412
413 /* get payload length */
414 if (cp + INT16SZ > eom)
415 {
416 dkim_error(dkim, "'%s' reply corrupt", query);
417 *res = DKIM_ATPS_UNKNOWN;
418 return DKIM_STAT_CANTVRFY;
419 }
420 GETSHORT(n, cp);
421
422 /* move forward for now */
423 cp += n;
424 }
425
426 /* if ancount went below 0, there were no good records */
427 if (txtfound == NULL)
428 {
429 dkim_error(dkim, "'%s' reply was unresolved CNAME", query);
430 *res = DKIM_ATPS_NOTFOUND;
431 return DKIM_STAT_OK;
432 }
433
434 /* come back to the one we found */
435 cp = txtfound;
436
437 /* get payload length */
438 if (cp + INT16SZ > eom)
439 {
440 dkim_error(dkim, "'%s' reply corrupt", query);
441 *res = DKIM_ATPS_UNKNOWN;
442 return DKIM_STAT_CANTVRFY;
443 }
444 GETSHORT(n, cp);
445
446 if (cp + n > eom)
447 {
448 dkim_error(dkim, "'%s' reply corrupt", query);
449 *res = DKIM_ATPS_UNKNOWN;
450 return DKIM_STAT_CANTVRFY;
451 }
452
453 /* extract the payload */
454 memset(buf, '\0', buflen);
455 p = buf;
456 eom = buf + buflen - 1;
457 while (n > 0 && p < eom)
458 {
459 c = *cp++;
460 n--;
461 while (c > 0 && p < eom)
462 {
463 *p++ = *cp++;
464 c--;
465 n--;
466 }
467 }
468
469 if (strcmp(buf, DKIM_ATPS_VALID) == 0)
470 *res = DKIM_ATPS_FOUND;
471 else
472 *res = DKIM_ATPS_NOTFOUND;
473
474 return DKIM_STAT_OK;
475
476 #else /* ! _FFR_ATPS */
477
478 return DKIM_STAT_NOTIMPLEMENT;
479
480 #endif /* ! _FFR_ATPS */
481 }
482