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