LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/gssapi/spnego - negoex_ctx.c (source / functions) Hit Total Coverage
Test: coverage report for master 469b22b8 Lines: 0 496 0.0 %
Date: 2024-06-10 12:05:21 Functions: 0 20 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011-2021 PADL Software Pty Ltd.
       3             :  * All rights reserved.
       4             :  *
       5             :  * Redistribution and use in source and binary forms, with or without
       6             :  * modification, are permitted provided that the following conditions
       7             :  * are met:
       8             :  *
       9             :  * * Redistributions of source code must retain the above copyright
      10             :  *   notice, this list of conditions and the following disclaimer.
      11             :  *
      12             :  * * Redistributions in binary form must reproduce the above copyright
      13             :  *   notice, this list of conditions and the following disclaimer in
      14             :  *   the documentation and/or other materials provided with the
      15             :  *   distribution.
      16             :  *
      17             :  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      18             :  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      19             :  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      20             :  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      21             :  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      22             :  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      23             :  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      24             :  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      25             :  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      26             :  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      27             :  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      28             :  * OF THE POSSIBILITY OF SUCH DAMAGE.
      29             :  */
      30             : 
      31             : #include "spnego_locl.h"
      32             : 
      33             : /*
      34             :  * The initial context token emitted by the initiator is a INITIATOR_NEGO
      35             :  * message followed by zero or more INITIATOR_META_DATA tokens, and zero
      36             :  * or one AP_REQUEST tokens.
      37             :  *
      38             :  * Upon receiving this, the acceptor computes the list of mutually supported
      39             :  * authentication mechanisms and performs the metadata exchange. The output
      40             :  * token is ACCEPTOR_NEGO followed by zero or more ACCEPTOR_META_DATA tokens,
      41             :  * and zero or one CHALLENGE tokens.
      42             :  *
      43             :  * Once the metadata exchange is complete and a mechanism is selected, the
      44             :  * selected mechanism's context token exchange continues with AP_REQUEST and
      45             :  * CHALLENGE messages.
      46             :  *
      47             :  * Once the context token exchange is complete, VERIFY messages are sent to
      48             :  * authenticate the entire exchange.
      49             :  */
      50             : 
      51             : static OM_uint32
      52           0 : buffer_set_to_crypto(OM_uint32 *minor,
      53             :                      krb5_context context,
      54             :                      gss_buffer_set_t buffers,
      55             :                      krb5_crypto *crypto)
      56             : {
      57           0 :     krb5_error_code ret;
      58           0 :     krb5_keyblock keyblock;
      59           0 :     OM_uint32 tmp;
      60             : 
      61             :     /*
      62             :      * Returned keys must be in two buffers, with the key contents in
      63             :      * the first and the enctype as a 32-bit little-endian integer in
      64             :      * the second.
      65             :      */
      66           0 :     if (buffers->count != 2 ||
      67           0 :         buffers->elements[1].length != sizeof(tmp)) {
      68           0 :         *minor = (OM_uint32)NEGOEX_NO_VERIFY_KEY;
      69           0 :         return GSS_S_FAILURE;
      70             :     }
      71             : 
      72           0 :     if (*crypto != NULL) {
      73           0 :         krb5_crypto_destroy(context, *crypto);
      74           0 :         *crypto = NULL;
      75             :     }
      76             : 
      77           0 :     keyblock.keyvalue.data = buffers->elements[0].value;
      78           0 :     keyblock.keyvalue.length = buffers->elements[0].length;
      79           0 :     _gss_mg_decode_le_uint32(buffers->elements[1].value, &tmp);
      80           0 :     keyblock.keytype = tmp;
      81             : 
      82           0 :     ret = krb5_crypto_init(context, &keyblock, 0, crypto);
      83           0 :     if (ret) {
      84           0 :         *minor = ret;
      85           0 :         return GSS_S_FAILURE;
      86             :     }
      87             : 
      88           0 :     return GSS_S_COMPLETE;
      89             : }
      90             : 
      91             : #define NEGOEX_SIGN_KEY     1
      92             : #define NEGOEX_VERIFY_KEY   2
      93             : #define NEGOEX_BOTH_KEYS    (NEGOEX_SIGN_KEY|NEGOEX_VERIFY_KEY)
      94             : 
      95             : static OM_uint32
      96           0 : get_session_keys(OM_uint32 *minor,
      97             :                  krb5_context context,
      98             :                  OM_uint32 flags,
      99             :                  struct negoex_auth_mech *mech)
     100             : {
     101           0 :     OM_uint32 major, tmpMinor;
     102           0 :     gss_buffer_set_t buffers = GSS_C_NO_BUFFER_SET;
     103             : 
     104           0 :     if (flags & NEGOEX_SIGN_KEY) {
     105           0 :         major = gss_inquire_sec_context_by_oid(&tmpMinor, mech->mech_context,
     106             :                                                GSS_C_INQ_NEGOEX_KEY, &buffers);
     107           0 :         if (major == GSS_S_COMPLETE) {
     108           0 :             major = buffer_set_to_crypto(minor, context,
     109             :                                          buffers, &mech->crypto);
     110           0 :             _gss_secure_release_buffer_set(&tmpMinor, &buffers);
     111           0 :             if (major != GSS_S_COMPLETE)
     112           0 :                 return major;
     113             :         }
     114             :     }
     115             : 
     116           0 :     if (flags & NEGOEX_VERIFY_KEY) {
     117           0 :         major = gss_inquire_sec_context_by_oid(&tmpMinor, mech->mech_context,
     118             :                                                GSS_C_INQ_NEGOEX_VERIFY_KEY,
     119             :                                                &buffers);
     120           0 :         if (major == GSS_S_COMPLETE) {
     121           0 :             major = buffer_set_to_crypto(minor, context,
     122             :                                          buffers, &mech->verify_crypto);
     123           0 :             _gss_secure_release_buffer_set(&tmpMinor, &buffers);
     124           0 :             if (major != GSS_S_COMPLETE)
     125           0 :                 return major;
     126             :         }
     127             :     }
     128             : 
     129           0 :     return GSS_S_COMPLETE;
     130             : }
     131             : 
     132             : static OM_uint32
     133           0 : emit_initiator_nego(OM_uint32 *minor, gssspnego_ctx ctx)
     134             : {
     135           0 :     uint8_t random[32];
     136           0 :     struct negoex_auth_mech *mech;
     137           0 :     size_t i = 0;
     138             : 
     139           0 :     krb5_generate_random_block(random, sizeof(random));
     140             : 
     141           0 :     HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
     142           0 :         _gss_negoex_log_auth_scheme(ctx->flags.local, ++i, mech->scheme);
     143             : 
     144           0 :     return _gss_negoex_add_nego_message(minor, ctx, INITIATOR_NEGO, random);
     145             : }
     146             : 
     147             : static OM_uint32
     148           0 : process_initiator_nego(OM_uint32 *minor,
     149             :                        gssspnego_ctx ctx,
     150             :                        struct negoex_message *messages,
     151             :                        size_t nmessages)
     152             : {
     153           0 :     struct nego_message *msg;
     154           0 :     size_t i;
     155             : 
     156           0 :     heim_assert(!ctx->flags.local && ctx->negoex_step == 1,
     157             :                 "NegoEx INITIATOR_NEGO token received after first leg");
     158             : 
     159           0 :     msg = _gss_negoex_locate_nego_message(messages, nmessages, INITIATOR_NEGO);
     160           0 :     if (msg == NULL) {
     161           0 :         *minor = (OM_uint32)NEGOEX_MISSING_NEGO_MESSAGE;
     162           0 :         return GSS_S_DEFECTIVE_TOKEN;
     163             :     }
     164             : 
     165           0 :     for (i = 0; i < msg->nschemes; i++)
     166           0 :         _gss_negoex_log_auth_scheme(ctx->flags.local, i + 1, &msg->schemes[i * GUID_LENGTH]);
     167             : 
     168           0 :     _gss_negoex_restrict_auth_schemes(ctx, msg->schemes, msg->nschemes);
     169             : 
     170           0 :     return GSS_S_COMPLETE;
     171             : }
     172             : 
     173             : static OM_uint32
     174           0 : emit_acceptor_nego(OM_uint32 *minor, gssspnego_ctx ctx)
     175             : {
     176           0 :     uint8_t random[32];
     177             : 
     178           0 :     krb5_generate_random_block(random, 32);
     179             : 
     180           0 :     return _gss_negoex_add_nego_message(minor, ctx, ACCEPTOR_NEGO, random);
     181             : }
     182             : 
     183             : static OM_uint32
     184           0 : process_acceptor_nego(OM_uint32 *minor,
     185             :                       gssspnego_ctx ctx,
     186             :                       struct negoex_message *messages,
     187             :                       size_t nmessages)
     188             : {
     189           0 :     struct nego_message *msg;
     190             : 
     191           0 :     msg = _gss_negoex_locate_nego_message(messages, nmessages, ACCEPTOR_NEGO);
     192           0 :     if (msg == NULL) {
     193           0 :         *minor = (OM_uint32)NEGOEX_MISSING_NEGO_MESSAGE;
     194           0 :         return GSS_S_DEFECTIVE_TOKEN;
     195             :     }
     196             : 
     197             :     /*
     198             :      * Reorder and prune our mech list to match the acceptor's list (or a
     199             :      * subset of it).
     200             :      */
     201           0 :     _gss_negoex_common_auth_schemes(ctx, msg->schemes, msg->nschemes);
     202             : 
     203           0 :     return GSS_S_COMPLETE;
     204             : }
     205             : 
     206             : static void
     207           0 : query_meta_data(gssspnego_ctx ctx,
     208             :                 struct gssspnego_optimistic_ctx *opt,
     209             :                 gss_cred_id_t cred,
     210             :                 OM_uint32 req_flags)
     211             : {
     212           0 :     OM_uint32 major, minor;
     213           0 :     struct negoex_auth_mech *p, *next;
     214             : 
     215             :     /*
     216             :      * Note that if we received an optimistic context token from SPNEGO,
     217             :      * then we will call QMD after ISC, rather than before. Mechanisms
     218             :      * must be prepared to handle this and must not assume the context
     219             :      * will be NULL on entry.
     220             :      */
     221           0 :     HEIM_TAILQ_FOREACH_SAFE(p, &ctx->negoex_mechs, links, next) {
     222           0 :         if (opt != NULL && memcmp(opt->scheme, p->scheme, GUID_LENGTH) == 0)
     223           0 :             p->mech_context = opt->gssctx;;
     224             : 
     225           0 :         major = gssspi_query_meta_data(&minor, p->oid, cred, &p->mech_context,
     226           0 :                                        ctx->target_name, req_flags, &p->metadata);
     227             :         /* GSS_Query_meta_data failure removes mechanism from list. */
     228           0 :         if (major != GSS_S_COMPLETE)
     229           0 :             _gss_negoex_delete_auth_mech(ctx, p);
     230             :     }
     231           0 : }
     232             : 
     233             : static void
     234           0 : exchange_meta_data(gssspnego_ctx ctx,
     235             :                    gss_cred_id_t cred,
     236             :                    OM_uint32 req_flags,
     237             :                    struct negoex_message *messages,
     238             :                    size_t nmessages)
     239             : {
     240           0 :     OM_uint32 major, minor;
     241           0 :     struct negoex_auth_mech *mech;
     242           0 :     enum message_type type;
     243           0 :     struct exchange_message *msg;
     244           0 :     uint32_t i;
     245             : 
     246           0 :     type = ctx->flags.local ? ACCEPTOR_META_DATA : INITIATOR_META_DATA;
     247             : 
     248           0 :     for (i = 0; i < nmessages; i++) {
     249           0 :         if (messages[i].type != type)
     250           0 :             continue;
     251           0 :         msg = &messages[i].u.e;
     252             : 
     253           0 :         mech = _gss_negoex_locate_auth_scheme(ctx, msg->scheme);
     254           0 :         if (mech == NULL)
     255           0 :             continue;
     256             : 
     257           0 :         major = gssspi_exchange_meta_data(&minor, mech->oid, cred,
     258             :                                           &mech->mech_context,
     259           0 :                                           ctx->target_name,
     260           0 :                                           req_flags, &msg->token);
     261             :         /* GSS_Exchange_meta_data failure removes mechanism from list. */
     262           0 :         if (major != GSS_S_COMPLETE)
     263           0 :             _gss_negoex_delete_auth_mech(ctx, mech);
     264             :     }
     265           0 : }
     266             : 
     267             : static void
     268           0 : release_mech_crypto(struct negoex_auth_mech *mech)
     269             : {
     270           0 :     krb5_context context = NULL;
     271             : 
     272           0 :     if (mech->crypto || mech->verify_crypto)
     273           0 :         context = _gss_mg_krb5_context();
     274             : 
     275           0 :     if (mech->crypto) {
     276           0 :         krb5_crypto_destroy(context, mech->crypto);
     277           0 :         mech->crypto = NULL;
     278             :     }
     279             : 
     280           0 :     if (mech->verify_crypto) {
     281           0 :         krb5_crypto_destroy(context, mech->verify_crypto);
     282           0 :         mech->verify_crypto = NULL;
     283             :     }
     284             : 
     285           0 :     mech->sent_checksum = FALSE;
     286           0 : }
     287             : 
     288             : /*
     289             :  * In the initiator, if we are processing the acceptor's first reply, discard
     290             :  * the optimistic context if the acceptor ignored the optimistic token. If the
     291             :  * acceptor continued the optimistic mech, discard all other mechs.
     292             :  */
     293             : static void
     294           0 : check_optimistic_result(gssspnego_ctx ctx,
     295             :                         struct negoex_message *messages,
     296             :                         size_t nmessages)
     297             : {
     298           0 :     struct negoex_auth_mech *mech;
     299           0 :     OM_uint32 tmpMinor;
     300             : 
     301           0 :     heim_assert(ctx->flags.local && ctx->negoex_step == 2,
     302             :                 "NegoEx optimistic result should only be checked in second leg");
     303             : 
     304             :     /* Do nothing if we didn't make an optimistic context. */
     305           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     306           0 :     if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)
     307           0 :         return;
     308             : 
     309             :     /*
     310             :      * If the acceptor used the optimistic token, it will send an acceptor
     311             :      * token or a checksum (or both) in its first reply.
     312             :      */
     313           0 :     if (_gss_negoex_locate_exchange_message(messages, nmessages,
     314           0 :                                             CHALLENGE) != NULL ||
     315           0 :         _gss_negoex_locate_verify_message(messages, nmessages) != NULL) {
     316             :         /*
     317             :          * The acceptor continued the optimistic mech, and metadata exchange
     318             :          * didn't remove it. Commit to this mechanism.
     319             :          */
     320           0 :         _gss_negoex_select_auth_mech(ctx, mech);
     321             :     } else {
     322             :         /*
     323             :          * The acceptor ignored the optimistic token. Restart the mech.
     324             :          */
     325           0 :         gss_delete_sec_context(&tmpMinor, &mech->mech_context, GSS_C_NO_BUFFER);
     326           0 :         release_mech_crypto(mech);
     327           0 :         mech->complete = FALSE;
     328             :     }
     329             : }
     330             : 
     331             : /* Perform an initiator step of the underlying mechanism exchange. */
     332             : static OM_uint32
     333           0 : mech_init(OM_uint32 *minor,
     334             :           struct gssspnego_optimistic_ctx *opt,
     335             :           gssspnego_ctx ctx,
     336             :           gss_cred_id_t cred,
     337             :           OM_uint32 req_flags,
     338             :           OM_uint32 time_req,
     339             :           const gss_channel_bindings_t input_chan_bindings,
     340             :           struct negoex_message *messages,
     341             :           size_t nmessages,
     342             :           gss_buffer_t output_token,
     343             :           int *mech_error)
     344             : {
     345           0 :     OM_uint32 major, first_major = GSS_S_COMPLETE, first_minor = 0;
     346           0 :     struct negoex_auth_mech *mech = NULL;
     347           0 :     gss_buffer_t input_token = GSS_C_NO_BUFFER;
     348           0 :     struct exchange_message *msg;
     349           0 :     int first_mech;
     350           0 :     krb5_context context = _gss_mg_krb5_context();
     351             : 
     352           0 :     output_token->value = NULL;
     353           0 :     output_token->length = 0;
     354             : 
     355           0 :     *mech_error = FALSE;
     356             : 
     357             :     /* Allow disabling of optimistic token for testing. */
     358           0 :     if (ctx->negoex_step == 1 &&
     359           0 :         secure_getenv("NEGOEX_NO_OPTIMISTIC_TOKEN") != NULL)
     360           0 :         return GSS_S_COMPLETE;
     361             : 
     362           0 :     if (HEIM_TAILQ_EMPTY(&ctx->negoex_mechs)) {
     363           0 :         *minor = (OM_uint32)NEGOEX_NO_AVAILABLE_MECHS;
     364           0 :         return GSS_S_FAILURE;
     365             :     }
     366             : 
     367             :     /*
     368             :      * Get the input token. The challenge could be for the optimistic mech,
     369             :      * which we might have discarded in metadata exchange, so ignore the
     370             :      * challenge if it doesn't match the first auth mech.
     371             :      */
     372           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     373           0 :     msg = _gss_negoex_locate_exchange_message(messages, nmessages, CHALLENGE);
     374           0 :     if (msg != NULL && GUID_EQ(msg->scheme, mech->scheme))
     375           0 :         input_token = &msg->token;
     376             : 
     377           0 :     if (mech->complete)
     378           0 :         return GSS_S_COMPLETE;
     379             : 
     380           0 :     first_mech = TRUE;
     381           0 :     major = GSS_S_BAD_MECH;
     382             : 
     383           0 :     while (!HEIM_TAILQ_EMPTY(&ctx->negoex_mechs)) {
     384           0 :         mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     385             : 
     386             :         /*
     387             :          * If SPNEGO generated an optimistic token when probing available
     388             :          * mechanisms, we can reuse it here. This avoids a potentially
     389             :          * expensive and redundant call to GSS_Init_sec_context();
     390             :          */
     391           0 :         if (opt != NULL && memcmp(opt->scheme, mech->scheme, GUID_LENGTH) == 0) {
     392           0 :             heim_assert(ctx->negoex_step == 1,
     393             :                         "SPNEGO optimistic token only valid for NegoEx first leg");
     394             : 
     395           0 :             major = _gss_copy_buffer(minor, &opt->optimistic_token, output_token);
     396           0 :             if (GSS_ERROR(major))
     397           0 :                 return major;
     398             : 
     399           0 :             ctx->negotiated_mech_type = opt->negotiated_mech_type;
     400           0 :             ctx->mech_flags = opt->optimistic_flags;
     401           0 :             ctx->mech_time_rec = opt->optimistic_time_rec;
     402             : 
     403           0 :             mech->mech_context = opt->gssctx;
     404           0 :             opt->gssctx = NULL; /* steal it */
     405             : 
     406           0 :             mech->complete = opt->complete;
     407           0 :             major = GSS_S_COMPLETE;
     408             :         } else {
     409           0 :             major = gss_init_sec_context(minor, cred, &mech->mech_context,
     410           0 :                                          ctx->target_name, mech->oid,
     411             :                                          req_flags, time_req,
     412             :                                          input_chan_bindings, input_token,
     413             :                                          &ctx->negotiated_mech_type, output_token,
     414             :                                          &ctx->mech_flags, &ctx->mech_time_rec);
     415           0 :             if (major == GSS_S_COMPLETE)
     416           0 :                 mech->complete = 1;
     417           0 :             else if (GSS_ERROR(major)) {
     418           0 :                 gss_mg_collect_error(mech->oid, major, *minor);
     419           0 :                 *mech_error = TRUE;
     420             :             }
     421             :         }
     422           0 :         if (!GSS_ERROR(major))
     423           0 :             return get_session_keys(minor, context, NEGOEX_BOTH_KEYS, mech);
     424             : 
     425             :         /* Remember the error we got from the first mech. */
     426           0 :         if (first_mech) {
     427           0 :             first_major = major;
     428           0 :             first_minor = *minor;
     429             :         }
     430             : 
     431             :         /* If we still have multiple mechs to try, move on to the next one. */
     432           0 :         _gss_negoex_delete_auth_mech(ctx, mech);
     433           0 :         first_mech = FALSE;
     434           0 :         input_token = GSS_C_NO_BUFFER;
     435             :     }
     436             : 
     437           0 :     if (HEIM_TAILQ_EMPTY(&ctx->negoex_mechs)) {
     438           0 :         major = first_major;
     439           0 :         *minor = first_minor;
     440             :     }
     441             : 
     442           0 :     return major;
     443             : }
     444             : 
     445             : /* Perform an acceptor step of the underlying mechanism exchange. */
     446             : static OM_uint32
     447           0 : mech_accept(OM_uint32 *minor,
     448             :             gssspnego_ctx ctx,
     449             :             gss_cred_id_t cred,
     450             :             const gss_channel_bindings_t input_chan_bindings,
     451             :             struct negoex_message *messages,
     452             :             size_t nmessages,
     453             :             gss_buffer_t output_token,
     454             :             gss_cred_id_t *deleg_cred,
     455             :             int *mech_error)
     456             : {
     457           0 :     OM_uint32 major, tmpMinor;
     458           0 :     struct negoex_auth_mech *mech;
     459           0 :     struct exchange_message *msg;
     460           0 :     krb5_context context = _gss_mg_krb5_context();
     461             : 
     462           0 :     heim_assert(!ctx->flags.local && !HEIM_TAILQ_EMPTY(&ctx->negoex_mechs),
     463             :                 "Acceptor NegoEx function called in wrong sequence");
     464             : 
     465           0 :     *mech_error = FALSE;
     466             : 
     467           0 :     msg = _gss_negoex_locate_exchange_message(messages, nmessages, AP_REQUEST);
     468           0 :     if (msg == NULL) {
     469             :         /*
     470             :          * No input token is okay on the first request or if the mech is
     471             :          * complete.
     472             :          */
     473           0 :         if (ctx->negoex_step == 1 ||
     474           0 :             HEIM_TAILQ_FIRST(&ctx->negoex_mechs)->complete)
     475           0 :             return GSS_S_COMPLETE;
     476           0 :         *minor = (OM_uint32)NEGOEX_MISSING_AP_REQUEST_MESSAGE;
     477           0 :         return GSS_S_DEFECTIVE_TOKEN;
     478             :     }
     479             : 
     480           0 :     if (ctx->negoex_step == 1) {
     481             :         /*
     482             :          * Ignore the optimistic token if it isn't for our most preferred
     483             :          * mech.
     484             :          */
     485           0 :         mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     486           0 :         if (!GUID_EQ(msg->scheme, mech->scheme)) {
     487           0 :             _gss_mg_log(10, "negoex ignored optimistic token as not for preferred mech");
     488           0 :             return GSS_S_COMPLETE;
     489             :         }
     490             :     } else {
     491             :         /* The initiator has selected a mech; discard other entries. */
     492           0 :         mech = _gss_negoex_locate_auth_scheme(ctx, msg->scheme);
     493           0 :         if (mech == NULL) {
     494           0 :             *minor = (OM_uint32)NEGOEX_NO_AVAILABLE_MECHS;
     495           0 :             return GSS_S_FAILURE;
     496             :         }
     497           0 :         _gss_negoex_select_auth_mech(ctx, mech);
     498             :     }
     499             : 
     500           0 :     if (mech->complete)
     501           0 :         return GSS_S_COMPLETE;
     502             : 
     503           0 :     if (ctx->mech_src_name != GSS_C_NO_NAME)
     504           0 :         gss_release_name(&tmpMinor, &ctx->mech_src_name);
     505           0 :     if (deleg_cred && *deleg_cred != GSS_C_NO_CREDENTIAL)
     506           0 :         gss_release_cred(&tmpMinor, deleg_cred);
     507             : 
     508           0 :     major = gss_accept_sec_context(minor, &mech->mech_context, cred,
     509           0 :                                    &msg->token, input_chan_bindings,
     510             :                                    &ctx->mech_src_name, &ctx->negotiated_mech_type,
     511             :                                    output_token, &ctx->mech_flags,
     512             :                                    &ctx->mech_time_rec, deleg_cred);
     513           0 :     if (major == GSS_S_COMPLETE)
     514           0 :         mech->complete = 1;
     515             : 
     516           0 :     if (!GSS_ERROR(major)) {
     517           0 :         if (major == GSS_S_COMPLETE &&
     518           0 :             !gss_oid_equal(ctx->negotiated_mech_type, mech->oid))
     519           0 :             _gss_mg_log(1, "negoex client didn't send the mech they said they would");
     520             : 
     521           0 :         major = get_session_keys(minor, context, NEGOEX_BOTH_KEYS, mech);
     522           0 :     } else if (ctx->negoex_step == 1) {
     523           0 :         gss_mg_collect_error(ctx->negotiated_mech_type, major, *minor);
     524           0 :         *mech_error = TRUE;
     525             : 
     526             :         /* This was an optimistic token; pretend this never happened. */
     527           0 :         major = GSS_S_COMPLETE;
     528           0 :         *minor = 0;
     529           0 :         gss_release_buffer(&tmpMinor, output_token);
     530           0 :         gss_delete_sec_context(&tmpMinor, &mech->mech_context, GSS_C_NO_BUFFER);
     531             :     }
     532             : 
     533           0 :     return major;
     534             : }
     535             : 
     536             : static krb5_keyusage
     537           0 : verify_keyusage(gssspnego_ctx ctx, int make_checksum)
     538             : {
     539             :     /* Of course, these are the wrong way around in the spec. */
     540           0 :     return (ctx->flags.local ^ !make_checksum) ?
     541           0 :         NEGOEX_KEYUSAGE_ACCEPTOR_CHECKSUM : NEGOEX_KEYUSAGE_INITIATOR_CHECKSUM;
     542             : }
     543             : 
     544             : static OM_uint32
     545           0 : verify_key_flags(gssspnego_ctx ctx, int make_checksum)
     546             : {
     547           0 :     return (ctx->flags.local ^ make_checksum) ?
     548           0 :         NEGOEX_SIGN_KEY : NEGOEX_VERIFY_KEY;
     549             : }
     550             : 
     551             : static OM_uint32
     552           0 : verify_checksum(OM_uint32 *minor,
     553             :                 gssspnego_ctx ctx,
     554             :                 struct negoex_message *messages,
     555             :                 size_t nmessages,
     556             :                 gss_const_buffer_t input_token,
     557             :                 int *send_alert_out)
     558             : {
     559           0 :     krb5_error_code ret;
     560           0 :     struct negoex_auth_mech *mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     561           0 :     struct verify_message *msg;
     562           0 :     krb5_context context = _gss_mg_krb5_context();
     563           0 :     krb5_crypto_iov iov[3];
     564           0 :     krb5_keyusage usage = verify_keyusage(ctx, FALSE);
     565             : 
     566           0 :     *send_alert_out = FALSE;
     567           0 :     heim_assert(mech != NULL, "Invalid null mech when verifying NegoEx checksum");
     568             : 
     569             :     /*
     570             :      * The other party may not be ready to send a verify token yet, or (in the
     571             :      * first initiator step) may send one for a mechanism we don't support.
     572             :      */
     573           0 :     msg = _gss_negoex_locate_verify_message(messages, nmessages);
     574           0 :     if (msg == NULL || !GUID_EQ(msg->scheme, mech->scheme))
     575           0 :         return GSS_S_COMPLETE;
     576             : 
     577             :     /*
     578             :      * Last chance attempt to obtain session key for imported exported partial
     579             :      * contexts (which do not carry the session key at the NegoEx layer).
     580             :      */
     581           0 :     if (mech->verify_crypto == NULL)
     582           0 :         get_session_keys(minor, context, verify_key_flags(ctx, FALSE), mech);
     583             : 
     584             :     /*
     585             :      * A recoverable error may cause us to be unable to verify a token from the
     586             :      * other party. In this case we should send an alert.
     587             :      */
     588           0 :     if (mech->verify_crypto == NULL) {
     589           0 :         *send_alert_out = TRUE;
     590           0 :         return GSS_S_COMPLETE;
     591             :     }
     592             : 
     593           0 :     if (!krb5_checksum_is_keyed(context, msg->cksum_type)) {
     594           0 :         *minor = (OM_uint32)NEGOEX_INVALID_CHECKSUM;
     595           0 :         return GSS_S_BAD_SIG;
     596             :     }
     597             : 
     598             :     /*
     599             :      * Verify the checksum over the existing transcript and the portion of the
     600             :      * input token leading up to the verify message.
     601             :      */
     602           0 :     iov[0].flags = KRB5_CRYPTO_TYPE_DATA;
     603           0 :     ret = krb5_storage_to_data(ctx->negoex_transcript, &iov[0].data);
     604           0 :     if (ret) {
     605           0 :         *minor = ret;
     606           0 :         return GSS_S_FAILURE;
     607             :     }
     608             : 
     609           0 :     iov[1].flags = KRB5_CRYPTO_TYPE_DATA;
     610           0 :     iov[1].data.data = input_token->value;
     611           0 :     iov[1].data.length = msg->offset_in_token;
     612             : 
     613           0 :     iov[2].flags = KRB5_CRYPTO_TYPE_CHECKSUM;
     614           0 :     iov[2].data.data = (uint8_t *)msg->cksum;
     615           0 :     iov[2].data.length = msg->cksum_len;
     616             : 
     617           0 :     ret = krb5_verify_checksum_iov(context, mech->verify_crypto, usage,
     618             :                                    iov, sizeof(iov) / sizeof(iov[0]), NULL);
     619           0 :     if (ret == 0)
     620           0 :         mech->verified_checksum = TRUE;
     621             :     else
     622           0 :         *minor = ret;
     623             : 
     624           0 :     krb5_data_free(&iov[0].data);
     625             : 
     626           0 :     return (ret == 0) ? GSS_S_COMPLETE : GSS_S_FAILURE;
     627             : }
     628             : 
     629             : static OM_uint32
     630           0 : make_checksum(OM_uint32 *minor, gssspnego_ctx ctx)
     631             : {
     632           0 :     krb5_error_code ret;
     633           0 :     krb5_context context = _gss_mg_krb5_context();
     634           0 :     krb5_data d;
     635           0 :     krb5_keyusage usage = verify_keyusage(ctx, TRUE);
     636           0 :     krb5_checksum cksum;
     637           0 :     struct negoex_auth_mech *mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     638           0 :     OM_uint32 major;
     639             : 
     640           0 :     heim_assert(mech != NULL, "Invalid null mech when making NegoEx checksum");
     641             : 
     642           0 :     if (mech->crypto == NULL) {
     643           0 :         if (mech->complete) {
     644             :             /*
     645             :              * Last chance attempt to obtain session key for imported exported partial
     646             :              * contexts (which do not carry the session key at the NegoEx layer).
     647             :              */
     648           0 :             get_session_keys(minor, context, verify_key_flags(ctx, TRUE), mech);
     649           0 :             if (mech->crypto == NULL) {
     650           0 :                 *minor = (OM_uint32)NEGOEX_NO_VERIFY_KEY;
     651           0 :                 return GSS_S_UNAVAILABLE;
     652             :             }
     653             :         } else {
     654           0 :             return GSS_S_COMPLETE;
     655             :         }
     656             :     }
     657             : 
     658           0 :     ret = krb5_storage_to_data(ctx->negoex_transcript, &d);
     659           0 :     if (ret) {
     660           0 :         *minor = ret;
     661           0 :         return GSS_S_FAILURE;
     662             :     }
     663             : 
     664           0 :     ret = krb5_create_checksum(context, mech->crypto,
     665             :                                usage, 0, d.data, d.length, &cksum);
     666           0 :     krb5_data_free(&d);
     667           0 :     if (ret) {
     668           0 :         *minor = ret;
     669           0 :         return GSS_S_FAILURE;
     670             :     }
     671             : 
     672           0 :     major = _gss_negoex_add_verify_message(minor, ctx, mech->scheme,
     673           0 :                                            cksum.cksumtype,
     674           0 :                                            cksum.checksum.data,
     675           0 :                                            cksum.checksum.length);
     676           0 :     free_Checksum(&cksum);
     677             : 
     678           0 :     if (major == GSS_S_COMPLETE)
     679           0 :         mech->sent_checksum = TRUE;
     680             : 
     681           0 :     return major;
     682             : }
     683             : 
     684             : /*
     685             :  * If the other side sent a VERIFY_NO_KEY pulse alert, clear the checksum state
     686             :  * on the mechanism so that we send another VERIFY message.
     687             :  */
     688             : static void
     689           0 : process_alerts(gssspnego_ctx ctx,
     690             :                struct negoex_message *messages,
     691             :                uint32_t nmessages)
     692             : {
     693           0 :     struct alert_message *msg;
     694           0 :     struct negoex_auth_mech *mech;
     695             : 
     696           0 :     msg = _gss_negoex_locate_alert_message(messages, nmessages);
     697           0 :     if (msg != NULL && msg->verify_no_key) {
     698           0 :         mech = _gss_negoex_locate_auth_scheme(ctx, msg->scheme);
     699           0 :         if (mech != NULL)
     700           0 :             release_mech_crypto(mech);
     701             :     }
     702           0 : }
     703             : 
     704             : static OM_uint32
     705           0 : make_output_token(OM_uint32 *minor,
     706             :                   gssspnego_ctx ctx,
     707             :                   gss_buffer_t mech_output_token,
     708             :                   int send_alert,
     709             :                   gss_buffer_t output_token)
     710             : {
     711           0 :     OM_uint32 major, tmpMinor;
     712           0 :     struct negoex_auth_mech *mech;
     713           0 :     enum message_type type;
     714           0 :     off_t old_transcript_len;
     715             : 
     716           0 :     output_token->length = 0;
     717           0 :     output_token->value = NULL;
     718             : 
     719           0 :     old_transcript_len = krb5_storage_seek(ctx->negoex_transcript, 0, SEEK_CUR);
     720             : 
     721             :     /*
     722             :      * If the mech is complete and we previously sent a checksum, we just
     723             :      * processed the last leg and don't need to send another token.
     724             :      */
     725           0 :     if (mech_output_token->length == 0 &&
     726           0 :         HEIM_TAILQ_FIRST(&ctx->negoex_mechs)->sent_checksum)
     727           0 :         return GSS_S_COMPLETE;
     728             : 
     729           0 :     if (ctx->negoex_step == 1) {
     730           0 :         if (ctx->flags.local)
     731           0 :             major = emit_initiator_nego(minor, ctx);
     732             :         else
     733           0 :             major = emit_acceptor_nego(minor, ctx);
     734           0 :         if (major != GSS_S_COMPLETE)
     735           0 :             return major;
     736             : 
     737           0 :         type = ctx->flags.local ? INITIATOR_META_DATA : ACCEPTOR_META_DATA;
     738           0 :         HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
     739           0 :             if (mech->metadata.length > 0) {
     740           0 :                 major = _gss_negoex_add_exchange_message(minor, ctx,
     741           0 :                                                          type, mech->scheme,
     742           0 :                                                          &mech->metadata);
     743           0 :                 if (major != GSS_S_COMPLETE)
     744           0 :                     return major;
     745             :             }
     746             :         }
     747             :     }
     748             : 
     749           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     750             : 
     751           0 :     if (mech_output_token->length > 0) {
     752           0 :         type = ctx->flags.local ? AP_REQUEST : CHALLENGE;
     753           0 :         major = _gss_negoex_add_exchange_message(minor, ctx,
     754           0 :                                                  type, mech->scheme,
     755             :                                                  mech_output_token);
     756           0 :         if (major != GSS_S_COMPLETE)
     757           0 :             return major;
     758             :     }
     759             : 
     760           0 :     if (send_alert) {
     761           0 :         major = _gss_negoex_add_verify_no_key_alert(minor, ctx, mech->scheme);
     762           0 :         if (major != GSS_S_COMPLETE)
     763           0 :             return major;
     764             :     }
     765             : 
     766             :     /* Try to add a VERIFY message if we haven't already done so. */
     767           0 :     if (!mech->sent_checksum) {
     768           0 :         major = make_checksum(minor, ctx);
     769           0 :         if (major != GSS_S_COMPLETE)
     770           0 :             return major;
     771             :     }
     772             : 
     773           0 :     heim_assert(ctx->negoex_transcript != NULL, "NegoEx context uninitialized");
     774             : 
     775           0 :     output_token->length =
     776           0 :         krb5_storage_seek(ctx->negoex_transcript, 0, SEEK_CUR) - old_transcript_len;
     777           0 :     output_token->value = malloc(output_token->length);
     778           0 :     if (output_token->value == NULL) {
     779           0 :         *minor = ENOMEM;
     780           0 :         return GSS_S_FAILURE;
     781             :     }
     782             : 
     783           0 :     krb5_storage_seek(ctx->negoex_transcript, old_transcript_len, SEEK_SET);
     784             : 
     785           0 :     if (krb5_storage_read(ctx->negoex_transcript,
     786             :                           output_token->value,
     787           0 :                           output_token->length) != output_token->length) {
     788           0 :         *minor = ERANGE;
     789           0 :         gss_release_buffer(&tmpMinor, output_token);
     790           0 :         return GSS_S_FAILURE;
     791             :     }
     792             : 
     793           0 :     krb5_storage_seek(ctx->negoex_transcript, 0, SEEK_END);
     794             : 
     795           0 :     return GSS_S_COMPLETE;
     796             : }
     797             : 
     798             : OM_uint32
     799           0 : _gss_negoex_init(OM_uint32 *minor,
     800             :                  struct gssspnego_optimistic_ctx *opt,
     801             :                  gssspnego_ctx ctx,
     802             :                  gss_cred_id_t cred,
     803             :                  OM_uint32 req_flags,
     804             :                  OM_uint32 time_req,
     805             :                  const gss_channel_bindings_t input_chan_bindings,
     806             :                  gss_const_buffer_t input_token,
     807             :                  gss_buffer_t output_token)
     808             : {
     809           0 :     OM_uint32 major, tmpMinor;
     810           0 :     gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
     811           0 :     struct negoex_message *messages = NULL;
     812           0 :     struct negoex_auth_mech *mech;
     813           0 :     size_t nmessages = 0;
     814           0 :     int send_alert = FALSE, mech_error = FALSE;
     815             : 
     816           0 :     output_token->length = 0;
     817           0 :     output_token->value = NULL;
     818             : 
     819           0 :     if (ctx->negoex_step == 0 && input_token != GSS_C_NO_BUFFER &&
     820           0 :         input_token->length != 0)
     821           0 :         return GSS_S_DEFECTIVE_TOKEN;
     822             : 
     823           0 :     major = _gss_negoex_begin(minor, ctx);
     824           0 :     if (major != GSS_S_COMPLETE)
     825           0 :         goto cleanup;
     826             : 
     827           0 :     ctx->negoex_step++;
     828             : 
     829           0 :     if (input_token != GSS_C_NO_BUFFER && input_token->length > 0) {
     830           0 :         major = _gss_negoex_parse_token(minor, ctx, input_token,
     831             :                                         &messages, &nmessages);
     832           0 :         if (major != GSS_S_COMPLETE)
     833           0 :             goto cleanup;
     834             :     }
     835             : 
     836           0 :     process_alerts(ctx, messages, nmessages);
     837             : 
     838           0 :     if (ctx->negoex_step == 1) {
     839             :         /* Choose a random conversation ID. */
     840           0 :         krb5_generate_random_block(ctx->negoex_conv_id, GUID_LENGTH);
     841             : 
     842             :         /* Query each mech for its metadata (this may prune the mech list). */
     843           0 :         query_meta_data(ctx, opt, cred, req_flags);
     844           0 :     } else if (ctx->negoex_step == 2) {
     845             :         /* See if the mech processed the optimistic token. */
     846           0 :         check_optimistic_result(ctx, messages, nmessages);
     847             : 
     848             :         /* Pass the acceptor metadata to each mech to prune the list. */
     849           0 :         exchange_meta_data(ctx, cred, req_flags, messages, nmessages);
     850             : 
     851             :         /* Process the ACCEPTOR_NEGO message. */
     852           0 :         major = process_acceptor_nego(minor, ctx, messages, nmessages);
     853           0 :         if (major != GSS_S_COMPLETE)
     854           0 :             goto cleanup;
     855             :     }
     856             : 
     857             :     /*
     858             :      * Process the input token and/or produce an output token. This may prune
     859             :      * the mech list, but on success there will be at least one mech entry.
     860             :      */
     861           0 :     major = mech_init(minor, opt, ctx, cred, req_flags, time_req,
     862             :                       input_chan_bindings, messages, nmessages,
     863             :                       &mech_output_token, &mech_error);
     864           0 :     if (major != GSS_S_COMPLETE)
     865           0 :         goto cleanup;
     866           0 :     heim_assert(!HEIM_TAILQ_EMPTY(&ctx->negoex_mechs),
     867             :                 "Invalid empty NegoEx mechanism list");
     868             : 
     869             :     /*
     870             :      * At this point in step 2 we have performed the metadata exchange and
     871             :      * chosen a mech we can use, so discard any fallback mech entries.
     872             :      */
     873           0 :     if (ctx->negoex_step == 2)
     874           0 :         _gss_negoex_select_auth_mech(ctx, HEIM_TAILQ_FIRST(&ctx->negoex_mechs));
     875             : 
     876           0 :     major = verify_checksum(minor, ctx, messages, nmessages, input_token,
     877             :                             &send_alert);
     878           0 :     if (major != GSS_S_COMPLETE)
     879           0 :         goto cleanup;
     880             : 
     881           0 :     if (input_token != GSS_C_NO_BUFFER) {
     882           0 :         if (krb5_storage_write(ctx->negoex_transcript,
     883           0 :                                input_token->value,
     884           0 :                                input_token->length) != input_token->length) {
     885           0 :             major = GSS_S_FAILURE;
     886           0 :             *minor = ENOMEM;
     887           0 :             goto cleanup;
     888             :         }
     889             :     }
     890             : 
     891           0 :     major = make_output_token(minor, ctx, &mech_output_token, send_alert,
     892             :                               output_token);
     893           0 :     if (major != GSS_S_COMPLETE)
     894           0 :         goto cleanup;
     895             : 
     896           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
     897           0 :     major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
     898             :         GSS_S_CONTINUE_NEEDED;
     899             : 
     900           0 : cleanup:
     901           0 :     free(messages);
     902           0 :     gss_release_buffer(&tmpMinor, &mech_output_token);
     903           0 :     _gss_negoex_end(ctx);
     904             : 
     905           0 :     if (GSS_ERROR(major)) {
     906           0 :         if (!mech_error) {
     907           0 :             krb5_context context = _gss_mg_krb5_context();
     908           0 :             const char *emsg = krb5_get_error_message(context, *minor);
     909             : 
     910           0 :             gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
     911             :                                     major, *minor,
     912             :                                     "NegoEx failed to initialize security context: %s",
     913             :                                     emsg);
     914           0 :             krb5_free_error_message(context, emsg);
     915             :         }
     916             : 
     917           0 :         _gss_negoex_release_context(ctx);
     918             :     }
     919             : 
     920           0 :     return major;
     921             : }
     922             : 
     923             : OM_uint32
     924           0 : _gss_negoex_accept(OM_uint32 *minor,
     925             :                    gssspnego_ctx ctx,
     926             :                    gss_cred_id_t cred,
     927             :                    gss_const_buffer_t input_token,
     928             :                    const gss_channel_bindings_t input_chan_bindings,
     929             :                    gss_buffer_t output_token,
     930             :                    gss_cred_id_t *deleg_cred)
     931             : {
     932           0 :     OM_uint32 major, tmpMinor;
     933           0 :     gss_buffer_desc mech_output_token = GSS_C_EMPTY_BUFFER;
     934           0 :     struct negoex_message *messages = NULL;
     935           0 :     struct negoex_auth_mech *mech;
     936           0 :     size_t nmessages;
     937           0 :     int send_alert = FALSE, mech_error = FALSE;
     938             : 
     939           0 :     output_token->length = 0;
     940           0 :     output_token->value = NULL;
     941           0 :     if (deleg_cred)
     942           0 :         *deleg_cred = GSS_C_NO_CREDENTIAL;
     943             : 
     944           0 :     if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) {
     945           0 :         major = GSS_S_DEFECTIVE_TOKEN;
     946           0 :         goto cleanup;
     947             :     }
     948             : 
     949           0 :     major = _gss_negoex_begin(minor, ctx);
     950           0 :     if (major != GSS_S_COMPLETE)
     951           0 :         goto cleanup;
     952             : 
     953           0 :     ctx->negoex_step++;
     954             : 
     955           0 :     major = _gss_negoex_parse_token(minor, ctx, input_token,
     956             :                                     &messages, &nmessages);
     957           0 :     if (major != GSS_S_COMPLETE)
     958           0 :         goto cleanup;
     959             : 
     960           0 :     process_alerts(ctx, messages, nmessages);
     961             : 
     962           0 :     if (ctx->negoex_step == 1) {
     963             :         /*
     964             :          * Read the INITIATOR_NEGO message to prune the candidate mech list.
     965             :          */
     966           0 :         major = process_initiator_nego(minor, ctx, messages, nmessages);
     967           0 :         if (major != GSS_S_COMPLETE)
     968           0 :             goto cleanup;
     969             : 
     970             :         /*
     971             :          * Pass the initiator metadata to each mech to prune the list, and
     972             :          * query each mech for its acceptor metadata (which may also prune the
     973             :          * list).
     974             :          */
     975           0 :         exchange_meta_data(ctx, cred, 0, messages, nmessages);
     976           0 :         query_meta_data(ctx, NULL, cred, 0);
     977             : 
     978           0 :         if (HEIM_TAILQ_EMPTY(&ctx->negoex_mechs)) {
     979           0 :             *minor = (OM_uint32)NEGOEX_NO_AVAILABLE_MECHS;
     980           0 :             major = GSS_S_FAILURE;
     981           0 :             goto cleanup;
     982             :         }
     983             :     }
     984             : 
     985             :     /*
     986             :      * Process the input token and possibly produce an output token. This may
     987             :      * prune the list to a single mech. Continue on error if an output token
     988             :      * is generated, so that we send the token to the initiator.
     989             :      */
     990           0 :     major = mech_accept(minor, ctx, cred, input_chan_bindings,
     991             :                         messages, nmessages, &mech_output_token,
     992             :                         deleg_cred, &mech_error);
     993           0 :     if (major != GSS_S_COMPLETE && mech_output_token.length == 0)
     994           0 :         goto cleanup;
     995             : 
     996           0 :     if (major == GSS_S_COMPLETE) {
     997           0 :         major = verify_checksum(minor, ctx, messages, nmessages, input_token,
     998             :                                 &send_alert);
     999           0 :         if (major != GSS_S_COMPLETE)
    1000           0 :             goto cleanup;
    1001             :     }
    1002             : 
    1003           0 :     if (krb5_storage_write(ctx->negoex_transcript,
    1004           0 :                            input_token->value,
    1005           0 :                            input_token->length) != input_token->length) {
    1006           0 :         major = GSS_S_FAILURE;
    1007           0 :         *minor = ENOMEM;
    1008           0 :         goto cleanup;
    1009             :     }
    1010             : 
    1011           0 :     major = make_output_token(minor, ctx, &mech_output_token, send_alert,
    1012             :                               output_token);
    1013           0 :     if (major != GSS_S_COMPLETE)
    1014           0 :         goto cleanup;
    1015             : 
    1016           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
    1017           0 :     major = (mech->complete && mech->verified_checksum) ? GSS_S_COMPLETE :
    1018             :         GSS_S_CONTINUE_NEEDED;
    1019             : 
    1020           0 : cleanup:
    1021           0 :     free(messages);
    1022           0 :     gss_release_buffer(&tmpMinor, &mech_output_token);
    1023           0 :     _gss_negoex_end(ctx);
    1024             : 
    1025           0 :     if (GSS_ERROR(major)) {
    1026           0 :         if (!mech_error) {
    1027           0 :             krb5_context context = _gss_mg_krb5_context();
    1028           0 :             const char *emsg = krb5_get_error_message(context, *minor);
    1029             : 
    1030           0 :             gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
    1031             :                                     major, *minor,
    1032             :                                     "NegoEx failed to accept security context: %s",
    1033             :                                     emsg);
    1034           0 :             krb5_free_error_message(context, emsg);
    1035             :         }
    1036             : 
    1037           0 :         _gss_negoex_release_context(ctx);
    1038             :     }
    1039             : 
    1040           0 :     return major;
    1041             : }

Generated by: LCOV version 1.14