1 /* $NetBSD: tkey.c,v 1.15 2025/01/26 16:25:25 christos Exp $ */ 2 3 /* 4 * Copyright (C) Internet Systems Consortium, Inc. ("ISC") 5 * 6 * SPDX-License-Identifier: MPL-2.0 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 /*! \file */ 17 18 #include <inttypes.h> 19 #include <stdbool.h> 20 21 #if HAVE_GSSAPI_GSSAPI_H 22 #include <gssapi/gssapi.h> 23 #elif HAVE_GSSAPI_H 24 #include <gssapi.h> 25 #endif 26 27 #include <isc/buffer.h> 28 #include <isc/hex.h> 29 #include <isc/md.h> 30 #include <isc/mem.h> 31 #include <isc/nonce.h> 32 #include <isc/random.h> 33 #include <isc/result.h> 34 #include <isc/string.h> 35 #include <isc/util.h> 36 37 #include <dns/dnssec.h> 38 #include <dns/fixedname.h> 39 #include <dns/keyvalues.h> 40 #include <dns/log.h> 41 #include <dns/message.h> 42 #include <dns/name.h> 43 #include <dns/rdata.h> 44 #include <dns/rdatalist.h> 45 #include <dns/rdataset.h> 46 #include <dns/rdatastruct.h> 47 #include <dns/result.h> 48 #include <dns/tkey.h> 49 #include <dns/tsig.h> 50 51 #include <dst/dst.h> 52 #include <dst/gssapi.h> 53 54 #include "dst_internal.h" 55 #include "tsig_p.h" 56 57 #define TEMP_BUFFER_SZ 8192 58 #define TKEY_RANDOM_AMOUNT 16 59 60 #define RETERR(x) \ 61 do { \ 62 result = (x); \ 63 if (result != ISC_R_SUCCESS) \ 64 goto failure; \ 65 } while (0) 66 67 static void 68 tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2); 69 70 static void 71 tkey_log(const char *fmt, ...) { 72 va_list ap; 73 74 va_start(ap, fmt); 75 isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_REQUEST, 76 ISC_LOG_DEBUG(4), fmt, ap); 77 va_end(ap); 78 } 79 80 isc_result_t 81 dns_tkeyctx_create(isc_mem_t *mctx, dns_tkeyctx_t **tctxp) { 82 REQUIRE(mctx != NULL); 83 REQUIRE(tctxp != NULL && *tctxp == NULL); 84 85 dns_tkeyctx_t *tctx = isc_mem_get(mctx, sizeof(*tctx)); 86 *tctx = (dns_tkeyctx_t){ 87 .mctx = NULL, 88 }; 89 isc_mem_attach(mctx, &tctx->mctx); 90 91 *tctxp = tctx; 92 return ISC_R_SUCCESS; 93 } 94 95 void 96 dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp) { 97 isc_mem_t *mctx = NULL; 98 dns_tkeyctx_t *tctx = NULL; 99 100 REQUIRE(tctxp != NULL && *tctxp != NULL); 101 102 tctx = *tctxp; 103 *tctxp = NULL; 104 mctx = tctx->mctx; 105 106 if (tctx->domain != NULL) { 107 if (dns_name_dynamic(tctx->domain)) { 108 dns_name_free(tctx->domain, mctx); 109 } 110 isc_mem_put(mctx, tctx->domain, sizeof(dns_name_t)); 111 } 112 if (tctx->gssapi_keytab != NULL) { 113 isc_mem_free(mctx, tctx->gssapi_keytab); 114 } 115 if (tctx->gsscred != NULL) { 116 dst_gssapi_releasecred(&tctx->gsscred); 117 } 118 isc_mem_putanddetach(&mctx, tctx, sizeof(dns_tkeyctx_t)); 119 } 120 121 static void 122 add_rdata_to_list(dns_message_t *msg, dns_name_t *name, dns_rdata_t *rdata, 123 uint32_t ttl, dns_namelist_t *namelist) { 124 isc_region_t r, newr; 125 dns_rdata_t *newrdata = NULL; 126 dns_name_t *newname = NULL; 127 dns_rdatalist_t *newlist = NULL; 128 dns_rdataset_t *newset = NULL; 129 isc_buffer_t *tmprdatabuf = NULL; 130 131 dns_message_gettemprdata(msg, &newrdata); 132 133 dns_rdata_toregion(rdata, &r); 134 isc_buffer_allocate(msg->mctx, &tmprdatabuf, r.length); 135 isc_buffer_availableregion(tmprdatabuf, &newr); 136 memmove(newr.base, r.base, r.length); 137 dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr); 138 dns_message_takebuffer(msg, &tmprdatabuf); 139 140 dns_message_gettempname(msg, &newname); 141 dns_name_copy(name, newname); 142 143 dns_message_gettemprdatalist(msg, &newlist); 144 newlist->rdclass = newrdata->rdclass; 145 newlist->type = newrdata->type; 146 newlist->ttl = ttl; 147 ISC_LIST_APPEND(newlist->rdata, newrdata, link); 148 149 dns_message_gettemprdataset(msg, &newset); 150 dns_rdatalist_tordataset(newlist, newset); 151 152 ISC_LIST_INIT(newname->list); 153 ISC_LIST_APPEND(newname->list, newset, link); 154 155 ISC_LIST_APPEND(*namelist, newname, link); 156 } 157 158 static void 159 free_namelist(dns_message_t *msg, dns_namelist_t *namelist) { 160 dns_name_t *name = NULL; 161 162 while ((name = ISC_LIST_HEAD(*namelist)) != NULL) { 163 dns_rdataset_t *set = NULL; 164 ISC_LIST_UNLINK(*namelist, name, link); 165 while ((set = ISC_LIST_HEAD(name->list)) != NULL) { 166 ISC_LIST_UNLINK(name->list, set, link); 167 if (dns_rdataset_isassociated(set)) { 168 dns_rdataset_disassociate(set); 169 } 170 dns_message_puttemprdataset(msg, &set); 171 } 172 dns_message_puttempname(msg, &name); 173 } 174 } 175 176 static isc_result_t 177 process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin, 178 dns_tkeyctx_t *tctx, dns_rdata_tkey_t *tkeyout, 179 dns_tsigkeyring_t *ring) { 180 isc_result_t result = ISC_R_SUCCESS; 181 dst_key_t *dstkey = NULL; 182 dns_tsigkey_t *tsigkey = NULL; 183 dns_fixedname_t fprincipal; 184 dns_name_t *principal = dns_fixedname_initname(&fprincipal); 185 isc_stdtime_t now = isc_stdtime_now(); 186 isc_region_t intoken; 187 isc_buffer_t *outtoken = NULL; 188 dns_gss_ctx_id_t gss_ctx = NULL; 189 190 /* 191 * You have to define either a gss credential (principal) to 192 * accept with tkey-gssapi-credential, or you have to 193 * configure a specific keytab (with tkey-gssapi-keytab) in 194 * order to use gsstkey. 195 */ 196 if (tctx->gsscred == NULL && tctx->gssapi_keytab == NULL) { 197 tkey_log("process_gsstkey(): no tkey-gssapi-credential " 198 "or tkey-gssapi-keytab configured"); 199 return DNS_R_REFUSED; 200 } 201 202 if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPI_NAME)) { 203 tkeyout->error = dns_tsigerror_badalg; 204 tkey_log("process_gsstkey(): dns_tsigerror_badalg"); 205 return ISC_R_SUCCESS; 206 } 207 208 /* 209 * XXXDCL need to check for key expiry per 4.1.1 210 * XXXDCL need a way to check fully established, perhaps w/key_flags 211 */ 212 result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring); 213 if (result == ISC_R_SUCCESS) { 214 gss_ctx = dst_key_getgssctx(tsigkey->key); 215 } 216 217 /* 218 * Note that tctx->gsscred may be NULL if tctx->gssapi_keytab is set 219 */ 220 intoken = (isc_region_t){ tkeyin->key, tkeyin->keylen }; 221 result = dst_gssapi_acceptctx(tctx->gsscred, tctx->gssapi_keytab, 222 &intoken, &outtoken, &gss_ctx, principal, 223 tctx->mctx); 224 if (result == DNS_R_INVALIDTKEY) { 225 if (tsigkey != NULL) { 226 dns_tsigkey_detach(&tsigkey); 227 } 228 tkeyout->error = dns_tsigerror_badkey; 229 tkey_log("process_gsstkey(): dns_tsigerror_badkey"); 230 return ISC_R_SUCCESS; 231 } 232 if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) { 233 goto failure; 234 } 235 236 /* 237 * XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times. 238 */ 239 if (dns_name_countlabels(principal) == 0U) { 240 if (tsigkey != NULL) { 241 dns_tsigkey_detach(&tsigkey); 242 } 243 } else if (tsigkey == NULL) { 244 #if HAVE_GSSAPI 245 OM_uint32 gret, minor, lifetime; 246 #endif /* HAVE_GSSAPI */ 247 uint32_t expire; 248 249 RETERR(dst_key_fromgssapi(name, gss_ctx, ring->mctx, &dstkey, 250 &intoken)); 251 /* 252 * Limit keys to 1 hour or the context's lifetime whichever 253 * is smaller. 254 */ 255 expire = now + 3600; 256 #if HAVE_GSSAPI 257 gret = gss_context_time(&minor, gss_ctx, &lifetime); 258 if (gret == GSS_S_COMPLETE && now + lifetime < expire) { 259 expire = now + lifetime; 260 } 261 #endif /* HAVE_GSSAPI */ 262 RETERR(dns_tsigkey_createfromkey( 263 name, dns__tsig_algfromname(&tkeyin->algorithm), dstkey, 264 true, false, principal, now, expire, ring->mctx, 265 &tsigkey)); 266 RETERR(dns_tsigkeyring_add(ring, tsigkey)); 267 dst_key_free(&dstkey); 268 tkeyout->inception = now; 269 tkeyout->expire = expire; 270 } else { 271 tkeyout->inception = tsigkey->inception; 272 tkeyout->expire = tsigkey->expire; 273 } 274 275 if (outtoken != NULL) { 276 tkeyout->key = isc_mem_get(tkeyout->mctx, 277 isc_buffer_usedlength(outtoken)); 278 tkeyout->keylen = isc_buffer_usedlength(outtoken); 279 memmove(tkeyout->key, isc_buffer_base(outtoken), 280 isc_buffer_usedlength(outtoken)); 281 isc_buffer_free(&outtoken); 282 } else { 283 tkeyout->key = isc_mem_get(tkeyout->mctx, tkeyin->keylen); 284 tkeyout->keylen = tkeyin->keylen; 285 memmove(tkeyout->key, tkeyin->key, tkeyin->keylen); 286 } 287 288 /* 289 * We found a TKEY to respond with. If the request is not TSIG signed, 290 * we need to make sure the response is signed (see RFC 3645, Section 291 * 2.2). 292 */ 293 if (tsigkey != NULL) { 294 if (msg->tsigkey == NULL && msg->sig0key == NULL) { 295 dns_message_settsigkey(msg, tsigkey); 296 } 297 dns_tsigkey_detach(&tsigkey); 298 } 299 300 return ISC_R_SUCCESS; 301 302 failure: 303 if (tsigkey != NULL) { 304 dns_tsigkey_detach(&tsigkey); 305 } 306 if (dstkey != NULL) { 307 dst_key_free(&dstkey); 308 } 309 if (outtoken != NULL) { 310 isc_buffer_free(&outtoken); 311 } 312 313 tkey_log("process_gsstkey(): %s", isc_result_totext(result)); 314 return result; 315 } 316 317 static isc_result_t 318 process_deletetkey(dns_name_t *signer, dns_name_t *name, 319 dns_rdata_tkey_t *tkeyin, dns_rdata_tkey_t *tkeyout, 320 dns_tsigkeyring_t *ring) { 321 isc_result_t result; 322 dns_tsigkey_t *tsigkey = NULL; 323 const dns_name_t *identity = NULL; 324 325 result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring); 326 if (result != ISC_R_SUCCESS) { 327 tkeyout->error = dns_tsigerror_badname; 328 return ISC_R_SUCCESS; 329 } 330 331 /* 332 * Only allow a delete if the identity that created the key is the 333 * same as the identity that signed the message. 334 */ 335 identity = dns_tsigkey_identity(tsigkey); 336 if (identity == NULL || !dns_name_equal(identity, signer)) { 337 dns_tsigkey_detach(&tsigkey); 338 return DNS_R_REFUSED; 339 } 340 341 /* 342 * Set the key to be deleted when no references are left. If the key 343 * was not generated with TKEY and is in the config file, it may be 344 * reloaded later. 345 */ 346 dns_tsigkey_delete(tsigkey); 347 348 /* Release the reference */ 349 dns_tsigkey_detach(&tsigkey); 350 351 return ISC_R_SUCCESS; 352 } 353 354 isc_result_t 355 dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx, 356 dns_tsigkeyring_t *ring) { 357 isc_result_t result = ISC_R_SUCCESS; 358 dns_rdata_tkey_t tkeyin, tkeyout; 359 dns_name_t *qname = NULL, *name = NULL; 360 dns_name_t *keyname = NULL, *signer = NULL; 361 dns_name_t tsigner = DNS_NAME_INITEMPTY; 362 dns_fixedname_t fkeyname; 363 dns_rdataset_t *tkeyset = NULL; 364 dns_rdata_t rdata = DNS_RDATA_INIT; 365 dns_namelist_t namelist = ISC_LIST_INITIALIZER; 366 char tkeyoutdata[512]; 367 isc_buffer_t tkeyoutbuf; 368 dns_tsigkey_t *tsigkey = NULL; 369 370 REQUIRE(msg != NULL); 371 REQUIRE(tctx != NULL); 372 REQUIRE(ring != NULL); 373 374 /* 375 * Interpret the question section. 376 */ 377 result = dns_message_firstname(msg, DNS_SECTION_QUESTION); 378 if (result != ISC_R_SUCCESS) { 379 return DNS_R_FORMERR; 380 } 381 382 dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname); 383 384 /* 385 * Look for a TKEY record that matches the question. 386 */ 387 result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname, 388 dns_rdatatype_tkey, 0, &name, &tkeyset); 389 if (result != ISC_R_SUCCESS) { 390 result = DNS_R_FORMERR; 391 tkey_log("dns_tkey_processquery: couldn't find a TKEY " 392 "matching the question"); 393 goto failure; 394 } 395 396 result = dns_rdataset_first(tkeyset); 397 if (result != ISC_R_SUCCESS) { 398 result = DNS_R_FORMERR; 399 goto failure; 400 } 401 402 dns_rdataset_current(tkeyset, &rdata); 403 RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL)); 404 405 if (tkeyin.error != dns_rcode_noerror) { 406 result = DNS_R_FORMERR; 407 goto failure; 408 } 409 410 /* 411 * Before we go any farther, verify that the message was signed. 412 * DNS_TKEYMODE_GSSAPI doesn't require a signature, but other 413 * modes do. 414 */ 415 result = dns_message_signer(msg, &tsigner); 416 if (result == ISC_R_SUCCESS) { 417 signer = &tsigner; 418 } else if (result != ISC_R_NOTFOUND || 419 tkeyin.mode != DNS_TKEYMODE_GSSAPI) 420 { 421 tkey_log("dns_tkey_processquery: query was not " 422 "properly signed - rejecting"); 423 result = DNS_R_FORMERR; 424 goto failure; 425 } 426 427 tkeyout = (dns_rdata_tkey_t){ 428 .common.rdclass = tkeyin.common.rdclass, 429 .common.rdtype = tkeyin.common.rdtype, 430 .common.link = ISC_LINK_INITIALIZER, 431 .mctx = msg->mctx, 432 .algorithm = DNS_NAME_INITEMPTY, 433 .mode = tkeyin.mode, 434 }; 435 dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm); 436 437 switch (tkeyin.mode) { 438 case DNS_TKEYMODE_DELETE: 439 /* 440 * A delete operation uses the fully specified qname. 441 */ 442 RETERR(process_deletetkey(signer, qname, &tkeyin, &tkeyout, 443 ring)); 444 break; 445 case DNS_TKEYMODE_GSSAPI: 446 /* 447 * For non-delete operations we do this: 448 * 449 * if (qname != ".") 450 * keyname = qname + defaultdomain 451 * else 452 * keyname = <random hex> + defaultdomain 453 */ 454 if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI) 455 { 456 tkey_log("dns_tkey_processquery: tkey-domain not set"); 457 result = DNS_R_REFUSED; 458 goto failure; 459 } 460 461 keyname = dns_fixedname_initname(&fkeyname); 462 463 if (!dns_name_equal(qname, dns_rootname)) { 464 unsigned int n = dns_name_countlabels(qname); 465 dns_name_copy(qname, keyname); 466 dns_name_getlabelsequence(keyname, 0, n - 1, keyname); 467 } else { 468 unsigned char randomdata[16]; 469 char randomtext[32]; 470 isc_buffer_t b; 471 isc_region_t r = { 472 .base = randomdata, 473 .length = sizeof(randomdata), 474 }; 475 476 isc_nonce_buf(randomdata, sizeof(randomdata)); 477 isc_buffer_init(&b, randomtext, sizeof(randomtext)); 478 RETERR(isc_hex_totext(&r, 2, "", &b)); 479 RETERR(dns_name_fromtext(keyname, &b, NULL, 0, NULL)); 480 } 481 RETERR(dns_name_concatenate(keyname, dns_rootname, keyname, 482 NULL)); 483 484 result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring); 485 if (result == ISC_R_SUCCESS) { 486 tkeyout.error = dns_tsigerror_badname; 487 dns_tsigkey_detach(&tsigkey); 488 break; 489 } else if (result == ISC_R_NOTFOUND) { 490 RETERR(process_gsstkey(msg, keyname, &tkeyin, tctx, 491 &tkeyout, ring)); 492 break; 493 } 494 goto failure; 495 case DNS_TKEYMODE_SERVERASSIGNED: 496 case DNS_TKEYMODE_RESOLVERASSIGNED: 497 result = DNS_R_NOTIMP; 498 goto failure; 499 default: 500 tkeyout.error = dns_tsigerror_badmode; 501 } 502 503 dns_rdata_init(&rdata); 504 isc_buffer_init(&tkeyoutbuf, tkeyoutdata, sizeof(tkeyoutdata)); 505 result = dns_rdata_fromstruct(&rdata, tkeyout.common.rdclass, 506 tkeyout.common.rdtype, &tkeyout, 507 &tkeyoutbuf); 508 if (tkeyout.key != NULL) { 509 isc_mem_put(tkeyout.mctx, tkeyout.key, tkeyout.keylen); 510 } 511 RETERR(result); 512 513 RETERR(dns_message_reply(msg, true)); 514 add_rdata_to_list(msg, keyname, &rdata, 0, &namelist); 515 while ((name = ISC_LIST_HEAD(namelist)) != NULL) { 516 ISC_LIST_UNLINK(namelist, name, link); 517 dns_message_addname(msg, name, DNS_SECTION_ANSWER); 518 } 519 return ISC_R_SUCCESS; 520 521 failure: 522 free_namelist(msg, &namelist); 523 return result; 524 } 525 526 static isc_result_t 527 buildquery(dns_message_t *msg, const dns_name_t *name, dns_rdata_tkey_t *tkey) { 528 dns_name_t *qname = NULL, *aname = NULL; 529 dns_rdataset_t *question = NULL, *tkeyset = NULL; 530 dns_rdatalist_t *tkeylist = NULL; 531 dns_rdata_t *rdata = NULL; 532 isc_buffer_t *dynbuf = NULL; 533 isc_result_t result; 534 unsigned int len; 535 536 REQUIRE(msg != NULL); 537 REQUIRE(name != NULL); 538 REQUIRE(tkey != NULL); 539 540 len = 16 + tkey->algorithm.length + tkey->keylen + tkey->otherlen; 541 isc_buffer_allocate(msg->mctx, &dynbuf, len); 542 dns_message_gettemprdata(msg, &rdata); 543 result = dns_rdata_fromstruct(rdata, dns_rdataclass_any, 544 dns_rdatatype_tkey, tkey, dynbuf); 545 if (result != ISC_R_SUCCESS) { 546 dns_message_puttemprdata(msg, &rdata); 547 isc_buffer_free(&dynbuf); 548 return result; 549 } 550 dns_message_takebuffer(msg, &dynbuf); 551 552 dns_message_gettempname(msg, &qname); 553 dns_message_gettempname(msg, &aname); 554 555 dns_message_gettemprdataset(msg, &question); 556 dns_rdataset_makequestion(question, dns_rdataclass_any, 557 dns_rdatatype_tkey); 558 559 dns_message_gettemprdatalist(msg, &tkeylist); 560 tkeylist->rdclass = dns_rdataclass_any; 561 tkeylist->type = dns_rdatatype_tkey; 562 ISC_LIST_APPEND(tkeylist->rdata, rdata, link); 563 564 dns_message_gettemprdataset(msg, &tkeyset); 565 dns_rdatalist_tordataset(tkeylist, tkeyset); 566 567 dns_name_copy(name, qname); 568 dns_name_copy(name, aname); 569 570 ISC_LIST_APPEND(qname->list, question, link); 571 ISC_LIST_APPEND(aname->list, tkeyset, link); 572 573 dns_message_addname(msg, qname, DNS_SECTION_QUESTION); 574 dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL); 575 576 return ISC_R_SUCCESS; 577 } 578 579 isc_result_t 580 dns_tkey_buildgssquery(dns_message_t *msg, const dns_name_t *name, 581 const dns_name_t *gname, uint32_t lifetime, 582 dns_gss_ctx_id_t *context, isc_mem_t *mctx, 583 char **err_message) { 584 dns_rdata_tkey_t tkey; 585 isc_result_t result; 586 isc_stdtime_t now = isc_stdtime_now(); 587 isc_buffer_t token; 588 unsigned char array[TEMP_BUFFER_SZ]; 589 590 REQUIRE(msg != NULL); 591 REQUIRE(name != NULL); 592 REQUIRE(gname != NULL); 593 REQUIRE(context != NULL); 594 REQUIRE(mctx != NULL); 595 596 isc_buffer_init(&token, array, sizeof(array)); 597 result = dst_gssapi_initctx(gname, NULL, &token, context, mctx, 598 err_message); 599 if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) { 600 return result; 601 } 602 603 tkey = (dns_rdata_tkey_t){ 604 .common.rdclass = dns_rdataclass_any, 605 .common.rdtype = dns_rdatatype_tkey, 606 .common.link = ISC_LINK_INITIALIZER, 607 .inception = now, 608 .expire = now + lifetime, 609 .algorithm = DNS_NAME_INITEMPTY, 610 .mode = DNS_TKEYMODE_GSSAPI, 611 .key = isc_buffer_base(&token), 612 .keylen = isc_buffer_usedlength(&token), 613 }; 614 dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm); 615 616 return buildquery(msg, name, &tkey); 617 } 618 619 static isc_result_t 620 find_tkey(dns_message_t *msg, dns_name_t **name, dns_rdata_t *rdata, 621 int section) { 622 isc_result_t result; 623 624 result = dns_message_firstname(msg, section); 625 while (result == ISC_R_SUCCESS) { 626 dns_rdataset_t *tkeyset = NULL; 627 dns_name_t *cur = NULL; 628 629 dns_message_currentname(msg, section, &cur); 630 result = dns_message_findtype(cur, dns_rdatatype_tkey, 0, 631 &tkeyset); 632 if (result == ISC_R_SUCCESS) { 633 result = dns_rdataset_first(tkeyset); 634 if (result != ISC_R_SUCCESS) { 635 break; 636 } 637 638 dns_rdataset_current(tkeyset, rdata); 639 *name = cur; 640 return ISC_R_SUCCESS; 641 } 642 result = dns_message_nextname(msg, section); 643 } 644 if (result == ISC_R_NOMORE) { 645 return ISC_R_NOTFOUND; 646 } 647 return result; 648 } 649 650 isc_result_t 651 dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg, 652 const dns_name_t *server, dns_gss_ctx_id_t *context, 653 dns_tsigkey_t **outkey, dns_tsigkeyring_t *ring, 654 char **err_message) { 655 isc_result_t result; 656 dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT; 657 dns_name_t *tkeyname = NULL; 658 dns_rdata_tkey_t rtkey, qtkey, tkey; 659 isc_buffer_t intoken, outtoken; 660 dst_key_t *dstkey = NULL; 661 unsigned char array[TEMP_BUFFER_SZ]; 662 dns_tsigkey_t *tsigkey = NULL; 663 664 REQUIRE(qmsg != NULL); 665 REQUIRE(rmsg != NULL); 666 REQUIRE(server != NULL); 667 REQUIRE(outkey == NULL || *outkey == NULL); 668 669 if (rmsg->rcode != dns_rcode_noerror) { 670 return dns_result_fromrcode(rmsg->rcode); 671 } 672 673 RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER)); 674 RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL)); 675 676 RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata, DNS_SECTION_ADDITIONAL)); 677 RETERR(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL)); 678 679 if (rtkey.error != dns_rcode_noerror || 680 rtkey.mode != DNS_TKEYMODE_GSSAPI || 681 !dns_name_equal(&rtkey.algorithm, &qtkey.algorithm)) 682 { 683 tkey_log("dns_tkey_gssnegotiate: tkey mode invalid " 684 "or error set(4)"); 685 result = DNS_R_INVALIDTKEY; 686 goto failure; 687 } 688 689 isc_buffer_init(&intoken, rtkey.key, rtkey.keylen); 690 isc_buffer_init(&outtoken, array, sizeof(array)); 691 692 result = dst_gssapi_initctx(server, &intoken, &outtoken, context, 693 ring->mctx, err_message); 694 if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) { 695 return result; 696 } 697 698 if (result == DNS_R_CONTINUE) { 699 tkey = (dns_rdata_tkey_t){ 700 .common.rdclass = dns_rdataclass_any, 701 .common.rdtype = dns_rdatatype_tkey, 702 .common.link = ISC_LINK_INITIALIZER, 703 .inception = qtkey.inception, 704 .expire = qtkey.expire, 705 .algorithm = DNS_NAME_INITEMPTY, 706 .mode = DNS_TKEYMODE_GSSAPI, 707 .key = isc_buffer_base(&outtoken), 708 .keylen = isc_buffer_usedlength(&outtoken), 709 }; 710 711 dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm); 712 713 dns_message_reset(qmsg, DNS_MESSAGE_INTENTRENDER); 714 RETERR(buildquery(qmsg, tkeyname, &tkey)); 715 return DNS_R_CONTINUE; 716 } 717 718 RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey, 719 NULL)); 720 721 /* 722 * XXXSRA This seems confused. If we got CONTINUE from initctx, 723 * the GSS negotiation hasn't completed yet, so we can't sign 724 * anything yet. 725 */ 726 RETERR(dns_tsigkey_createfromkey(tkeyname, DST_ALG_GSSAPI, dstkey, true, 727 false, NULL, rtkey.inception, 728 rtkey.expire, ring->mctx, &tsigkey)); 729 RETERR(dns_tsigkeyring_add(ring, tsigkey)); 730 if (outkey == NULL) { 731 dns_tsigkey_detach(&tsigkey); 732 } else { 733 *outkey = tsigkey; 734 } 735 736 dst_key_free(&dstkey); 737 return result; 738 739 failure: 740 if (tsigkey != NULL) { 741 dns_tsigkey_detach(&tsigkey); 742 } 743 if (dstkey != NULL) { 744 dst_key_free(&dstkey); 745 } 746 return result; 747 } 748