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