xref: /netbsd-src/external/mpl/bind/dist/lib/isccc/cc.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: cc.c,v 1.9 2025/01/26 16:25:44 christos Exp $	*/
2 
3 /*
4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
5  *
6  * SPDX-License-Identifier: MPL-2.0 AND ISC
7  *
8  * This Source Code Form is subject to the terms of the Mozilla Public
9  * License, v. 2.0. If a copy of the MPL was not distributed with this
10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
11  *
12  * See the COPYRIGHT file distributed with this work for additional
13  * information regarding copyright ownership.
14  */
15 
16 /*
17  * Copyright (C) 2001 Nominum, Inc.
18  *
19  * Permission to use, copy, modify, and/or distribute this software for any
20  * purpose with or without fee is hereby granted, provided that the above
21  * copyright notice and this permission notice appear in all copies.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL
24  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
25  * OF MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY
26  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
27  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
28  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
29  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
30  */
31 
32 /*! \file */
33 
34 #include <errno.h>
35 #include <inttypes.h>
36 #include <stdbool.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 
41 #include <isc/assertions.h>
42 #include <isc/hmac.h>
43 #include <isc/result.h>
44 #include <isc/safe.h>
45 
46 #include <isccc/alist.h>
47 #include <isccc/base64.h>
48 #include <isccc/cc.h>
49 #include <isccc/sexpr.h>
50 #include <isccc/symtab.h>
51 #include <isccc/symtype.h>
52 #include <isccc/util.h>
53 
54 #define MAX_TAGS     256
55 #define DUP_LIFETIME 900
56 #ifndef ISCCC_MAXDEPTH
57 #define ISCCC_MAXDEPTH \
58 	10 /* Big enough for rndc which just sends a string each way. */
59 #endif
60 
61 typedef isccc_sexpr_t *sexpr_ptr;
62 
63 static unsigned char auth_hmd5[] = {
64 	0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
65 	ISCCC_CCMSGTYPE_TABLE,		    /*%< message type */
66 	0x00, 0x00, 0x00, 0x20,		    /*%< length == 32 */
67 	0x04, 0x68, 0x6d, 0x64, 0x35,	    /*%< len + hmd5 */
68 	ISCCC_CCMSGTYPE_BINARYDATA,	    /*%< message type */
69 	0x00, 0x00, 0x00, 0x16,		    /*%< length == 22 */
70 	/*
71 	 * The base64 encoding of one of our HMAC-MD5 signatures is
72 	 * 22 bytes.
73 	 */
74 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
76 };
77 
78 #define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */
79 #define HMD5_LENGTH 22
80 
81 static unsigned char auth_hsha[] = {
82 	0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */
83 	ISCCC_CCMSGTYPE_TABLE,		    /*%< message type */
84 	0x00, 0x00, 0x00, 0x63,		    /*%< length == 99 */
85 	0x04, 0x68, 0x73, 0x68, 0x61,	    /*%< len + hsha */
86 	ISCCC_CCMSGTYPE_BINARYDATA,	    /*%< message type */
87 	0x00, 0x00, 0x00, 0x59,		    /*%< length == 89 */
88 	0x00,				    /*%< algorithm */
89 	/*
90 	 * The base64 encoding of one of our HMAC-SHA* signatures is
91 	 * 88 bytes.
92 	 */
93 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
95 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
96 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
97 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
98 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
99 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
100 	0x00, 0x00, 0x00, 0x00
101 };
102 
103 #define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */
104 #define HSHA_LENGTH 88
105 
106 static isc_result_t
107 table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
108 
109 static isc_result_t
110 list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer);
111 
112 static isc_result_t
113 value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) {
114 	unsigned int len;
115 	isccc_region_t *vr;
116 	isc_result_t result;
117 
118 	if (isccc_sexpr_binaryp(elt)) {
119 		vr = isccc_sexpr_tobinary(elt);
120 		len = REGION_SIZE(*vr);
121 		result = isc_buffer_reserve(*buffer, 1 + 4);
122 		if (result != ISC_R_SUCCESS) {
123 			return ISC_R_NOSPACE;
124 		}
125 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA);
126 		isc_buffer_putuint32(*buffer, len);
127 
128 		result = isc_buffer_reserve(*buffer, len);
129 		if (result != ISC_R_SUCCESS) {
130 			return ISC_R_NOSPACE;
131 		}
132 		isc_buffer_putmem(*buffer, vr->rstart, len);
133 	} else if (isccc_alist_alistp(elt)) {
134 		unsigned int used;
135 		isc_buffer_t b;
136 
137 		result = isc_buffer_reserve(*buffer, 1 + 4);
138 		if (result != ISC_R_SUCCESS) {
139 			return ISC_R_NOSPACE;
140 		}
141 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE);
142 		/*
143 		 * Emit a placeholder length.
144 		 */
145 		used = (*buffer)->used;
146 		isc_buffer_putuint32(*buffer, 0);
147 
148 		/*
149 		 * Emit the table.
150 		 */
151 		result = table_towire(elt, buffer);
152 		if (result != ISC_R_SUCCESS) {
153 			return result;
154 		}
155 
156 		len = (*buffer)->used - used;
157 		/*
158 		 * 'len' is 4 bytes too big, since it counts
159 		 * the placeholder length too.	Adjust and
160 		 * emit.
161 		 */
162 		INSIST(len >= 4U);
163 		len -= 4;
164 
165 		isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
166 		isc_buffer_putuint32(&b, len);
167 	} else if (isccc_sexpr_listp(elt)) {
168 		unsigned int used;
169 		isc_buffer_t b;
170 
171 		result = isc_buffer_reserve(*buffer, 1 + 4);
172 		if (result != ISC_R_SUCCESS) {
173 			return ISC_R_NOSPACE;
174 		}
175 		isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST);
176 		/*
177 		 * Emit a placeholder length.
178 		 */
179 		used = (*buffer)->used;
180 		isc_buffer_putuint32(*buffer, 0);
181 
182 		/*
183 		 * Emit the list.
184 		 */
185 		result = list_towire(elt, buffer);
186 		if (result != ISC_R_SUCCESS) {
187 			return result;
188 		}
189 
190 		len = (*buffer)->used - used;
191 		/*
192 		 * 'len' is 4 bytes too big, since it counts
193 		 * the placeholder length too.	Adjust and
194 		 * emit.
195 		 */
196 		INSIST(len >= 4U);
197 		len -= 4;
198 
199 		isc_buffer_init(&b, (unsigned char *)(*buffer)->base + used, 4);
200 		isc_buffer_putuint32(&b, len);
201 	}
202 
203 	return ISC_R_SUCCESS;
204 }
205 
206 static isc_result_t
207 table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) {
208 	isccc_sexpr_t *kv, *elt, *k, *v;
209 	char *ks;
210 	isc_result_t result;
211 	unsigned int len;
212 
213 	for (elt = isccc_alist_first(alist); elt != NULL;
214 	     elt = ISCCC_SEXPR_CDR(elt))
215 	{
216 		kv = ISCCC_SEXPR_CAR(elt);
217 		k = ISCCC_SEXPR_CAR(kv);
218 		ks = isccc_sexpr_tostring(k);
219 		v = ISCCC_SEXPR_CDR(kv);
220 		len = (unsigned int)strlen(ks);
221 		INSIST(len <= 255U);
222 		/*
223 		 * Emit the key name.
224 		 */
225 		result = isc_buffer_reserve(*buffer, 1 + len);
226 		if (result != ISC_R_SUCCESS) {
227 			return ISC_R_NOSPACE;
228 		}
229 		isc_buffer_putuint8(*buffer, (uint8_t)len);
230 		isc_buffer_putmem(*buffer, (const unsigned char *)ks, len);
231 		/*
232 		 * Emit the value.
233 		 */
234 		result = value_towire(v, buffer);
235 		if (result != ISC_R_SUCCESS) {
236 			return result;
237 		}
238 	}
239 
240 	return ISC_R_SUCCESS;
241 }
242 
243 static isc_result_t
244 list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) {
245 	isc_result_t result;
246 
247 	while (list != NULL) {
248 		result = value_towire(ISCCC_SEXPR_CAR(list), buffer);
249 		if (result != ISC_R_SUCCESS) {
250 			return result;
251 		}
252 		list = ISCCC_SEXPR_CDR(list);
253 	}
254 
255 	return ISC_R_SUCCESS;
256 }
257 
258 static isc_result_t
259 sign(unsigned char *data, unsigned int length, unsigned char *out,
260      uint32_t algorithm, isccc_region_t *secret) {
261 	const isc_md_type_t *md_type;
262 	isc_result_t result;
263 	isccc_region_t source, target;
264 	unsigned char digest[ISC_MAX_MD_SIZE];
265 	unsigned int digestlen = sizeof(digest);
266 	unsigned char digestb64[HSHA_LENGTH + 4];
267 
268 	source.rstart = digest;
269 
270 	switch (algorithm) {
271 	case ISCCC_ALG_HMACMD5:
272 		md_type = ISC_MD_MD5;
273 		break;
274 	case ISCCC_ALG_HMACSHA1:
275 		md_type = ISC_MD_SHA1;
276 		break;
277 	case ISCCC_ALG_HMACSHA224:
278 		md_type = ISC_MD_SHA224;
279 		break;
280 	case ISCCC_ALG_HMACSHA256:
281 		md_type = ISC_MD_SHA256;
282 		break;
283 	case ISCCC_ALG_HMACSHA384:
284 		md_type = ISC_MD_SHA384;
285 		break;
286 	case ISCCC_ALG_HMACSHA512:
287 		md_type = ISC_MD_SHA512;
288 		break;
289 	default:
290 		return ISC_R_NOTIMPLEMENTED;
291 	}
292 
293 	result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
294 			  length, digest, &digestlen);
295 	if (result != ISC_R_SUCCESS) {
296 		return result;
297 	}
298 	source.rend = digest + digestlen;
299 
300 	memset(digestb64, 0, sizeof(digestb64));
301 	target.rstart = digestb64;
302 	target.rend = digestb64 + sizeof(digestb64);
303 	result = isccc_base64_encode(&source, 64, "", &target);
304 	if (result != ISC_R_SUCCESS) {
305 		return result;
306 	}
307 	if (algorithm == ISCCC_ALG_HMACMD5) {
308 		PUT_MEM(digestb64, HMD5_LENGTH, out);
309 	} else {
310 		PUT_MEM(digestb64, HSHA_LENGTH, out);
311 	}
312 	return ISC_R_SUCCESS;
313 }
314 
315 isc_result_t
316 isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, uint32_t algorithm,
317 		isccc_region_t *secret) {
318 	unsigned int hmac_base, signed_base;
319 	isc_result_t result;
320 
321 	result = isc_buffer_reserve(*buffer,
322 				    4 + ((algorithm == ISCCC_ALG_HMACMD5)
323 						 ? sizeof(auth_hmd5)
324 						 : sizeof(auth_hsha)));
325 	if (result != ISC_R_SUCCESS) {
326 		return ISC_R_NOSPACE;
327 	}
328 
329 	/*
330 	 * Emit protocol version.
331 	 */
332 	isc_buffer_putuint32(*buffer, 1);
333 
334 	if (secret != NULL) {
335 		/*
336 		 * Emit _auth section with zeroed HMAC signature.
337 		 * We'll replace the zeros with the real signature once
338 		 * we know what it is.
339 		 */
340 		if (algorithm == ISCCC_ALG_HMACMD5) {
341 			hmac_base = (*buffer)->used + HMD5_OFFSET;
342 			isc_buffer_putmem(*buffer, auth_hmd5,
343 					  sizeof(auth_hmd5));
344 		} else {
345 			unsigned char *hmac_alg;
346 
347 			hmac_base = (*buffer)->used + HSHA_OFFSET;
348 			hmac_alg = (unsigned char *)isc_buffer_used(*buffer) +
349 				   HSHA_OFFSET - 1;
350 			isc_buffer_putmem(*buffer, auth_hsha,
351 					  sizeof(auth_hsha));
352 			*hmac_alg = algorithm;
353 		}
354 	} else {
355 		hmac_base = 0;
356 	}
357 	signed_base = (*buffer)->used;
358 	/*
359 	 * Delete any existing _auth section so that we don't try
360 	 * to encode it.
361 	 */
362 	isccc_alist_delete(alist, "_auth");
363 	/*
364 	 * Emit the message.
365 	 */
366 	result = table_towire(alist, buffer);
367 	if (result != ISC_R_SUCCESS) {
368 		return result;
369 	}
370 	if (secret != NULL) {
371 		return sign((unsigned char *)(*buffer)->base + signed_base,
372 			    (*buffer)->used - signed_base,
373 			    (unsigned char *)(*buffer)->base + hmac_base,
374 			    algorithm, secret);
375 	}
376 	return ISC_R_SUCCESS;
377 }
378 
379 static isc_result_t
380 verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length,
381        uint32_t algorithm, isccc_region_t *secret) {
382 	const isc_md_type_t *md_type;
383 	isccc_region_t source;
384 	isccc_region_t target;
385 	isc_result_t result;
386 	isccc_sexpr_t *_auth, *hmacvalue;
387 	unsigned char digest[ISC_MAX_MD_SIZE];
388 	unsigned int digestlen = sizeof(digest);
389 	unsigned char digestb64[HSHA_LENGTH * 4];
390 
391 	/*
392 	 * Extract digest.
393 	 */
394 	_auth = isccc_alist_lookup(alist, "_auth");
395 	if (!isccc_alist_alistp(_auth)) {
396 		return ISC_R_FAILURE;
397 	}
398 	if (algorithm == ISCCC_ALG_HMACMD5) {
399 		hmacvalue = isccc_alist_lookup(_auth, "hmd5");
400 	} else {
401 		hmacvalue = isccc_alist_lookup(_auth, "hsha");
402 	}
403 	if (!isccc_sexpr_binaryp(hmacvalue)) {
404 		return ISC_R_FAILURE;
405 	}
406 	/*
407 	 * Compute digest.
408 	 */
409 	source.rstart = digest;
410 
411 	switch (algorithm) {
412 	case ISCCC_ALG_HMACMD5:
413 		md_type = ISC_MD_MD5;
414 		break;
415 	case ISCCC_ALG_HMACSHA1:
416 		md_type = ISC_MD_SHA1;
417 		break;
418 	case ISCCC_ALG_HMACSHA224:
419 		md_type = ISC_MD_SHA224;
420 		break;
421 	case ISCCC_ALG_HMACSHA256:
422 		md_type = ISC_MD_SHA256;
423 		break;
424 	case ISCCC_ALG_HMACSHA384:
425 		md_type = ISC_MD_SHA384;
426 		break;
427 	case ISCCC_ALG_HMACSHA512:
428 		md_type = ISC_MD_SHA512;
429 		break;
430 	default:
431 		return ISC_R_NOTIMPLEMENTED;
432 	}
433 
434 	result = isc_hmac(md_type, secret->rstart, REGION_SIZE(*secret), data,
435 			  length, digest, &digestlen);
436 	if (result != ISC_R_SUCCESS) {
437 		return result;
438 	}
439 	source.rend = digest + digestlen;
440 
441 	target.rstart = digestb64;
442 	target.rend = digestb64 + sizeof(digestb64);
443 	memset(digestb64, 0, sizeof(digestb64));
444 	result = isccc_base64_encode(&source, 64, "", &target);
445 	if (result != ISC_R_SUCCESS) {
446 		return result;
447 	}
448 
449 	/*
450 	 * Verify.
451 	 */
452 	if (algorithm == ISCCC_ALG_HMACMD5) {
453 		isccc_region_t *region;
454 		unsigned char *value;
455 
456 		region = isccc_sexpr_tobinary(hmacvalue);
457 		if ((region->rend - region->rstart) != HMD5_LENGTH) {
458 			return ISCCC_R_BADAUTH;
459 		}
460 		value = region->rstart;
461 		if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) {
462 			return ISCCC_R_BADAUTH;
463 		}
464 	} else {
465 		isccc_region_t *region;
466 		unsigned char *value;
467 		uint32_t valalg;
468 
469 		region = isccc_sexpr_tobinary(hmacvalue);
470 
471 		/*
472 		 * Note: with non-MD5 algorithms, there's an extra octet
473 		 * to identify which algorithm is in use.
474 		 */
475 		if ((region->rend - region->rstart) != HSHA_LENGTH + 1) {
476 			return ISCCC_R_BADAUTH;
477 		}
478 		value = region->rstart;
479 		GET8(valalg, value);
480 		if ((valalg != algorithm) ||
481 		    !isc_safe_memequal(value, digestb64, HSHA_LENGTH))
482 		{
483 			return ISCCC_R_BADAUTH;
484 		}
485 	}
486 
487 	return ISC_R_SUCCESS;
488 }
489 
490 static isc_result_t
491 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
492 	       uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp);
493 
494 static isc_result_t
495 list_fromwire(isccc_region_t *source, unsigned int depth,
496 	      isccc_sexpr_t **listp);
497 
498 static isc_result_t
499 value_fromwire(isccc_region_t *source, unsigned int depth,
500 	       isccc_sexpr_t **valuep) {
501 	unsigned int msgtype;
502 	uint32_t len;
503 	isccc_sexpr_t *value;
504 	isccc_region_t active;
505 	isc_result_t result;
506 
507 	if (depth > ISCCC_MAXDEPTH) {
508 		return ISCCC_R_MAXDEPTH;
509 	}
510 
511 	if (REGION_SIZE(*source) < 1 + 4) {
512 		return ISC_R_UNEXPECTEDEND;
513 	}
514 	GET8(msgtype, source->rstart);
515 	GET32(len, source->rstart);
516 	if (REGION_SIZE(*source) < len) {
517 		return ISC_R_UNEXPECTEDEND;
518 	}
519 	active.rstart = source->rstart;
520 	active.rend = active.rstart + len;
521 	source->rstart = active.rend;
522 	if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) {
523 		value = isccc_sexpr_frombinary(&active);
524 		if (value != NULL) {
525 			*valuep = value;
526 			result = ISC_R_SUCCESS;
527 		} else {
528 			result = ISC_R_NOMEMORY;
529 		}
530 	} else if (msgtype == ISCCC_CCMSGTYPE_TABLE) {
531 		result = table_fromwire(&active, NULL, 0, depth + 1, valuep);
532 	} else if (msgtype == ISCCC_CCMSGTYPE_LIST) {
533 		result = list_fromwire(&active, depth + 1, valuep);
534 	} else {
535 		result = ISCCC_R_SYNTAX;
536 	}
537 
538 	return result;
539 }
540 
541 static isc_result_t
542 table_fromwire(isccc_region_t *source, isccc_region_t *secret,
543 	       uint32_t algorithm, unsigned int depth, isccc_sexpr_t **alistp) {
544 	char key[256];
545 	uint32_t len;
546 	isc_result_t result;
547 	isccc_sexpr_t *alist, *value;
548 	bool first_tag;
549 	unsigned char *checksum_rstart;
550 
551 	REQUIRE(alistp != NULL && *alistp == NULL);
552 
553 	if (depth > ISCCC_MAXDEPTH) {
554 		return ISCCC_R_MAXDEPTH;
555 	}
556 
557 	checksum_rstart = NULL;
558 	first_tag = true;
559 	alist = isccc_alist_create();
560 	if (alist == NULL) {
561 		return ISC_R_NOMEMORY;
562 	}
563 
564 	while (!REGION_EMPTY(*source)) {
565 		GET8(len, source->rstart);
566 		if (REGION_SIZE(*source) < len) {
567 			result = ISC_R_UNEXPECTEDEND;
568 			goto bad;
569 		}
570 		GET_MEM(key, len, source->rstart);
571 		key[len] = '\0'; /* Ensure NUL termination. */
572 		value = NULL;
573 		result = value_fromwire(source, depth + 1, &value);
574 		if (result != ISC_R_SUCCESS) {
575 			goto bad;
576 		}
577 		if (isccc_alist_define(alist, key, value) == NULL) {
578 			result = ISC_R_NOMEMORY;
579 			goto bad;
580 		}
581 		if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) {
582 			checksum_rstart = source->rstart;
583 		}
584 		first_tag = false;
585 	}
586 
587 	if (secret != NULL) {
588 		if (checksum_rstart != NULL) {
589 			result = verify(
590 				alist, checksum_rstart,
591 				(unsigned int)(source->rend - checksum_rstart),
592 				algorithm, secret);
593 		} else {
594 			result = ISCCC_R_BADAUTH;
595 		}
596 	} else {
597 		result = ISC_R_SUCCESS;
598 	}
599 
600 bad:
601 	if (result == ISC_R_SUCCESS) {
602 		*alistp = alist;
603 	} else {
604 		isccc_sexpr_free(&alist);
605 	}
606 
607 	return result;
608 }
609 
610 static isc_result_t
611 list_fromwire(isccc_region_t *source, unsigned int depth,
612 	      isccc_sexpr_t **listp) {
613 	isccc_sexpr_t *list, *value;
614 	isc_result_t result;
615 
616 	if (depth > ISCCC_MAXDEPTH) {
617 		return ISCCC_R_MAXDEPTH;
618 	}
619 
620 	list = NULL;
621 	while (!REGION_EMPTY(*source)) {
622 		value = NULL;
623 		result = value_fromwire(source, depth + 1, &value);
624 		if (result != ISC_R_SUCCESS) {
625 			isccc_sexpr_free(&list);
626 			return result;
627 		}
628 		if (isccc_sexpr_addtolist(&list, value) == NULL) {
629 			isccc_sexpr_free(&value);
630 			isccc_sexpr_free(&list);
631 			return ISC_R_NOMEMORY;
632 		}
633 	}
634 
635 	*listp = list;
636 
637 	return ISC_R_SUCCESS;
638 }
639 
640 isc_result_t
641 isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp,
642 		  uint32_t algorithm, isccc_region_t *secret) {
643 	unsigned int size;
644 	uint32_t version;
645 
646 	size = REGION_SIZE(*source);
647 	if (size < 4) {
648 		return ISC_R_UNEXPECTEDEND;
649 	}
650 	GET32(version, source->rstart);
651 	if (version != 1) {
652 		return ISCCC_R_UNKNOWNVERSION;
653 	}
654 
655 	return table_fromwire(source, secret, algorithm, 0, alistp);
656 }
657 
658 static isc_result_t
659 createmessage(uint32_t version, const char *from, const char *to,
660 	      uint32_t serial, isccc_time_t now, isccc_time_t expires,
661 	      isccc_sexpr_t **alistp, bool want_expires) {
662 	isccc_sexpr_t *alist, *_ctrl, *_data;
663 	isc_result_t result;
664 
665 	REQUIRE(alistp != NULL && *alistp == NULL);
666 
667 	if (version != 1) {
668 		return ISCCC_R_UNKNOWNVERSION;
669 	}
670 
671 	alist = isccc_alist_create();
672 	if (alist == NULL) {
673 		return ISC_R_NOMEMORY;
674 	}
675 
676 	result = ISC_R_NOMEMORY;
677 
678 	_ctrl = isccc_alist_create();
679 	if (_ctrl == NULL) {
680 		goto bad;
681 	}
682 	if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) {
683 		isccc_sexpr_free(&_ctrl);
684 		goto bad;
685 	}
686 
687 	_data = isccc_alist_create();
688 	if (_data == NULL) {
689 		goto bad;
690 	}
691 	if (isccc_alist_define(alist, "_data", _data) == NULL) {
692 		isccc_sexpr_free(&_data);
693 		goto bad;
694 	}
695 
696 	if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL ||
697 	    isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL ||
698 	    (want_expires &&
699 	     isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL))
700 	{
701 		goto bad;
702 	}
703 	if (from != NULL && isccc_cc_definestring(_ctrl, "_frm", from) == NULL)
704 	{
705 		goto bad;
706 	}
707 	if (to != NULL && isccc_cc_definestring(_ctrl, "_to", to) == NULL) {
708 		goto bad;
709 	}
710 
711 	*alistp = alist;
712 
713 	return ISC_R_SUCCESS;
714 
715 bad:
716 	isccc_sexpr_free(&alist);
717 
718 	return result;
719 }
720 
721 isc_result_t
722 isccc_cc_createmessage(uint32_t version, const char *from, const char *to,
723 		       uint32_t serial, isccc_time_t now, isccc_time_t expires,
724 		       isccc_sexpr_t **alistp) {
725 	return createmessage(version, from, to, serial, now, expires, alistp,
726 			     true);
727 }
728 
729 isc_result_t
730 isccc_cc_createack(isccc_sexpr_t *message, bool ok, isccc_sexpr_t **ackp) {
731 	char *_frm, *_to;
732 	uint32_t serial;
733 	isccc_sexpr_t *ack, *_ctrl;
734 	isc_result_t result;
735 	isccc_time_t t;
736 
737 	REQUIRE(ackp != NULL && *ackp == NULL);
738 
739 	_ctrl = isccc_alist_lookup(message, "_ctrl");
740 	if (!isccc_alist_alistp(_ctrl) ||
741 	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
742 	    isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS)
743 	{
744 		return ISC_R_FAILURE;
745 	}
746 	/*
747 	 * _frm and _to are optional.
748 	 */
749 	_frm = NULL;
750 	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
751 	_to = NULL;
752 	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
753 	/*
754 	 * Create the ack.
755 	 */
756 	ack = NULL;
757 	result = createmessage(1, _to, _frm, serial, t, 0, &ack, false);
758 	if (result != ISC_R_SUCCESS) {
759 		return result;
760 	}
761 
762 	_ctrl = isccc_alist_lookup(ack, "_ctrl");
763 	if (_ctrl == NULL) {
764 		result = ISC_R_FAILURE;
765 		goto bad;
766 	}
767 	if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) {
768 		result = ISC_R_NOMEMORY;
769 		goto bad;
770 	}
771 
772 	*ackp = ack;
773 
774 	return ISC_R_SUCCESS;
775 
776 bad:
777 	isccc_sexpr_free(&ack);
778 
779 	return result;
780 }
781 
782 bool
783 isccc_cc_isack(isccc_sexpr_t *message) {
784 	isccc_sexpr_t *_ctrl;
785 
786 	_ctrl = isccc_alist_lookup(message, "_ctrl");
787 	if (!isccc_alist_alistp(_ctrl)) {
788 		return false;
789 	}
790 	if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) {
791 		return true;
792 	}
793 	return false;
794 }
795 
796 bool
797 isccc_cc_isreply(isccc_sexpr_t *message) {
798 	isccc_sexpr_t *_ctrl;
799 
800 	_ctrl = isccc_alist_lookup(message, "_ctrl");
801 	if (!isccc_alist_alistp(_ctrl)) {
802 		return false;
803 	}
804 	if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) {
805 		return true;
806 	}
807 	return false;
808 }
809 
810 isc_result_t
811 isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now,
812 			isccc_time_t expires, isccc_sexpr_t **alistp) {
813 	char *_frm, *_to, *type = NULL;
814 	uint32_t serial;
815 	isccc_sexpr_t *alist, *_ctrl, *_data;
816 	isc_result_t result;
817 
818 	REQUIRE(alistp != NULL && *alistp == NULL);
819 
820 	_ctrl = isccc_alist_lookup(message, "_ctrl");
821 	_data = isccc_alist_lookup(message, "_data");
822 	if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) ||
823 	    isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS ||
824 	    isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS)
825 	{
826 		return ISC_R_FAILURE;
827 	}
828 	/*
829 	 * _frm and _to are optional.
830 	 */
831 	_frm = NULL;
832 	(void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm);
833 	_to = NULL;
834 	(void)isccc_cc_lookupstring(_ctrl, "_to", &_to);
835 	/*
836 	 * Create the response.
837 	 */
838 	alist = NULL;
839 	result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires,
840 					&alist);
841 	if (result != ISC_R_SUCCESS) {
842 		return result;
843 	}
844 
845 	_ctrl = isccc_alist_lookup(alist, "_ctrl");
846 	if (_ctrl == NULL) {
847 		result = ISC_R_FAILURE;
848 		goto bad;
849 	}
850 
851 	_data = isccc_alist_lookup(alist, "_data");
852 	if (_data == NULL) {
853 		result = ISC_R_FAILURE;
854 		goto bad;
855 	}
856 
857 	if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL ||
858 	    isccc_cc_definestring(_data, "type", type) == NULL)
859 	{
860 		result = ISC_R_NOMEMORY;
861 		goto bad;
862 	}
863 
864 	*alistp = alist;
865 
866 	return ISC_R_SUCCESS;
867 
868 bad:
869 	isccc_sexpr_free(&alist);
870 	return result;
871 }
872 
873 isccc_sexpr_t *
874 isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) {
875 	size_t len;
876 	isccc_region_t r;
877 
878 	len = strlen(str);
879 	r.rstart = UNCONST(str);
880 	r.rend = r.rstart + len;
881 
882 	return isccc_alist_definebinary(alist, key, &r);
883 }
884 
885 isccc_sexpr_t *
886 isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) {
887 	char b[100];
888 	size_t len;
889 	isccc_region_t r;
890 
891 	snprintf(b, sizeof(b), "%u", i);
892 	len = strlen(b);
893 	r.rstart = (unsigned char *)b;
894 	r.rend = (unsigned char *)b + len;
895 
896 	return isccc_alist_definebinary(alist, key, &r);
897 }
898 
899 isc_result_t
900 isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) {
901 	isccc_sexpr_t *kv, *v;
902 
903 	REQUIRE(strp == NULL || *strp == NULL);
904 
905 	kv = isccc_alist_assq(alist, key);
906 	if (kv != NULL) {
907 		v = ISCCC_SEXPR_CDR(kv);
908 		if (isccc_sexpr_binaryp(v)) {
909 			if (strp != NULL) {
910 				*strp = isccc_sexpr_tostring(v);
911 			}
912 			return ISC_R_SUCCESS;
913 		} else {
914 			return ISC_R_EXISTS;
915 		}
916 	}
917 
918 	return ISC_R_NOTFOUND;
919 }
920 
921 isc_result_t
922 isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, uint32_t *uintp) {
923 	isccc_sexpr_t *kv, *v;
924 
925 	kv = isccc_alist_assq(alist, key);
926 	if (kv != NULL) {
927 		v = ISCCC_SEXPR_CDR(kv);
928 		if (isccc_sexpr_binaryp(v)) {
929 			if (uintp != NULL) {
930 				*uintp = (uint32_t)strtoul(
931 					isccc_sexpr_tostring(v), NULL, 10);
932 			}
933 			return ISC_R_SUCCESS;
934 		} else {
935 			return ISC_R_EXISTS;
936 		}
937 	}
938 
939 	return ISC_R_NOTFOUND;
940 }
941 
942 static void
943 symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value,
944 		void *arg) {
945 	UNUSED(type);
946 	UNUSED(value);
947 	UNUSED(arg);
948 
949 	free(key);
950 }
951 
952 static bool
953 symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) {
954 	isccc_time_t *now;
955 
956 	UNUSED(key);
957 	UNUSED(type);
958 
959 	now = arg;
960 
961 	if (*now < value.as_uinteger) {
962 		return false;
963 	}
964 	if ((*now - value.as_uinteger) < DUP_LIFETIME) {
965 		return false;
966 	}
967 	return true;
968 }
969 
970 isc_result_t
971 isccc_cc_createsymtab(isccc_symtab_t **symtabp) {
972 	return isccc_symtab_create(11897, symtab_undefine, NULL, false,
973 				   symtabp);
974 }
975 
976 void
977 isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) {
978 	isccc_symtab_foreach(symtab, symtab_clean, &now);
979 }
980 
981 static bool
982 has_whitespace(const char *str) {
983 	char c;
984 
985 	if (str == NULL) {
986 		return false;
987 	}
988 	while ((c = *str++) != '\0') {
989 		if (c == ' ' || c == '\t' || c == '\n') {
990 			return true;
991 		}
992 	}
993 	return false;
994 }
995 
996 isc_result_t
997 isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message,
998 		  isccc_time_t now) {
999 	const char *_frm;
1000 	const char *_to;
1001 	char *_ser = NULL, *_tim = NULL, *tmp;
1002 	isc_result_t result;
1003 	char *key;
1004 	size_t len;
1005 	isccc_symvalue_t value;
1006 	isccc_sexpr_t *_ctrl;
1007 
1008 	_ctrl = isccc_alist_lookup(message, "_ctrl");
1009 	if (!isccc_alist_alistp(_ctrl) ||
1010 	    isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS ||
1011 	    isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS)
1012 	{
1013 		return ISC_R_FAILURE;
1014 	}
1015 
1016 	INSIST(_ser != NULL);
1017 	INSIST(_tim != NULL);
1018 
1019 	/*
1020 	 * _frm and _to are optional.
1021 	 */
1022 	tmp = NULL;
1023 	if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) {
1024 		_frm = "";
1025 	} else {
1026 		_frm = tmp;
1027 		INSIST(_frm != NULL);
1028 	}
1029 	tmp = NULL;
1030 	if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) {
1031 		_to = "";
1032 	} else {
1033 		_to = tmp;
1034 		INSIST(_to != NULL);
1035 	}
1036 	/*
1037 	 * Ensure there is no newline in any of the strings.  This is so
1038 	 * we can write them to a file later.
1039 	 */
1040 	if (has_whitespace(_frm) || has_whitespace(_to) ||
1041 	    has_whitespace(_ser) || has_whitespace(_tim))
1042 	{
1043 		return ISC_R_FAILURE;
1044 	}
1045 	len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4;
1046 	key = malloc(len);
1047 	if (key == NULL) {
1048 		return ISC_R_NOMEMORY;
1049 	}
1050 	snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim);
1051 	value.as_uinteger = now;
1052 	result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value,
1053 				     isccc_symexists_reject);
1054 	if (result != ISC_R_SUCCESS) {
1055 		free(key);
1056 		return result;
1057 	}
1058 
1059 	return ISC_R_SUCCESS;
1060 }
1061