gssapictx.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004-2015  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 2000, 2001  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id: gssapictx.c,v 1.29 2011/08/29 06:33:25 marka Exp $ */
00019 
00020 #include <config.h>
00021 
00022 #include <ctype.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 
00026 #include <isc/buffer.h>
00027 #include <isc/dir.h>
00028 #include <isc/entropy.h>
00029 #include <isc/file.h>
00030 #include <isc/lex.h>
00031 #include <isc/mem.h>
00032 #include <isc/once.h>
00033 #include <isc/print.h>
00034 #include <isc/platform.h>
00035 #include <isc/random.h>
00036 #include <isc/string.h>
00037 #include <isc/time.h>
00038 #include <isc/util.h>
00039 
00040 #include <dns/fixedname.h>
00041 #include <dns/name.h>
00042 #include <dns/rdata.h>
00043 #include <dns/rdataclass.h>
00044 #include <dns/result.h>
00045 #include <dns/types.h>
00046 #include <dns/keyvalues.h>
00047 #include <dns/log.h>
00048 
00049 #include <dst/gssapi.h>
00050 #include <dst/result.h>
00051 
00052 #include "dst_internal.h"
00053 
00054 /*
00055  * If we're using our own SPNEGO implementation (see configure.in),
00056  * pull it in now.  Otherwise, we just use whatever GSSAPI supplies.
00057  */
00058 #if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
00059 #include "spnego.h"
00060 #define gss_accept_sec_context  gss_accept_sec_context_spnego
00061 #define gss_init_sec_context    gss_init_sec_context_spnego
00062 #endif
00063 
00064 /*
00065  * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
00066  * one for anything but Kerberos.  Supplying an explicit OID set
00067  * doesn't appear to hurt anything in other implementations, so we
00068  * always use one.  If we're not using our own SPNEGO implementation,
00069  * we include SPNEGO's OID.
00070  */
00071 #ifdef GSSAPI
00072 #ifdef WIN32
00073 #include <krb5/krb5.h>
00074 #else
00075 #include ISC_PLATFORM_KRB5HEADER
00076 #endif
00077 
00078 static unsigned char krb5_mech_oid_bytes[] = {
00079         0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
00080 };
00081 
00082 #ifndef USE_ISC_SPNEGO
00083 static unsigned char spnego_mech_oid_bytes[] = {
00084         0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
00085 };
00086 #endif
00087 
00088 static gss_OID_desc mech_oid_set_array[] = {
00089         { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
00090 #ifndef USE_ISC_SPNEGO
00091         { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
00092 #endif
00093 };
00094 
00095 static gss_OID_set_desc mech_oid_set = {
00096         sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
00097         mech_oid_set_array
00098 };
00099 
00100 #endif
00101 
00102 #define REGION_TO_GBUFFER(r, gb) \
00103         do { \
00104                 (gb).length = (r).length; \
00105                 (gb).value = (r).base; \
00106         } while (0)
00107 
00108 #define GBUFFER_TO_REGION(gb, r) \
00109         do { \
00110           (r).length = (unsigned int)(gb).length; \
00111                 (r).base = (gb).value; \
00112         } while (0)
00113 
00114 
00115 #define RETERR(x) do { \
00116         result = (x); \
00117         if (result != ISC_R_SUCCESS) \
00118                 goto out; \
00119         } while (0)
00120 
00121 #ifdef GSSAPI
00122 static inline void
00123 name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
00124                 gss_buffer_desc *gbuffer)
00125 {
00126         dns_name_t tname, *namep;
00127         isc_region_t r;
00128         isc_result_t result;
00129 
00130         if (!dns_name_isabsolute(name))
00131                 namep = name;
00132         else
00133         {
00134                 unsigned int labels;
00135                 dns_name_init(&tname, NULL);
00136                 labels = dns_name_countlabels(name);
00137                 dns_name_getlabelsequence(name, 0, labels - 1, &tname);
00138                 namep = &tname;
00139         }
00140 
00141         result = dns_name_toprincipal(namep, buffer);
00142         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00143         isc_buffer_putuint8(buffer, 0);
00144         isc_buffer_usedregion(buffer, &r);
00145         REGION_TO_GBUFFER(r, *gbuffer);
00146 }
00147 
00148 static void
00149 log_cred(const gss_cred_id_t cred) {
00150         OM_uint32 gret, minor, lifetime;
00151         gss_name_t gname;
00152         gss_buffer_desc gbuffer;
00153         gss_cred_usage_t usage;
00154         const char *usage_text;
00155         char buf[1024];
00156 
00157         gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
00158         if (gret != GSS_S_COMPLETE) {
00159                 gss_log(3, "failed gss_inquire_cred: %s",
00160                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00161                 return;
00162         }
00163 
00164         gret = gss_display_name(&minor, gname, &gbuffer, NULL);
00165         if (gret != GSS_S_COMPLETE)
00166                 gss_log(3, "failed gss_display_name: %s",
00167                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00168         else {
00169                 switch (usage) {
00170                 case GSS_C_BOTH:
00171                         usage_text = "GSS_C_BOTH";
00172                         break;
00173                 case GSS_C_INITIATE:
00174                         usage_text = "GSS_C_INITIATE";
00175                         break;
00176                 case GSS_C_ACCEPT:
00177                         usage_text = "GSS_C_ACCEPT";
00178                         break;
00179                 default:
00180                         usage_text = "???";
00181                 }
00182                 gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
00183                         usage_text, (unsigned long)lifetime);
00184         }
00185 
00186         if (gret == GSS_S_COMPLETE) {
00187                 if (gbuffer.length != 0U) {
00188                         gret = gss_release_buffer(&minor, &gbuffer);
00189                         if (gret != GSS_S_COMPLETE)
00190                                 gss_log(3, "failed gss_release_buffer: %s",
00191                                         gss_error_tostring(gret, minor, buf,
00192                                                            sizeof(buf)));
00193                 }
00194         }
00195 
00196         gret = gss_release_name(&minor, &gname);
00197         if (gret != GSS_S_COMPLETE)
00198                 gss_log(3, "failed gss_release_name: %s",
00199                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00200 }
00201 #endif
00202 
00203 #ifdef GSSAPI
00204 /*
00205  * check for the most common configuration errors.
00206  *
00207  * The errors checked for are:
00208  *   - tkey-gssapi-credential doesn't start with DNS/
00209  *   - the default realm in /etc/krb5.conf and the
00210  *     tkey-gssapi-credential bind config option don't match
00211  *
00212  * Note that if tkey-gssapi-keytab is set then these configure checks
00213  * are not performed, and runtime errors from gssapi are used instead
00214  */
00215 static void
00216 check_config(const char *gss_name) {
00217         const char *p;
00218         krb5_context krb5_ctx;
00219         char *krb5_realm_name = NULL;
00220 
00221         if (strncasecmp(gss_name, "DNS/", 4) != 0) {
00222                 gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) "
00223                         "should start with 'DNS/'", gss_name);
00224                 return;
00225         }
00226 
00227         if (krb5_init_context(&krb5_ctx) != 0) {
00228                 gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
00229                 return;
00230         }
00231         if (krb5_get_default_realm(krb5_ctx, &krb5_realm_name) != 0) {
00232                 gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
00233                 krb5_free_context(krb5_ctx);
00234                 return;
00235         }
00236         p = strchr(gss_name, '@');
00237         if (p == NULL) {
00238                 gss_log(ISC_LOG_ERROR, "badly formatted "
00239                         "tkey-gssapi-credentials (%s)", gss_name);
00240                 krb5_free_context(krb5_ctx);
00241                 return;
00242         }
00243         if (strcasecmp(p + 1, krb5_realm_name) != 0) {
00244                 gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) "
00245                         "does not match tkey-gssapi-credential (%s)",
00246                         krb5_realm_name, gss_name);
00247                 krb5_free_context(krb5_ctx);
00248                 return;
00249         }
00250         krb5_free_context(krb5_ctx);
00251 }
00252 #endif
00253 
00254 isc_result_t
00255 dst_gssapi_acquirecred(dns_name_t *name, isc_boolean_t initiate,
00256                        gss_cred_id_t *cred)
00257 {
00258 #ifdef GSSAPI
00259         isc_result_t result;
00260         isc_buffer_t namebuf;
00261         gss_name_t gname;
00262         gss_buffer_desc gnamebuf;
00263         unsigned char array[DNS_NAME_MAXTEXT + 1];
00264         OM_uint32 gret, minor;
00265         OM_uint32 lifetime;
00266         gss_cred_usage_t usage;
00267         char buf[1024];
00268 
00269         REQUIRE(cred != NULL && *cred == NULL);
00270 
00271         /*
00272          * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
00273          * here when we're in the acceptor role, which would let us
00274          * default the hostname and use a compiled in default service
00275          * name of "DNS", giving one less thing to configure in
00276          * named.conf.  Unfortunately, this creates a circular
00277          * dependency due to DNS-based realm lookup in at least one
00278          * GSSAPI implementation (Heimdal).  Oh well.
00279          */
00280         if (name != NULL) {
00281                 isc_buffer_init(&namebuf, array, sizeof(array));
00282                 name_to_gbuffer(name, &namebuf, &gnamebuf);
00283                 gret = gss_import_name(&minor, &gnamebuf,
00284                                        GSS_C_NO_OID, &gname);
00285                 if (gret != GSS_S_COMPLETE) {
00286                         check_config((char *)array);
00287 
00288                         gss_log(3, "failed gss_import_name: %s",
00289                                 gss_error_tostring(gret, minor, buf,
00290                                                    sizeof(buf)));
00291                         return (ISC_R_FAILURE);
00292                 }
00293         } else
00294                 gname = NULL;
00295 
00296         /* Get the credentials. */
00297         if (gname != NULL)
00298                 gss_log(3, "acquiring credentials for %s",
00299                         (char *)gnamebuf.value);
00300         else {
00301                 /* XXXDCL does this even make any sense? */
00302                 gss_log(3, "acquiring credentials for ?");
00303         }
00304 
00305         if (initiate)
00306                 usage = GSS_C_INITIATE;
00307         else
00308                 usage = GSS_C_ACCEPT;
00309 
00310         gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
00311                                 &mech_oid_set, usage, cred, NULL, &lifetime);
00312 
00313         if (gret != GSS_S_COMPLETE) {
00314                 gss_log(3, "failed to acquire %s credentials for %s: %s",
00315                         initiate ? "initiate" : "accept",
00316                         (gname != NULL) ? (char *)gnamebuf.value : "?",
00317                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00318                 if (gname != NULL)
00319                         check_config((char *)array);
00320                 result = ISC_R_FAILURE;
00321                 goto cleanup;
00322         }
00323 
00324         gss_log(4, "acquired %s credentials for %s",
00325                 initiate ? "initiate" : "accept",
00326                 (gname != NULL) ? (char *)gnamebuf.value : "?");
00327 
00328         log_cred(*cred);
00329         result = ISC_R_SUCCESS;
00330 
00331 cleanup:
00332         if (gname != NULL) {
00333                 gret = gss_release_name(&minor, &gname);
00334                 if (gret != GSS_S_COMPLETE)
00335                         gss_log(3, "failed gss_release_name: %s",
00336                                 gss_error_tostring(gret, minor, buf,
00337                                                    sizeof(buf)));
00338         }
00339 
00340         return (result);
00341 #else
00342         REQUIRE(cred != NULL && *cred == NULL);
00343 
00344         UNUSED(name);
00345         UNUSED(initiate);
00346         UNUSED(cred);
00347 
00348         return (ISC_R_NOTIMPLEMENTED);
00349 #endif
00350 }
00351 
00352 isc_boolean_t
00353 dst_gssapi_identitymatchesrealmkrb5(dns_name_t *signer, dns_name_t *name,
00354                                     dns_name_t *realm)
00355 {
00356 #ifdef GSSAPI
00357         char sbuf[DNS_NAME_FORMATSIZE];
00358         char nbuf[DNS_NAME_FORMATSIZE];
00359         char rbuf[DNS_NAME_FORMATSIZE];
00360         char *sname;
00361         char *rname;
00362         isc_buffer_t buffer;
00363         isc_result_t result;
00364 
00365         /*
00366          * It is far, far easier to write the names we are looking at into
00367          * a string, and do string operations on them.
00368          */
00369         isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
00370         result = dns_name_toprincipal(signer, &buffer);
00371         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00372         isc_buffer_putuint8(&buffer, 0);
00373         if (name != NULL)
00374                 dns_name_format(name, nbuf, sizeof(nbuf));
00375         dns_name_format(realm, rbuf, sizeof(rbuf));
00376 
00377         /*
00378          * Find the realm portion.  This is the part after the @.  If it
00379          * does not exist, we don't have something we like, so we fail our
00380          * compare.
00381          */
00382         rname = strchr(sbuf, '@');
00383         if (rname == NULL)
00384                 return (isc_boolean_false);
00385         *rname = '\0';
00386         rname++;
00387 
00388         /*
00389          * Find the host portion of the signer's name.  We do this by
00390          * searching for the first / character.  We then check to make
00391          * certain the instance name is "host"
00392          *
00393          * This will work for
00394          *    host/example.com@EXAMPLE.COM
00395          */
00396         sname = strchr(sbuf, '/');
00397         if (sname == NULL)
00398                 return (isc_boolean_false);
00399         *sname = '\0';
00400         sname++;
00401         if (strcmp(sbuf, "host") != 0)
00402                 return (isc_boolean_false);
00403 
00404         /*
00405          * Now, we do a simple comparison between the name and the realm.
00406          */
00407         if (name != NULL) {
00408                 if ((strcasecmp(sname, nbuf) == 0)
00409                     && (strcmp(rname, rbuf) == 0))
00410                         return (isc_boolean_true);
00411         } else {
00412                 if (strcmp(rname, rbuf) == 0)
00413                         return (isc_boolean_true);
00414         }
00415 
00416         return (isc_boolean_false);
00417 #else
00418         UNUSED(signer);
00419         UNUSED(name);
00420         UNUSED(realm);
00421         return (isc_boolean_false);
00422 #endif
00423 }
00424 
00425 isc_boolean_t
00426 dst_gssapi_identitymatchesrealmms(dns_name_t *signer, dns_name_t *name,
00427                                   dns_name_t *realm)
00428 {
00429 #ifdef GSSAPI
00430         char sbuf[DNS_NAME_FORMATSIZE];
00431         char nbuf[DNS_NAME_FORMATSIZE];
00432         char rbuf[DNS_NAME_FORMATSIZE];
00433         char *sname;
00434         char *nname;
00435         char *rname;
00436         isc_buffer_t buffer;
00437         isc_result_t result;
00438 
00439         /*
00440          * It is far, far easier to write the names we are looking at into
00441          * a string, and do string operations on them.
00442          */
00443         isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
00444         result = dns_name_toprincipal(signer, &buffer);
00445         RUNTIME_CHECK(result == ISC_R_SUCCESS);
00446         isc_buffer_putuint8(&buffer, 0);
00447         if (name != NULL)
00448                 dns_name_format(name, nbuf, sizeof(nbuf));
00449         dns_name_format(realm, rbuf, sizeof(rbuf));
00450 
00451         /*
00452          * Find the realm portion.  This is the part after the @.  If it
00453          * does not exist, we don't have something we like, so we fail our
00454          * compare.
00455          */
00456         rname = strchr(sbuf, '@');
00457         if (rname == NULL)
00458                 return (isc_boolean_false);
00459         sname = strchr(sbuf, '$');
00460         if (sname == NULL)
00461                 return (isc_boolean_false);
00462 
00463         /*
00464          * Verify that the $ and @ follow one another.
00465          */
00466         if (rname - sname != 1)
00467                 return (isc_boolean_false);
00468 
00469         /*
00470          * Find the host portion of the signer's name.  Zero out the $ so
00471          * it terminates the signer's name, and skip past the @ for
00472          * the realm.
00473          *
00474          * All service principals in Microsoft format seem to be in
00475          *    machinename$@EXAMPLE.COM
00476          * format.
00477          */
00478         rname++;
00479         *sname = '\0';
00480         sname = sbuf;
00481 
00482         /*
00483          * Find the first . in the target name, and make it the end of
00484          * the string.   The rest of the name has to match the realm.
00485          */
00486         if (name != NULL) {
00487                 nname = strchr(nbuf, '.');
00488                 if (nname == NULL)
00489                         return (isc_boolean_false);
00490                 *nname++ = '\0';
00491         }
00492 
00493         /*
00494          * Now, we do a simple comparison between the name and the realm.
00495          */
00496         if (name != NULL) {
00497                 if ((strcasecmp(sname, nbuf) == 0)
00498                     && (strcmp(rname, rbuf) == 0)
00499                     && (strcasecmp(nname, rbuf) == 0))
00500                         return (isc_boolean_true);
00501         } else {
00502                 if (strcmp(rname, rbuf) == 0)
00503                         return (isc_boolean_true);
00504         }
00505 
00506 
00507         return (isc_boolean_false);
00508 #else
00509         UNUSED(signer);
00510         UNUSED(name);
00511         UNUSED(realm);
00512         return (isc_boolean_false);
00513 #endif
00514 }
00515 
00516 isc_result_t
00517 dst_gssapi_releasecred(gss_cred_id_t *cred) {
00518 #ifdef GSSAPI
00519         OM_uint32 gret, minor;
00520         char buf[1024];
00521 
00522         REQUIRE(cred != NULL && *cred != NULL);
00523 
00524         gret = gss_release_cred(&minor, cred);
00525         if (gret != GSS_S_COMPLETE) {
00526                 /* Log the error, but still free the credential's memory */
00527                 gss_log(3, "failed releasing credential: %s",
00528                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00529         }
00530         *cred = NULL;
00531 
00532         return(ISC_R_SUCCESS);
00533 #else
00534         UNUSED(cred);
00535 
00536         return (ISC_R_NOTIMPLEMENTED);
00537 #endif
00538 }
00539 
00540 #ifdef GSSAPI
00541 /*
00542  * Format a gssapi error message info into a char ** on the given memory
00543  * context. This is used to return gssapi error messages back up the
00544  * call chain for reporting to the user.
00545  */
00546 static void
00547 gss_err_message(isc_mem_t *mctx, isc_uint32_t major, isc_uint32_t minor,
00548                 char **err_message)
00549 {
00550         char buf[1024];
00551         char *estr;
00552 
00553         if (err_message == NULL || mctx == NULL) {
00554                 /* the caller doesn't want any error messages */
00555                 return;
00556         }
00557 
00558         estr = gss_error_tostring(major, minor, buf, sizeof(buf));
00559         if (estr != NULL)
00560                 (*err_message) = isc_mem_strdup(mctx, estr);
00561 }
00562 #endif
00563 
00564 isc_result_t
00565 dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
00566                    isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
00567                    isc_mem_t *mctx, char **err_message)
00568 {
00569 #ifdef GSSAPI
00570         isc_region_t r;
00571         isc_buffer_t namebuf;
00572         gss_name_t gname;
00573         OM_uint32 gret, minor, ret_flags, flags;
00574         gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
00575         isc_result_t result;
00576         gss_buffer_desc gnamebuf;
00577         unsigned char array[DNS_NAME_MAXTEXT + 1];
00578 
00579         /* Client must pass us a valid gss_ctx_id_t here */
00580         REQUIRE(gssctx != NULL);
00581         REQUIRE(mctx != NULL);
00582 
00583         isc_buffer_init(&namebuf, array, sizeof(array));
00584         name_to_gbuffer(name, &namebuf, &gnamebuf);
00585 
00586         /* Get the name as a GSS name */
00587         gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
00588         if (gret != GSS_S_COMPLETE) {
00589                 gss_err_message(mctx, gret, minor, err_message);
00590                 result = ISC_R_FAILURE;
00591                 goto out;
00592         }
00593 
00594         if (intoken != NULL) {
00595                 /* Don't call gss_release_buffer for gintoken! */
00596                 REGION_TO_GBUFFER(*intoken, gintoken);
00597                 gintokenp = &gintoken;
00598         } else {
00599                 gintokenp = NULL;
00600         }
00601 
00602         /*
00603          * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
00604          * servers don't like it.
00605          */
00606         flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
00607 
00608         gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
00609                                     gname, GSS_SPNEGO_MECHANISM, flags,
00610                                     0, NULL, gintokenp,
00611                                     NULL, &gouttoken, &ret_flags, NULL);
00612 
00613         if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
00614                 gss_err_message(mctx, gret, minor, err_message);
00615                 if (err_message != NULL && *err_message != NULL)
00616                         gss_log(3, "Failure initiating security context: %s",
00617                                 *err_message);
00618                 else
00619                         gss_log(3, "Failure initiating security context");
00620 
00621                 result = ISC_R_FAILURE;
00622                 goto out;
00623         }
00624 
00625         /*
00626          * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
00627          * MUTUAL and INTEG flags, fail if either not set.
00628          */
00629 
00630         /*
00631          * RFC 2744 states the a valid output token has a non-zero length.
00632          */
00633         if (gouttoken.length != 0U) {
00634                 GBUFFER_TO_REGION(gouttoken, r);
00635                 RETERR(isc_buffer_copyregion(outtoken, &r));
00636                 (void)gss_release_buffer(&minor, &gouttoken);
00637         }
00638 
00639         if (gret == GSS_S_COMPLETE)
00640                 result = ISC_R_SUCCESS;
00641         else
00642                 result = DNS_R_CONTINUE;
00643 
00644  out:
00645         (void)gss_release_name(&minor, &gname);
00646         return (result);
00647 #else
00648         UNUSED(name);
00649         UNUSED(intoken);
00650         UNUSED(outtoken);
00651         UNUSED(gssctx);
00652         UNUSED(mctx);
00653         UNUSED(err_message);
00654 
00655         return (ISC_R_NOTIMPLEMENTED);
00656 #endif
00657 }
00658 
00659 isc_result_t
00660 dst_gssapi_acceptctx(gss_cred_id_t cred,
00661                      const char *gssapi_keytab,
00662                      isc_region_t *intoken, isc_buffer_t **outtoken,
00663                      gss_ctx_id_t *ctxout, dns_name_t *principal,
00664                      isc_mem_t *mctx)
00665 {
00666 #ifdef GSSAPI
00667         isc_region_t r;
00668         isc_buffer_t namebuf;
00669         gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
00670                         gouttoken = GSS_C_EMPTY_BUFFER;
00671         OM_uint32 gret, minor;
00672         gss_ctx_id_t context = GSS_C_NO_CONTEXT;
00673         gss_name_t gname = NULL;
00674         isc_result_t result;
00675         char buf[1024];
00676 
00677         REQUIRE(outtoken != NULL && *outtoken == NULL);
00678 
00679         REGION_TO_GBUFFER(*intoken, gintoken);
00680 
00681         if (*ctxout == NULL)
00682                 context = GSS_C_NO_CONTEXT;
00683         else
00684                 context = *ctxout;
00685 
00686         if (gssapi_keytab != NULL) {
00687 #if defined(ISC_PLATFORM_GSSAPI_KRB5_HEADER) || defined(WIN32)
00688                 gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
00689                 if (gret != GSS_S_COMPLETE) {
00690                         gss_log(3, "failed "
00691                                 "gsskrb5_register_acceptor_identity(%s): %s",
00692                                 gssapi_keytab,
00693                                 gss_error_tostring(gret, 0, buf, sizeof(buf)));
00694                         return (DNS_R_INVALIDTKEY);
00695                 }
00696 #else
00697                 /*
00698                  * Minimize memory leakage by only setting KRB5_KTNAME
00699                  * if it needs to change.
00700                  */
00701                 const char *old = getenv("KRB5_KTNAME");
00702                 if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
00703                         char *kt = malloc(strlen(gssapi_keytab) + 13);
00704                         if (kt == NULL)
00705                                 return (ISC_R_NOMEMORY);
00706                         sprintf(kt, "KRB5_KTNAME=%s", gssapi_keytab);
00707                         if (putenv(kt) != 0)
00708                                 return (ISC_R_NOMEMORY);
00709                 }
00710 #endif
00711         }
00712 
00713         log_cred(cred);
00714 
00715         gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
00716                                       GSS_C_NO_CHANNEL_BINDINGS, &gname,
00717                                       NULL, &gouttoken, NULL, NULL, NULL);
00718 
00719         result = ISC_R_FAILURE;
00720 
00721         switch (gret) {
00722         case GSS_S_COMPLETE:
00723                 result = ISC_R_SUCCESS;
00724                 break;
00725         case GSS_S_CONTINUE_NEEDED:
00726                 result = DNS_R_CONTINUE;
00727                 break;
00728         case GSS_S_DEFECTIVE_TOKEN:
00729         case GSS_S_DEFECTIVE_CREDENTIAL:
00730         case GSS_S_BAD_SIG:
00731         case GSS_S_DUPLICATE_TOKEN:
00732         case GSS_S_OLD_TOKEN:
00733         case GSS_S_NO_CRED:
00734         case GSS_S_CREDENTIALS_EXPIRED:
00735         case GSS_S_BAD_BINDINGS:
00736         case GSS_S_NO_CONTEXT:
00737         case GSS_S_BAD_MECH:
00738         case GSS_S_FAILURE:
00739                 result = DNS_R_INVALIDTKEY;
00740                 /* fall through */
00741         default:
00742                 gss_log(3, "failed gss_accept_sec_context: %s",
00743                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00744                 return (result);
00745         }
00746 
00747         if (gouttoken.length > 0U) {
00748                 RETERR(isc_buffer_allocate(mctx, outtoken,
00749                                            (unsigned int)gouttoken.length));
00750                 GBUFFER_TO_REGION(gouttoken, r);
00751                 RETERR(isc_buffer_copyregion(*outtoken, &r));
00752                 (void)gss_release_buffer(&minor, &gouttoken);
00753         }
00754 
00755         if (gret == GSS_S_COMPLETE) {
00756                 gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
00757                 if (gret != GSS_S_COMPLETE) {
00758                         gss_log(3, "failed gss_display_name: %s",
00759                                 gss_error_tostring(gret, minor,
00760                                                    buf, sizeof(buf)));
00761                         RETERR(ISC_R_FAILURE);
00762                 }
00763 
00764                 /*
00765                  * Compensate for a bug in Solaris8's implementation
00766                  * of gss_display_name().  Should be harmless in any
00767                  * case, since principal names really should not
00768                  * contain null characters.
00769                  */
00770                 if (gnamebuf.length > 0U &&
00771                     ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
00772                         gnamebuf.length--;
00773 
00774                 gss_log(3, "gss-api source name (accept) is %.*s",
00775                         (int)gnamebuf.length, (char *)gnamebuf.value);
00776 
00777                 GBUFFER_TO_REGION(gnamebuf, r);
00778                 isc_buffer_init(&namebuf, r.base, r.length);
00779                 isc_buffer_add(&namebuf, r.length);
00780 
00781                 RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
00782                                          0, NULL));
00783 
00784                 if (gnamebuf.length != 0U) {
00785                         gret = gss_release_buffer(&minor, &gnamebuf);
00786                         if (gret != GSS_S_COMPLETE)
00787                                 gss_log(3, "failed gss_release_buffer: %s",
00788                                         gss_error_tostring(gret, minor, buf,
00789                                                            sizeof(buf)));
00790                 }
00791         }
00792 
00793         *ctxout = context;
00794 
00795  out:
00796         if (gname != NULL) {
00797                 gret = gss_release_name(&minor, &gname);
00798                 if (gret != GSS_S_COMPLETE)
00799                         gss_log(3, "failed gss_release_name: %s",
00800                                 gss_error_tostring(gret, minor, buf,
00801                                                    sizeof(buf)));
00802         }
00803 
00804         return (result);
00805 #else
00806         UNUSED(cred);
00807         UNUSED(gssapi_keytab);
00808         UNUSED(intoken);
00809         UNUSED(outtoken);
00810         UNUSED(ctxout);
00811         UNUSED(principal);
00812         UNUSED(mctx);
00813 
00814         return (ISC_R_NOTIMPLEMENTED);
00815 #endif
00816 }
00817 
00818 isc_result_t
00819 dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
00820 {
00821 #ifdef GSSAPI
00822         OM_uint32 gret, minor;
00823         char buf[1024];
00824 
00825         UNUSED(mctx);
00826 
00827         REQUIRE(gssctx != NULL && *gssctx != NULL);
00828 
00829         /* Delete the context from the GSS provider */
00830         gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
00831         if (gret != GSS_S_COMPLETE) {
00832                 /* Log the error, but still free the context's memory */
00833                 gss_log(3, "Failure deleting security context %s",
00834                         gss_error_tostring(gret, minor, buf, sizeof(buf)));
00835         }
00836         return(ISC_R_SUCCESS);
00837 #else
00838         UNUSED(mctx);
00839         UNUSED(gssctx);
00840         return (ISC_R_NOTIMPLEMENTED);
00841 #endif
00842 }
00843 
00844 char *
00845 gss_error_tostring(isc_uint32_t major, isc_uint32_t minor,
00846                    char *buf, size_t buflen) {
00847 #ifdef GSSAPI
00848         gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
00849                         msg_major = GSS_C_EMPTY_BUFFER;
00850         OM_uint32 msg_ctx, minor_stat;
00851 
00852         /* Handle major status */
00853         msg_ctx = 0;
00854         (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
00855                                  GSS_C_NULL_OID, &msg_ctx, &msg_major);
00856 
00857         /* Handle minor status */
00858         msg_ctx = 0;
00859         (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
00860                                  GSS_C_NULL_OID, &msg_ctx, &msg_minor);
00861 
00862         snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
00863                 (char *)msg_major.value, (char *)msg_minor.value);
00864 
00865         if (msg_major.length != 0U)
00866                 (void)gss_release_buffer(&minor_stat, &msg_major);
00867         if (msg_minor.length != 0U)
00868                 (void)gss_release_buffer(&minor_stat, &msg_minor);
00869         return(buf);
00870 #else
00871         snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
00872                  major, minor);
00873 
00874         return (buf);
00875 #endif
00876 }
00877 
00878 void
00879 gss_log(int level, const char *fmt, ...) {
00880         va_list ap;
00881 
00882         va_start(ap, fmt);
00883         isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
00884                        DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
00885         va_end(ap);
00886 }
00887 
00888 /*! \file */

Generated on Tue Apr 28 17:40:56 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha