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