"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/lib/dns/gssapictx.c" (7 Sep 2020, 21940 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "gssapictx.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 
   13 #include <config.h>
   14 
   15 #include <ctype.h>
   16 #include <inttypes.h>
   17 #include <stdbool.h>
   18 #include <stdlib.h>
   19 #include <string.h>
   20 
   21 #include <isc/buffer.h>
   22 #include <isc/dir.h>
   23 #include <isc/entropy.h>
   24 #include <isc/file.h>
   25 #include <isc/lex.h>
   26 #include <isc/mem.h>
   27 #include <isc/once.h>
   28 #include <isc/print.h>
   29 #include <isc/platform.h>
   30 #include <isc/random.h>
   31 #include <isc/string.h>
   32 #include <isc/time.h>
   33 #include <isc/util.h>
   34 
   35 #include <dns/fixedname.h>
   36 #include <dns/name.h>
   37 #include <dns/rdata.h>
   38 #include <dns/rdataclass.h>
   39 #include <dns/result.h>
   40 #include <dns/types.h>
   41 #include <dns/keyvalues.h>
   42 #include <dns/log.h>
   43 
   44 #include <dst/gssapi.h>
   45 #include <dst/result.h>
   46 
   47 #include "dst_internal.h"
   48 
   49 /*
   50  * If we're using our own SPNEGO implementation (see configure.in),
   51  * pull it in now.  Otherwise, we just use whatever GSSAPI supplies.
   52  */
   53 #if defined(GSSAPI) && defined(USE_ISC_SPNEGO)
   54 #include "spnego.h"
   55 #define gss_accept_sec_context  gss_accept_sec_context_spnego
   56 #define gss_init_sec_context    gss_init_sec_context_spnego
   57 #endif
   58 
   59 /*
   60  * Solaris8 apparently needs an explicit OID set, and Solaris10 needs
   61  * one for anything but Kerberos.  Supplying an explicit OID set
   62  * doesn't appear to hurt anything in other implementations, so we
   63  * always use one.  If we're not using our own SPNEGO implementation,
   64  * we include SPNEGO's OID.
   65  */
   66 #ifdef GSSAPI
   67 #ifdef WIN32
   68 #include <krb5/krb5.h>
   69 #else
   70 #include ISC_PLATFORM_KRB5HEADER
   71 #endif
   72 
   73 static unsigned char krb5_mech_oid_bytes[] = {
   74     0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x01, 0x02, 0x02
   75 };
   76 
   77 #ifndef USE_ISC_SPNEGO
   78 static unsigned char spnego_mech_oid_bytes[] = {
   79     0x2b, 0x06, 0x01, 0x05, 0x05, 0x02
   80 };
   81 #endif
   82 
   83 static gss_OID_desc mech_oid_set_array[] = {
   84     { sizeof(krb5_mech_oid_bytes), krb5_mech_oid_bytes },
   85 #ifndef USE_ISC_SPNEGO
   86     { sizeof(spnego_mech_oid_bytes), spnego_mech_oid_bytes },
   87 #endif
   88 };
   89 
   90 static gss_OID_set_desc mech_oid_set = {
   91     sizeof(mech_oid_set_array) / sizeof(*mech_oid_set_array),
   92     mech_oid_set_array
   93 };
   94 
   95 #endif
   96 
   97 #define REGION_TO_GBUFFER(r, gb) \
   98     do { \
   99         (gb).length = (r).length; \
  100         (gb).value = (r).base; \
  101     } while (0)
  102 
  103 #define GBUFFER_TO_REGION(gb, r) \
  104     do { \
  105       (r).length = (unsigned int)(gb).length; \
  106         (r).base = (gb).value; \
  107     } while (0)
  108 
  109 
  110 #define RETERR(x) do { \
  111     result = (x); \
  112     if (result != ISC_R_SUCCESS) \
  113         goto out; \
  114     } while (0)
  115 
  116 #ifdef GSSAPI
  117 static inline void
  118 name_to_gbuffer(dns_name_t *name, isc_buffer_t *buffer,
  119         gss_buffer_desc *gbuffer)
  120 {
  121     dns_name_t tname, *namep;
  122     isc_region_t r;
  123     isc_result_t result;
  124 
  125     if (!dns_name_isabsolute(name))
  126         namep = name;
  127     else
  128     {
  129         unsigned int labels;
  130         dns_name_init(&tname, NULL);
  131         labels = dns_name_countlabels(name);
  132         dns_name_getlabelsequence(name, 0, labels - 1, &tname);
  133         namep = &tname;
  134     }
  135 
  136     result = dns_name_toprincipal(namep, buffer);
  137     RUNTIME_CHECK(result == ISC_R_SUCCESS);
  138     isc_buffer_putuint8(buffer, 0);
  139     isc_buffer_usedregion(buffer, &r);
  140     REGION_TO_GBUFFER(r, *gbuffer);
  141 }
  142 
  143 static void
  144 log_cred(const gss_cred_id_t cred) {
  145     OM_uint32 gret, minor, lifetime;
  146     gss_name_t gname;
  147     gss_buffer_desc gbuffer;
  148     gss_cred_usage_t usage;
  149     const char *usage_text;
  150     char buf[1024];
  151 
  152     gret = gss_inquire_cred(&minor, cred, &gname, &lifetime, &usage, NULL);
  153     if (gret != GSS_S_COMPLETE) {
  154         gss_log(3, "failed gss_inquire_cred: %s",
  155             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  156         return;
  157     }
  158 
  159     gret = gss_display_name(&minor, gname, &gbuffer, NULL);
  160     if (gret != GSS_S_COMPLETE)
  161         gss_log(3, "failed gss_display_name: %s",
  162             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  163     else {
  164         switch (usage) {
  165         case GSS_C_BOTH:
  166             usage_text = "GSS_C_BOTH";
  167             break;
  168         case GSS_C_INITIATE:
  169             usage_text = "GSS_C_INITIATE";
  170             break;
  171         case GSS_C_ACCEPT:
  172             usage_text = "GSS_C_ACCEPT";
  173             break;
  174         default:
  175             usage_text = "???";
  176         }
  177         gss_log(3, "gss cred: \"%s\", %s, %lu", (char *)gbuffer.value,
  178             usage_text, (unsigned long)lifetime);
  179     }
  180 
  181     if (gret == GSS_S_COMPLETE) {
  182         if (gbuffer.length != 0U) {
  183             gret = gss_release_buffer(&minor, &gbuffer);
  184             if (gret != GSS_S_COMPLETE)
  185                 gss_log(3, "failed gss_release_buffer: %s",
  186                     gss_error_tostring(gret, minor, buf,
  187                                sizeof(buf)));
  188         }
  189     }
  190 
  191     gret = gss_release_name(&minor, &gname);
  192     if (gret != GSS_S_COMPLETE)
  193         gss_log(3, "failed gss_release_name: %s",
  194             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  195 }
  196 #endif
  197 
  198 #ifdef GSSAPI
  199 /*
  200  * check for the most common configuration errors.
  201  *
  202  * The errors checked for are:
  203  *   - tkey-gssapi-credential doesn't start with DNS/
  204  *   - the default realm in /etc/krb5.conf and the
  205  *     tkey-gssapi-credential bind config option don't match
  206  *
  207  * Note that if tkey-gssapi-keytab is set then these configure checks
  208  * are not performed, and runtime errors from gssapi are used instead
  209  */
  210 static void
  211 check_config(const char *gss_name) {
  212     const char *p;
  213     krb5_context krb5_ctx;
  214     char *krb5_realm_name = NULL;
  215 
  216     if (strncasecmp(gss_name, "DNS/", 4) != 0) {
  217         gss_log(ISC_LOG_ERROR, "tkey-gssapi-credential (%s) "
  218             "should start with 'DNS/'", gss_name);
  219         return;
  220     }
  221 
  222     if (krb5_init_context(&krb5_ctx) != 0) {
  223         gss_log(ISC_LOG_ERROR, "Unable to initialise krb5 context");
  224         return;
  225     }
  226     if (krb5_get_default_realm(krb5_ctx, &krb5_realm_name) != 0) {
  227         gss_log(ISC_LOG_ERROR, "Unable to get krb5 default realm");
  228         krb5_free_context(krb5_ctx);
  229         return;
  230     }
  231     p = strchr(gss_name, '@');
  232     if (p == NULL) {
  233         gss_log(ISC_LOG_ERROR, "badly formatted "
  234             "tkey-gssapi-credentials (%s)", gss_name);
  235         krb5_free_context(krb5_ctx);
  236         return;
  237     }
  238     if (strcasecmp(p + 1, krb5_realm_name) != 0) {
  239         gss_log(ISC_LOG_ERROR, "default realm from krb5.conf (%s) "
  240             "does not match tkey-gssapi-credential (%s)",
  241             krb5_realm_name, gss_name);
  242         krb5_free_context(krb5_ctx);
  243         return;
  244     }
  245     krb5_free_context(krb5_ctx);
  246 }
  247 #endif
  248 
  249 isc_result_t
  250 dst_gssapi_acquirecred(dns_name_t *name, bool initiate,
  251                gss_cred_id_t *cred)
  252 {
  253 #ifdef GSSAPI
  254     isc_result_t result;
  255     isc_buffer_t namebuf;
  256     gss_name_t gname;
  257     gss_buffer_desc gnamebuf;
  258     unsigned char array[DNS_NAME_MAXTEXT + 1];
  259     OM_uint32 gret, minor;
  260     OM_uint32 lifetime;
  261     gss_cred_usage_t usage;
  262     char buf[1024];
  263 
  264     REQUIRE(cred != NULL && *cred == NULL);
  265 
  266     /*
  267      * XXXSRA In theory we could use GSS_C_NT_HOSTBASED_SERVICE
  268      * here when we're in the acceptor role, which would let us
  269      * default the hostname and use a compiled in default service
  270      * name of "DNS", giving one less thing to configure in
  271      * named.conf.  Unfortunately, this creates a circular
  272      * dependency due to DNS-based realm lookup in at least one
  273      * GSSAPI implementation (Heimdal).  Oh well.
  274      */
  275     if (name != NULL) {
  276         isc_buffer_init(&namebuf, array, sizeof(array));
  277         name_to_gbuffer(name, &namebuf, &gnamebuf);
  278         gret = gss_import_name(&minor, &gnamebuf,
  279                        GSS_C_NO_OID, &gname);
  280         if (gret != GSS_S_COMPLETE) {
  281             check_config((char *)array);
  282 
  283             gss_log(3, "failed gss_import_name: %s",
  284                 gss_error_tostring(gret, minor, buf,
  285                            sizeof(buf)));
  286             return (ISC_R_FAILURE);
  287         }
  288     } else
  289         gname = NULL;
  290 
  291     /* Get the credentials. */
  292     if (gname != NULL)
  293         gss_log(3, "acquiring credentials for %s",
  294             (char *)gnamebuf.value);
  295     else {
  296         /* XXXDCL does this even make any sense? */
  297         gss_log(3, "acquiring credentials for ?");
  298     }
  299 
  300     if (initiate)
  301         usage = GSS_C_INITIATE;
  302     else
  303         usage = GSS_C_ACCEPT;
  304 
  305     gret = gss_acquire_cred(&minor, gname, GSS_C_INDEFINITE,
  306                 &mech_oid_set, usage, cred, NULL, &lifetime);
  307 
  308     if (gret != GSS_S_COMPLETE) {
  309         gss_log(3, "failed to acquire %s credentials for %s: %s",
  310             initiate ? "initiate" : "accept",
  311             (gname != NULL) ? (char *)gnamebuf.value : "?",
  312             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  313         if (gname != NULL)
  314             check_config((char *)array);
  315         result = ISC_R_FAILURE;
  316         goto cleanup;
  317     }
  318 
  319     gss_log(4, "acquired %s credentials for %s",
  320         initiate ? "initiate" : "accept",
  321         (gname != NULL) ? (char *)gnamebuf.value : "?");
  322 
  323     log_cred(*cred);
  324     result = ISC_R_SUCCESS;
  325 
  326 cleanup:
  327     if (gname != NULL) {
  328         gret = gss_release_name(&minor, &gname);
  329         if (gret != GSS_S_COMPLETE)
  330             gss_log(3, "failed gss_release_name: %s",
  331                 gss_error_tostring(gret, minor, buf,
  332                            sizeof(buf)));
  333     }
  334 
  335     return (result);
  336 #else
  337     REQUIRE(cred != NULL && *cred == NULL);
  338 
  339     UNUSED(name);
  340     UNUSED(initiate);
  341     UNUSED(cred);
  342 
  343     return (ISC_R_NOTIMPLEMENTED);
  344 #endif
  345 }
  346 
  347 bool
  348 dst_gssapi_identitymatchesrealmkrb5(const dns_name_t *signer,
  349                     const dns_name_t *name,
  350                     const dns_name_t *realm,
  351                     bool subdomain)
  352 {
  353 #ifdef GSSAPI
  354     char sbuf[DNS_NAME_FORMATSIZE];
  355     char rbuf[DNS_NAME_FORMATSIZE];
  356     char *sname;
  357     char *rname;
  358     isc_buffer_t buffer;
  359     isc_result_t result;
  360 
  361     /*
  362      * It is far, far easier to write the names we are looking at into
  363      * a string, and do string operations on them.
  364      */
  365     isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
  366     result = dns_name_toprincipal(signer, &buffer);
  367     RUNTIME_CHECK(result == ISC_R_SUCCESS);
  368     isc_buffer_putuint8(&buffer, 0);
  369     dns_name_format(realm, rbuf, sizeof(rbuf));
  370 
  371     /*
  372      * Find the realm portion.  This is the part after the @.  If it
  373      * does not exist, we don't have something we like, so we fail our
  374      * compare.
  375      */
  376     rname = strchr(sbuf, '@');
  377     if (rname == NULL)
  378         return (false);
  379     *rname = '\0';
  380     rname++;
  381 
  382     if (strcmp(rname, rbuf) != 0) {
  383         return (false);
  384     }
  385 
  386     /*
  387      * Find the host portion of the signer's name.  We do this by
  388      * searching for the first / character.  We then check to make
  389      * certain the instance name is "host"
  390      *
  391      * This will work for
  392      *    host/example.com@EXAMPLE.COM
  393      */
  394     sname = strchr(sbuf, '/');
  395     if (sname == NULL)
  396         return (false);
  397     *sname = '\0';
  398     sname++;
  399     if (strcmp(sbuf, "host") != 0)
  400         return (false);
  401 
  402     /*
  403      * If name is non NULL check that it matches against the
  404      * machine name as expected.
  405      */
  406     if (name != NULL) {
  407         dns_fixedname_t fixed;
  408         dns_name_t *machine;
  409 
  410         machine = dns_fixedname_initname(&fixed);
  411         result = dns_name_fromstring(machine, sname, 0, NULL);
  412         if (result != ISC_R_SUCCESS) {
  413             return (false);
  414         }
  415         if (subdomain) {
  416             return (dns_name_issubdomain(name, machine));
  417         }
  418         return (dns_name_equal(name, machine));
  419     }
  420 
  421     return (true);
  422 #else
  423     UNUSED(signer);
  424     UNUSED(name);
  425     UNUSED(realm);
  426     UNUSED(subdomain);
  427     return (false);
  428 #endif
  429 }
  430 
  431 bool
  432 dst_gssapi_identitymatchesrealmms(const dns_name_t *signer,
  433                   const dns_name_t *name,
  434                   const dns_name_t *realm,
  435                   bool subdomain)
  436 {
  437 #ifdef GSSAPI
  438     char sbuf[DNS_NAME_FORMATSIZE];
  439     char rbuf[DNS_NAME_FORMATSIZE];
  440     char *sname;
  441     char *rname;
  442     isc_buffer_t buffer;
  443     isc_result_t result;
  444 
  445     /*
  446      * It is far, far easier to write the names we are looking at into
  447      * a string, and do string operations on them.
  448      */
  449     isc_buffer_init(&buffer, sbuf, sizeof(sbuf));
  450     result = dns_name_toprincipal(signer, &buffer);
  451     RUNTIME_CHECK(result == ISC_R_SUCCESS);
  452     isc_buffer_putuint8(&buffer, 0);
  453     dns_name_format(realm, rbuf, sizeof(rbuf));
  454 
  455     /*
  456      * Find the realm portion.  This is the part after the @.  If it
  457      * does not exist, we don't have something we like, so we fail our
  458      * compare.
  459      */
  460     rname = strchr(sbuf, '@');
  461     if (rname == NULL)
  462         return (false);
  463     sname = strchr(sbuf, '$');
  464     if (sname == NULL)
  465         return (false);
  466 
  467     /*
  468      * Verify that the $ and @ follow one another.
  469      */
  470     if (rname - sname != 1)
  471         return (false);
  472 
  473     /*
  474      * Find the host portion of the signer's name.  Zero out the $ so
  475      * it terminates the signer's name, and skip past the @ for
  476      * the realm.
  477      *
  478      * All service principals in Microsoft format seem to be in
  479      *    machinename$@EXAMPLE.COM
  480      * format.
  481      */
  482     rname++;
  483     *sname = '\0';
  484 
  485     if (strcmp(rname, rbuf) != 0) {
  486         return (false);
  487     }
  488 
  489     /*
  490      * Now, we check that the realm matches (case sensitive) and that
  491      * 'name' matches against 'machinename' qualified with 'realm'.
  492      */
  493     if (name != NULL) {
  494         dns_fixedname_t fixed;
  495         dns_name_t *machine;
  496 
  497         machine = dns_fixedname_initname(&fixed);
  498         result = dns_name_fromstring2(machine, sbuf, realm, 0, NULL);
  499         if (result != ISC_R_SUCCESS) {
  500             return (false);
  501         }
  502         if (subdomain) {
  503             return (dns_name_issubdomain(name, machine));
  504         }
  505         return (dns_name_equal(name, machine));
  506     }
  507 
  508     return (true);
  509 #else
  510     UNUSED(signer);
  511     UNUSED(name);
  512     UNUSED(realm);
  513     UNUSED(subdomain);
  514     return (false);
  515 #endif
  516 }
  517 
  518 isc_result_t
  519 dst_gssapi_releasecred(gss_cred_id_t *cred) {
  520 #ifdef GSSAPI
  521     OM_uint32 gret, minor;
  522     char buf[1024];
  523 
  524     REQUIRE(cred != NULL && *cred != NULL);
  525 
  526     gret = gss_release_cred(&minor, cred);
  527     if (gret != GSS_S_COMPLETE) {
  528         /* Log the error, but still free the credential's memory */
  529         gss_log(3, "failed releasing credential: %s",
  530             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  531     }
  532     *cred = NULL;
  533 
  534     return(ISC_R_SUCCESS);
  535 #else
  536     UNUSED(cred);
  537 
  538     return (ISC_R_NOTIMPLEMENTED);
  539 #endif
  540 }
  541 
  542 #ifdef GSSAPI
  543 /*
  544  * Format a gssapi error message info into a char ** on the given memory
  545  * context. This is used to return gssapi error messages back up the
  546  * call chain for reporting to the user.
  547  */
  548 static void
  549 gss_err_message(isc_mem_t *mctx, uint32_t major, uint32_t minor,
  550         char **err_message)
  551 {
  552     char buf[1024];
  553     char *estr;
  554 
  555     if (err_message == NULL || mctx == NULL) {
  556         /* the caller doesn't want any error messages */
  557         return;
  558     }
  559 
  560     estr = gss_error_tostring(major, minor, buf, sizeof(buf));
  561     if (estr != NULL)
  562         (*err_message) = isc_mem_strdup(mctx, estr);
  563 }
  564 #endif
  565 
  566 isc_result_t
  567 dst_gssapi_initctx(dns_name_t *name, isc_buffer_t *intoken,
  568            isc_buffer_t *outtoken, gss_ctx_id_t *gssctx,
  569            isc_mem_t *mctx, char **err_message)
  570 {
  571 #ifdef GSSAPI
  572     isc_region_t r;
  573     isc_buffer_t namebuf;
  574     gss_name_t gname;
  575     OM_uint32 gret, minor, ret_flags, flags;
  576     gss_buffer_desc gintoken, *gintokenp, gouttoken = GSS_C_EMPTY_BUFFER;
  577     isc_result_t result;
  578     gss_buffer_desc gnamebuf;
  579     unsigned char array[DNS_NAME_MAXTEXT + 1];
  580 
  581     /* Client must pass us a valid gss_ctx_id_t here */
  582     REQUIRE(gssctx != NULL);
  583     REQUIRE(mctx != NULL);
  584 
  585     isc_buffer_init(&namebuf, array, sizeof(array));
  586     name_to_gbuffer(name, &namebuf, &gnamebuf);
  587 
  588     /* Get the name as a GSS name */
  589     gret = gss_import_name(&minor, &gnamebuf, GSS_C_NO_OID, &gname);
  590     if (gret != GSS_S_COMPLETE) {
  591         gss_err_message(mctx, gret, minor, err_message);
  592         result = ISC_R_FAILURE;
  593         goto out;
  594     }
  595 
  596     if (intoken != NULL) {
  597         /* Don't call gss_release_buffer for gintoken! */
  598         REGION_TO_GBUFFER(*intoken, gintoken);
  599         gintokenp = &gintoken;
  600     } else {
  601         gintokenp = NULL;
  602     }
  603 
  604     /*
  605      * Note that we don't set GSS_C_SEQUENCE_FLAG as Windows DNS
  606      * servers don't like it.
  607      */
  608     flags = GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
  609 
  610     gret = gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, gssctx,
  611                     gname, GSS_SPNEGO_MECHANISM, flags,
  612                     0, NULL, gintokenp,
  613                     NULL, &gouttoken, &ret_flags, NULL);
  614 
  615     if (gret != GSS_S_COMPLETE && gret != GSS_S_CONTINUE_NEEDED) {
  616         gss_err_message(mctx, gret, minor, err_message);
  617         if (err_message != NULL && *err_message != NULL)
  618             gss_log(3, "Failure initiating security context: %s",
  619                 *err_message);
  620         else
  621             gss_log(3, "Failure initiating security context");
  622 
  623         result = ISC_R_FAILURE;
  624         goto out;
  625     }
  626 
  627     /*
  628      * XXXSRA Not handled yet: RFC 3645 3.1.1: check ret_flags
  629      * MUTUAL and INTEG flags, fail if either not set.
  630      */
  631 
  632     /*
  633      * RFC 2744 states the a valid output token has a non-zero length.
  634      */
  635     if (gouttoken.length != 0U) {
  636         GBUFFER_TO_REGION(gouttoken, r);
  637         RETERR(isc_buffer_copyregion(outtoken, &r));
  638     }
  639 
  640     if (gret == GSS_S_COMPLETE)
  641         result = ISC_R_SUCCESS;
  642     else
  643         result = DNS_R_CONTINUE;
  644 
  645  out:
  646     if (gouttoken.length != 0U)
  647         (void)gss_release_buffer(&minor, &gouttoken);
  648     (void)gss_release_name(&minor, &gname);
  649     return (result);
  650 #else
  651     UNUSED(name);
  652     UNUSED(intoken);
  653     UNUSED(outtoken);
  654     UNUSED(gssctx);
  655     UNUSED(mctx);
  656     UNUSED(err_message);
  657 
  658     return (ISC_R_NOTIMPLEMENTED);
  659 #endif
  660 }
  661 
  662 isc_result_t
  663 dst_gssapi_acceptctx(gss_cred_id_t cred,
  664              const char *gssapi_keytab,
  665              isc_region_t *intoken, isc_buffer_t **outtoken,
  666              gss_ctx_id_t *ctxout, dns_name_t *principal,
  667              isc_mem_t *mctx)
  668 {
  669 #ifdef GSSAPI
  670     isc_region_t r;
  671     isc_buffer_t namebuf;
  672     gss_buffer_desc gnamebuf = GSS_C_EMPTY_BUFFER, gintoken,
  673             gouttoken = GSS_C_EMPTY_BUFFER;
  674     OM_uint32 gret, minor;
  675     gss_ctx_id_t context = GSS_C_NO_CONTEXT;
  676     gss_name_t gname = NULL;
  677     isc_result_t result;
  678     char buf[1024];
  679 
  680     REQUIRE(outtoken != NULL && *outtoken == NULL);
  681 
  682     REGION_TO_GBUFFER(*intoken, gintoken);
  683 
  684     if (*ctxout == NULL)
  685         context = GSS_C_NO_CONTEXT;
  686     else
  687         context = *ctxout;
  688 
  689     if (gssapi_keytab != NULL) {
  690 #if defined(ISC_PLATFORM_GSSAPI_KRB5_HEADER) || defined(WIN32)
  691         gret = gsskrb5_register_acceptor_identity(gssapi_keytab);
  692         if (gret != GSS_S_COMPLETE) {
  693             gss_log(3, "failed "
  694                 "gsskrb5_register_acceptor_identity(%s): %s",
  695                 gssapi_keytab,
  696                 gss_error_tostring(gret, 0, buf, sizeof(buf)));
  697             return (DNS_R_INVALIDTKEY);
  698         }
  699 #else
  700         /*
  701          * Minimize memory leakage by only setting KRB5_KTNAME
  702          * if it needs to change.
  703          */
  704         const char *old = getenv("KRB5_KTNAME");
  705         if (old == NULL || strcmp(old, gssapi_keytab) != 0) {
  706             size_t size;
  707             char *kt;
  708 
  709             size = strlen(gssapi_keytab) + 13;
  710             kt = malloc(size);
  711             if (kt == NULL)
  712                 return (ISC_R_NOMEMORY);
  713             snprintf(kt, size, "KRB5_KTNAME=%s", gssapi_keytab);
  714             if (putenv(kt) != 0)
  715                 return (ISC_R_NOMEMORY);
  716         }
  717 #endif
  718     }
  719 
  720     log_cred(cred);
  721 
  722     gret = gss_accept_sec_context(&minor, &context, cred, &gintoken,
  723                       GSS_C_NO_CHANNEL_BINDINGS, &gname,
  724                       NULL, &gouttoken, NULL, NULL, NULL);
  725 
  726     result = ISC_R_FAILURE;
  727 
  728     switch (gret) {
  729     case GSS_S_COMPLETE:
  730     case GSS_S_CONTINUE_NEEDED:
  731         break;
  732     case GSS_S_DEFECTIVE_TOKEN:
  733     case GSS_S_DEFECTIVE_CREDENTIAL:
  734     case GSS_S_BAD_SIG:
  735     case GSS_S_DUPLICATE_TOKEN:
  736     case GSS_S_OLD_TOKEN:
  737     case GSS_S_NO_CRED:
  738     case GSS_S_CREDENTIALS_EXPIRED:
  739     case GSS_S_BAD_BINDINGS:
  740     case GSS_S_NO_CONTEXT:
  741     case GSS_S_BAD_MECH:
  742     case GSS_S_FAILURE:
  743         result = DNS_R_INVALIDTKEY;
  744         /* fall through */
  745     default:
  746         gss_log(3, "failed gss_accept_sec_context: %s",
  747             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  748         return (result);
  749     }
  750 
  751     if (gouttoken.length > 0U) {
  752         RETERR(isc_buffer_allocate(mctx, outtoken,
  753                        (unsigned int)gouttoken.length));
  754         GBUFFER_TO_REGION(gouttoken, r);
  755         RETERR(isc_buffer_copyregion(*outtoken, &r));
  756         (void)gss_release_buffer(&minor, &gouttoken);
  757     }
  758 
  759     if (gret == GSS_S_COMPLETE) {
  760         gret = gss_display_name(&minor, gname, &gnamebuf, NULL);
  761         if (gret != GSS_S_COMPLETE) {
  762             gss_log(3, "failed gss_display_name: %s",
  763                 gss_error_tostring(gret, minor,
  764                            buf, sizeof(buf)));
  765             RETERR(ISC_R_FAILURE);
  766         }
  767 
  768         /*
  769          * Compensate for a bug in Solaris8's implementation
  770          * of gss_display_name().  Should be harmless in any
  771          * case, since principal names really should not
  772          * contain null characters.
  773          */
  774         if (gnamebuf.length > 0U &&
  775             ((char *)gnamebuf.value)[gnamebuf.length - 1] == '\0')
  776             gnamebuf.length--;
  777 
  778         gss_log(3, "gss-api source name (accept) is %.*s",
  779             (int)gnamebuf.length, (char *)gnamebuf.value);
  780 
  781         GBUFFER_TO_REGION(gnamebuf, r);
  782         isc_buffer_init(&namebuf, r.base, r.length);
  783         isc_buffer_add(&namebuf, r.length);
  784 
  785         RETERR(dns_name_fromtext(principal, &namebuf, dns_rootname,
  786                      0, NULL));
  787 
  788         if (gnamebuf.length != 0U) {
  789             gret = gss_release_buffer(&minor, &gnamebuf);
  790             if (gret != GSS_S_COMPLETE)
  791                 gss_log(3, "failed gss_release_buffer: %s",
  792                     gss_error_tostring(gret, minor, buf,
  793                                sizeof(buf)));
  794         }
  795     } else
  796         result = DNS_R_CONTINUE;
  797 
  798     *ctxout = context;
  799 
  800  out:
  801     if (gname != NULL) {
  802         gret = gss_release_name(&minor, &gname);
  803         if (gret != GSS_S_COMPLETE)
  804             gss_log(3, "failed gss_release_name: %s",
  805                 gss_error_tostring(gret, minor, buf,
  806                            sizeof(buf)));
  807     }
  808 
  809     return (result);
  810 #else
  811     UNUSED(cred);
  812     UNUSED(gssapi_keytab);
  813     UNUSED(intoken);
  814     UNUSED(outtoken);
  815     UNUSED(ctxout);
  816     UNUSED(principal);
  817     UNUSED(mctx);
  818 
  819     return (ISC_R_NOTIMPLEMENTED);
  820 #endif
  821 }
  822 
  823 isc_result_t
  824 dst_gssapi_deletectx(isc_mem_t *mctx, gss_ctx_id_t *gssctx)
  825 {
  826 #ifdef GSSAPI
  827     OM_uint32 gret, minor;
  828     char buf[1024];
  829 
  830     UNUSED(mctx);
  831 
  832     REQUIRE(gssctx != NULL && *gssctx != NULL);
  833 
  834     /* Delete the context from the GSS provider */
  835     gret = gss_delete_sec_context(&minor, gssctx, GSS_C_NO_BUFFER);
  836     if (gret != GSS_S_COMPLETE) {
  837         /* Log the error, but still free the context's memory */
  838         gss_log(3, "Failure deleting security context %s",
  839             gss_error_tostring(gret, minor, buf, sizeof(buf)));
  840     }
  841     return(ISC_R_SUCCESS);
  842 #else
  843     UNUSED(mctx);
  844     UNUSED(gssctx);
  845     return (ISC_R_NOTIMPLEMENTED);
  846 #endif
  847 }
  848 
  849 char *
  850 gss_error_tostring(uint32_t major, uint32_t minor,
  851            char *buf, size_t buflen) {
  852 #ifdef GSSAPI
  853     gss_buffer_desc msg_minor = GSS_C_EMPTY_BUFFER,
  854             msg_major = GSS_C_EMPTY_BUFFER;
  855     OM_uint32 msg_ctx, minor_stat;
  856 
  857     /* Handle major status */
  858     msg_ctx = 0;
  859     (void)gss_display_status(&minor_stat, major, GSS_C_GSS_CODE,
  860                  GSS_C_NULL_OID, &msg_ctx, &msg_major);
  861 
  862     /* Handle minor status */
  863     msg_ctx = 0;
  864     (void)gss_display_status(&minor_stat, minor, GSS_C_MECH_CODE,
  865                  GSS_C_NULL_OID, &msg_ctx, &msg_minor);
  866 
  867     snprintf(buf, buflen, "GSSAPI error: Major = %s, Minor = %s.",
  868         (char *)msg_major.value, (char *)msg_minor.value);
  869 
  870     if (msg_major.length != 0U)
  871         (void)gss_release_buffer(&minor_stat, &msg_major);
  872     if (msg_minor.length != 0U)
  873         (void)gss_release_buffer(&minor_stat, &msg_minor);
  874     return(buf);
  875 #else
  876     snprintf(buf, buflen, "GSSAPI error: Major = %u, Minor = %u.",
  877          major, minor);
  878 
  879     return (buf);
  880 #endif
  881 }
  882 
  883 void
  884 gss_log(int level, const char *fmt, ...) {
  885     va_list ap;
  886 
  887     va_start(ap, fmt);
  888     isc_log_vwrite(dns_lctx, DNS_LOGCATEGORY_GENERAL,
  889                DNS_LOGMODULE_TKEY, ISC_LOG_DEBUG(level), fmt, ap);
  890     va_end(ap);
  891 }
  892 
  893 /*! \file */