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