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