17c478bd9Sstevel@tonic-gate /*
2*9525b14bSRao Shoaib  * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
37c478bd9Sstevel@tonic-gate  * Copyright (c) 1999 by Internet Software Consortium, Inc.
47c478bd9Sstevel@tonic-gate  *
57c478bd9Sstevel@tonic-gate  * Permission to use, copy, modify, and distribute this software for any
67c478bd9Sstevel@tonic-gate  * purpose with or without fee is hereby granted, provided that the above
77c478bd9Sstevel@tonic-gate  * copyright notice and this permission notice appear in all copies.
87c478bd9Sstevel@tonic-gate  *
9*9525b14bSRao Shoaib  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
10*9525b14bSRao Shoaib  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11*9525b14bSRao Shoaib  * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
12*9525b14bSRao Shoaib  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13*9525b14bSRao Shoaib  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14*9525b14bSRao Shoaib  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
15*9525b14bSRao Shoaib  * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
167c478bd9Sstevel@tonic-gate  */
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate #ifndef lint
19*9525b14bSRao Shoaib static const char rcsid[] = "$Id: ns_verify.c,v 1.5 2006/03/09 23:57:56 marka Exp $";
207c478bd9Sstevel@tonic-gate #endif
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate /* Import. */
237c478bd9Sstevel@tonic-gate 
247c478bd9Sstevel@tonic-gate #include "port_before.h"
257c478bd9Sstevel@tonic-gate #include "fd_setsize.h"
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include <sys/types.h>
287c478bd9Sstevel@tonic-gate #include <sys/param.h>
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate #include <netinet/in.h>
317c478bd9Sstevel@tonic-gate #include <arpa/nameser.h>
327c478bd9Sstevel@tonic-gate #include <arpa/inet.h>
337c478bd9Sstevel@tonic-gate 
347c478bd9Sstevel@tonic-gate #include <errno.h>
357c478bd9Sstevel@tonic-gate #include <netdb.h>
367c478bd9Sstevel@tonic-gate #include <resolv.h>
377c478bd9Sstevel@tonic-gate #include <stdio.h>
387c478bd9Sstevel@tonic-gate #include <stdlib.h>
397c478bd9Sstevel@tonic-gate #include <string.h>
407c478bd9Sstevel@tonic-gate #include <time.h>
417c478bd9Sstevel@tonic-gate #include <unistd.h>
427c478bd9Sstevel@tonic-gate 
437c478bd9Sstevel@tonic-gate #include <isc/dst.h>
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate #include "port_after.h"
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate /* Private. */
487c478bd9Sstevel@tonic-gate 
497c478bd9Sstevel@tonic-gate #define BOUNDS_CHECK(ptr, count) \
507c478bd9Sstevel@tonic-gate 	do { \
517c478bd9Sstevel@tonic-gate 		if ((ptr) + (count) > eom) { \
527c478bd9Sstevel@tonic-gate 			return (NS_TSIG_ERROR_FORMERR); \
537c478bd9Sstevel@tonic-gate 		} \
547c478bd9Sstevel@tonic-gate 	} while (0)
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /* Public. */
577c478bd9Sstevel@tonic-gate 
587c478bd9Sstevel@tonic-gate u_char *
597c478bd9Sstevel@tonic-gate ns_find_tsig(u_char *msg, u_char *eom) {
607c478bd9Sstevel@tonic-gate 	HEADER *hp = (HEADER *)msg;
617c478bd9Sstevel@tonic-gate 	int n, type;
627c478bd9Sstevel@tonic-gate 	u_char *cp = msg, *start;
637c478bd9Sstevel@tonic-gate 
647c478bd9Sstevel@tonic-gate 	if (msg == NULL || eom == NULL || msg > eom)
657c478bd9Sstevel@tonic-gate 		return (NULL);
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	if (cp + HFIXEDSZ >= eom)
687c478bd9Sstevel@tonic-gate 		return (NULL);
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	if (hp->arcount == 0)
717c478bd9Sstevel@tonic-gate 		return (NULL);
727c478bd9Sstevel@tonic-gate 
737c478bd9Sstevel@tonic-gate 	cp += HFIXEDSZ;
747c478bd9Sstevel@tonic-gate 
757c478bd9Sstevel@tonic-gate 	n = ns_skiprr(cp, eom, ns_s_qd, ntohs(hp->qdcount));
767c478bd9Sstevel@tonic-gate 	if (n < 0)
777c478bd9Sstevel@tonic-gate 		return (NULL);
787c478bd9Sstevel@tonic-gate 	cp += n;
797c478bd9Sstevel@tonic-gate 
807c478bd9Sstevel@tonic-gate 	n = ns_skiprr(cp, eom, ns_s_an, ntohs(hp->ancount));
817c478bd9Sstevel@tonic-gate 	if (n < 0)
827c478bd9Sstevel@tonic-gate 		return (NULL);
837c478bd9Sstevel@tonic-gate 	cp += n;
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	n = ns_skiprr(cp, eom, ns_s_ns, ntohs(hp->nscount));
867c478bd9Sstevel@tonic-gate 	if (n < 0)
877c478bd9Sstevel@tonic-gate 		return (NULL);
887c478bd9Sstevel@tonic-gate 	cp += n;
897c478bd9Sstevel@tonic-gate 
907c478bd9Sstevel@tonic-gate 	n = ns_skiprr(cp, eom, ns_s_ar, ntohs(hp->arcount) - 1);
917c478bd9Sstevel@tonic-gate 	if (n < 0)
927c478bd9Sstevel@tonic-gate 		return (NULL);
937c478bd9Sstevel@tonic-gate 	cp += n;
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate 	start = cp;
967c478bd9Sstevel@tonic-gate 	n = dn_skipname(cp, eom);
977c478bd9Sstevel@tonic-gate 	if (n < 0)
987c478bd9Sstevel@tonic-gate 		return (NULL);
997c478bd9Sstevel@tonic-gate 	cp += n;
1007c478bd9Sstevel@tonic-gate 	if (cp + INT16SZ >= eom)
1017c478bd9Sstevel@tonic-gate 		return (NULL);
1027c478bd9Sstevel@tonic-gate 
1037c478bd9Sstevel@tonic-gate 	GETSHORT(type, cp);
1047c478bd9Sstevel@tonic-gate 	if (type != ns_t_tsig)
1057c478bd9Sstevel@tonic-gate 		return (NULL);
1067c478bd9Sstevel@tonic-gate 	return (start);
1077c478bd9Sstevel@tonic-gate }
1087c478bd9Sstevel@tonic-gate 
1097c478bd9Sstevel@tonic-gate /* ns_verify
110*9525b14bSRao Shoaib  *
1117c478bd9Sstevel@tonic-gate  * Parameters:
112*9525b14bSRao Shoaib  *\li	statp		res stuff
113*9525b14bSRao Shoaib  *\li	msg		received message
114*9525b14bSRao Shoaib  *\li	msglen		length of message
115*9525b14bSRao Shoaib  *\li	key		tsig key used for verifying.
116*9525b14bSRao Shoaib  *\li	querysig	(response), the signature in the query
117*9525b14bSRao Shoaib  *\li	querysiglen	(response), the length of the signature in the query
118*9525b14bSRao Shoaib  *\li	sig		(query), a buffer to hold the signature
119*9525b14bSRao Shoaib  *\li	siglen		(query), input - length of signature buffer
1207c478bd9Sstevel@tonic-gate  *				 output - length of signature
1217c478bd9Sstevel@tonic-gate  *
1227c478bd9Sstevel@tonic-gate  * Errors:
123*9525b14bSRao Shoaib  *\li	- bad input (-1)
124*9525b14bSRao Shoaib  *\li	- invalid dns message (NS_TSIG_ERROR_FORMERR)
125*9525b14bSRao Shoaib  *\li	- TSIG is not present (NS_TSIG_ERROR_NO_TSIG)
126*9525b14bSRao Shoaib  *\li	- key doesn't match (-ns_r_badkey)
127*9525b14bSRao Shoaib  *\li	- TSIG verification fails with BADKEY (-ns_r_badkey)
128*9525b14bSRao Shoaib  *\li	- TSIG verification fails with BADSIG (-ns_r_badsig)
129*9525b14bSRao Shoaib  *\li	- TSIG verification fails with BADTIME (-ns_r_badtime)
130*9525b14bSRao Shoaib  *\li	- TSIG verification succeeds, error set to BAKEY (ns_r_badkey)
131*9525b14bSRao Shoaib  *\li	- TSIG verification succeeds, error set to BADSIG (ns_r_badsig)
132*9525b14bSRao Shoaib  *\li	- TSIG verification succeeds, error set to BADTIME (ns_r_badtime)
1337c478bd9Sstevel@tonic-gate  */
1347c478bd9Sstevel@tonic-gate int
1357c478bd9Sstevel@tonic-gate ns_verify(u_char *msg, int *msglen, void *k,
1367c478bd9Sstevel@tonic-gate 	  const u_char *querysig, int querysiglen, u_char *sig, int *siglen,
1377c478bd9Sstevel@tonic-gate 	  time_t *timesigned, int nostrip)
1387c478bd9Sstevel@tonic-gate {
1397c478bd9Sstevel@tonic-gate 	HEADER *hp = (HEADER *)msg;
1407c478bd9Sstevel@tonic-gate 	DST_KEY *key = (DST_KEY *)k;
1417c478bd9Sstevel@tonic-gate 	u_char *cp = msg, *eom;
1427c478bd9Sstevel@tonic-gate 	char name[MAXDNAME], alg[MAXDNAME];
1437c478bd9Sstevel@tonic-gate 	u_char *recstart, *rdatastart;
1447c478bd9Sstevel@tonic-gate 	u_char *sigstart, *otherstart;
1457c478bd9Sstevel@tonic-gate 	int n;
1467c478bd9Sstevel@tonic-gate 	int error;
1477c478bd9Sstevel@tonic-gate 	u_int16_t type, length;
148*9525b14bSRao Shoaib 	u_int16_t fudge, sigfieldlen, otherfieldlen;
1497c478bd9Sstevel@tonic-gate 
1507c478bd9Sstevel@tonic-gate 	dst_init();
1517c478bd9Sstevel@tonic-gate 	if (msg == NULL || msglen == NULL || *msglen < 0)
1527c478bd9Sstevel@tonic-gate 		return (-1);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	eom = msg + *msglen;
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate 	recstart = ns_find_tsig(msg, eom);
1577c478bd9Sstevel@tonic-gate 	if (recstart == NULL)
1587c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_NO_TSIG);
1597c478bd9Sstevel@tonic-gate 
1607c478bd9Sstevel@tonic-gate 	cp = recstart;
1617c478bd9Sstevel@tonic-gate 
1627c478bd9Sstevel@tonic-gate 	/* Read the key name. */
1637c478bd9Sstevel@tonic-gate 	n = dn_expand(msg, eom, cp, name, MAXDNAME);
1647c478bd9Sstevel@tonic-gate 	if (n < 0)
1657c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
1667c478bd9Sstevel@tonic-gate 	cp += n;
1677c478bd9Sstevel@tonic-gate 
1687c478bd9Sstevel@tonic-gate 	/* Read the type. */
1697c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
1707c478bd9Sstevel@tonic-gate 	GETSHORT(type, cp);
1717c478bd9Sstevel@tonic-gate 	if (type != ns_t_tsig)
1727c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_NO_TSIG);
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate 	/* Skip the class and TTL, save the length. */
1757c478bd9Sstevel@tonic-gate 	cp += INT16SZ + INT32SZ;
1767c478bd9Sstevel@tonic-gate 	GETSHORT(length, cp);
1777c478bd9Sstevel@tonic-gate 	if (eom - cp != length)
1787c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate 	/* Read the algorithm name. */
1817c478bd9Sstevel@tonic-gate 	rdatastart = cp;
1827c478bd9Sstevel@tonic-gate 	n = dn_expand(msg, eom, cp, alg, MAXDNAME);
1837c478bd9Sstevel@tonic-gate 	if (n < 0)
1847c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
1857c478bd9Sstevel@tonic-gate 	if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
1867c478bd9Sstevel@tonic-gate 		return (-ns_r_badkey);
1877c478bd9Sstevel@tonic-gate 	cp += n;
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate 	/* Read the time signed and fudge. */
1907c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
1917c478bd9Sstevel@tonic-gate 	cp += INT16SZ;
1927c478bd9Sstevel@tonic-gate 	GETLONG((*timesigned), cp);
1937c478bd9Sstevel@tonic-gate 	GETSHORT(fudge, cp);
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	/* Read the signature. */
1967c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ);
1977c478bd9Sstevel@tonic-gate 	GETSHORT(sigfieldlen, cp);
1987c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, sigfieldlen);
1997c478bd9Sstevel@tonic-gate 	sigstart = cp;
2007c478bd9Sstevel@tonic-gate 	cp += sigfieldlen;
2017c478bd9Sstevel@tonic-gate 
202*9525b14bSRao Shoaib 	/* Skip id and read error. */
2037c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, 2*INT16SZ);
204*9525b14bSRao Shoaib 	cp += INT16SZ;
2057c478bd9Sstevel@tonic-gate 	GETSHORT(error, cp);
2067c478bd9Sstevel@tonic-gate 
2077c478bd9Sstevel@tonic-gate 	/* Parse the other data. */
2087c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ);
2097c478bd9Sstevel@tonic-gate 	GETSHORT(otherfieldlen, cp);
2107c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, otherfieldlen);
2117c478bd9Sstevel@tonic-gate 	otherstart = cp;
2127c478bd9Sstevel@tonic-gate 	cp += otherfieldlen;
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate 	if (cp != eom)
2157c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
2167c478bd9Sstevel@tonic-gate 
2177c478bd9Sstevel@tonic-gate 	/* Verify that the key used is OK. */
2187c478bd9Sstevel@tonic-gate 	if (key != NULL) {
2197c478bd9Sstevel@tonic-gate 		if (key->dk_alg != KEY_HMAC_MD5)
2207c478bd9Sstevel@tonic-gate 			return (-ns_r_badkey);
2217c478bd9Sstevel@tonic-gate 		if (error != ns_r_badsig && error != ns_r_badkey) {
2227c478bd9Sstevel@tonic-gate 			if (ns_samename(key->dk_key_name, name) != 1)
2237c478bd9Sstevel@tonic-gate 				return (-ns_r_badkey);
2247c478bd9Sstevel@tonic-gate 		}
2257c478bd9Sstevel@tonic-gate 	}
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate 	hp->arcount = htons(ntohs(hp->arcount) - 1);
2287c478bd9Sstevel@tonic-gate 
2297c478bd9Sstevel@tonic-gate 	/*
2307c478bd9Sstevel@tonic-gate 	 * Do the verification.
2317c478bd9Sstevel@tonic-gate 	 */
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 	if (key != NULL && error != ns_r_badsig && error != ns_r_badkey) {
2347c478bd9Sstevel@tonic-gate 		void *ctx;
2357c478bd9Sstevel@tonic-gate 		u_char buf[MAXDNAME];
2367c478bd9Sstevel@tonic-gate 		u_char buf2[MAXDNAME];
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 		/* Digest the query signature, if this is a response. */
2397c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_INIT, key, &ctx, NULL, 0, NULL, 0);
2407c478bd9Sstevel@tonic-gate 		if (querysiglen > 0 && querysig != NULL) {
2417c478bd9Sstevel@tonic-gate 			u_int16_t len_n = htons(querysiglen);
2427c478bd9Sstevel@tonic-gate 			dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
2437c478bd9Sstevel@tonic-gate 					(u_char *)&len_n, INT16SZ, NULL, 0);
2447c478bd9Sstevel@tonic-gate 			dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
2457c478bd9Sstevel@tonic-gate 					querysig, querysiglen, NULL, 0);
2467c478bd9Sstevel@tonic-gate 		}
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate  		/* Digest the message. */
2497c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, msg, recstart - msg,
2507c478bd9Sstevel@tonic-gate 				NULL, 0);
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 		/* Digest the key name. */
2537c478bd9Sstevel@tonic-gate 		n = ns_name_pton(name, buf2, sizeof(buf2));
2547c478bd9Sstevel@tonic-gate 		if (n < 0)
2557c478bd9Sstevel@tonic-gate 			return (-1);
2567c478bd9Sstevel@tonic-gate 		n = ns_name_ntol(buf2, buf, sizeof(buf));
2577c478bd9Sstevel@tonic-gate 		if (n < 0)
2587c478bd9Sstevel@tonic-gate 			return (-1);
2597c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
2607c478bd9Sstevel@tonic-gate 
2617c478bd9Sstevel@tonic-gate 		/* Digest the class and TTL. */
2627c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
2637c478bd9Sstevel@tonic-gate 				recstart + dn_skipname(recstart, eom) + INT16SZ,
2647c478bd9Sstevel@tonic-gate 				INT16SZ + INT32SZ, NULL, 0);
2657c478bd9Sstevel@tonic-gate 
2667c478bd9Sstevel@tonic-gate 		/* Digest the algorithm. */
2677c478bd9Sstevel@tonic-gate 		n = ns_name_pton(alg, buf2, sizeof(buf2));
2687c478bd9Sstevel@tonic-gate 		if (n < 0)
2697c478bd9Sstevel@tonic-gate 			return (-1);
2707c478bd9Sstevel@tonic-gate 		n = ns_name_ntol(buf2, buf, sizeof(buf));
2717c478bd9Sstevel@tonic-gate 		if (n < 0)
2727c478bd9Sstevel@tonic-gate 			return (-1);
2737c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx, buf, n, NULL, 0);
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 		/* Digest the time signed and fudge. */
2767c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
2777c478bd9Sstevel@tonic-gate 				rdatastart + dn_skipname(rdatastart, eom),
2787c478bd9Sstevel@tonic-gate 				INT16SZ + INT32SZ + INT16SZ, NULL, 0);
2797c478bd9Sstevel@tonic-gate 
2807c478bd9Sstevel@tonic-gate 		/* Digest the error and other data. */
2817c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, key, &ctx,
2827c478bd9Sstevel@tonic-gate 				otherstart - INT16SZ - INT16SZ,
2837c478bd9Sstevel@tonic-gate 				otherfieldlen + INT16SZ + INT16SZ, NULL, 0);
2847c478bd9Sstevel@tonic-gate 
2857c478bd9Sstevel@tonic-gate 		n = dst_verify_data(SIG_MODE_FINAL, key, &ctx, NULL, 0,
2867c478bd9Sstevel@tonic-gate 				    sigstart, sigfieldlen);
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 		if (n < 0)
2897c478bd9Sstevel@tonic-gate 			return (-ns_r_badsig);
2907c478bd9Sstevel@tonic-gate 
2917c478bd9Sstevel@tonic-gate 		if (sig != NULL && siglen != NULL) {
2927c478bd9Sstevel@tonic-gate 			if (*siglen < sigfieldlen)
2937c478bd9Sstevel@tonic-gate 				return (NS_TSIG_ERROR_NO_SPACE);
2947c478bd9Sstevel@tonic-gate 			memcpy(sig, sigstart, sigfieldlen);
2957c478bd9Sstevel@tonic-gate 			*siglen = sigfieldlen;
2967c478bd9Sstevel@tonic-gate 		}
2977c478bd9Sstevel@tonic-gate 	} else {
2987c478bd9Sstevel@tonic-gate 		if (sigfieldlen > 0)
2997c478bd9Sstevel@tonic-gate 			return (NS_TSIG_ERROR_FORMERR);
3007c478bd9Sstevel@tonic-gate 		if (sig != NULL && siglen != NULL)
3017c478bd9Sstevel@tonic-gate 			*siglen = 0;
3027c478bd9Sstevel@tonic-gate 	}
3037c478bd9Sstevel@tonic-gate 
3047c478bd9Sstevel@tonic-gate 	/* Reset the counter, since we still need to check for badtime. */
3057c478bd9Sstevel@tonic-gate 	hp->arcount = htons(ntohs(hp->arcount) + 1);
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	/* Verify the time. */
3087c478bd9Sstevel@tonic-gate 	if (abs((*timesigned) - time(NULL)) > fudge)
3097c478bd9Sstevel@tonic-gate 		return (-ns_r_badtime);
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	if (nostrip == 0) {
3127c478bd9Sstevel@tonic-gate 		*msglen = recstart - msg;
3137c478bd9Sstevel@tonic-gate 		hp->arcount = htons(ntohs(hp->arcount) - 1);
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate 
3167c478bd9Sstevel@tonic-gate 	if (error != NOERROR)
3177c478bd9Sstevel@tonic-gate 		return (error);
3187c478bd9Sstevel@tonic-gate 
3197c478bd9Sstevel@tonic-gate 	return (0);
3207c478bd9Sstevel@tonic-gate }
3217c478bd9Sstevel@tonic-gate 
3227c478bd9Sstevel@tonic-gate int
3237c478bd9Sstevel@tonic-gate ns_verify_tcp_init(void *k, const u_char *querysig, int querysiglen,
3247c478bd9Sstevel@tonic-gate 		   ns_tcp_tsig_state *state)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate 	dst_init();
3277c478bd9Sstevel@tonic-gate 	if (state == NULL || k == NULL || querysig == NULL || querysiglen < 0)
3287c478bd9Sstevel@tonic-gate 		return (-1);
3297c478bd9Sstevel@tonic-gate 	state->counter = -1;
3307c478bd9Sstevel@tonic-gate 	state->key = k;
3317c478bd9Sstevel@tonic-gate 	if (state->key->dk_alg != KEY_HMAC_MD5)
3327c478bd9Sstevel@tonic-gate 		return (-ns_r_badkey);
3337c478bd9Sstevel@tonic-gate 	if (querysiglen > (int)sizeof(state->sig))
3347c478bd9Sstevel@tonic-gate 		return (-1);
3357c478bd9Sstevel@tonic-gate 	memcpy(state->sig, querysig, querysiglen);
3367c478bd9Sstevel@tonic-gate 	state->siglen = querysiglen;
3377c478bd9Sstevel@tonic-gate 	return (0);
3387c478bd9Sstevel@tonic-gate }
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate int
3417c478bd9Sstevel@tonic-gate ns_verify_tcp(u_char *msg, int *msglen, ns_tcp_tsig_state *state,
3427c478bd9Sstevel@tonic-gate 	      int required)
3437c478bd9Sstevel@tonic-gate {
3447c478bd9Sstevel@tonic-gate 	HEADER *hp = (HEADER *)msg;
345*9525b14bSRao Shoaib 	u_char *recstart, *sigstart;
3467c478bd9Sstevel@tonic-gate 	unsigned int sigfieldlen, otherfieldlen;
347*9525b14bSRao Shoaib 	u_char *cp, *eom, *cp2;
3487c478bd9Sstevel@tonic-gate 	char name[MAXDNAME], alg[MAXDNAME];
3497c478bd9Sstevel@tonic-gate 	u_char buf[MAXDNAME];
350*9525b14bSRao Shoaib 	int n, type, length, fudge, error;
3517c478bd9Sstevel@tonic-gate 	time_t timesigned;
3527c478bd9Sstevel@tonic-gate 
3537c478bd9Sstevel@tonic-gate 	if (msg == NULL || msglen == NULL || state == NULL)
3547c478bd9Sstevel@tonic-gate 		return (-1);
3557c478bd9Sstevel@tonic-gate 
356*9525b14bSRao Shoaib 	eom = msg + *msglen;
357*9525b14bSRao Shoaib 
3587c478bd9Sstevel@tonic-gate 	state->counter++;
3597c478bd9Sstevel@tonic-gate 	if (state->counter == 0)
3607c478bd9Sstevel@tonic-gate 		return (ns_verify(msg, msglen, state->key,
3617c478bd9Sstevel@tonic-gate 				  state->sig, state->siglen,
3627c478bd9Sstevel@tonic-gate 				  state->sig, &state->siglen, &timesigned, 0));
3637c478bd9Sstevel@tonic-gate 
3647c478bd9Sstevel@tonic-gate 	if (state->siglen > 0) {
3657c478bd9Sstevel@tonic-gate 		u_int16_t siglen_n = htons(state->siglen);
3667c478bd9Sstevel@tonic-gate 
3677c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_INIT, state->key, &state->ctx,
3687c478bd9Sstevel@tonic-gate 				NULL, 0, NULL, 0);
3697c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
3707c478bd9Sstevel@tonic-gate 				(u_char *)&siglen_n, INT16SZ, NULL, 0);
3717c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
3727c478bd9Sstevel@tonic-gate 				state->sig, state->siglen, NULL, 0);
3737c478bd9Sstevel@tonic-gate 		state->siglen = 0;
3747c478bd9Sstevel@tonic-gate 	}
3757c478bd9Sstevel@tonic-gate 
3767c478bd9Sstevel@tonic-gate 	cp = recstart = ns_find_tsig(msg, eom);
3777c478bd9Sstevel@tonic-gate 
3787c478bd9Sstevel@tonic-gate 	if (recstart == NULL) {
3797c478bd9Sstevel@tonic-gate 		if (required)
3807c478bd9Sstevel@tonic-gate 			return (NS_TSIG_ERROR_NO_TSIG);
3817c478bd9Sstevel@tonic-gate 		dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
3827c478bd9Sstevel@tonic-gate 				msg, *msglen, NULL, 0);
3837c478bd9Sstevel@tonic-gate 		return (0);
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate 	hp->arcount = htons(ntohs(hp->arcount) - 1);
3877c478bd9Sstevel@tonic-gate 	dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
3887c478bd9Sstevel@tonic-gate 			msg, recstart - msg, NULL, 0);
3897c478bd9Sstevel@tonic-gate 
3907c478bd9Sstevel@tonic-gate 	/* Read the key name. */
3917c478bd9Sstevel@tonic-gate 	n = dn_expand(msg, eom, cp, name, MAXDNAME);
3927c478bd9Sstevel@tonic-gate 	if (n < 0)
3937c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
3947c478bd9Sstevel@tonic-gate 	cp += n;
3957c478bd9Sstevel@tonic-gate 
3967c478bd9Sstevel@tonic-gate 	/* Read the type. */
3977c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, 2*INT16SZ + INT32SZ + INT16SZ);
3987c478bd9Sstevel@tonic-gate 	GETSHORT(type, cp);
3997c478bd9Sstevel@tonic-gate 	if (type != ns_t_tsig)
4007c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_NO_TSIG);
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate 	/* Skip the class and TTL, save the length. */
4037c478bd9Sstevel@tonic-gate 	cp += INT16SZ + INT32SZ;
4047c478bd9Sstevel@tonic-gate 	GETSHORT(length, cp);
4057c478bd9Sstevel@tonic-gate 	if (eom - cp != length)
4067c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
4077c478bd9Sstevel@tonic-gate 
4087c478bd9Sstevel@tonic-gate 	/* Read the algorithm name. */
4097c478bd9Sstevel@tonic-gate 	n = dn_expand(msg, eom, cp, alg, MAXDNAME);
4107c478bd9Sstevel@tonic-gate 	if (n < 0)
4117c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
4127c478bd9Sstevel@tonic-gate 	if (ns_samename(alg, NS_TSIG_ALG_HMAC_MD5) != 1)
4137c478bd9Sstevel@tonic-gate 		return (-ns_r_badkey);
4147c478bd9Sstevel@tonic-gate 	cp += n;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	/* Verify that the key used is OK. */
4177c478bd9Sstevel@tonic-gate 	if ((ns_samename(state->key->dk_key_name, name) != 1 ||
4187c478bd9Sstevel@tonic-gate 	     state->key->dk_alg != KEY_HMAC_MD5))
4197c478bd9Sstevel@tonic-gate 		return (-ns_r_badkey);
4207c478bd9Sstevel@tonic-gate 
4217c478bd9Sstevel@tonic-gate 	/* Read the time signed and fudge. */
4227c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ + INT32SZ + INT16SZ);
4237c478bd9Sstevel@tonic-gate 	cp += INT16SZ;
4247c478bd9Sstevel@tonic-gate 	GETLONG(timesigned, cp);
4257c478bd9Sstevel@tonic-gate 	GETSHORT(fudge, cp);
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	/* Read the signature. */
4287c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ);
4297c478bd9Sstevel@tonic-gate 	GETSHORT(sigfieldlen, cp);
4307c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, sigfieldlen);
4317c478bd9Sstevel@tonic-gate 	sigstart = cp;
4327c478bd9Sstevel@tonic-gate 	cp += sigfieldlen;
4337c478bd9Sstevel@tonic-gate 
434*9525b14bSRao Shoaib 	/* Skip id and read error. */
4357c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, 2*INT16SZ);
436*9525b14bSRao Shoaib 	cp += INT16SZ;
4377c478bd9Sstevel@tonic-gate 	GETSHORT(error, cp);
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 	/* Parse the other data. */
4407c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, INT16SZ);
4417c478bd9Sstevel@tonic-gate 	GETSHORT(otherfieldlen, cp);
4427c478bd9Sstevel@tonic-gate 	BOUNDS_CHECK(cp, otherfieldlen);
4437c478bd9Sstevel@tonic-gate 	cp += otherfieldlen;
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	if (cp != eom)
4467c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_FORMERR);
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	/*
4497c478bd9Sstevel@tonic-gate 	 * Do the verification.
4507c478bd9Sstevel@tonic-gate 	 */
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	/* Digest the time signed and fudge. */
4537c478bd9Sstevel@tonic-gate 	cp2 = buf;
454*9525b14bSRao Shoaib 	PUTSHORT(0, cp2);       /*%< Top 16 bits of time. */
4557c478bd9Sstevel@tonic-gate 	PUTLONG(timesigned, cp2);
4567c478bd9Sstevel@tonic-gate 	PUTSHORT(NS_TSIG_FUDGE, cp2);
4577c478bd9Sstevel@tonic-gate 
4587c478bd9Sstevel@tonic-gate 	dst_verify_data(SIG_MODE_UPDATE, state->key, &state->ctx,
4597c478bd9Sstevel@tonic-gate 			buf, cp2 - buf, NULL, 0);
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	n = dst_verify_data(SIG_MODE_FINAL, state->key, &state->ctx, NULL, 0,
4627c478bd9Sstevel@tonic-gate 			    sigstart, sigfieldlen);
4637c478bd9Sstevel@tonic-gate 	if (n < 0)
4647c478bd9Sstevel@tonic-gate 		return (-ns_r_badsig);
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	if (sigfieldlen > sizeof(state->sig))
4677c478bd9Sstevel@tonic-gate 		return (NS_TSIG_ERROR_NO_SPACE);
4687c478bd9Sstevel@tonic-gate 
4697c478bd9Sstevel@tonic-gate 	memcpy(state->sig, sigstart, sigfieldlen);
4707c478bd9Sstevel@tonic-gate 	state->siglen = sigfieldlen;
4717c478bd9Sstevel@tonic-gate 
4727c478bd9Sstevel@tonic-gate 	/* Verify the time. */
4737c478bd9Sstevel@tonic-gate 	if (abs(timesigned - time(NULL)) > fudge)
4747c478bd9Sstevel@tonic-gate 		return (-ns_r_badtime);
4757c478bd9Sstevel@tonic-gate 
4767c478bd9Sstevel@tonic-gate 	*msglen = recstart - msg;
4777c478bd9Sstevel@tonic-gate 
4787c478bd9Sstevel@tonic-gate 	if (error != NOERROR)
4797c478bd9Sstevel@tonic-gate 		return (error);
4807c478bd9Sstevel@tonic-gate 
4817c478bd9Sstevel@tonic-gate 	return (0);
4827c478bd9Sstevel@tonic-gate }
483*9525b14bSRao Shoaib 
484*9525b14bSRao Shoaib /*! \file */
485