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