summaryrefslogtreecommitdiffstats
path: root/lib/dns/gssapictx.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dns/gssapictx.c')
-rw-r--r--lib/dns/gssapictx.c96
1 files changed, 79 insertions, 17 deletions
diff --git a/lib/dns/gssapictx.c b/lib/dns/gssapictx.c
index f365a64..707745c 100644
--- a/lib/dns/gssapictx.c
+++ b/lib/dns/gssapictx.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2004-2008, 2010 Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2004-2011 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2000, 2001 Internet Software Consortium.
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -15,16 +15,18 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
-/* $Id: gssapictx.c,v 1.12.118.5 2010-12-22 02:37:55 marka Exp $ */
+/* $Id: gssapictx.c,v 1.26 2011-01-10 03:49:49 marka Exp $ */
#include <config.h>
+#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <isc/buffer.h>
#include <isc/dir.h>
#include <isc/entropy.h>
+#include <isc/file.h>
#include <isc/lex.h>
#include <isc/mem.h>
#include <isc/once.h>
@@ -201,9 +203,12 @@ log_cred(const gss_cred_id_t cred) {
* - tkey-gssapi-credential doesn't start with DNS/
* - the default realm in /etc/krb5.conf and the
* tkey-gssapi-credential bind config option don't match
+ *
+ * Note that if tkey-gssapi-keytab is set then these configure checks
+ * are not performed, and runtime errors from gssapi are used instead
*/
static void
-dst_gssapi_check_config(const char *gss_name) {
+check_config(const char *gss_name) {
const char *p;
krb5_context krb5_ctx;
char *krb5_realm = NULL;
@@ -263,7 +268,7 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
* here when we're in the acceptor role, which would let us
* default the hostname and use a compiled in default service
* name of "DNS", giving one less thing to configure in
- * named.conf. Unfortunately, this creates a circular
+ * named.conf. Unfortunately, this creates a circular
* dependency due to DNS-based realm lookup in at least one
* GSSAPI implementation (Heimdal). Oh well.
*/
@@ -273,7 +278,7 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
gret = gss_import_name(&minor, &gnamebuf,
GSS_C_NO_OID, &gname);
if (gret != GSS_S_COMPLETE) {
- dst_gssapi_check_config((char *)array);
+ check_config((char *)array);
gss_log(3, "failed gss_import_name: %s",
gss_error_tostring(gret, minor, buf,
@@ -306,7 +311,7 @@ dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
initiate ? "initiate" : "accept",
(char *)gnamebuf.value,
gss_error_tostring(gret, minor, buf, sizeof(buf)));
- dst_gssapi_check_config((char *)array);
+ check_config((char *)array);
return (ISC_R_FAILURE);
}
@@ -361,7 +366,7 @@ dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
rname++;
/*
- * Find the host portion of the signer's name. We do this by
+ * Find the host portion of the signer's name. We do this by
* searching for the first / character. We then check to make
* certain the instance name is "host"
*
@@ -440,7 +445,7 @@ dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
return (isc_boolean_false);
/*
- * Find the host portion of the signer's name. Zero out the $ so
+ * Find the host portion of the signer's name. Zero out the $ so
* it terminates the signer's name, and skip past the @ for
* the realm.
*
@@ -454,7 +459,7 @@ dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
/*
* Find the first . in the target name, and make it the end of
- * the string. The rest of the name has to match the realm.
+ * the string. The rest of the name has to match the realm.
*/
if (name != NULL) {
nname = strchr(nbuf, '.');
@@ -510,9 +515,34 @@ dst_gssapi_releasecred(gss_cred_id_t *cred) {
#endif
}
+#ifdef GSSAPI
+/*
+ * Format a gssapi error message info into a char ** on the given memory
+ * context. This is used to return gssapi error messages back up the
+ * call chain for reporting to the user.
+ */
+static void
+gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor,
+ char **err_message)
+{
+ char buf[1024];
+ char *estr;
+
+ if (err_message == NULL || mctx == NULL) {
+ /* the caller doesn't want any error messages */
+ return;
+ }
+
+ estr = gss_error_tostring(major, minor, buf, sizeof(buf));
+ if (estr)
+ (*err_message) = isc_mem_strdup(mctx, estr);
+}
+#endif
+
isc_result_t
dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
- isc_buffer_t *outtoken, gss_ctx_id_t *gssctx)
+ isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
+ isc_mem_t *mctx, char **err_message)
{
#ifdef GSSAPI
isc_region_t r;
@@ -523,10 +553,10 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
isc_result_t result;
gss_buffer_desc gnamebuf;
unsigned char array[DNS_NAME_MAXTEXT + 1];
- char buf[1024];
/* Client must pass us a valid gss_ctx_id_t here */
REQUIRE(gssctx != NULL);
+ REQUIRE(mctx != NULL);
isc_buffer_init(&namebuf, array, sizeof(array));
name_to_gbuffer(name, &namebuf, &gnamebuf);
@@ -534,6 +564,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
/* Get the name as a GSS name */
gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
if (gret != GSS_S_COMPLETE) {
+ gss_err_message(mctx, gret, minor, err_message);
result = ISC_R_FAILURE;
goto out;
}
@@ -550,8 +581,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
* Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
* servers don't like it.
*/
- flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_DELEG_FLAG |
- GSS_C_INTEG_FLAG;
+ flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
gname, GSS_SPNEGO_MECHANISM, flags,
@@ -559,9 +589,9 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
NULL, &gouttoken, &ret_flags, NULL);
if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
- gss_log(3, "Failure initiating security context");
- gss_log(3, "%s", gss_error_tostring(gret, minor,
- buf, sizeof(buf)));
+ gss_err_message(mctx, gret, minor, err_message);
+ gss_log(3, "Failure initiating security context: %s",
+ *err_message);
result = ISC_R_FAILURE;
goto out;
}
@@ -593,6 +623,8 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
UNUSED(intoken);
UNUSED(outtoken);
UNUSED(gssctx);
+ UNUSED(mctx);
+ UNUSED(err_message);
return (ISC_R_NOTIMPLEMENTED);
#endif
@@ -600,6 +632,7 @@ dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
isc_result_t
dst_gssapi_acceptctx(gss_cred_id_t cred,
+ const char *gssapi_keytab,
isc_region_t *intoken, isc_buffer_t **outtoken,
gss_ctx_id_t *ctxout, dns_name_t *principal,
isc_mem_t *mctx)
@@ -626,6 +659,34 @@ dst_gssapi_acceptctx(gss_cred_id_t cred,
else
context = *ctxout;
+ if (gssapi_keytab != NULL) {
+#ifdef ISC_PLATFORM_GSSAPI_KRB5_HEADER
+ gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
+ if (gret != GSS_S_COMPLETE) {
+ gss_log(3, "failed "
+ "gsskrb5_register_acceptor_identity(%s): %s",
+ gssapi_keytab,
+ gss_error_tostring(gret, minor,
+ buf, sizeof(buf)));
+ return (DNS_R_INVALIDTKEY);
+ }
+#else
+ /*
+ * Minimize memory leakage by only setting KRB5_KTNAME
+ * if it needs to change.
+ */
+ const char *old = getenv("KRB5_KTNAME");
+ if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
+ char *kt = malloc(strlen(gssapi_keytab) + 13);
+ if (kt == NULL)
+ return (ISC_R_NOMEMORY);
+ sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab);
+ if (putenv(kt) != 0)
+ return (ISC_R_NOMEMORY);
+ }
+#endif
+ }
+
gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
GSS_C_NO_CHANNEL_BINDINGS, &gname,
NULL, &gouttoken, NULL, NULL, NULL);
@@ -692,7 +753,7 @@ dst_gssapi_acceptctx(gss_cred_id_t cred,
isc_buffer_add(&namebuf, r.length);
RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
- ISC_FALSE, NULL));
+ 0, NULL));
if (gnamebuf.length != 0) {
gret = gss_release_buffer(&minor, &gnamebuf);
@@ -717,6 +778,7 @@ dst_gssapi_acceptctx(gss_cred_id_t cred,
return (result);
#else
UNUSED(cred);
+ UNUSED(gssapi_keytab);
UNUSED(intoken);
UNUSED(outtoken);
UNUSED(ctxout);
OpenPOWER on IntegriCloud