diff options
Diffstat (limited to '')
-rw-r--r-- | lib/isccc/cc.c | 1084 |
1 files changed, 1084 insertions, 0 deletions
diff --git a/lib/isccc/cc.c b/lib/isccc/cc.c new file mode 100644 index 0000000..c2740cb --- /dev/null +++ b/lib/isccc/cc.c @@ -0,0 +1,1084 @@ +/* + * Portions Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + * + * Portions Copyright (C) 2001 Nominum, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND ISC AND NOMINUM DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/*! \file */ + +#include <config.h> + +#include <stdbool.h> +#include <stdio.h> +#include <inttypes.h> +#include <string.h> +#include <errno.h> + +#include <isc/assertions.h> +#include <isc/hmacmd5.h> +#include <isc/hmacsha.h> +#include <isc/print.h> +#include <isc/safe.h> +#include <isc/stdlib.h> + +#include <pk11/site.h> + +#include <isccc/alist.h> +#include <isccc/base64.h> +#include <isccc/cc.h> +#include <isccc/result.h> +#include <isccc/sexpr.h> +#include <isccc/symtab.h> +#include <isccc/symtype.h> +#include <isccc/util.h> + +#define MAX_TAGS 256 +#define DUP_LIFETIME 900 + +typedef isccc_sexpr_t *sexpr_ptr; + +#ifndef PK11_MD5_DISABLE +static unsigned char auth_hmd5[] = { + 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */ + ISCCC_CCMSGTYPE_TABLE, /*%< message type */ + 0x00, 0x00, 0x00, 0x20, /*%< length == 32 */ + 0x04, 0x68, 0x6d, 0x64, 0x35, /*%< len + hmd5 */ + ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */ + 0x00, 0x00, 0x00, 0x16, /*%< length == 22 */ + /* + * The base64 encoding of one of our HMAC-MD5 signatures is + * 22 bytes. + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#define HMD5_OFFSET 21 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 */ +#define HMD5_LENGTH 22 +#endif + +static unsigned char auth_hsha[] = { + 0x05, 0x5f, 0x61, 0x75, 0x74, 0x68, /*%< len + _auth */ + ISCCC_CCMSGTYPE_TABLE, /*%< message type */ + 0x00, 0x00, 0x00, 0x63, /*%< length == 99 */ + 0x04, 0x68, 0x73, 0x68, 0x61, /*%< len + hsha */ + ISCCC_CCMSGTYPE_BINARYDATA, /*%< message type */ + 0x00, 0x00, 0x00, 0x59, /*%< length == 89 */ + 0x00, /*%< algorithm */ + /* + * The base64 encoding of one of our HMAC-SHA* signatures is + * 88 bytes. + */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +#define HSHA_OFFSET 22 /*%< 21 = 6 + 1 + 4 + 5 + 1 + 4 + 1 */ +#define HSHA_LENGTH 88 + +static isc_result_t +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); + +static isc_result_t +list_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer); + +static isc_result_t +value_towire(isccc_sexpr_t *elt, isc_buffer_t **buffer) { + unsigned int len; + isccc_region_t *vr; + isc_result_t result; + + if (isccc_sexpr_binaryp(elt)) { + vr = isccc_sexpr_tobinary(elt); + len = REGION_SIZE(*vr); + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_BINARYDATA); + isc_buffer_putuint32(*buffer, len); + + result = isc_buffer_reserve(buffer, len); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + isc_buffer_putmem(*buffer, vr->rstart, len); + } else if (isccc_alist_alistp(elt)) { + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_TABLE); + /* + * Emit a placeholder length. + */ + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + + /* + * Emit the table. + */ + result = table_towire(elt, buffer); + if (result != ISC_R_SUCCESS) + return (result); + + len = (*buffer)->used - used; + /* + * 'len' is 4 bytes too big, since it counts + * the placeholder length too. Adjust and + * emit. + */ + INSIST(len >= 4U); + len -= 4; + + isc_buffer_init(&b, (unsigned char *) (*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); + } else if (isccc_sexpr_listp(elt)) { + unsigned int used; + isc_buffer_t b; + + result = isc_buffer_reserve(buffer, 1 + 4); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(*buffer, ISCCC_CCMSGTYPE_LIST); + /* + * Emit a placeholder length. + */ + used = (*buffer)->used; + isc_buffer_putuint32(*buffer, 0); + + /* + * Emit the list. + */ + result = list_towire(elt, buffer); + if (result != ISC_R_SUCCESS) + return (result); + + len = (*buffer)->used - used; + /* + * 'len' is 4 bytes too big, since it counts + * the placeholder length too. Adjust and + * emit. + */ + INSIST(len >= 4U); + len -= 4; + + isc_buffer_init(&b, (unsigned char *) (*buffer)->base + used, 4); + isc_buffer_putuint32(&b, len); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +table_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer) { + isccc_sexpr_t *kv, *elt, *k, *v; + char *ks; + isc_result_t result; + unsigned int len; + + for (elt = isccc_alist_first(alist); + elt != NULL; + elt = ISCCC_SEXPR_CDR(elt)) { + kv = ISCCC_SEXPR_CAR(elt); + k = ISCCC_SEXPR_CAR(kv); + ks = isccc_sexpr_tostring(k); + v = ISCCC_SEXPR_CDR(kv); + len = (unsigned int)strlen(ks); + INSIST(len <= 255U); + /* + * Emit the key name. + */ + result = isc_buffer_reserve(buffer, 1 + len); + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + isc_buffer_putuint8(*buffer, (uint8_t)len); + isc_buffer_putmem(*buffer, (const unsigned char *) ks, len); + /* + * Emit the value. + */ + result = value_towire(v, buffer); + if (result != ISC_R_SUCCESS) + return (result); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +list_towire(isccc_sexpr_t *list, isc_buffer_t **buffer) { + isc_result_t result; + + while (list != NULL) { + result = value_towire(ISCCC_SEXPR_CAR(list), buffer); + if (result != ISC_R_SUCCESS) + return (result); + list = ISCCC_SEXPR_CDR(list); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +sign(unsigned char *data, unsigned int length, unsigned char *hmac, + uint32_t algorithm, isccc_region_t *secret) +{ + union { +#ifndef PK11_MD5_DISABLE + isc_hmacmd5_t hmd5; +#endif + isc_hmacsha1_t hsha; + isc_hmacsha224_t h224; + isc_hmacsha256_t h256; + isc_hmacsha384_t h384; + isc_hmacsha512_t h512; + } ctx; + isc_result_t result; + isccc_region_t source, target; + unsigned char digest[ISC_SHA512_DIGESTLENGTH]; + unsigned char digestb64[HSHA_LENGTH + 4]; + + source.rstart = digest; + + switch (algorithm) { +#ifndef PK11_MD5_DISABLE + case ISCCC_ALG_HMACMD5: + isc_hmacmd5_init(&ctx.hmd5, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacmd5_update(&ctx.hmd5, data, length); + isc_hmacmd5_sign(&ctx.hmd5, digest); + source.rend = digest + ISC_MD5_DIGESTLENGTH; + break; +#endif + + case ISCCC_ALG_HMACSHA1: + isc_hmacsha1_init(&ctx.hsha, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha1_update(&ctx.hsha, data, length); + isc_hmacsha1_sign(&ctx.hsha, digest, + ISC_SHA1_DIGESTLENGTH); + source.rend = digest + ISC_SHA1_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA224: + isc_hmacsha224_init(&ctx.h224, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha224_update(&ctx.h224, data, length); + isc_hmacsha224_sign(&ctx.h224, digest, + ISC_SHA224_DIGESTLENGTH); + source.rend = digest + ISC_SHA224_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA256: + isc_hmacsha256_init(&ctx.h256, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha256_update(&ctx.h256, data, length); + isc_hmacsha256_sign(&ctx.h256, digest, + ISC_SHA256_DIGESTLENGTH); + source.rend = digest + ISC_SHA256_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA384: + isc_hmacsha384_init(&ctx.h384, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha384_update(&ctx.h384, data, length); + isc_hmacsha384_sign(&ctx.h384, digest, + ISC_SHA384_DIGESTLENGTH); + source.rend = digest + ISC_SHA384_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA512: + isc_hmacsha512_init(&ctx.h512, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha512_update(&ctx.h512, data, length); + isc_hmacsha512_sign(&ctx.h512, digest, + ISC_SHA512_DIGESTLENGTH); + source.rend = digest + ISC_SHA512_DIGESTLENGTH; + break; + + default: + return (ISC_R_FAILURE); + } + + memset(digestb64, 0, sizeof(digestb64)); + target.rstart = digestb64; + target.rend = digestb64 + sizeof(digestb64); + result = isccc_base64_encode(&source, 64, "", &target); + if (result != ISC_R_SUCCESS) + return (result); +#ifndef PK11_MD5_DISABLE + if (algorithm == ISCCC_ALG_HMACMD5) + PUT_MEM(digestb64, HMD5_LENGTH, hmac); + else +#endif + PUT_MEM(digestb64, HSHA_LENGTH, hmac); + return (ISC_R_SUCCESS); +} + +isc_result_t +isccc_cc_towire(isccc_sexpr_t *alist, isc_buffer_t **buffer, + uint32_t algorithm, isccc_region_t *secret) +{ + unsigned int hmac_base, signed_base; + isc_result_t result; + +#ifndef PK11_MD5_DISABLE + result = isc_buffer_reserve(buffer, + 4 + ((algorithm == ISCCC_ALG_HMACMD5) ? + sizeof(auth_hmd5) : + sizeof(auth_hsha))); +#else + if (algorithm == ISCCC_ALG_HMACMD5) + return (ISC_R_NOTIMPLEMENTED); + result = isc_buffer_reserve(buffer, 4 + sizeof(auth_hsha)); +#endif + if (result != ISC_R_SUCCESS) + return (ISC_R_NOSPACE); + + /* + * Emit protocol version. + */ + isc_buffer_putuint32(*buffer, 1); + + if (secret != NULL) { + /* + * Emit _auth section with zeroed HMAC signature. + * We'll replace the zeros with the real signature once + * we know what it is. + */ +#ifndef PK11_MD5_DISABLE + if (algorithm == ISCCC_ALG_HMACMD5) { + hmac_base = (*buffer)->used + HMD5_OFFSET; + isc_buffer_putmem(*buffer, + auth_hmd5, sizeof(auth_hmd5)); + } else +#endif + { + unsigned char *hmac_alg; + + hmac_base = (*buffer)->used + HSHA_OFFSET; + hmac_alg = (unsigned char *) isc_buffer_used(*buffer) + + HSHA_OFFSET - 1; + isc_buffer_putmem(*buffer, + auth_hsha, sizeof(auth_hsha)); + *hmac_alg = algorithm; + } + } else + hmac_base = 0; + signed_base = (*buffer)->used; + /* + * Delete any existing _auth section so that we don't try + * to encode it. + */ + isccc_alist_delete(alist, "_auth"); + /* + * Emit the message. + */ + result = table_towire(alist, buffer); + if (result != ISC_R_SUCCESS) + return (result); + if (secret != NULL) + return (sign((unsigned char *) (*buffer)->base + signed_base, + (*buffer)->used - signed_base, + (unsigned char *) (*buffer)->base + hmac_base, + algorithm, secret)); + return (ISC_R_SUCCESS); +} + +static isc_result_t +verify(isccc_sexpr_t *alist, unsigned char *data, unsigned int length, + uint32_t algorithm, isccc_region_t *secret) +{ + union { +#ifndef PK11_MD5_DISABLE + isc_hmacmd5_t hmd5; +#endif + isc_hmacsha1_t hsha; + isc_hmacsha224_t h224; + isc_hmacsha256_t h256; + isc_hmacsha384_t h384; + isc_hmacsha512_t h512; + } ctx; + isccc_region_t source; + isccc_region_t target; + isc_result_t result; + isccc_sexpr_t *_auth, *hmac; + unsigned char digest[ISC_SHA512_DIGESTLENGTH]; + unsigned char digestb64[HSHA_LENGTH * 4]; + + /* + * Extract digest. + */ + _auth = isccc_alist_lookup(alist, "_auth"); + if (!isccc_alist_alistp(_auth)) + return (ISC_R_FAILURE); +#ifndef PK11_MD5_DISABLE + if (algorithm == ISCCC_ALG_HMACMD5) + hmac = isccc_alist_lookup(_auth, "hmd5"); + else +#endif + hmac = isccc_alist_lookup(_auth, "hsha"); + if (!isccc_sexpr_binaryp(hmac)) + return (ISC_R_FAILURE); + /* + * Compute digest. + */ + source.rstart = digest; + target.rstart = digestb64; + switch (algorithm) { +#ifndef PK11_MD5_DISABLE + case ISCCC_ALG_HMACMD5: + isc_hmacmd5_init(&ctx.hmd5, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacmd5_update(&ctx.hmd5, data, length); + isc_hmacmd5_sign(&ctx.hmd5, digest); + source.rend = digest + ISC_MD5_DIGESTLENGTH; + break; +#endif + + case ISCCC_ALG_HMACSHA1: + isc_hmacsha1_init(&ctx.hsha, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha1_update(&ctx.hsha, data, length); + isc_hmacsha1_sign(&ctx.hsha, digest, + ISC_SHA1_DIGESTLENGTH); + source.rend = digest + ISC_SHA1_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA224: + isc_hmacsha224_init(&ctx.h224, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha224_update(&ctx.h224, data, length); + isc_hmacsha224_sign(&ctx.h224, digest, + ISC_SHA224_DIGESTLENGTH); + source.rend = digest + ISC_SHA224_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA256: + isc_hmacsha256_init(&ctx.h256, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha256_update(&ctx.h256, data, length); + isc_hmacsha256_sign(&ctx.h256, digest, + ISC_SHA256_DIGESTLENGTH); + source.rend = digest + ISC_SHA256_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA384: + isc_hmacsha384_init(&ctx.h384, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha384_update(&ctx.h384, data, length); + isc_hmacsha384_sign(&ctx.h384, digest, + ISC_SHA384_DIGESTLENGTH); + source.rend = digest + ISC_SHA384_DIGESTLENGTH; + break; + + case ISCCC_ALG_HMACSHA512: + isc_hmacsha512_init(&ctx.h512, secret->rstart, + REGION_SIZE(*secret)); + isc_hmacsha512_update(&ctx.h512, data, length); + isc_hmacsha512_sign(&ctx.h512, digest, + ISC_SHA512_DIGESTLENGTH); + source.rend = digest + ISC_SHA512_DIGESTLENGTH; + break; + + default: + return (ISC_R_FAILURE); + } + target.rstart = digestb64; + target.rend = digestb64 + sizeof(digestb64); + memset(digestb64, 0, sizeof(digestb64)); + result = isccc_base64_encode(&source, 64, "", &target); + if (result != ISC_R_SUCCESS) + return (result); + + /* + * Verify. + */ +#ifndef PK11_MD5_DISABLE + if (algorithm == ISCCC_ALG_HMACMD5) { + isccc_region_t *region; + unsigned char *value; + + region = isccc_sexpr_tobinary(hmac); + if ((region->rend - region->rstart) != HMD5_LENGTH) + return (ISCCC_R_BADAUTH); + value = region->rstart; + if (!isc_safe_memequal(value, digestb64, HMD5_LENGTH)) + return (ISCCC_R_BADAUTH); + } else +#endif + { + isccc_region_t *region; + unsigned char *value; + uint32_t valalg; + + region = isccc_sexpr_tobinary(hmac); + + /* + * Note: with non-MD5 algorithms, there's an extra octet + * to identify which algorithm is in use. + */ + if ((region->rend - region->rstart) != HSHA_LENGTH + 1) + return (ISCCC_R_BADAUTH); + value = region->rstart; + GET8(valalg, value); + if ((valalg != algorithm) || + !isc_safe_memequal(value, digestb64, HSHA_LENGTH)) + return (ISCCC_R_BADAUTH); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +table_fromwire(isccc_region_t *source, isccc_region_t *secret, + uint32_t algorithm, isccc_sexpr_t **alistp); + +static isc_result_t +list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp); + +static isc_result_t +value_fromwire(isccc_region_t *source, isccc_sexpr_t **valuep) { + unsigned int msgtype; + uint32_t len; + isccc_sexpr_t *value; + isccc_region_t active; + isc_result_t result; + + if (REGION_SIZE(*source) < 1 + 4) + return (ISC_R_UNEXPECTEDEND); + GET8(msgtype, source->rstart); + GET32(len, source->rstart); + if (REGION_SIZE(*source) < len) + return (ISC_R_UNEXPECTEDEND); + active.rstart = source->rstart; + active.rend = active.rstart + len; + source->rstart = active.rend; + if (msgtype == ISCCC_CCMSGTYPE_BINARYDATA) { + value = isccc_sexpr_frombinary(&active); + if (value != NULL) { + *valuep = value; + result = ISC_R_SUCCESS; + } else + result = ISC_R_NOMEMORY; + } else if (msgtype == ISCCC_CCMSGTYPE_TABLE) + result = table_fromwire(&active, NULL, 0, valuep); + else if (msgtype == ISCCC_CCMSGTYPE_LIST) + result = list_fromwire(&active, valuep); + else + result = ISCCC_R_SYNTAX; + + return (result); +} + +static isc_result_t +table_fromwire(isccc_region_t *source, isccc_region_t *secret, + uint32_t algorithm, isccc_sexpr_t **alistp) +{ + char key[256]; + uint32_t len; + isc_result_t result; + isccc_sexpr_t *alist, *value; + bool first_tag; + unsigned char *checksum_rstart; + + REQUIRE(alistp != NULL && *alistp == NULL); + + checksum_rstart = NULL; + first_tag = true; + alist = isccc_alist_create(); + if (alist == NULL) + return (ISC_R_NOMEMORY); + + while (!REGION_EMPTY(*source)) { + GET8(len, source->rstart); + if (REGION_SIZE(*source) < len) { + result = ISC_R_UNEXPECTEDEND; + goto bad; + } + GET_MEM(key, len, source->rstart); + key[len] = '\0'; /* Ensure NUL termination. */ + value = NULL; + result = value_fromwire(source, &value); + if (result != ISC_R_SUCCESS) + goto bad; + if (isccc_alist_define(alist, key, value) == NULL) { + result = ISC_R_NOMEMORY; + goto bad; + } + if (first_tag && secret != NULL && strcmp(key, "_auth") == 0) + checksum_rstart = source->rstart; + first_tag = false; + } + + if (secret != NULL) { + if (checksum_rstart != NULL) + result = verify(alist, checksum_rstart, + (unsigned int) + (source->rend - checksum_rstart), + algorithm, secret); + else + result = ISCCC_R_BADAUTH; + } else + result = ISC_R_SUCCESS; + + bad: + if (result == ISC_R_SUCCESS) + *alistp = alist; + else + isccc_sexpr_free(&alist); + + return (result); +} + +static isc_result_t +list_fromwire(isccc_region_t *source, isccc_sexpr_t **listp) { + isccc_sexpr_t *list, *value; + isc_result_t result; + + list = NULL; + while (!REGION_EMPTY(*source)) { + value = NULL; + result = value_fromwire(source, &value); + if (result != ISC_R_SUCCESS) { + isccc_sexpr_free(&list); + return (result); + } + if (isccc_sexpr_addtolist(&list, value) == NULL) { + isccc_sexpr_free(&value); + isccc_sexpr_free(&list); + return (ISC_R_NOMEMORY); + } + } + + *listp = list; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isccc_cc_fromwire(isccc_region_t *source, isccc_sexpr_t **alistp, + uint32_t algorithm, isccc_region_t *secret) +{ + unsigned int size; + uint32_t version; + + size = REGION_SIZE(*source); + if (size < 4) + return (ISC_R_UNEXPECTEDEND); + GET32(version, source->rstart); + if (version != 1) + return (ISCCC_R_UNKNOWNVERSION); + + return (table_fromwire(source, secret, algorithm, alistp)); +} + +static isc_result_t +createmessage(uint32_t version, const char *from, const char *to, + uint32_t serial, isccc_time_t now, + isccc_time_t expires, isccc_sexpr_t **alistp, + bool want_expires) +{ + isccc_sexpr_t *alist, *_ctrl, *_data; + isc_result_t result; + + REQUIRE(alistp != NULL && *alistp == NULL); + + if (version != 1) + return (ISCCC_R_UNKNOWNVERSION); + + alist = isccc_alist_create(); + if (alist == NULL) + return (ISC_R_NOMEMORY); + + result = ISC_R_NOMEMORY; + + _ctrl = isccc_alist_create(); + if (_ctrl == NULL) + goto bad; + if (isccc_alist_define(alist, "_ctrl", _ctrl) == NULL) { + isccc_sexpr_free(&_ctrl); + goto bad; + } + + _data = isccc_alist_create(); + if (_data == NULL) + goto bad; + if (isccc_alist_define(alist, "_data", _data) == NULL) { + isccc_sexpr_free(&_data); + goto bad; + } + + if (isccc_cc_defineuint32(_ctrl, "_ser", serial) == NULL || + isccc_cc_defineuint32(_ctrl, "_tim", now) == NULL || + (want_expires && + isccc_cc_defineuint32(_ctrl, "_exp", expires) == NULL)) + goto bad; + if (from != NULL && + isccc_cc_definestring(_ctrl, "_frm", from) == NULL) + goto bad; + if (to != NULL && + isccc_cc_definestring(_ctrl, "_to", to) == NULL) + goto bad; + + *alistp = alist; + + return (ISC_R_SUCCESS); + + bad: + isccc_sexpr_free(&alist); + + return (result); +} + +isc_result_t +isccc_cc_createmessage(uint32_t version, const char *from, const char *to, + uint32_t serial, isccc_time_t now, + isccc_time_t expires, isccc_sexpr_t **alistp) +{ + return (createmessage(version, from, to, serial, now, expires, + alistp, true)); +} + +isc_result_t +isccc_cc_createack(isccc_sexpr_t *message, bool ok, + isccc_sexpr_t **ackp) +{ + char *_frm, *_to; + uint32_t serial; + isccc_sexpr_t *ack, *_ctrl; + isc_result_t result; + isccc_time_t t; + + REQUIRE(ackp != NULL && *ackp == NULL); + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl) || + isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || + isccc_cc_lookupuint32(_ctrl, "_tim", &t) != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + /* + * _frm and _to are optional. + */ + _frm = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); + _to = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); + /* + * Create the ack. + */ + ack = NULL; + result = createmessage(1, _to, _frm, serial, t, 0, &ack, false); + if (result != ISC_R_SUCCESS) + return (result); + + _ctrl = isccc_alist_lookup(ack, "_ctrl"); + if (_ctrl == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + if (isccc_cc_definestring(ack, "_ack", (ok) ? "1" : "0") == NULL) { + result = ISC_R_NOMEMORY; + goto bad; + } + + *ackp = ack; + + return (ISC_R_SUCCESS); + + bad: + isccc_sexpr_free(&ack); + + return (result); +} + +bool +isccc_cc_isack(isccc_sexpr_t *message) { + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl)) + return (false); + if (isccc_cc_lookupstring(_ctrl, "_ack", NULL) == ISC_R_SUCCESS) + return (true); + return (false); +} + +bool +isccc_cc_isreply(isccc_sexpr_t *message) { + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl)) + return (false); + if (isccc_cc_lookupstring(_ctrl, "_rpl", NULL) == ISC_R_SUCCESS) + return (true); + return (false); +} + +isc_result_t +isccc_cc_createresponse(isccc_sexpr_t *message, isccc_time_t now, + isccc_time_t expires, isccc_sexpr_t **alistp) +{ + char *_frm, *_to, *type = NULL; + uint32_t serial; + isccc_sexpr_t *alist, *_ctrl, *_data; + isc_result_t result; + + REQUIRE(alistp != NULL && *alistp == NULL); + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + _data = isccc_alist_lookup(message, "_data"); + if (!isccc_alist_alistp(_ctrl) || !isccc_alist_alistp(_data) || + isccc_cc_lookupuint32(_ctrl, "_ser", &serial) != ISC_R_SUCCESS || + isccc_cc_lookupstring(_data, "type", &type) != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + /* + * _frm and _to are optional. + */ + _frm = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_frm", &_frm); + _to = NULL; + (void)isccc_cc_lookupstring(_ctrl, "_to", &_to); + /* + * Create the response. + */ + alist = NULL; + result = isccc_cc_createmessage(1, _to, _frm, serial, now, expires, + &alist); + if (result != ISC_R_SUCCESS) + return (result); + + _ctrl = isccc_alist_lookup(alist, "_ctrl"); + if (_ctrl == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + + _data = isccc_alist_lookup(alist, "_data"); + if (_data == NULL) { + result = ISC_R_FAILURE; + goto bad; + } + + if (isccc_cc_definestring(_ctrl, "_rpl", "1") == NULL || + isccc_cc_definestring(_data, "type", type) == NULL) + { + result = ISC_R_NOMEMORY; + goto bad; + } + + *alistp = alist; + + return (ISC_R_SUCCESS); + + bad: + isccc_sexpr_free(&alist); + return (result); +} + +isccc_sexpr_t * +isccc_cc_definestring(isccc_sexpr_t *alist, const char *key, const char *str) { + size_t len; + isccc_region_t r; + + len = strlen(str); + DE_CONST(str, r.rstart); + r.rend = r.rstart + len; + + return (isccc_alist_definebinary(alist, key, &r)); +} + +isccc_sexpr_t * +isccc_cc_defineuint32(isccc_sexpr_t *alist, const char *key, uint32_t i) { + char b[100]; + size_t len; + isccc_region_t r; + + snprintf(b, sizeof(b), "%u", i); + len = strlen(b); + r.rstart = (unsigned char *)b; + r.rend = (unsigned char *)b + len; + + return (isccc_alist_definebinary(alist, key, &r)); +} + +isc_result_t +isccc_cc_lookupstring(isccc_sexpr_t *alist, const char *key, char **strp) { + isccc_sexpr_t *kv, *v; + + REQUIRE(strp == NULL || *strp == NULL); + + kv = isccc_alist_assq(alist, key); + if (kv != NULL) { + v = ISCCC_SEXPR_CDR(kv); + if (isccc_sexpr_binaryp(v)) { + if (strp != NULL) + *strp = isccc_sexpr_tostring(v); + return (ISC_R_SUCCESS); + } else + return (ISC_R_EXISTS); + } + + return (ISC_R_NOTFOUND); +} + +isc_result_t +isccc_cc_lookupuint32(isccc_sexpr_t *alist, const char *key, + uint32_t *uintp) +{ + isccc_sexpr_t *kv, *v; + + kv = isccc_alist_assq(alist, key); + if (kv != NULL) { + v = ISCCC_SEXPR_CDR(kv); + if (isccc_sexpr_binaryp(v)) { + if (uintp != NULL) + *uintp = (uint32_t) + strtoul(isccc_sexpr_tostring(v), + NULL, 10); + return (ISC_R_SUCCESS); + } else + return (ISC_R_EXISTS); + } + + return (ISC_R_NOTFOUND); +} + +static void +symtab_undefine(char *key, unsigned int type, isccc_symvalue_t value, + void *arg) +{ + UNUSED(type); + UNUSED(value); + UNUSED(arg); + + free(key); +} + +static bool +symtab_clean(char *key, unsigned int type, isccc_symvalue_t value, void *arg) { + isccc_time_t *now; + + UNUSED(key); + UNUSED(type); + + now = arg; + + if (*now < value.as_uinteger) + return (false); + if ((*now - value.as_uinteger) < DUP_LIFETIME) + return (false); + return (true); +} + +isc_result_t +isccc_cc_createsymtab(isccc_symtab_t **symtabp) { + return (isccc_symtab_create(11897, symtab_undefine, NULL, false, + symtabp)); +} + +void +isccc_cc_cleansymtab(isccc_symtab_t *symtab, isccc_time_t now) { + isccc_symtab_foreach(symtab, symtab_clean, &now); +} + +static bool +has_whitespace(const char *str) { + char c; + + if (str == NULL) + return (false); + while ((c = *str++) != '\0') { + if (c == ' ' || c == '\t' || c == '\n') + return (true); + } + return (false); +} + +isc_result_t +isccc_cc_checkdup(isccc_symtab_t *symtab, isccc_sexpr_t *message, + isccc_time_t now) +{ + const char *_frm; + const char *_to; + char *_ser = NULL, *_tim = NULL, *tmp; + isc_result_t result; + char *key; + size_t len; + isccc_symvalue_t value; + isccc_sexpr_t *_ctrl; + + _ctrl = isccc_alist_lookup(message, "_ctrl"); + if (!isccc_alist_alistp(_ctrl) || + isccc_cc_lookupstring(_ctrl, "_ser", &_ser) != ISC_R_SUCCESS || + isccc_cc_lookupstring(_ctrl, "_tim", &_tim) != ISC_R_SUCCESS) + return (ISC_R_FAILURE); + + INSIST(_ser != NULL); + INSIST(_tim != NULL); + + /* + * _frm and _to are optional. + */ + tmp = NULL; + if (isccc_cc_lookupstring(_ctrl, "_frm", &tmp) != ISC_R_SUCCESS) + _frm = ""; + else + _frm = tmp; + tmp = NULL; + if (isccc_cc_lookupstring(_ctrl, "_to", &tmp) != ISC_R_SUCCESS) + _to = ""; + else + _to = tmp; + /* + * Ensure there is no newline in any of the strings. This is so + * we can write them to a file later. + */ + if (has_whitespace(_frm) || has_whitespace(_to) || + has_whitespace(_ser) || has_whitespace(_tim)) + return (ISC_R_FAILURE); + len = strlen(_frm) + strlen(_to) + strlen(_ser) + strlen(_tim) + 4; + key = malloc(len); + if (key == NULL) + return (ISC_R_NOMEMORY); + snprintf(key, len, "%s;%s;%s;%s", _frm, _to, _ser, _tim); + value.as_uinteger = now; + result = isccc_symtab_define(symtab, key, ISCCC_SYMTYPE_CCDUP, value, + isccc_symexists_reject); + if (result != ISC_R_SUCCESS) { + free(key); + return (result); + } + + return (ISC_R_SUCCESS); +} |