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