1/*
2 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (c) 1999 by Internet Software Consortium, Inc.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#ifndef lint
19static const char rcsid[] = "$Id: ns_verify.c,v 1.5 2006/03/09 23:57:56 marka Exp $";
20#endif
21
22/* Import. */
23
24#include "port_before.h"
25#include "fd_setsize.h"
26
27#include <sys/types.h>
28#include <sys/param.h>
29
30#include <netinet/in.h>
31#include <arpa/nameser.h>
32#include <arpa/inet.h>
33
34#include <errno.h>
35#include <netdb.h>
36#include <resolv.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40#include <time.h>
41#include <unistd.h>
42
43#include <isc/dst.h>
44
45#include "port_after.h"
46
47/* Private. */
48
49#define BOUNDS_CHECK(ptr, count) \
50	do { \
51		if ((ptr) + (count) > eom) { \
52			return (NS_TSIG_ERROR_FORMERR); \
53		} \
54	} while (0)
55
56/* Public. */
57
58u_char *
59ns_find_tsig(u_char *msg, u_char *eom) {
60	HEADER *hp = (HEADER *)msg;
61	int n, type;
62	u_char *cp = msg, *start;
63
64	if (msg == NULL || eom == NULL || msg > eom)
65		return (NULL);
66
67	if (cp + HFIXEDSZ >= eom)
68		return (NULL);
69
70	if (hp->arcount == 0)
71		return (NULL);
72
73	cp += HFIXEDSZ;
74
75	n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount));
76	if (n < 0)
77		return (NULL);
78	cp += n;
79
80	n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount));
81	if (n < 0)
82		return (NULL);
83	cp += n;
84
85	n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount));
86	if (n < 0)
87		return (NULL);
88	cp += n;
89
90	n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1);
91	if (n < 0)
92		return (NULL);
93	cp += n;
94
95	start = cp;
96	n = dn_skipname(cp, eom);
97	if (n < 0)
98		return (NULL);
99	cp += n;
100	if (cp + INT16SZ >= eom)
101		return (NULL);
102
103	GETSHORT(type, cp);
104	if (type != ns_t_tsig)
105		return (NULL);
106	return (start);
107}
108
109/* ns_verify
110 *
111 * Parameters:
112 *\li	statp		res stuff
113 *\li	msg		received message
114 *\li	msglen		length of message
115 *\li	key		tsig key used for verifying.
116 *\li	querysig	(response), the signature in the query
117 *\li	querysiglen	(response), the length of the signature in the query
118 *\li	sig		(query), a buffer to hold the signature
119 *\li	siglen		(query), input - length of signature buffer
120 *				 output - length of signature
121 *
122 * Errors:
123 *\li	- bad input (-1)
124 *\li	- invalid dns message (NS_TSIG_ERROR_FORMERR)
125 *\li	- TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
126 *\li	- key doesn't match (-ns_r_badkey)
127 *\li	- TSIG verification fails with BADKEY (-ns_r_badkey)
128 *\li	- TSIG verification fails with BADSIG (-ns_r_badsig)
129 *\li	- TSIG verification fails with BADTIME (-ns_r_badtime)
130 *\li	- TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
131 *\li	- TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
132 *\li	- TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
133 */
134int
135ns_verify(u_char *msg, int *msglen, void *k,
136	  const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
137	  time_t *timesigned, int nostrip)
138{
139	HEADER *hp = (HEADER *)msg;
140	DST_KEY *key = (DST_KEY *)k;
141	u_char *cp = msg, *eom;
142	char name[MAXDNAME], alg[MAXDNAME];
143	u_char *recstart, *rdatastart;
144	u_char *sigstart, *otherstart;
145	int n;
146	int error;
147	u_int16_t type, length;
148	u_int16_t fudge, sigfieldlen, otherfieldlen;
149
150	dst_init();
151	if (msg == NULL || msglen == NULL || *msglen < 0)
152		return (-1);
153
154	eom = msg + *msglen;
155
156	recstart = ns_find_tsig(msg, eom);
157	if (recstart == NULL)
158		return (NS_TSIG_ERROR_NO_TSIG);
159
160	cp = recstart;
161
162	/* Read the key name. */
163	n = dn_expand(msg, eom, cp, name, MAXDNAME);
164	if (n < 0)
165		return (NS_TSIG_ERROR_FORMERR);
166	cp += n;
167
168	/* Read the type. */
169	BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
170	GETSHORT(type, cp);
171	if (type != ns_t_tsig)
172		return (NS_TSIG_ERROR_NO_TSIG);
173
174	/* Skip the class and TTL, save the length. */
175	cp += INT16SZ + INT32SZ;
176	GETSHORT(length, cp);
177	if (eom - cp != length)
178		return (NS_TSIG_ERROR_FORMERR);
179
180	/* Read the algorithm name. */
181	rdatastart = cp;
182	n = dn_expand(msg, eom, cp, alg, MAXDNAME);
183	if (n < 0)
184		return (NS_TSIG_ERROR_FORMERR);
185	if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
186		return (-ns_r_badkey);
187	cp += n;
188
189	/* Read the time signed and fudge. */
190	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
191	cp += INT16SZ;
192	GETLONG((*timesigned), cp);
193	GETSHORT(fudge, cp);
194
195	/* Read the signature. */
196	BOUNDS_CHECK(cp, INT16SZ);
197	GETSHORT(sigfieldlen, cp);
198	BOUNDS_CHECK(cp, sigfieldlen);
199	sigstart = cp;
200	cp += sigfieldlen;
201
202	/* Skip id and read error. */
203	BOUNDS_CHECK(cp, 2*INT16SZ);
204	cp += INT16SZ;
205	GETSHORT(error, cp);
206
207	/* Parse the other data. */
208	BOUNDS_CHECK(cp, INT16SZ);
209	GETSHORT(otherfieldlen, cp);
210	BOUNDS_CHECK(cp, otherfieldlen);
211	otherstart = cp;
212	cp += otherfieldlen;
213
214	if (cp != eom)
215		return (NS_TSIG_ERROR_FORMERR);
216
217	/* Verify that the key used is OK. */
218	if (key != NULL) {
219		if (key->dk_alg != KEY_HMAC_MD5)
220			return (-ns_r_badkey);
221		if (error != ns_r_badsig && error != ns_r_badkey) {
222			if (ns_samename(key->dk_key_name, name) != 1)
223				return (-ns_r_badkey);
224		}
225	}
226
227	hp->arcount = htons(ntohs(hp->arcount) - 1);
228
229	/*
230	 * Do the verification.
231	 */
232
233	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
234		void *ctx;
235		u_char buf[MAXDNAME];
236		u_char buf2[MAXDNAME];
237
238		/* Digest the query signature, if this is a response. */
239		dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
240		if (querysiglen > 0 && querysig != NULL) {
241			u_int16_t len_n = htons(querysiglen);
242			dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
243					(u_char *)&len_n, INT16SZ, NULL, 0);
244			dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
245					querysig, querysiglen, NULL, 0);
246		}
247
248 		/* Digest the message. */
249		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg,
250				NULL, 0);
251
252		/* Digest the key name. */
253		n = ns_name_pton(name, buf2, sizeof(buf2));
254		if (n < 0)
255			return (-1);
256		n = ns_name_ntol(buf2, buf, sizeof(buf));
257		if (n < 0)
258			return (-1);
259		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
260
261		/* Digest the class and TTL. */
262		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
263				recstart + dn_skipname(recstart, eom) + INT16SZ,
264				INT16SZ + INT32SZ, NULL, 0);
265
266		/* Digest the algorithm. */
267		n = ns_name_pton(alg, buf2, sizeof(buf2));
268		if (n < 0)
269			return (-1);
270		n = ns_name_ntol(buf2, buf, sizeof(buf));
271		if (n < 0)
272			return (-1);
273		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
274
275		/* Digest the time signed and fudge. */
276		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
277				rdatastart + dn_skipname(rdatastart, eom),
278				INT16SZ + INT32SZ + INT16SZ, NULL, 0);
279
280		/* Digest the error and other data. */
281		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
282				otherstart - INT16SZ - INT16SZ,
283				otherfieldlen + INT16SZ + INT16SZ, NULL, 0);
284
285		n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
286				    sigstart, sigfieldlen);
287
288		if (n < 0)
289			return (-ns_r_badsig);
290
291		if (sig != NULL && siglen != NULL) {
292			if (*siglen < sigfieldlen)
293				return (NS_TSIG_ERROR_NO_SPACE);
294			memcpy(sig, sigstart, sigfieldlen);
295			*siglen = sigfieldlen;
296		}
297	} else {
298		if (sigfieldlen > 0)
299			return (NS_TSIG_ERROR_FORMERR);
300		if (sig != NULL && siglen != NULL)
301			*siglen = 0;
302	}
303
304	/* Reset the counter, since we still need to check for badtime. */
305	hp->arcount = htons(ntohs(hp->arcount) + 1);
306
307	/* Verify the time. */
308	if (abs((*timesigned) - time(NULL)) > fudge)
309		return (-ns_r_badtime);
310
311	if (nostrip == 0) {
312		*msglen = recstart - msg;
313		hp->arcount = htons(ntohs(hp->arcount) - 1);
314	}
315
316	if (error != NOERROR)
317		return (error);
318
319	return (0);
320}
321
322int
323ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen,
324		   ns_tcp_tsig_state *state)
325{
326	dst_init();
327	if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
328		return (-1);
329	state->counter = -1;
330	state->key = k;
331	if (state->key->dk_alg != KEY_HMAC_MD5)
332		return (-ns_r_badkey);
333	if (querysiglen > (int)sizeof(state->sig))
334		return (-1);
335	memcpy(state->sig, querysig, querysiglen);
336	state->siglen = querysiglen;
337	return (0);
338}
339
340int
341ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state,
342	      int required)
343{
344	HEADER *hp = (HEADER *)msg;
345	u_char *recstart, *sigstart;
346	unsigned int sigfieldlen, otherfieldlen;
347	u_char *cp, *eom, *cp2;
348	char name[MAXDNAME], alg[MAXDNAME];
349	u_char buf[MAXDNAME];
350	int n, type, length, fudge, error;
351	time_t timesigned;
352
353	if (msg == NULL || msglen == NULL || state == NULL)
354		return (-1);
355
356	eom = msg + *msglen;
357
358	state->counter++;
359	if (state->counter == 0)
360		return (ns_verify(msg, msglen, state->key,
361				  state->sig, state->siglen,
362				  state->sig, &state->siglen, &timesigned, 0));
363
364	if (state->siglen > 0) {
365		u_int16_t siglen_n = htons(state->siglen);
366
367		dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx,
368				NULL, 0, NULL, 0);
369		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
370				(u_char *)&siglen_n, INT16SZ, NULL, 0);
371		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
372				state->sig, state->siglen, NULL, 0);
373		state->siglen = 0;
374	}
375
376	cp = recstart = ns_find_tsig(msg, eom);
377
378	if (recstart == NULL) {
379		if (required)
380			return (NS_TSIG_ERROR_NO_TSIG);
381		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
382				msg, *msglen, NULL, 0);
383		return (0);
384	}
385
386	hp->arcount = htons(ntohs(hp->arcount) - 1);
387	dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
388			msg, recstart - msg, NULL, 0);
389
390	/* Read the key name. */
391	n = dn_expand(msg, eom, cp, name, MAXDNAME);
392	if (n < 0)
393		return (NS_TSIG_ERROR_FORMERR);
394	cp += n;
395
396	/* Read the type. */
397	BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
398	GETSHORT(type, cp);
399	if (type != ns_t_tsig)
400		return (NS_TSIG_ERROR_NO_TSIG);
401
402	/* Skip the class and TTL, save the length. */
403	cp += INT16SZ + INT32SZ;
404	GETSHORT(length, cp);
405	if (eom - cp != length)
406		return (NS_TSIG_ERROR_FORMERR);
407
408	/* Read the algorithm name. */
409	n = dn_expand(msg, eom, cp, alg, MAXDNAME);
410	if (n < 0)
411		return (NS_TSIG_ERROR_FORMERR);
412	if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
413		return (-ns_r_badkey);
414	cp += n;
415
416	/* Verify that the key used is OK. */
417	if ((ns_samename(state->key->dk_key_name, name) != 1 ||
418	     state->key->dk_alg != KEY_HMAC_MD5))
419		return (-ns_r_badkey);
420
421	/* Read the time signed and fudge. */
422	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
423	cp += INT16SZ;
424	GETLONG(timesigned, cp);
425	GETSHORT(fudge, cp);
426
427	/* Read the signature. */
428	BOUNDS_CHECK(cp, INT16SZ);
429	GETSHORT(sigfieldlen, cp);
430	BOUNDS_CHECK(cp, sigfieldlen);
431	sigstart = cp;
432	cp += sigfieldlen;
433
434	/* Skip id and read error. */
435	BOUNDS_CHECK(cp, 2*INT16SZ);
436	cp += INT16SZ;
437	GETSHORT(error, cp);
438
439	/* Parse the other data. */
440	BOUNDS_CHECK(cp, INT16SZ);
441	GETSHORT(otherfieldlen, cp);
442	BOUNDS_CHECK(cp, otherfieldlen);
443	cp += otherfieldlen;
444
445	if (cp != eom)
446		return (NS_TSIG_ERROR_FORMERR);
447
448	/*
449	 * Do the verification.
450	 */
451
452	/* Digest the time signed and fudge. */
453	cp2 = buf;
454	PUTSHORT(0, cp2);       /*%< Top 16 bits of time. */
455	PUTLONG(timesigned, cp2);
456	PUTSHORT(NS_TSIG_FUDGE, cp2);
457
458	dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
459			buf, cp2 - buf, NULL, 0);
460
461	n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
462			    sigstart, sigfieldlen);
463	if (n < 0)
464		return (-ns_r_badsig);
465
466	if (sigfieldlen > sizeof(state->sig))
467		return (NS_TSIG_ERROR_NO_SPACE);
468
469	memcpy(state->sig, sigstart, sigfieldlen);
470	state->siglen = sigfieldlen;
471
472	/* Verify the time. */
473	if (abs(timesigned - time(NULL)) > fudge)
474		return (-ns_r_badtime);
475
476	*msglen = recstart - msg;
477
478	if (error != NOERROR)
479		return (error);
480
481	return (0);
482}
483
484/*! \file */
485