xref: /titanic_44/usr/src/lib/libresolv2/common/nameser/ns_verify.c (revision 9525b14bcdeb5b5f6f95ab27c2f48f18bd2ec829)
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
19 static 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 
58 u_char *
ns_find_tsig(u_char * msg,u_char * eom)59 ns_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  */
134 int
ns_verify(u_char * msg,int * msglen,void * k,const u_char * querysig,int querysiglen,u_char * sig,int * siglen,time_t * timesigned,int nostrip)135 ns_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 
322 int
ns_verify_tcp_init(void * k,const u_char * querysig,int querysiglen,ns_tcp_tsig_state * state)323 ns_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 
340 int
ns_verify_tcp(u_char * msg,int * msglen,ns_tcp_tsig_state * state,int required)341 ns_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