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