Home | History | Annotate | Line # | Download | only in isccc
      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