"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/lib/dns/gssapictx.c" (4 Sep 2020, 23793 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


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