2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-22 18:19:42 +00:00
bind/lib/dns/tkey.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

744 lines
20 KiB
C
Raw Normal View History

1999-10-26 15:39:59 +00:00
/*
2011-01-08 23:47:01 +00:00
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
1999-10-26 15:39:59 +00:00
*
* SPDX-License-Identifier: MPL-2.0
*
1999-10-26 15:39:59 +00:00
* 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/.
*
1999-10-26 15:39:59 +00:00
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
1999-10-26 15:39:59 +00:00
#include <inttypes.h>
#include <stdbool.h>
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#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/log.h>
#include <isc/md.h>
2000-01-21 20:18:41 +00:00
#include <isc/mem.h>
#include <isc/nonce.h>
#include <isc/random.h>
#include <isc/result.h>
#include <isc/string.h>
2000-04-28 01:12:23 +00:00
#include <isc/util.h>
1999-10-26 15:39:59 +00:00
#include <dns/dnssec.h>
2001-02-14 00:43:10 +00:00
#include <dns/fixedname.h>
1999-10-26 15:39:59 +00:00
#include <dns/keyvalues.h>
#include <dns/message.h>
#include <dns/name.h>
1999-10-26 15:39:59 +00:00
#include <dns/rdata.h>
#include <dns/rdatalist.h>
#include <dns/rdataset.h>
#include <dns/rdatastruct.h>
#include <dns/result.h>
1999-10-26 15:39:59 +00:00
#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
1999-10-26 17:25:07 +00:00
#define RETERR(x) \
do { \
1999-10-26 15:39:59 +00:00
result = (x); \
if (result != ISC_R_SUCCESS) \
goto failure; \
} while (0)
static void
tkey_log(const char *fmt, ...) ISC_FORMAT_PRINTF(1, 2);
2000-06-23 01:51:35 +00:00
static void
tkey_log(const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
isc_log_vwrite(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_REQUEST,
2000-06-23 01:51:35 +00:00
ISC_LOG_DEBUG(4), fmt, ap);
va_end(ap);
}
1999-10-26 15:39:59 +00:00
void
dns_tkeyctx_create(isc_mem_t *mctx, dns_tkeyctx_t **tctxp) {
1999-10-26 15:39:59 +00:00
REQUIRE(mctx != NULL);
REQUIRE(tctxp != NULL && *tctxp == NULL);
2000-01-21 20:18:41 +00:00
dns_tkeyctx_t *tctx = isc_mem_get(mctx, sizeof(*tctx));
*tctx = (dns_tkeyctx_t){
.mctx = NULL,
};
isc_mem_attach(mctx, &tctx->mctx);
*tctxp = tctx;
1999-10-26 15:39:59 +00:00
}
void
dns_tkeyctx_destroy(dns_tkeyctx_t **tctxp) {
isc_mem_t *mctx = NULL;
dns_tkeyctx_t *tctx = NULL;
2000-01-21 20:18:41 +00:00
REQUIRE(tctxp != NULL && *tctxp != NULL);
tctx = *tctxp;
*tctxp = NULL;
mctx = tctx->mctx;
2000-01-21 20:18:41 +00:00
if (tctx->domain != NULL) {
if (dns_name_dynamic(tctx->domain)) {
dns_name_free(tctx->domain, mctx);
}
isc_mem_put(mctx, tctx->domain, sizeof(dns_name_t));
}
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
1999-10-26 15:39:59 +00:00
add_rdata_to_list(dns_message_t *msg, dns_name_t *name, dns_rdata_t *rdata,
uint32_t ttl, dns_namelist_t *namelist) {
1999-10-26 15:39:59 +00:00
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;
1999-10-26 15:39:59 +00:00
dns_message_gettemprdata(msg, &newrdata);
1999-10-26 15:39:59 +00:00
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);
1999-10-26 15:39:59 +00:00
dns_rdata_fromregion(newrdata, rdata->rdclass, rdata->type, &newr);
dns_message_takebuffer(msg, &tmprdatabuf);
dns_message_gettempname(msg, &newname);
dns_name_copy(name, newname);
1999-10-26 15:39:59 +00:00
dns_message_gettemprdatalist(msg, &newlist);
1999-10-26 15:39:59 +00:00
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);
1999-10-26 15:39:59 +00:00
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
2019-01-29 18:10:27 +01:00
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;
2010-12-18 23:47:11 +00:00
/*
* 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
2019-01-29 18:10:27 +01:00
* 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;
}
2010-12-18 23:47:11 +00:00
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;
}
/*
* XXXDCL need to check for key expiry per 4.1.1
* XXXDCL need a way to check fully established, perhaps w/key_flags
*/
result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
if (result == ISC_R_SUCCESS) {
gss_ctx = dst_key_getgssctx(tsigkey->key);
}
/*
2010-12-18 23:47:11 +00:00
* 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 == DNS_R_INVALIDTKEY) {
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
tkeyout->error = dns_tsigerror_badkey;
tkey_log("process_gsstkey(): dns_tsigerror_badkey");
return ISC_R_SUCCESS;
}
if (result != DNS_R_CONTINUE && result != ISC_R_SUCCESS) {
goto failure;
}
/*
* XXXDCL Section 4.1.3: Limit GSS_S_CONTINUE_NEEDED to 10 times.
*/
if (dns_name_countlabels(principal) == 0U) {
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
} else if (tsigkey == NULL) {
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#if HAVE_GSSAPI
OM_uint32 gret, minor, lifetime;
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#endif /* HAVE_GSSAPI */
uint32_t expire;
RETERR(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;
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#if HAVE_GSSAPI
gret = gss_context_time(&minor, gss_ctx, &lifetime);
if (gret == GSS_S_COMPLETE && now + lifetime < expire) {
expire = now + lifetime;
}
Complete rewrite the BIND 9 build system The rewrite of BIND 9 build system is a large work and cannot be reasonable split into separate merge requests. Addition of the automake has a positive effect on the readability and maintainability of the build system as it is more declarative, it allows conditional and we are able to drop all of the custom make code that BIND 9 developed over the years to overcome the deficiencies of autoconf + custom Makefile.in files. This squashed commit contains following changes: - conversion (or rather fresh rewrite) of all Makefile.in files to Makefile.am by using automake - the libtool is now properly integrated with automake (the way we used it was rather hackish as the only official way how to use libtool is via automake - the dynamic module loading was rewritten from a custom patchwork to libtool's libltdl (which includes the patchwork to support module loading on different systems internally) - conversion of the unit test executor from kyua to automake parallel driver - conversion of the system test executor from custom make/shell to automake parallel driver - The GSSAPI has been refactored, the custom SPNEGO on the basis that all major KRB5/GSSAPI (mit-krb5, heimdal and Windows) implementations support SPNEGO mechanism. - The various defunct tests from bin/tests have been removed: bin/tests/optional and bin/tests/pkcs11 - The text files generated from the MD files have been removed, the MarkDown has been designed to be readable by both humans and computers - The xsl header is now generated by a simple sed command instead of perl helper - The <irs/platform.h> header has been removed - cleanups of configure.ac script to make it more simpler, addition of multiple macros (there's still work to be done though) - the tarball can now be prepared with `make dist` - the system tests are partially able to run in oot build Here's a list of unfinished work that needs to be completed in subsequent merge requests: - `make distcheck` doesn't yet work (because of system tests oot run is not yet finished) - documentation is not yet built, there's a different merge request with docbook to sphinx-build rst conversion that needs to be rebased and adapted on top of the automake - msvc build is non functional yet and we need to decide whether we will just cross-compile bind9 using mingw-w64 or fix the msvc build - contributed dlz modules are not included neither in the autoconf nor automake
2018-08-07 16:46:53 +02:00
#endif /* HAVE_GSSAPI */
RETERR(dns_tsigkey_createfromkey(
name, dns__tsig_algfromname(&tkeyin->algorithm), dstkey,
true, false, principal, now, expire, ring->mctx,
&tsigkey));
RETERR(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);
}
2019-01-29 18:10:27 +01:00
/*
2019-01-30 11:12:49 +01:00
* 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).
2019-01-29 18:10:27 +01:00
*/
if (tsigkey != NULL) {
if (msg->tsigkey == NULL && msg->sig0key == NULL) {
dns_message_settsigkey(msg, tsigkey);
}
dns_tsigkey_detach(&tsigkey);
}
return ISC_R_SUCCESS;
failure:
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
if (dstkey != NULL) {
dst_key_free(&dstkey);
}
if (outtoken != NULL) {
isc_buffer_free(&outtoken);
}
tkey_log("process_gsstkey(): %s", isc_result_totext(result));
return result;
}
1999-10-26 15:39:59 +00:00
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) {
1999-10-26 15:39:59 +00:00
isc_result_t result;
dns_tsigkey_t *tsigkey = NULL;
const dns_name_t *identity = NULL;
1999-10-26 15:39:59 +00:00
2000-01-21 20:18:41 +00:00
result = dns_tsigkey_find(&tsigkey, name, &tkeyin->algorithm, ring);
if (result != ISC_R_SUCCESS) {
1999-10-26 15:39:59 +00:00
tkeyout->error = dns_tsigerror_badname;
return ISC_R_SUCCESS;
}
1999-10-26 15:39:59 +00:00
/*
* Only allow a delete if the identity that created the key is the
* same as the identity that signed the message.
1999-10-26 15:39:59 +00:00
*/
identity = dns_tsigkey_identity(tsigkey);
2001-02-14 00:43:10 +00:00
if (identity == NULL || !dns_name_equal(identity, signer)) {
dns_tsigkey_detach(&tsigkey);
1999-10-26 15:39:59 +00:00
return DNS_R_REFUSED;
2001-02-14 00:43:10 +00:00
}
1999-10-26 15:39:59 +00:00
/*
* 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);
2001-02-14 00:43:10 +00:00
1999-10-26 15:39:59 +00:00
/* Release the reference */
dns_tsigkey_detach(&tsigkey);
1999-10-26 15:39:59 +00:00
return ISC_R_SUCCESS;
1999-10-26 15:39:59 +00:00
}
isc_result_t
dns_tkey_processquery(dns_message_t *msg, dns_tkeyctx_t *tctx,
dns_tsigkeyring_t *ring) {
1999-10-26 15:39:59 +00:00
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;
2001-02-14 00:43:10 +00:00
dns_fixedname_t fkeyname;
dns_rdataset_t *tkeyset = NULL;
dns_rdata_t rdata = DNS_RDATA_INIT;
dns_namelist_t namelist = ISC_LIST_INITIALIZER;
2001-02-14 00:43:10 +00:00
char tkeyoutdata[512];
isc_buffer_t tkeyoutbuf;
dns_tsigkey_t *tsigkey = NULL;
1999-10-26 15:39:59 +00:00
2000-01-21 20:18:41 +00:00
REQUIRE(msg != NULL);
REQUIRE(tctx != NULL);
REQUIRE(ring != NULL);
/*
* Interpret the question section.
*/
1999-10-26 15:39:59 +00:00
result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
if (result != ISC_R_SUCCESS) {
return DNS_R_FORMERR;
}
1999-10-26 15:39:59 +00:00
dns_message_currentname(msg, DNS_SECTION_QUESTION, &qname);
/*
* Look for a TKEY record that matches the question.
*/
1999-10-26 15:39:59 +00:00
result = dns_message_findname(msg, DNS_SECTION_ADDITIONAL, qname,
dns_rdatatype_tkey, 0, &name, &tkeyset);
if (result != ISC_R_SUCCESS) {
result = DNS_R_FORMERR;
tkey_log("dns_tkey_processquery: couldn't find a TKEY "
"matching the question");
goto failure;
1999-10-26 15:39:59 +00:00
}
1999-10-26 15:39:59 +00:00
result = dns_rdataset_first(tkeyset);
if (result != ISC_R_SUCCESS) {
result = DNS_R_FORMERR;
goto failure;
}
dns_rdataset_current(tkeyset, &rdata);
2001-02-14 00:43:10 +00:00
RETERR(dns_rdata_tostruct(&rdata, &tkeyin, NULL));
1999-10-26 15:39:59 +00:00
if (tkeyin.error != dns_rcode_noerror) {
result = DNS_R_FORMERR;
goto failure;
}
/*
* 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");
result = DNS_R_FORMERR;
goto failure;
}
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,
};
2001-02-14 00:43:10 +00:00
dns_name_clone(&tkeyin.algorithm, &tkeyout.algorithm);
1999-10-26 15:39:59 +00:00
switch (tkeyin.mode) {
case DNS_TKEYMODE_DELETE:
/*
* A delete operation uses the fully specified qname.
*/
RETERR(process_deletetkey(signer, qname, &tkeyin, &tkeyout,
ring));
break;
case DNS_TKEYMODE_GSSAPI:
/*
* For non-delete operations we do this:
*
* if (qname != ".")
* keyname = qname + defaultdomain
* else
* keyname = <random hex> + defaultdomain
*/
if (tctx->domain == NULL && tkeyin.mode != DNS_TKEYMODE_GSSAPI)
{
2001-02-14 00:43:10 +00:00
tkey_log("dns_tkey_processquery: tkey-domain not set");
result = DNS_R_REFUSED;
goto failure;
}
keyname = dns_fixedname_initname(&fkeyname);
1999-10-26 15:39:59 +00:00
if (!dns_name_equal(qname, dns_rootname)) {
unsigned int n = dns_name_countlabels(qname);
dns_name_copy(qname, keyname);
2001-10-09 17:26:33 +00:00
dns_name_getlabelsequence(keyname, 0, n - 1, keyname);
2001-12-03 19:44:08 +00:00
} else {
2001-02-14 00:43:10 +00:00
unsigned char randomdata[16];
char randomtext[32];
isc_buffer_t b;
isc_region_t r = {
.base = randomdata,
.length = sizeof(randomdata),
};
1999-10-26 15:39:59 +00:00
isc_nonce_buf(randomdata, sizeof(randomdata));
isc_buffer_init(&b, randomtext, sizeof(randomtext));
RETERR(isc_hex_totext(&r, 2, "", &b));
RETERR(dns_name_fromtext(keyname, &b, NULL, 0, NULL));
}
RETERR(dns_name_concatenate(keyname, dns_rootname, keyname));
1999-10-26 15:39:59 +00:00
2000-01-21 20:18:41 +00:00
result = dns_tsigkey_find(&tsigkey, keyname, NULL, ring);
1999-10-26 15:39:59 +00:00
if (result == ISC_R_SUCCESS) {
tkeyout.error = dns_tsigerror_badname;
dns_tsigkey_detach(&tsigkey);
break;
} else if (result == ISC_R_NOTFOUND) {
RETERR(process_gsstkey(msg, keyname, &tkeyin, tctx,
&tkeyout, ring));
break;
}
goto failure;
1999-10-26 15:39:59 +00:00
case DNS_TKEYMODE_SERVERASSIGNED:
case DNS_TKEYMODE_RESOLVERASSIGNED:
result = DNS_R_NOTIMP;
1999-10-26 15:39:59 +00:00
goto failure;
default:
tkeyout.error = dns_tsigerror_badmode;
}
2001-02-14 00:43:10 +00:00
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);
}
RETERR(result);
1999-10-26 15:39:59 +00:00
RETERR(dns_message_reply(msg, true));
add_rdata_to_list(msg, keyname, &rdata, 0, &namelist);
while ((name = ISC_LIST_HEAD(namelist)) != NULL) {
1999-10-26 15:39:59 +00:00
ISC_LIST_UNLINK(namelist, name, link);
dns_message_addname(msg, name, DNS_SECTION_ANSWER);
1999-10-26 15:39:59 +00:00
}
return ISC_R_SUCCESS;
failure:
free_namelist(msg, &namelist);
1999-10-26 15:39:59 +00:00
return result;
}
static isc_result_t
buildquery(dns_message_t *msg, const dns_name_t *name, dns_rdata_tkey_t *tkey) {
1999-10-26 15:39:59 +00:00
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;
1999-10-26 15:39:59 +00:00
isc_result_t result;
unsigned int len;
1999-10-26 15:39:59 +00:00
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);
1999-10-26 15:39:59 +00:00
dns_message_gettemprdataset(msg, &question);
dns_rdataset_makequestion(question, dns_rdataclass_any,
dns_rdatatype_tkey);
1999-10-26 15:39:59 +00:00
dns_message_gettemprdatalist(msg, &tkeylist);
tkeylist->rdclass = dns_rdataclass_any;
1999-10-26 15:39:59 +00:00
tkeylist->type = dns_rdatatype_tkey;
ISC_LIST_APPEND(tkeylist->rdata, rdata, link);
dns_message_gettemprdataset(msg, &tkeyset);
dns_rdatalist_tordataset(tkeylist, tkeyset);
1999-10-26 15:39:59 +00:00
dns_name_copy(name, qname);
dns_name_copy(name, aname);
1999-10-26 15:39:59 +00:00
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);
1999-10-26 15:39:59 +00:00
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);
}
1999-10-26 15:39:59 +00:00
static isc_result_t
find_tkey(dns_message_t *msg, dns_name_t **name, dns_rdata_t *rdata,
int section) {
1999-10-26 15:39:59 +00:00
isc_result_t result;
result = dns_message_firstname(msg, section);
1999-10-26 15:39:59 +00:00
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,
1999-10-26 15:39:59 +00:00
&tkeyset);
if (result == ISC_R_SUCCESS) {
result = dns_rdataset_first(tkeyset);
if (result != ISC_R_SUCCESS) {
break;
}
1999-10-26 15:39:59 +00:00
dns_rdataset_current(tkeyset, rdata);
*name = cur;
1999-10-26 15:39:59 +00:00
return ISC_R_SUCCESS;
}
result = dns_message_nextname(msg, section);
1999-10-26 15:39:59 +00:00
}
if (result == ISC_R_NOMORE) {
return ISC_R_NOTFOUND;
}
1999-10-26 15:39:59 +00:00
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);
}
RETERR(find_tkey(rmsg, &tkeyname, &rtkeyrdata, DNS_SECTION_ANSWER));
RETERR(dns_rdata_tostruct(&rtkeyrdata, &rtkey, NULL));
RETERR(find_tkey(qmsg, &tkeyname, &qtkeyrdata, DNS_SECTION_ADDITIONAL));
RETERR(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)");
result = DNS_R_INVALIDTKEY;
goto failure;
}
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);
RETERR(buildquery(qmsg, tkeyname, &tkey));
return DNS_R_CONTINUE;
}
RETERR(dst_key_fromgssapi(dns_rootname, *context, rmsg->mctx, &dstkey,
NULL));
/*
* XXXSRA This seems confused. If we got CONTINUE from initctx,
* the GSS negotiation hasn't completed yet, so we can't sign
* anything yet.
*/
RETERR(dns_tsigkey_createfromkey(tkeyname, DST_ALG_GSSAPI, dstkey, true,
false, NULL, rtkey.inception,
rtkey.expire, ring->mctx, &tsigkey));
RETERR(dns_tsigkeyring_add(ring, tsigkey));
if (outkey == NULL) {
dns_tsigkey_detach(&tsigkey);
} else {
*outkey = tsigkey;
}
dst_key_free(&dstkey);
return result;
failure:
if (tsigkey != NULL) {
dns_tsigkey_detach(&tsigkey);
}
if (dstkey != NULL) {
dst_key_free(&dstkey);
}
return result;
}