gssapictx.c revision 71bd858d8ed62672e7c23999dc7c02fd16a55089
/*
* Copyright (C) 2004-2010 Internet Systems Consortium, Inc. ("ISC")
* Copyright (C) 2000, 2001 Internet Software Consortium.
*
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/* $Id: gssapictx.c,v 1.19 2010/12/18 01:56:22 each Exp $ */
#include <config.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <isc/platform.h>
#include <dns/fixedname.h>
#include <dns/rdataclass.h>
#include <dns/keyvalues.h>
#include "dst_internal.h"
/*
* If we're using our own SPNEGO implementation (see configure.in),
* pull it in now. Otherwise, we just use whatever GSSAPI supplies.
*/
#if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
#include "spnego.h"
#endif
/*
* Solaris8 apparently needs an explicit OID set, and Solaris10 needs
* one for anything but Kerberos. Supplying an explicit OID set
* doesn't appear to hurt anything in other implementations, so we
* always use one. If we're not using our own SPNEGO implementation,
* we include SPNEGO's OID.
*/
#if defined(GSSAPI)
#include ISC_PLATFORM_KRB5HEADER
static unsigned char krb5_mech_oid_bytes[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
};
#ifndef USE_ISC_SPNEGO
static unsigned char spnego_mech_oid_bytes[] = {
0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
};
#endif
static gss_OID_desc mech_oid_set_array[] = {
{ sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
#ifndef USE_ISC_SPNEGO
{ sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
#endif
};
static gss_OID_set_desc mech_oid_set = {
sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
};
#endif
#define REGION_TO_GBUFFER(r, gb) \
do { \
} while (0)
#define GBUFFER_TO_REGION(gb, r) \
do { \
} while (0)
#define RETERR(x) do { \
result = (x); \
if (result != ISC_R_SUCCESS) \
goto out; \
} while (0)
#ifdef GSSAPI
static inline void
{
isc_region_t r;
if (!dns_name_isabsolute(name))
else
{
unsigned int labels;
}
isc_buffer_usedregion(buffer, &r);
REGION_TO_GBUFFER(r, *gbuffer);
}
static void
const char *usage_text;
char buf[1024];
if (gret != GSS_S_COMPLETE) {
return;
}
if (gret != GSS_S_COMPLETE)
else {
switch (usage) {
case GSS_C_BOTH:
usage_text = "GSS_C_BOTH";
break;
case GSS_C_INITIATE:
usage_text = "GSS_C_INITIATE";
break;
case GSS_C_ACCEPT:
usage_text = "GSS_C_ACCEPT";
break;
default:
usage_text = "???";
}
usage_text, (unsigned long)lifetime);
}
if (gret == GSS_S_COMPLETE) {
if (gret != GSS_S_COMPLETE)
sizeof(buf)));
}
}
if (gret != GSS_S_COMPLETE)
}
#endif
#ifdef GSSAPI
/*
* check for the most common configuration errors.
*
* The errors checked for are:
* - tkey-gssapi-credential doesn't start with DNS/
* 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
check_config(const char *gss_name) {
const char *p;
char *krb5_realm = NULL;
"should start with 'DNS/'", gss_name);
return;
}
if (krb5_init_context(&krb5_ctx) != 0) {
return;
}
return;
}
if (p == NULL) {
"tkey-gssapi-credentials (%s)", gss_name);
return;
}
"does not match tkey-gssapi-credential (%s)",
return;
}
}
#endif
{
#ifdef GSSAPI
char buf[1024];
/*
* XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
* 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
* dependency due to DNS-based realm lookup in at least one
* GSSAPI implementation (Heimdal). Oh well.
*/
GSS_C_NO_OID, &gname);
if (gret != GSS_S_COMPLETE) {
check_config((char *)array);
sizeof(buf)));
return (ISC_R_FAILURE);
}
} else
/* Get the credentials. */
else {
/* XXXDCL does this even make any sense? */
}
if (initiate)
else
if (gret != GSS_S_COMPLETE) {
check_config((char *)array);
return (ISC_R_FAILURE);
}
return (ISC_R_SUCCESS);
#else
return (ISC_R_NOTIMPLEMENTED);
#endif
}
{
#ifdef GSSAPI
char sbuf[DNS_NAME_FORMATSIZE];
char nbuf[DNS_NAME_FORMATSIZE];
char rbuf[DNS_NAME_FORMATSIZE];
char *sname;
char *rname;
/*
* It is far, far easier to write the names we are looking at into
* a string, and do string operations on them.
*/
isc_buffer_putuint8(&buffer, 0);
/*
* Find the realm portion. This is the part after the @. If it
* does not exist, we don't have something we like, so we fail our
* compare.
*/
return (isc_boolean_false);
*rname = '\0';
rname += 2;
/*
* 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"
*
* This will work for
* host/example.com@EXAMPLE.COM
*/
return (isc_boolean_false);
*sname = '\0';
sname++;
return (isc_boolean_false);
/*
* Now, we do a simple comparison between the name and the realm.
*/
return (isc_boolean_true);
} else {
return (isc_boolean_true);
}
return (isc_boolean_false);
#else
return (isc_boolean_false);
#endif
}
{
#ifdef GSSAPI
char sbuf[DNS_NAME_FORMATSIZE];
char nbuf[DNS_NAME_FORMATSIZE];
char rbuf[DNS_NAME_FORMATSIZE];
char *sname;
char *nname;
char *rname;
/*
* It is far, far easier to write the names we are looking at into
* a string, and do string operations on them.
*/
isc_buffer_putuint8(&buffer, 0);
/*
* Find the realm portion. This is the part after the @. If it
* does not exist, we don't have something we like, so we fail our
* compare.
*/
return (isc_boolean_false);
return (isc_boolean_false);
/*
* Verify that the $ and @ follow one another.
*/
return (isc_boolean_false);
/*
* 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.
*
* All service principals in Microsoft format seem to be in
* machinename$@EXAMPLE.COM
* format.
*/
rname++;
*sname = '\0';
/*
* 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.
*/
return (isc_boolean_false);
*nname++ = '\0';
}
/*
* Now, we do a simple comparison between the name and the realm.
*/
return (isc_boolean_true);
} else {
return (isc_boolean_true);
}
return (isc_boolean_false);
#else
return (isc_boolean_false);
#endif
}
#ifdef GSSAPI
char buf[1024];
if (gret != GSS_S_COMPLETE) {
/* Log the error, but still free the credential's memory */
}
return(ISC_R_SUCCESS);
#else
return (ISC_R_NOTIMPLEMENTED);
#endif
}
#ifdef GSSAPI
/*
* GSSAPI with krb5 doesn't have a way to set the default realm, as it
* doesn't offer any access to the krb5 context that it uses. The only
* way to do an nsupdate call on a realm that isn't the default realm in
* realm in there as the default realm, then set KRB5_CONFIG to point
* at that temporary krb5.conf. This is a disgusting hack, but it is
* the best we can do with GSSAPI.
*
* To try to reduce the impact, this routine checks if the default
* realm is already correct. If it is, then we don't need to do
* anything. If not, then we create the temporary krb5.conf.
*/
static void
int kret;
char *realm;
char buf[1024];
char *p, *template;
/* the user has specifically set a KRB5_CONFIG to
use. Don't override it, as they may know what they are
doing */
return;
}
/* gssapi wants the realm in upper case */
for (p=buf; *p; p++) {
if (islower((int)*p))
*p = toupper((int)*p);
}
if (kret != 0)
return;
/* the krb5.conf is correct. */
return;
}
return;
}
if (ret != ISC_R_SUCCESS) {
return;
}
}
/*
* 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
char **err_message)
{
char buf[1024];
char *estr;
/* the caller doesn't want any error messages */
return;
}
if (estr)
}
#endif
{
#ifdef GSSAPI
isc_region_t r;
/* Client must pass us a valid gss_ctx_id_t here */
}
/* Get the name as a GSS name */
if (gret != GSS_S_COMPLETE) {
goto out;
}
/* Don't call gss_release_buffer for gintoken! */
} else {
}
/*
* Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
* servers don't like it.
*/
*err_message);
goto out;
}
/*
* XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
* MUTUAL and INTEG flags, fail if either not set.
*/
/*
* RFC 2744 states the a valid output token has a non-zero length.
*/
}
if (gret == GSS_S_COMPLETE)
else
out:
if (tmpfile) {
unsetenv("KRB5_CONFIG");
}
return (result);
#else
return (ISC_R_NOTIMPLEMENTED);
#endif
}
const char *gssapi_keytab,
{
#ifdef GSSAPI
isc_region_t r;
char buf[1024];
else
if (gssapi_keytab) {
if (gret != GSS_S_COMPLETE) {
"gsskrb5_register_acceptor_identity(%s): %s",
return (DNS_R_INVALIDTKEY);
}
}
switch (gret) {
case GSS_S_COMPLETE:
break;
case GSS_S_CONTINUE_NEEDED:
break;
case GSS_S_DEFECTIVE_TOKEN:
case GSS_S_BAD_SIG:
case GSS_S_DUPLICATE_TOKEN:
case GSS_S_OLD_TOKEN:
case GSS_S_NO_CRED:
case GSS_S_BAD_BINDINGS:
case GSS_S_NO_CONTEXT:
case GSS_S_BAD_MECH:
case GSS_S_FAILURE:
/* fall through */
default:
return (result);
}
}
if (gret == GSS_S_COMPLETE) {
if (gret != GSS_S_COMPLETE) {
}
/*
* Compensate for a bug in Solaris8's implementation
* of gss_display_name(). Should be harmless in any
* case, since principal names really should not
* contain null characters.
*/
0, NULL));
if (gret != GSS_S_COMPLETE)
sizeof(buf)));
}
}
out:
if (gret != GSS_S_COMPLETE)
sizeof(buf)));
}
return (result);
#else
return (ISC_R_NOTIMPLEMENTED);
#endif
}
{
#ifdef GSSAPI
char buf[1024];
/* Delete the context from the GSS provider */
if (gret != GSS_S_COMPLETE) {
/* Log the error, but still free the context's memory */
}
return(ISC_R_SUCCESS);
#else
return (ISC_R_NOTIMPLEMENTED);
#endif
}
char *
#ifdef GSSAPI
/* Handle major status */
msg_ctx = 0;
/* Handle minor status */
msg_ctx = 0;
return(buf);
#else
return (buf);
#endif
}
void
}
/*! \file */