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