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