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