bind9/lib/dns/tkey.c
Ondřej Surý 11276087a7
Fix output token and GSS context leaks in TKEY/GSS-API error paths
In dst_gssapi_acceptctx(), rename outtoken to outtokenp (matching BIND
convention for output pointer parameters) and free the allocated output
token buffer on error in the cleanup path.

In process_gsstkey(), route the empty-principal error path through
cleanup via CLEANUP() instead of returning early, so that the output
token, GSS context, and TSIG key are all freed consistently by the
existing cleanup block.

(cherry picked from commit 6c46c85d02849fb659584275313529794039f433)
2026-05-07 13:09:18 +02:00

711 lines
19 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <inttypes.h>
#include <stdbool.h>
#if HAVE_GSSAPI_GSSAPI_H
#include <gssapi/gssapi.h>
#elif HAVE_GSSAPI_H
#include <gssapi.h>
#endif
#include <isc/buffer.h>
#include <isc/hex.h>
#include <isc/md.h>
#include <isc/mem.h>
#include <isc/nonce.h>
#include <isc/random.h>
#include <isc/result.h>
#include <isc/string.h>
#include <isc/util.h>
#include <dns/dnssec.h>
#include <dns/fixedname.h>
#include <dns/keyvalues.h>
#include <dns/log.h>
#include <dns/message.h>
#include <dns/name.h>
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
#include <dns/tkey.h>
#include <dns/tsig.h>
#include <dst/dst.h>
#include <dst/gssapi.h>
#include "dst_internal.h"
#include "tsig_p.h"
#define TEMP_BUFFER_SZ 8192
#define TKEY_RANDOM_AMOUNT 16
static void
tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2);
static void
tkey_log(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_REQUEST,
ISC_LOG_DEBUG(4), fmt, ap);
va_end(ap);
}
isc_result_t
dns_tkeyctx_create(isc_mem_t *mctx, dns_tkeyctx_t **tctxp) {
REQUIRE(mctx != NULL);
REQUIRE(tctxp != NULL && *tctxp == NULL);
dns_tkeyctx_t *tctx = isc_mem_get(mctx, sizeof(*tctx));
*tctx = (dns_tkeyctx_t){
.mctx = NULL,
};
isc_mem_attach(mctx, &tctx->mctx);
*tctxp = tctx;
return ISC_R_SUCCESS;
}
void
dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp) {
isc_mem_t *mctx = NULL;
dns_tkeyctx_t *tctx = NULL;
REQUIRE(tctxp != NULL && *tctxp != NULL);
tctx = *tctxp;
*tctxp = NULL;
mctx = tctx->mctx;
if (tctx->gssapi_keytab != NULL) {
isc_mem_free(mctx, tctx->gssapi_keytab);
}
if (tctx->gsscred != NULL) {
dst_gssapi_releasecred(&tctx->gsscred);
}
isc_mem_putanddetach(&mctx, tctx, sizeof(dns_tkeyctx_t));
}
static void
add_rdata_to_list(dns_message_t *msg, dns_name_t *name, dns_rdata_t *rdata,
uint32_t ttl, dns_namelist_t *namelist) {
isc_region_t r, newr;
dns_rdata_t *newrdata = NULL;
dns_name_t *newname = NULL;
dns_rdatalist_t *newlist = NULL;
dns_rdataset_t *newset = NULL;
isc_buffer_t *tmprdatabuf = NULL;
dns_message_gettemprdata(msg, &newrdata);
dns_rdata_toregion(rdata, &r);
isc_buffer_allocate(msg->mctx, &tmprdatabuf, r.length);
isc_buffer_availableregion(tmprdatabuf, &newr);
memmove(newr.base, r.base, r.length);
dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr);
dns_message_takebuffer(msg, &tmprdatabuf);
dns_message_gettempname(msg, &newname);
dns_name_copy(name, newname);
dns_message_gettemprdatalist(msg, &newlist);
newlist->rdclass = newrdata->rdclass;
newlist->type = newrdata->type;
newlist->ttl = ttl;
ISC_LIST_APPEND(newlist->rdata, newrdata, link);
dns_message_gettemprdataset(msg, &newset);
dns_rdatalist_tordataset(newlist, newset);
ISC_LIST_INIT(newname->list);
ISC_LIST_APPEND(newname->list, newset, link);
ISC_LIST_APPEND(*namelist, newname, link);
}
static void
free_namelist(dns_message_t *msg, dns_namelist_t *namelist) {
dns_name_t *name = NULL;
while ((name = ISC_LIST_HEAD(*namelist)) != NULL) {
dns_rdataset_t *set = NULL;
ISC_LIST_UNLINK(*namelist, name, link);
while ((set = ISC_LIST_HEAD(name->list)) != NULL) {
ISC_LIST_UNLINK(name->list, set, link);
if (dns_rdataset_isassociated(set)) {
dns_rdataset_disassociate(set);
}
dns_message_puttemprdataset(msg, &set);
}
dns_message_puttempname(msg, &name);
}
}
static isc_result_t
process_gsstkey(dns_message_t *msg, dns_name_t *name, dns_rdata_tkey_t *tkeyin,
dns_tkeyctx_t *tctx, dns_rdata_tkey_t *tkeyout,
dns_tsigkeyring_t *ring) {
isc_result_t result = ISC_R_SUCCESS;
dst_key_t *dstkey = NULL;
dns_tsigkey_t *tsigkey = NULL;
dns_fixedname_t fprincipal;
dns_name_t *principal = dns_fixedname_initname(&fprincipal);
isc_stdtime_t now = isc_stdtime_now();
isc_region_t intoken;
isc_buffer_t *outtoken = NULL;
dns_gss_ctx_id_t gss_ctx = NULL;
/*
* You have to define either a gss credential (principal) to
* accept with tkey-gssapi-credential, or you have to
* configure a specific keytab (with tkey-gssapi-keytab) in
* order to use gsstkey.
*/
if (tctx->gsscred == NULL && tctx->gssapi_keytab == NULL) {
tkey_log("process_gsstkey(): no tkey-gssapi-credential "
"or tkey-gssapi-keytab configured");
return DNS_R_REFUSED;
}
if (!dns_name_equal(&tkeyin->algorithm, DNS_TSIG_GSSAPI_NAME)) {
tkeyout->error = dns_tsigerror_badalg;
tkey_log("process_gsstkey(): dns_tsigerror_badalg");
return ISC_R_SUCCESS;
}
/*
* Note that tctx->gsscred may be NULL if tctx->gssapi_keytab is set
*/
intoken = (isc_region_t){ tkeyin->key, tkeyin->keylen };
result = dst_gssapi_acceptctx(tctx->gsscred, tctx->gssapi_keytab,
&intoken, &outtoken, &gss_ctx, principal,
tctx->mctx);
if (result != ISC_R_SUCCESS) {
tkeyout->error = dns_tsigerror_badkey;
tkey_log("process_gsstkey(): dns_tsigerror_badkey");
result = ISC_R_SUCCESS;
goto cleanup;
}
/*
* Multi-round GSS-API negotiation (GSS_S_CONTINUE_NEEDED) is
* rejected in dst_gssapi_acceptctx(), so if we reach here the
* negotiation is complete and the principal must be set.
*/
if (dns_name_countlabels(principal) == 0U) {
tkeyout->error = dns_tsigerror_badkey;
tkey_log("process_gsstkey(): "
"completed context with empty principal");
result = ISC_R_SUCCESS;
goto cleanup;
} else if (tsigkey == NULL) {
#if HAVE_GSSAPI
OM_uint32 gret, minor, lifetime;
#endif /* HAVE_GSSAPI */
uint32_t expire;
CHECK(dst_key_fromgssapi(name, gss_ctx, ring->mctx, &dstkey,
&intoken));
/*
* Limit keys to 1 hour or the context's lifetime whichever
* is smaller.
*/
expire = now + 3600;
#if HAVE_GSSAPI
gret = gss_context_time(&minor, gss_ctx, &lifetime);
if (gret == GSS_S_COMPLETE && now + lifetime < expire) {
expire = now + lifetime;
}
#endif /* HAVE_GSSAPI */
CHECK(dns_tsigkey_createfromkey(
name, dns__tsig_algfromname(&tkeyin->algorithm), dstkey,
true, false, principal, now, expire, ring->mctx,
&tsigkey));
CHECK(dns_tsigkeyring_add(ring, tsigkey));
dst_key_free(&dstkey);
tkeyout->inception = now;
tkeyout->expire = expire;
} else {
tkeyout->inception = tsigkey->inception;
tkeyout->expire = tsigkey->expire;
}
if (outtoken != NULL) {
tkeyout->key = isc_mem_get(tkeyout->mctx,
isc_buffer_usedlength(outtoken));
tkeyout->keylen = isc_buffer_usedlength(outtoken);
memmove(tkeyout->key, isc_buffer_base(outtoken),
isc_buffer_usedlength(outtoken));
isc_buffer_free(&outtoken);
} else {
tkeyout->key = isc_mem_get(tkeyout->mctx, tkeyin->keylen);
tkeyout->keylen = tkeyin->keylen;
memmove(tkeyout->key, tkeyin->key, tkeyin->keylen);
}
/*
* We found a TKEY to respond with. If the request is not TSIG signed,
* we need to make sure the response is signed (see RFC 3645, Section
* 2.2).
*/
if (tsigkey != NULL) {
if (msg->tsigkey == NULL && msg->sig0key == NULL) {
dns_message_settsigkey(msg, tsigkey);
}
dns_tsigkey_detach(&tsigkey);
}
return ISC_R_SUCCESS;
cleanup:
if (dstkey == NULL && gss_ctx != NULL) {
dst_gssapi_deletectx(tctx->mctx, &gss_ctx);
}
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
if (dstkey != NULL) {
dst_key_free(&dstkey);
}
if (outtoken != NULL) {
isc_buffer_free(&outtoken);
}
if (result != ISC_R_SUCCESS) {
tkey_log("process_gsstkey(): %s", isc_result_totext(result));
}
return result;
}
static isc_result_t
process_deletetkey(dns_name_t *signer, dns_name_t *name,
dns_rdata_tkey_t *tkeyin, dns_rdata_tkey_t *tkeyout,
dns_tsigkeyring_t *ring) {
isc_result_t result;
dns_tsigkey_t *tsigkey = NULL;
const dns_name_t *identity = NULL;
result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
if (result != ISC_R_SUCCESS) {
tkeyout->error = dns_tsigerror_badname;
return ISC_R_SUCCESS;
}
/*
* Only allow a delete if the identity that created the key is the
* same as the identity that signed the message.
*/
identity = dns_tsigkey_identity(tsigkey);
if (identity == NULL || !dns_name_equal(identity, signer)) {
dns_tsigkey_detach(&tsigkey);
return DNS_R_REFUSED;
}
/*
* Set the key to be deleted when no references are left. If the key
* was not generated with TKEY and is in the config file, it may be
* reloaded later.
*/
dns_tsigkey_delete(tsigkey);
/* Release the reference */
dns_tsigkey_detach(&tsigkey);
return ISC_R_SUCCESS;
}
isc_result_t
dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
dns_tsigkeyring_t *ring) {
isc_result_t result = ISC_R_SUCCESS;
dns_rdata_tkey_t tkeyin, tkeyout;
dns_name_t *qname = NULL, *name = NULL;
dns_name_t *keyname = NULL, *signer = NULL;
dns_name_t tsigner = DNS_NAME_INITEMPTY;
dns_fixedname_t fkeyname;
dns_rdataset_t *tkeyset = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_namelist_t namelist = ISC_LIST_INITIALIZER;
char tkeyoutdata[512];
isc_buffer_t tkeyoutbuf;
dns_tsigkey_t *tsigkey = NULL;
REQUIRE(msg != NULL);
REQUIRE(tctx != NULL);
REQUIRE(ring != NULL);
/*
* Interpret the question section.
*/
result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
if (result != ISC_R_SUCCESS) {
return DNS_R_FORMERR;
}
dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);
/*
* Look for a TKEY record that matches the question.
*/
result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
dns_rdatatype_tkey, 0, &name, &tkeyset);
if (result != ISC_R_SUCCESS) {
tkey_log("dns_tkey_processquery: couldn't find a TKEY "
"matching the question");
CHECK(DNS_R_FORMERR);
}
result = dns_rdataset_first(tkeyset);
if (result != ISC_R_SUCCESS) {
CHECK(DNS_R_FORMERR);
}
dns_rdataset_current(tkeyset, &rdata);
CHECK(dns_rdata_tostruct(&rdata, &tkeyin, NULL));
if (tkeyin.error != dns_rcode_noerror) {
CHECK(DNS_R_FORMERR);
}
/*
* Before we go any farther, verify that the message was signed.
* DNS_TKEYMODE_GSSAPI doesn't require a signature, but other
* modes do.
*/
result = dns_message_signer(msg, &tsigner);
if (result == ISC_R_SUCCESS) {
signer = &tsigner;
} else if (result != ISC_R_NOTFOUND ||
tkeyin.mode != DNS_TKEYMODE_GSSAPI)
{
tkey_log("dns_tkey_processquery: query was not "
"properly signed - rejecting");
CHECK(DNS_R_FORMERR);
}
tkeyout = (dns_rdata_tkey_t){
.common.rdclass = tkeyin.common.rdclass,
.common.rdtype = tkeyin.common.rdtype,
.common.link = ISC_LINK_INITIALIZER,
.mctx = msg->mctx,
.algorithm = DNS_NAME_INITEMPTY,
.mode = tkeyin.mode,
};
dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm);
switch (tkeyin.mode) {
case DNS_TKEYMODE_DELETE:
/*
* A delete operation uses the fully specified qname.
*/
keyname = qname;
CHECK(process_deletetkey(signer, keyname, &tkeyin, &tkeyout,
ring));
break;
case DNS_TKEYMODE_GSSAPI:
keyname = dns_fixedname_initname(&fkeyname);
if (!dns_name_equal(qname, dns_rootname)) {
unsigned int n = dns_name_countlabels(qname);
dns_name_copy(qname, keyname);
dns_name_getlabelsequence(keyname, 0, n - 1, keyname);
} else {
unsigned char randomdata[16];
char randomtext[32];
isc_buffer_t b;
isc_region_t r = {
.base = randomdata,
.length = sizeof(randomdata),
};
isc_nonce_buf(randomdata, sizeof(randomdata));
isc_buffer_init(&b, randomtext, sizeof(randomtext));
CHECK(isc_hex_totext(&r, 2, "", &b));
CHECK(dns_name_fromtext(keyname, &b, NULL, 0, NULL));
}
CHECK(dns_name_concatenate(keyname, dns_rootname, keyname,
NULL));
result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);
if (result == ISC_R_SUCCESS) {
tkeyout.error = dns_tsigerror_badname;
dns_tsigkey_detach(&tsigkey);
break;
} else if (result == ISC_R_NOTFOUND) {
CHECK(process_gsstkey(msg, keyname, &tkeyin, tctx,
&tkeyout, ring));
break;
}
goto cleanup;
case DNS_TKEYMODE_SERVERASSIGNED:
case DNS_TKEYMODE_RESOLVERASSIGNED:
result = DNS_R_NOTIMP;
goto cleanup;
default:
/*
* For unrecognized modes also use the fully specified qname.
*/
keyname = qname;
tkeyout.error = dns_tsigerror_badmode;
}
dns_rdata_init(&rdata);
isc_buffer_init(&tkeyoutbuf, tkeyoutdata, sizeof(tkeyoutdata));
result = dns_rdata_fromstruct(&rdata, tkeyout.common.rdclass,
tkeyout.common.rdtype, &tkeyout,
&tkeyoutbuf);
if (tkeyout.key != NULL) {
isc_mem_put(tkeyout.mctx, tkeyout.key, tkeyout.keylen);
}
CHECK(result);
CHECK(dns_message_reply(msg, true));
add_rdata_to_list(msg, keyname, &rdata, 0, &namelist);
while ((name = ISC_LIST_HEAD(namelist)) != NULL) {
ISC_LIST_UNLINK(namelist, name, link);
dns_message_addname(msg, name, DNS_SECTION_ANSWER);
}
return ISC_R_SUCCESS;
cleanup:
free_namelist(msg, &namelist);
return result;
}
static isc_result_t
buildquery(dns_message_t *msg, const dns_name_t *name, dns_rdata_tkey_t *tkey) {
dns_name_t *qname = NULL, *aname = NULL;
dns_rdataset_t *question = NULL, *tkeyset = NULL;
dns_rdatalist_t *tkeylist = NULL;
dns_rdata_t *rdata = NULL;
isc_buffer_t *dynbuf = NULL;
isc_result_t result;
unsigned int len;
REQUIRE(msg != NULL);
REQUIRE(name != NULL);
REQUIRE(tkey != NULL);
len = 16 + tkey->algorithm.length + tkey->keylen + tkey->otherlen;
isc_buffer_allocate(msg->mctx, &dynbuf, len);
dns_message_gettemprdata(msg, &rdata);
result = dns_rdata_fromstruct(rdata, dns_rdataclass_any,
dns_rdatatype_tkey, tkey, dynbuf);
if (result != ISC_R_SUCCESS) {
dns_message_puttemprdata(msg, &rdata);
isc_buffer_free(&dynbuf);
return result;
}
dns_message_takebuffer(msg, &dynbuf);
dns_message_gettempname(msg, &qname);
dns_message_gettempname(msg, &aname);
dns_message_gettemprdataset(msg, &question);
dns_rdataset_makequestion(question, dns_rdataclass_any,
dns_rdatatype_tkey);
dns_message_gettemprdatalist(msg, &tkeylist);
tkeylist->rdclass = dns_rdataclass_any;
tkeylist->type = dns_rdatatype_tkey;
ISC_LIST_APPEND(tkeylist->rdata, rdata, link);
dns_message_gettemprdataset(msg, &tkeyset);
dns_rdatalist_tordataset(tkeylist, tkeyset);
dns_name_copy(name, qname);
dns_name_copy(name, aname);
ISC_LIST_APPEND(qname->list, question, link);
ISC_LIST_APPEND(aname->list, tkeyset, link);
dns_message_addname(msg, qname, DNS_SECTION_QUESTION);
dns_message_addname(msg, aname, DNS_SECTION_ADDITIONAL);
return ISC_R_SUCCESS;
}
isc_result_t
dns_tkey_buildgssquery(dns_message_t *msg, const dns_name_t *name,
const dns_name_t *gname, uint32_t lifetime,
dns_gss_ctx_id_t *context, isc_mem_t *mctx,
char **err_message) {
dns_rdata_tkey_t tkey;
isc_result_t result;
isc_stdtime_t now = isc_stdtime_now();
isc_buffer_t token;
unsigned char array[TEMP_BUFFER_SZ];
REQUIRE(msg != NULL);
REQUIRE(name != NULL);
REQUIRE(gname != NULL);
REQUIRE(context != NULL);
REQUIRE(mctx != NULL);
isc_buffer_init(&token, array, sizeof(array));
result = dst_gssapi_initctx(gname, NULL, &token, context, mctx,
err_message);
if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
return result;
}
tkey = (dns_rdata_tkey_t){
.common.rdclass = dns_rdataclass_any,
.common.rdtype = dns_rdatatype_tkey,
.common.link = ISC_LINK_INITIALIZER,
.inception = now,
.expire = now + lifetime,
.algorithm = DNS_NAME_INITEMPTY,
.mode = DNS_TKEYMODE_GSSAPI,
.key = isc_buffer_base(&token),
.keylen = isc_buffer_usedlength(&token),
};
dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
return buildquery(msg, name, &tkey);
}
static isc_result_t
find_tkey(dns_message_t *msg, dns_name_t **name, dns_rdata_t *rdata,
int section) {
isc_result_t result;
result = dns_message_firstname(msg, section);
while (result == ISC_R_SUCCESS) {
dns_rdataset_t *tkeyset = NULL;
dns_name_t *cur = NULL;
dns_message_currentname(msg, section, &cur);
result = dns_message_findtype(cur, dns_rdatatype_tkey, 0,
&tkeyset);
if (result == ISC_R_SUCCESS) {
result = dns_rdataset_first(tkeyset);
if (result != ISC_R_SUCCESS) {
break;
}
dns_rdataset_current(tkeyset, rdata);
*name = cur;
return ISC_R_SUCCESS;
}
result = dns_message_nextname(msg, section);
}
if (result == ISC_R_NOMORE) {
return ISC_R_NOTFOUND;
}
return result;
}
isc_result_t
dns_tkey_gssnegotiate(dns_message_t *qmsg, dns_message_t *rmsg,
const dns_name_t *server, dns_gss_ctx_id_t *context,
dns_tsigkey_t **outkey, dns_tsigkeyring_t *ring,
char **err_message) {
isc_result_t result;
dns_rdata_t rtkeyrdata = DNS_RDATA_INIT, qtkeyrdata = DNS_RDATA_INIT;
dns_name_t *tkeyname = NULL;
dns_rdata_tkey_t rtkey, qtkey, tkey;
isc_buffer_t intoken, outtoken;
dst_key_t *dstkey = NULL;
unsigned char array[TEMP_BUFFER_SZ];
dns_tsigkey_t *tsigkey = NULL;
REQUIRE(qmsg != NULL);
REQUIRE(rmsg != NULL);
REQUIRE(server != NULL);
REQUIRE(outkey == NULL || *outkey == NULL);
if (rmsg->rcode != dns_rcode_noerror) {
return dns_result_fromrcode(rmsg->rcode);
}
CHECK(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
CHECK(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
CHECK(find_tkey(qmsg, &tkeyname, &qtkeyrdata, DNS_SECTION_ADDITIONAL));
CHECK(dns_rdata_tostruct(&qtkeyrdata, &qtkey, NULL));
if (rtkey.error != dns_rcode_noerror ||
rtkey.mode != DNS_TKEYMODE_GSSAPI ||
!dns_name_equal(&rtkey.algorithm, &qtkey.algorithm))
{
tkey_log("dns_tkey_gssnegotiate: tkey mode invalid "
"or error set(4)");
CHECK(DNS_R_INVALIDTKEY);
}
isc_buffer_init(&intoken, rtkey.key, rtkey.keylen);
isc_buffer_init(&outtoken, array, sizeof(array));
result = dst_gssapi_initctx(server, &intoken, &outtoken, context,
ring->mctx, err_message);
if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
return result;
}
if (result == DNS_R_CONTINUE) {
tkey = (dns_rdata_tkey_t){
.common.rdclass = dns_rdataclass_any,
.common.rdtype = dns_rdatatype_tkey,
.common.link = ISC_LINK_INITIALIZER,
.inception = qtkey.inception,
.expire = qtkey.expire,
.algorithm = DNS_NAME_INITEMPTY,
.mode = DNS_TKEYMODE_GSSAPI,
.key = isc_buffer_base(&outtoken),
.keylen = isc_buffer_usedlength(&outtoken),
};
dns_name_clone(DNS_TSIG_GSSAPI_NAME, &tkey.algorithm);
dns_message_reset(qmsg, DNS_MESSAGE_INTENTRENDER);
CHECK(buildquery(qmsg, tkeyname, &tkey));
return DNS_R_CONTINUE;
}
CHECK(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey,
NULL));
/*
* GSS negotiation is complete (CONTINUE returned earlier).
* Create the TSIG key from the established context.
*/
CHECK(dns_tsigkey_createfromkey(tkeyname, DST_ALG_GSSAPI, dstkey, true,
false, NULL, rtkey.inception,
rtkey.expire, ring->mctx, &tsigkey));
CHECK(dns_tsigkeyring_add(ring, tsigkey));
if (outkey == NULL) {
dns_tsigkey_detach(&tsigkey);
} else {
*outkey = tsigkey;
}
dst_key_free(&dstkey);
return result;
cleanup:
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
if (dstkey != NULL) {
dst_key_free(&dstkey);
}
return result;
}