LCOV - code coverage report
Current view: top level - third_party/heimdal/lib/gssapi/spnego - negoex_util.c (source / functions) Hit Total Coverage
Test: coverage report for master 2b515b7d Lines: 0 560 0.0 %
Date: 2024-02-28 12:06:22 Functions: 0 35 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (C) 2011-2019 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             :  * SPNEGO expects to find the active mech context in ctx->negotiated_ctx_id,
      35             :  * but the metadata exchange APIs force us to have one mech context per mech
      36             :  * entry. To address this mismatch, move the active mech context (if we have
      37             :  * one) to ctx->negotiated_ctx_id at the end of NegoEx processing.
      38             :  */
      39             : void
      40           0 : _gss_negoex_end(gssspnego_ctx ctx)
      41             : {
      42           0 :     struct negoex_auth_mech *mech;
      43             : 
      44           0 :     mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
      45           0 :     if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)
      46           0 :         return;
      47             : 
      48           0 :     heim_assert(ctx->negotiated_ctx_id == GSS_C_NO_CONTEXT,
      49             :                 "SPNEGO/NegoEx context mismatch");
      50           0 :     ctx->negotiated_ctx_id = mech->mech_context;
      51           0 :     mech->mech_context = GSS_C_NO_CONTEXT;
      52             : }
      53             : 
      54             : OM_uint32
      55           0 : _gss_negoex_begin(OM_uint32 *minor, gssspnego_ctx ctx)
      56             : {
      57           0 :     struct negoex_auth_mech *mech;
      58             : 
      59           0 :     if (ctx->negoex_transcript != NULL) {
      60             :         /*
      61             :          * The context is already initialized for NegoEx; undo what
      62             :          * _gss_negoex_end() did, if applicable.
      63             :          */
      64           0 :         if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) {
      65           0 :             mech = HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
      66           0 :             heim_assert(mech != NULL && mech->mech_context == GSS_C_NO_CONTEXT,
      67             :                         "NegoEx/SPNEGO context mismatch");
      68           0 :             mech->mech_context = ctx->negotiated_ctx_id;
      69           0 :             ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
      70             :         }
      71           0 :         return GSS_S_COMPLETE;
      72             :     }
      73             : 
      74           0 :     ctx->negoex_transcript = krb5_storage_emem();
      75           0 :     if (ctx->negoex_transcript == NULL) {
      76           0 :         *minor = ENOMEM;
      77           0 :         return GSS_S_FAILURE;
      78             :     }
      79             : 
      80           0 :     krb5_storage_set_byteorder(ctx->negoex_transcript,
      81             :                                KRB5_STORAGE_BYTEORDER_LE);
      82             : 
      83           0 :     return GSS_S_COMPLETE;
      84             : }
      85             : 
      86             : static void
      87           0 : release_all_mechs(gssspnego_ctx ctx, krb5_context context)
      88             : {
      89           0 :     struct negoex_auth_mech *mech, *next;
      90           0 :     struct negoex_auth_mech *prev = NULL;
      91             : 
      92           0 :     HEIM_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next) {
      93           0 :         if (prev)
      94           0 :             _gss_negoex_release_auth_mech(context, prev);
      95           0 :         prev = mech;
      96             :     }
      97           0 :     if (prev)
      98           0 :         _gss_negoex_release_auth_mech(context, mech);
      99             : 
     100           0 :     HEIM_TAILQ_INIT(&ctx->negoex_mechs);
     101           0 : }
     102             : 
     103             : void
     104           0 : _gss_negoex_release_context(gssspnego_ctx ctx)
     105             : {
     106           0 :     krb5_context context = _gss_mg_krb5_context();
     107             : 
     108           0 :     if (ctx->negoex_transcript != NULL) {
     109           0 :         krb5_storage_free(ctx->negoex_transcript);
     110           0 :         ctx->negoex_transcript = NULL;
     111             :     }
     112             : 
     113           0 :     release_all_mechs(ctx, context);
     114           0 : }
     115             : 
     116             : static int
     117           0 : guid_to_string(const uint8_t guid[16], char *buffer, size_t bufsiz)
     118             : {
     119           0 :     uint32_t data1;
     120           0 :     uint16_t data2, data3;
     121             : 
     122           0 :     _gss_mg_decode_le_uint32(&guid[0], &data1);
     123           0 :     _gss_mg_decode_le_uint16(&guid[4], &data2);
     124           0 :     _gss_mg_decode_le_uint16(&guid[6], &data3);
     125             : 
     126           0 :     return snprintf(buffer, bufsiz,
     127             :                     "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
     128           0 :                     data1, data2, data3, guid[8], guid[9], guid[10], guid[11],
     129           0 :                     guid[12], guid[13], guid[14], guid[15]);
     130             : }
     131             : 
     132             : void
     133           0 : _gss_negoex_log_auth_scheme(int initiator,
     134             :                             int index,
     135             :                             const auth_scheme scheme)
     136             : {
     137           0 :     char scheme_str[37];
     138             : 
     139           0 :     guid_to_string(scheme, scheme_str, sizeof(scheme_str));
     140             : 
     141           0 :     _gss_mg_log(NEGOEX_LOG_LEVEL,
     142             :                 "negoex: %s authentication scheme %d %s",
     143             :                 initiator ? "proposing" : "received", index, scheme_str);
     144           0 : }
     145             : 
     146             : void
     147           0 : _gss_negoex_log_message(int direction,
     148             :                         enum message_type type,
     149             :                         const conversation_id conv_id,
     150             :                         unsigned int seqnum,
     151             :                         unsigned int header_len,
     152             :                         unsigned int msg_len)
     153             : {
     154           0 :     char conv_str[37];
     155           0 :     char *typestr;
     156             : 
     157           0 :     if (type == INITIATOR_NEGO)
     158           0 :         typestr = "INITIATOR_NEGO";
     159           0 :     else if (type == ACCEPTOR_NEGO)
     160           0 :         typestr = "ACCEPTOR_NEGO";
     161           0 :     else if (type == INITIATOR_META_DATA)
     162           0 :         typestr = "INITIATOR_META_DATA";
     163           0 :     else if (type == ACCEPTOR_META_DATA)
     164           0 :         typestr = "ACCEPTOR_META_DATA";
     165           0 :     else if (type == CHALLENGE)
     166           0 :         typestr = "CHALLENGE";
     167           0 :     else if (type == AP_REQUEST)
     168           0 :         typestr = "AP_REQUEST";
     169           0 :     else if (type == VERIFY)
     170           0 :         typestr = "VERIFY";
     171           0 :     else if (type == ALERT)
     172           0 :         typestr = "ALERT";
     173             :     else
     174           0 :         typestr = "UNKNOWN";
     175             : 
     176           0 :     guid_to_string(conv_id, conv_str, sizeof(conv_str));
     177           0 :     _gss_mg_log(NEGOEX_LOG_LEVEL,
     178             :                 "negoex: %s (%d)%s conversation %s",
     179             :                 direction ? "received" : "sending",
     180             :                 seqnum, typestr, conv_str);
     181           0 : }
     182             : 
     183             : /*
     184             :  * Check that the described vector lies within the message, and return a
     185             :  * pointer to its first element.
     186             :  */
     187             : static inline const uint8_t *
     188           0 : vector_base(size_t offset, size_t count, size_t width,
     189             :             const uint8_t *msg_base, size_t msg_len)
     190             : {
     191           0 :     if (offset > msg_len || count > (msg_len - offset) / width)
     192           0 :         return NULL;
     193           0 :     return msg_base + offset;
     194             : }
     195             : 
     196             : static OM_uint32
     197           0 : parse_nego_message(OM_uint32 *minor, krb5_storage *sp,
     198             :                    const uint8_t *msg_base, size_t msg_len,
     199             :                    struct nego_message *msg)
     200             : {
     201           0 :     krb5_error_code ret;
     202           0 :     const uint8_t *p;
     203           0 :     uint64_t protocol_version;
     204           0 :     uint32_t extension_type, offset;
     205           0 :     uint16_t count;
     206           0 :     size_t i;
     207             : 
     208           0 :     if (krb5_storage_read(sp, msg->random,
     209             :                           sizeof(msg->random)) != sizeof(msg->random)) {
     210           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     211           0 :         return GSS_S_DEFECTIVE_TOKEN;
     212             :     }
     213             : 
     214           0 :     ret = krb5_ret_uint64(sp, &protocol_version);
     215           0 :     if (ret) {
     216           0 :         *minor = ret;
     217           0 :         return GSS_S_DEFECTIVE_TOKEN;
     218             :     }
     219             : 
     220           0 :     if (protocol_version != 0) {
     221           0 :         *minor = (OM_uint32)NEGOEX_UNSUPPORTED_VERSION;
     222           0 :         return GSS_S_UNAVAILABLE;
     223             :     }
     224             : 
     225           0 :     ret = krb5_ret_uint32(sp, &offset);
     226           0 :     if (ret == 0)
     227           0 :         ret = krb5_ret_uint16(sp, &count);
     228           0 :     if (ret) {
     229           0 :         *minor = ret;
     230           0 :         return GSS_S_DEFECTIVE_TOKEN;
     231             :     }
     232             : 
     233           0 :     msg->schemes = vector_base(offset, count, GUID_LENGTH, msg_base, msg_len);
     234           0 :     msg->nschemes = count;
     235           0 :     if (msg->schemes == NULL) {
     236           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     237           0 :         return GSS_S_DEFECTIVE_TOKEN;
     238             :     }
     239             : 
     240           0 :     ret = krb5_ret_uint32(sp, &offset);
     241           0 :     if (ret == 0)
     242           0 :         ret = krb5_ret_uint16(sp, &count);
     243           0 :     if (ret) {
     244           0 :         *minor = ret;
     245           0 :         return GSS_S_DEFECTIVE_TOKEN;
     246             :     }
     247           0 :     p = vector_base(offset, count, EXTENSION_LENGTH, msg_base, msg_len);
     248           0 :     for (i = 0; i < count; i++) {
     249           0 :         _gss_mg_decode_le_uint32(p + i * EXTENSION_LENGTH, &extension_type);
     250           0 :         if (extension_type & EXTENSION_FLAG_CRITICAL) {
     251           0 :             *minor = (OM_uint32)NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION;
     252           0 :             return GSS_S_UNAVAILABLE;
     253             :         }
     254             :     }
     255             : 
     256           0 :     return GSS_S_COMPLETE;
     257             : }
     258             : 
     259             : static OM_uint32
     260           0 : parse_exchange_message(OM_uint32 *minor, krb5_storage *sp,
     261             :                        const uint8_t *msg_base, size_t msg_len,
     262             :                        struct exchange_message *msg)
     263             : {
     264           0 :     krb5_error_code ret;
     265           0 :     const uint8_t *p;
     266           0 :     uint32_t offset;
     267           0 :     uint16_t len;
     268             : 
     269           0 :     if (krb5_storage_read(sp, msg->scheme, GUID_LENGTH) != GUID_LENGTH) {
     270           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     271           0 :         return GSS_S_DEFECTIVE_TOKEN;
     272             :     }
     273             : 
     274           0 :     ret = krb5_ret_uint32(sp, &offset);
     275           0 :     if (ret == 0)
     276           0 :         ret = krb5_ret_uint16(sp, &len);
     277           0 :     if (ret) {
     278           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     279           0 :         return GSS_S_DEFECTIVE_TOKEN;
     280             :     }
     281             : 
     282           0 :     p = vector_base(offset, len, 1, msg_base, msg_len);
     283           0 :     if (p == NULL) {
     284           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     285           0 :         return GSS_S_DEFECTIVE_TOKEN;
     286             :     }
     287           0 :     msg->token.value = (void *)p;
     288           0 :     msg->token.length = len;
     289             : 
     290           0 :     return GSS_S_COMPLETE;
     291             : }
     292             : 
     293             : static OM_uint32
     294           0 : parse_verify_message(OM_uint32 *minor, krb5_storage *sp,
     295             :                      const uint8_t *msg_base, size_t msg_len,
     296             :                      size_t token_offset, struct verify_message *msg)
     297             : {
     298           0 :     krb5_error_code ret;
     299           0 :     uint32_t hdrlen, cksum_scheme;
     300           0 :     uint32_t offset, len;
     301             : 
     302           0 :     if (krb5_storage_read(sp, msg->scheme, GUID_LENGTH) == GUID_LENGTH)
     303           0 :         ret = 0;
     304             :     else
     305           0 :         ret = NEGOEX_INVALID_MESSAGE_SIZE;
     306           0 :     if (ret == 0)
     307           0 :         ret = krb5_ret_uint32(sp, &hdrlen);
     308           0 :     if (ret) {
     309           0 :         *minor = ret;
     310           0 :         return GSS_S_DEFECTIVE_TOKEN;
     311             :     }
     312             : 
     313           0 :     if (hdrlen != CHECKSUM_HEADER_LENGTH) {
     314           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     315           0 :         return GSS_S_DEFECTIVE_TOKEN;
     316             :     }
     317             : 
     318           0 :     ret = krb5_ret_uint32(sp, &cksum_scheme);
     319           0 :     if (ret == 0)
     320           0 :         ret = krb5_ret_uint32(sp, &msg->cksum_type);
     321           0 :     if (ret) {
     322           0 :         *minor = ret;
     323           0 :         return GSS_S_DEFECTIVE_TOKEN;
     324             :     }
     325             : 
     326           0 :     if (cksum_scheme != CHECKSUM_SCHEME_RFC3961) {
     327           0 :         *minor = (OM_uint32)NEGOEX_UNKNOWN_CHECKSUM_SCHEME;
     328           0 :         return GSS_S_UNAVAILABLE;
     329             :     }
     330             : 
     331           0 :     ret = krb5_ret_uint32(sp, &offset);
     332           0 :     if (ret == 0)
     333           0 :         ret = krb5_ret_uint32(sp, &len);
     334           0 :     if (ret) {
     335           0 :         *minor = ret;
     336           0 :         return GSS_S_DEFECTIVE_TOKEN;
     337             :     }
     338             : 
     339           0 :     msg->cksum = vector_base(offset, len, 1, msg_base, msg_len);
     340           0 :     msg->cksum_len = len;
     341           0 :     if (msg->cksum == NULL) {
     342           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     343           0 :         return GSS_S_DEFECTIVE_TOKEN;
     344             :     }
     345             : 
     346           0 :     msg->offset_in_token = token_offset;
     347           0 :     return GSS_S_COMPLETE;
     348             : }
     349             : 
     350             : static OM_uint32
     351           0 : storage_from_memory(OM_uint32 *minor,
     352             :                     const uint8_t *data,
     353             :                     size_t length,
     354             :                     krb5_storage **sp)
     355             : {
     356           0 :     *sp = krb5_storage_from_readonly_mem(data, length);
     357           0 :     if (*sp == NULL) {
     358           0 :         *minor = ENOMEM;
     359           0 :         return GSS_S_FAILURE;
     360             :     }
     361             : 
     362           0 :     krb5_storage_set_byteorder(*sp, KRB5_STORAGE_BYTEORDER_LE);
     363           0 :     krb5_storage_set_eof_code(*sp, NEGOEX_INVALID_MESSAGE_SIZE);
     364             : 
     365           0 :     return 0;
     366             : }
     367             : 
     368             : static OM_uint32
     369           0 : parse_alert_message(OM_uint32 *minor, krb5_storage *sp,
     370             :                     const uint8_t *msg_base, size_t msg_len,
     371             :                     struct alert_message *msg)
     372             : {
     373           0 :     OM_uint32 major;
     374           0 :     krb5_error_code ret;
     375           0 :     const uint8_t *p;
     376           0 :     uint32_t error_code, atype;
     377           0 :     uint32_t alerts_offset, nalerts, value_offset, value_len;
     378           0 :     size_t i;
     379           0 :     krb5_storage *alerts;
     380             : 
     381           0 :     if (krb5_storage_read(sp, msg->scheme, GUID_LENGTH) == GUID_LENGTH)
     382           0 :         ret = 0;
     383             :     else
     384           0 :         ret = NEGOEX_INVALID_MESSAGE_SIZE;
     385           0 :     if (ret == 0)
     386           0 :         ret = krb5_ret_uint32(sp, &error_code);
     387           0 :     if (ret) {
     388           0 :         *minor = ret;
     389           0 :         return GSS_S_DEFECTIVE_TOKEN;
     390             :     }
     391             : 
     392           0 :     ret = krb5_ret_uint32(sp, &alerts_offset);
     393           0 :     if (ret == 0)
     394           0 :         ret = krb5_ret_uint32(sp, &nalerts);
     395           0 :     if (ret) {
     396           0 :         *minor = ret;
     397           0 :         return GSS_S_DEFECTIVE_TOKEN;
     398             :     }
     399             : 
     400           0 :     p = vector_base(alerts_offset, nalerts, ALERT_LENGTH, msg_base, msg_len);
     401           0 :     if (p == NULL) {
     402           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     403           0 :         return GSS_S_DEFECTIVE_TOKEN;
     404             :     }
     405             : 
     406             :     /* Look for a VERIFY_NO_KEY pulse alert in the alerts vector. */
     407           0 :     msg->verify_no_key = FALSE;
     408             : 
     409           0 :     major = storage_from_memory(minor, p, nalerts * ALERT_LENGTH, &alerts);
     410           0 :     if (major != GSS_S_COMPLETE)
     411           0 :         return major;
     412             : 
     413           0 :     for (i = 0; i < nalerts; i++) {
     414           0 :         ret = krb5_ret_uint32(alerts, &atype);
     415           0 :         if (ret == 0)
     416           0 :             ret = krb5_ret_uint32(alerts, &value_offset);
     417           0 :         if (ret == 0)
     418           0 :             ret = krb5_ret_uint32(alerts, &value_len);
     419           0 :         if (ret) {
     420           0 :             *minor = ret;
     421           0 :             major = GSS_S_DEFECTIVE_TOKEN;
     422           0 :             break;
     423             :         }
     424             : 
     425           0 :         p = vector_base(value_offset, value_len, 1, msg_base, msg_len);
     426           0 :         if (p == NULL) {
     427           0 :             *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     428           0 :             major = GSS_S_DEFECTIVE_TOKEN;
     429           0 :             break;
     430             :         }
     431             : 
     432           0 :         if (atype == ALERT_TYPE_PULSE && value_len >= ALERT_PULSE_LENGTH) {
     433           0 :             krb5_storage *pulse;
     434           0 :             uint32_t hdrlen, reason;
     435             : 
     436           0 :             major = storage_from_memory(minor, p, value_len, &pulse);
     437           0 :             if (major != GSS_S_COMPLETE)
     438           0 :                 break;
     439             : 
     440           0 :             ret = krb5_ret_uint32(pulse, &hdrlen);
     441           0 :             if (ret == 0)
     442           0 :                 ret = krb5_ret_uint32(pulse, &reason);
     443           0 :             krb5_storage_free(pulse);
     444           0 :             if (ret) {
     445           0 :                 *minor = ret;
     446           0 :                 major = GSS_S_DEFECTIVE_TOKEN;
     447           0 :                 break;
     448             :             }
     449             : 
     450           0 :             if (reason == ALERT_VERIFY_NO_KEY)
     451           0 :                 msg->verify_no_key = TRUE;
     452             :         }
     453             :     }
     454             : 
     455           0 :     krb5_storage_free(alerts);
     456             : 
     457           0 :     return major;
     458             : }
     459             : 
     460             : static OM_uint32
     461           0 : parse_message(OM_uint32 *minor,
     462             :               gssspnego_ctx ctx,
     463             :               gss_const_buffer_t token,
     464             :               size_t *token_offset,
     465             :               struct negoex_message *msg)
     466             : {
     467           0 :     OM_uint32 major;
     468           0 :     krb5_error_code ret;
     469           0 :     krb5_storage *sp;
     470           0 :     uint64_t signature;
     471           0 :     uint32_t header_len, msg_len;
     472           0 :     uint32_t type, seqnum;
     473           0 :     conversation_id conv_id;
     474           0 :     size_t token_remaining = token->length - *token_offset;
     475           0 :     const uint8_t *msg_base = (uint8_t *)token->value + *token_offset;
     476             : 
     477           0 :     major = storage_from_memory(minor, msg_base, token_remaining, &sp);
     478           0 :     if (major != GSS_S_COMPLETE)
     479           0 :         return major;
     480             : 
     481           0 :     major = GSS_S_DEFECTIVE_TOKEN;
     482             : 
     483           0 :     ret = krb5_ret_uint64(sp, &signature);
     484           0 :     if (ret == 0)
     485           0 :         ret = krb5_ret_uint32(sp, &type);
     486           0 :     if (ret == 0)
     487           0 :         ret = krb5_ret_uint32(sp, &seqnum);
     488           0 :     if (ret == 0)
     489           0 :         ret = krb5_ret_uint32(sp, &header_len);
     490           0 :     if (ret == 0)
     491           0 :         ret = krb5_ret_uint32(sp, &msg_len);
     492           0 :     if (ret == 0) {
     493           0 :         if (krb5_storage_read(sp, conv_id, GUID_LENGTH) != GUID_LENGTH)
     494           0 :             ret = NEGOEX_INVALID_MESSAGE_SIZE;
     495             :     }
     496           0 :     if (ret) {
     497           0 :         *minor = ret;
     498           0 :         goto cleanup;
     499             :     }
     500             : 
     501           0 :     if (msg_len > token_remaining || header_len > msg_len) {
     502           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     503           0 :         goto cleanup;
     504             :     }
     505           0 :     if (signature != MESSAGE_SIGNATURE) {
     506           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIGNATURE;
     507           0 :         goto cleanup;
     508             :     }
     509           0 :     if (seqnum != ctx->negoex_seqnum) {
     510           0 :         *minor = (OM_uint32)NEGOEX_MESSAGE_OUT_OF_SEQUENCE;
     511           0 :         goto cleanup;
     512             :     }
     513           0 :     if (seqnum == 0) {
     514           0 :         memcpy(ctx->negoex_conv_id, conv_id, GUID_LENGTH);
     515           0 :     } else if (!GUID_EQ(conv_id, ctx->negoex_conv_id)) {
     516           0 :         *minor = (OM_uint32)NEGOEX_INVALID_CONVERSATION_ID;
     517           0 :         goto cleanup;
     518             :     }
     519             : 
     520           0 :     krb5_storage_truncate(sp, msg_len);
     521             : 
     522           0 :     msg->type = type;
     523           0 :     if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO) {
     524           0 :         major = parse_nego_message(minor, sp, msg_base, msg_len, &msg->u.n);
     525           0 :     } else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||
     526           0 :                type == CHALLENGE || type == AP_REQUEST) {
     527           0 :         major = parse_exchange_message(minor, sp, msg_base, msg_len,
     528             :                                        &msg->u.e);
     529           0 :     } else if (type == VERIFY) {
     530           0 :         major = parse_verify_message(minor, sp, msg_base, msg_len,
     531           0 :                                      msg_base - (uint8_t *)token->value,
     532             :                                      &msg->u.v);
     533           0 :     } else if (type == ALERT) {
     534           0 :         major = parse_alert_message(minor, sp, msg_base, msg_len, &msg->u.a);
     535             :     } else {
     536           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_TYPE;
     537           0 :         goto cleanup;
     538             :     }
     539             : 
     540           0 : cleanup:
     541           0 :     krb5_storage_free(sp);
     542             : 
     543           0 :     if (major == GSS_S_COMPLETE) {
     544           0 :         _gss_negoex_log_message(1, msg->type,
     545           0 :                                 ctx->negoex_conv_id, ctx->negoex_seqnum,
     546             :                                 header_len, msg_len);
     547           0 :         ctx->negoex_seqnum++;
     548           0 :         *token_offset += msg_len;
     549             :     }
     550             : 
     551           0 :     return major;
     552             : }
     553             : 
     554             : /*
     555             :  * Parse token into an array of negoex_message structures. All pointer fields
     556             :  * within the parsed messages are aliases into token, so the result can be
     557             :  * freed with free(). An unknown protocol version, a critical extension, or an
     558             :  * unknown checksum scheme will cause a parsing failure. Increment the
     559             :  * sequence number in ctx for each message, and record and check the
     560             :  * conversation ID in ctx as appropriate.
     561             :  */
     562             : OM_uint32
     563           0 : _gss_negoex_parse_token(OM_uint32 *minor,
     564             :                         gssspnego_ctx ctx,
     565             :                         gss_const_buffer_t token,
     566             :                         struct negoex_message **messages_out,
     567             :                         size_t *count_out)
     568             : {
     569           0 :     OM_uint32 major = GSS_S_DEFECTIVE_TOKEN;
     570           0 :     size_t count = 0;
     571           0 :     size_t token_offset = 0;
     572           0 :     struct negoex_message *messages = NULL, *newptr;
     573             : 
     574           0 :     *messages_out = NULL;
     575           0 :     *count_out = 0;
     576           0 :     heim_assert(token != GSS_C_NO_BUFFER, "Invalid null NegoEx input token");
     577             : 
     578           0 :     while (token_offset < token->length) {
     579           0 :         newptr = realloc(messages, (count + 1) * sizeof(*newptr));
     580           0 :         if (newptr == NULL) {
     581           0 :             free(messages);
     582           0 :             *minor = ENOMEM;
     583           0 :             return GSS_S_FAILURE;
     584             :         }
     585           0 :         messages = newptr;
     586             : 
     587           0 :         major = parse_message(minor, ctx, token, &token_offset,
     588           0 :                               &messages[count]);
     589           0 :         if (major != GSS_S_COMPLETE)
     590           0 :             break;
     591             : 
     592           0 :         count++;
     593             :     }
     594             : 
     595           0 :     if (token_offset != token->length) {
     596           0 :         *minor = (OM_uint32)NEGOEX_INVALID_MESSAGE_SIZE;
     597           0 :         major = GSS_S_DEFECTIVE_TOKEN;
     598             :     }
     599           0 :     if (major != GSS_S_COMPLETE) {
     600           0 :         free(messages);
     601           0 :         return major;
     602             :     }
     603             : 
     604           0 :     *messages_out = messages;
     605           0 :     *count_out = count;
     606           0 :     return GSS_S_COMPLETE;
     607             : }
     608             : 
     609             : static struct negoex_message *
     610           0 : locate_message(struct negoex_message *messages, size_t nmessages,
     611             :                enum message_type type)
     612             : {
     613           0 :     uint32_t i;
     614             : 
     615           0 :     for (i = 0; i < nmessages; i++) {
     616           0 :         if (messages[i].type == type)
     617           0 :             return &messages[i];
     618             :     }
     619             : 
     620           0 :     return NULL;
     621             : }
     622             : 
     623             : struct nego_message *
     624           0 : _gss_negoex_locate_nego_message(struct negoex_message *messages,
     625             :                                 size_t nmessages,
     626             :                                 enum message_type type)
     627             : {
     628           0 :     struct negoex_message *msg = locate_message(messages, nmessages, type);
     629             : 
     630           0 :     return (msg == NULL) ? NULL : &msg->u.n;
     631             : }
     632             : 
     633             : struct exchange_message *
     634           0 : _gss_negoex_locate_exchange_message(struct negoex_message *messages,
     635             :                                     size_t nmessages,
     636             :                                     enum message_type type)
     637             : {
     638           0 :     struct negoex_message *msg = locate_message(messages, nmessages, type);
     639             : 
     640           0 :     return (msg == NULL) ? NULL : &msg->u.e;
     641             : }
     642             : 
     643             : struct verify_message *
     644           0 : _gss_negoex_locate_verify_message(struct negoex_message *messages,
     645             :                                   size_t nmessages)
     646             : {
     647           0 :     struct negoex_message *msg = locate_message(messages, nmessages, VERIFY);
     648             : 
     649           0 :     return (msg == NULL) ? NULL : &msg->u.v;
     650             : }
     651             : 
     652             : struct alert_message *
     653           0 : _gss_negoex_locate_alert_message(struct negoex_message *messages,
     654             :                                  size_t nmessages)
     655             : {
     656           0 :     struct negoex_message *msg = locate_message(messages, nmessages, ALERT);
     657             : 
     658           0 :     return (msg == NULL) ? NULL : &msg->u.a;
     659             : }
     660             : 
     661             : /*
     662             :  * Add the encoding of a MESSAGE_HEADER structure to buf, given the number of
     663             :  * bytes of the payload following the full header. Increment the sequence
     664             :  * number in ctx. Set *payload_start_out to the position of the payload within
     665             :  * the message.
     666             :  */
     667             : static OM_uint32
     668           0 : put_message_header(OM_uint32 *minor, gssspnego_ctx ctx,
     669             :                    enum message_type type, uint32_t payload_len,
     670             :                    uint32_t *payload_start_out)
     671             : {
     672           0 :     krb5_error_code ret;
     673           0 :     size_t header_len = 0;
     674             : 
     675           0 :     if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO)
     676           0 :         header_len = NEGO_MESSAGE_HEADER_LENGTH;
     677           0 :     else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||
     678           0 :              type == CHALLENGE || type == AP_REQUEST)
     679           0 :         header_len = EXCHANGE_MESSAGE_HEADER_LENGTH;
     680           0 :     else if (type == VERIFY)
     681           0 :         header_len = VERIFY_MESSAGE_HEADER_LENGTH;
     682           0 :     else if (type == ALERT)
     683           0 :         header_len = ALERT_MESSAGE_HEADER_LENGTH;
     684             :     else
     685           0 :         heim_assert(0, "Invalid NegoEx message type");
     686             : 
     687             :     /* Signature */
     688           0 :     CHECK(ret, krb5_store_uint64(ctx->negoex_transcript, MESSAGE_SIGNATURE));
     689             :     /* MessageType */
     690           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, type));
     691             :     /* SequenceNum */
     692           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, ctx->negoex_seqnum));
     693             :     /* cbHeaderLength */
     694           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, header_len));
     695             :     /* cbMessageLength */
     696           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, header_len + payload_len));
     697             :     /* ConversationId */
     698           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, ctx->negoex_conv_id, GUID_LENGTH));
     699             : 
     700           0 :     _gss_negoex_log_message(0, type,
     701           0 :                             ctx->negoex_conv_id, ctx->negoex_seqnum,
     702             :                             header_len,
     703             :                             header_len + payload_len);
     704             : 
     705           0 :     ctx->negoex_seqnum++;
     706             : 
     707           0 :     *payload_start_out = header_len;
     708           0 :     return GSS_S_COMPLETE;
     709             : 
     710           0 : fail:
     711           0 :     *minor = ret;
     712           0 :     return GSS_S_FAILURE;
     713             : }
     714             : 
     715             : OM_uint32
     716           0 : _gss_negoex_add_nego_message(OM_uint32 *minor,
     717             :                              gssspnego_ctx ctx,
     718             :                              enum message_type type,
     719             :                              uint8_t random[32])
     720             : {
     721           0 :     OM_uint32 major;
     722           0 :     krb5_error_code ret;
     723           0 :     struct negoex_auth_mech *mech;
     724           0 :     uint32_t payload_start;
     725           0 :     uint16_t nschemes;
     726             : 
     727           0 :     nschemes = 0;
     728           0 :     HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
     729           0 :         nschemes++;
     730             : 
     731           0 :     major = put_message_header(minor, ctx, type,
     732           0 :                                nschemes * GUID_LENGTH, &payload_start);
     733           0 :     if (major != GSS_S_COMPLETE)
     734           0 :         return major;
     735             : 
     736           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, random, 32));
     737             :     /* ProtocolVersion */
     738           0 :     CHECK(ret, krb5_store_uint64(ctx->negoex_transcript, 0));
     739             :     /* AuthSchemes vector */
     740           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, payload_start));
     741           0 :     CHECK(ret, krb5_store_uint16(ctx->negoex_transcript, nschemes));
     742             :     /* Extensions vector */
     743           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, payload_start));
     744           0 :     CHECK(ret, krb5_store_uint16(ctx->negoex_transcript, 0));
     745             :     /* Four bytes of padding to reach a multiple of 8 bytes. */
     746           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, "\0\0\0\0", 4));
     747             : 
     748             :     /* Payload (auth schemes) */
     749           0 :     HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
     750           0 :         CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, mech->scheme, GUID_LENGTH));
     751             :     }
     752             : 
     753           0 :     return GSS_S_COMPLETE;
     754             : 
     755           0 : fail:
     756           0 :     *minor = ret;
     757           0 :     return GSS_S_FAILURE;
     758             : }
     759             : 
     760             : OM_uint32
     761           0 : _gss_negoex_add_exchange_message(OM_uint32 *minor,
     762             :                                  gssspnego_ctx ctx,
     763             :                                  enum message_type type,
     764             :                                  const auth_scheme scheme,
     765             :                                  gss_buffer_t token)
     766             : {
     767           0 :     OM_uint32 major;
     768           0 :     krb5_error_code ret;
     769           0 :     uint32_t payload_start;
     770             : 
     771           0 :     major = put_message_header(minor, ctx, type, token->length, &payload_start);
     772           0 :     if (major != GSS_S_COMPLETE)
     773           0 :         return major;
     774             : 
     775           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, scheme, GUID_LENGTH));
     776             :     /* Exchange byte vector */
     777           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, payload_start));
     778           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, token->length));
     779             :     /* Payload (token) */
     780           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, token->value, token->length));
     781             : 
     782           0 :     return GSS_S_COMPLETE;
     783             : 
     784           0 : fail:
     785           0 :     *minor = ret;
     786           0 :     return GSS_S_FAILURE;
     787             : }
     788             : 
     789             : OM_uint32
     790           0 : _gss_negoex_add_verify_message(OM_uint32 *minor,
     791             :                                gssspnego_ctx ctx,
     792             :                                const auth_scheme scheme,
     793             :                                uint32_t cksum_type,
     794             :                                const uint8_t *cksum,
     795             :                                uint32_t cksum_len)
     796             : {
     797           0 :     OM_uint32 major;
     798           0 :     krb5_error_code ret;
     799           0 :     uint32_t payload_start;
     800             : 
     801           0 :     major = put_message_header(minor, ctx, VERIFY, cksum_len, &payload_start);
     802           0 :     if (major != GSS_S_COMPLETE)
     803           0 :         return major;
     804             : 
     805           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, scheme, GUID_LENGTH));
     806           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, CHECKSUM_HEADER_LENGTH));
     807           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, CHECKSUM_SCHEME_RFC3961));
     808           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, cksum_type));
     809             :     /* ChecksumValue vector */
     810           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, payload_start));
     811           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, cksum_len));
     812             :     /* Four bytes of padding to reach a multiple of 8 bytes. */
     813           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, "\0\0\0\0", 4));
     814             :     /* Payload (checksum contents) */
     815           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, cksum, cksum_len));
     816             : 
     817           0 :     return GSS_S_COMPLETE;
     818             : 
     819           0 : fail:
     820           0 :     *minor = ret;
     821           0 :     return GSS_S_FAILURE;
     822             : }
     823             : 
     824             : /*
     825             :  * Add an ALERT_MESSAGE containing a single ALERT_TYPE_PULSE alert with the
     826             :  * reason ALERT_VERIFY_NO_KEY.
     827             :  */
     828             : OM_uint32
     829           0 : _gss_negoex_add_verify_no_key_alert(OM_uint32 *minor,
     830             :                                     gssspnego_ctx ctx,
     831             :                                     const auth_scheme scheme)
     832             : {
     833           0 :     OM_uint32 major;
     834           0 :     krb5_error_code ret;
     835           0 :     uint32_t payload_start;
     836             : 
     837           0 :     major = put_message_header(minor, ctx,
     838             :                                ALERT, ALERT_LENGTH + ALERT_PULSE_LENGTH,
     839             :                                &payload_start);
     840           0 :     if (major != GSS_S_COMPLETE)
     841           0 :         return major;
     842             : 
     843           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, scheme, GUID_LENGTH));
     844             :     /* ErrorCode */
     845           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, 0));
     846             :     /* Alerts vector */
     847           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, payload_start));
     848           0 :     CHECK(ret, krb5_store_uint16(ctx->negoex_transcript, 1));
     849             :     /* Six bytes of padding to reach a multiple of 8 bytes. */
     850           0 :     CHECK(ret, krb5_store_bytes(ctx->negoex_transcript, "\0\0\0\0\0\0", 6));
     851             :     /* Payload part 1: a single ALERT element */
     852           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, ALERT_TYPE_PULSE));
     853           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript,
     854             :                                  payload_start + ALERT_LENGTH));
     855           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, ALERT_PULSE_LENGTH));
     856             :     /* Payload part 2: ALERT_PULSE */
     857           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, ALERT_PULSE_LENGTH));
     858           0 :     CHECK(ret, krb5_store_uint32(ctx->negoex_transcript, ALERT_VERIFY_NO_KEY));
     859             : 
     860           0 :     return GSS_S_COMPLETE;
     861             : 
     862           0 : fail:
     863           0 :     *minor = ret;
     864           0 :     return GSS_S_FAILURE;
     865             : }
     866             : 
     867             : 
     868             : void
     869           0 : _gss_negoex_release_auth_mech(krb5_context context,
     870             :                               struct negoex_auth_mech *mech)
     871             : {
     872           0 :     OM_uint32 tmpmin;
     873             : 
     874           0 :     if (mech == NULL)
     875           0 :         return;
     876             : 
     877           0 :     gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);
     878           0 :     gss_release_oid(&tmpmin, &mech->oid);
     879           0 :     gss_release_buffer(&tmpmin, &mech->metadata);
     880           0 :     if (mech->crypto)
     881           0 :         krb5_crypto_destroy(context, mech->crypto);
     882           0 :     if (mech->verify_crypto)
     883           0 :         krb5_crypto_destroy(context, mech->verify_crypto);
     884             : 
     885           0 :     free(mech);
     886             : }
     887             : 
     888             : void
     889           0 : _gss_negoex_delete_auth_mech(gssspnego_ctx ctx,
     890             :                              struct negoex_auth_mech *mech)
     891             : {
     892           0 :     krb5_context context = _gss_mg_krb5_context();
     893             : 
     894           0 :     HEIM_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
     895           0 :     _gss_negoex_release_auth_mech(context, mech);
     896           0 : }
     897             : 
     898             : /* Remove all auth mech entries except for mech from ctx->mechs. */
     899             : void
     900           0 : _gss_negoex_select_auth_mech(gssspnego_ctx ctx,
     901             :                              struct negoex_auth_mech *mech)
     902             : {
     903           0 :     krb5_context context = _gss_mg_krb5_context();
     904             : 
     905           0 :     heim_assert(mech != NULL, "Invalid null NegoEx mech");
     906           0 :     HEIM_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
     907           0 :     release_all_mechs(ctx, context);
     908           0 :     HEIM_TAILQ_INSERT_HEAD(&ctx->negoex_mechs, mech, links);
     909           0 : }
     910             : 
     911             : OM_uint32
     912           0 : _gss_negoex_add_auth_mech(OM_uint32 *minor,
     913             :                           gssspnego_ctx ctx,
     914             :                           gss_const_OID oid,
     915             :                           auth_scheme scheme)
     916             : {
     917           0 :     OM_uint32 major;
     918           0 :     struct negoex_auth_mech *mech;
     919             : 
     920           0 :     mech = calloc(1, sizeof(*mech));
     921           0 :     if (mech == NULL) {
     922           0 :         *minor = ENOMEM;
     923           0 :         return GSS_S_FAILURE;
     924             :     }
     925             : 
     926           0 :     major = gss_duplicate_oid(minor, (gss_OID)oid, &mech->oid);
     927           0 :     if (major != GSS_S_COMPLETE) {
     928           0 :         free(mech);
     929           0 :         return major;
     930             :     }
     931             : 
     932           0 :     memcpy(mech->scheme, scheme, GUID_LENGTH);
     933             : 
     934           0 :     HEIM_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
     935             : 
     936           0 :     *minor = 0;
     937           0 :     return GSS_S_COMPLETE;
     938             : }
     939             : 
     940             : struct negoex_auth_mech *
     941           0 : _gss_negoex_locate_auth_scheme(gssspnego_ctx ctx,
     942             :                                const auth_scheme scheme)
     943             : {
     944           0 :     struct negoex_auth_mech *mech;
     945             : 
     946           0 :     HEIM_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
     947           0 :         if (GUID_EQ(mech->scheme, scheme))
     948           0 :             return mech;
     949             :     }
     950             : 
     951           0 :     return NULL;
     952             : }
     953             : 
     954             : /*
     955             :  * Prune ctx->mechs to the schemes present in schemes, and reorder them to
     956             :  * match its order.
     957             :  */
     958             : void
     959           0 : _gss_negoex_common_auth_schemes(gssspnego_ctx ctx,
     960             :                                 const uint8_t *schemes,
     961             :                                 uint16_t nschemes)
     962             : {
     963           0 :     struct negoex_mech_list list;
     964           0 :     struct negoex_auth_mech *mech;
     965           0 :     uint16_t i;
     966           0 :     krb5_context context = _gss_mg_krb5_context();
     967             : 
     968             :     /* Construct a new list in the order of schemes. */
     969           0 :     HEIM_TAILQ_INIT(&list);
     970           0 :     for (i = 0; i < nschemes; i++) {
     971           0 :         mech = _gss_negoex_locate_auth_scheme(ctx, schemes + i * GUID_LENGTH);
     972           0 :         if (mech == NULL)
     973           0 :             continue;
     974           0 :         HEIM_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
     975           0 :         HEIM_TAILQ_INSERT_TAIL(&list, mech, links);
     976             :     }
     977             : 
     978             :     /* Release any leftover entries and replace the context list. */
     979           0 :     release_all_mechs(ctx, context);
     980           0 :     HEIM_TAILQ_CONCAT(&ctx->negoex_mechs, &list, links);
     981           0 : }
     982             : 
     983             : /*
     984             :  * Prune ctx->mechs to the schemes present in schemes, but do not change
     985             :  * their order.
     986             :  */
     987             : void
     988           0 : _gss_negoex_restrict_auth_schemes(gssspnego_ctx ctx,
     989             :                                   const uint8_t *schemes,
     990             :                                   uint16_t nschemes)
     991             : {
     992           0 :     struct negoex_auth_mech *mech, *next;
     993           0 :     uint16_t i;
     994           0 :     int found;
     995             : 
     996           0 :     HEIM_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next) {
     997           0 :         found = FALSE;
     998           0 :         for (i = 0; i < nschemes && !found; i++) {
     999           0 :             if (GUID_EQ(mech->scheme, schemes + i * GUID_LENGTH))
    1000           0 :                 found = TRUE;
    1001             :         }
    1002             : 
    1003           0 :         if (!found)
    1004           0 :             _gss_negoex_delete_auth_mech(ctx, mech);
    1005             :     }
    1006           0 : }
    1007             : 
    1008             : /*
    1009             :  * Return the OID of the current NegoEx mechanism.
    1010             :  */
    1011             : struct negoex_auth_mech *
    1012           0 : _gss_negoex_negotiated_mech(gssspnego_ctx ctx)
    1013             : {
    1014           0 :     return HEIM_TAILQ_FIRST(&ctx->negoex_mechs);
    1015             : }
    1016             : 
    1017             : /*
    1018             :  * Returns TRUE if mechanism can be negotiated by both NegoEx and SPNEGO
    1019             :  */
    1020             : 
    1021             : int
    1022           0 : _gss_negoex_and_spnego_mech_p(gss_const_OID mech)
    1023             : {
    1024           0 :     OM_uint32 major, minor;
    1025           0 :     gss_OID_set attrs = GSS_C_NO_OID_SET;
    1026           0 :     int negoex_and_spnego = FALSE;
    1027             : 
    1028           0 :     major = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
    1029           0 :     if (major == GSS_S_COMPLETE) {
    1030           0 :         gss_test_oid_set_member(&minor, GSS_C_MA_NEGOEX_AND_SPNEGO,
    1031             :                                 attrs, &negoex_and_spnego);
    1032           0 :         gss_release_oid_set(&minor, &attrs);
    1033             :     }
    1034             : 
    1035           0 :     return negoex_and_spnego;
    1036             : }
    1037             : 
    1038             : int
    1039           0 : _gss_negoex_mech_p(gss_const_OID mech)
    1040             : {
    1041           0 :     OM_uint32 minor;
    1042           0 :     auth_scheme scheme;
    1043             : 
    1044           0 :     return gssspi_query_mechanism_info(&minor, mech,
    1045           0 :                                        scheme) == GSS_S_COMPLETE;
    1046             : }
    1047             : 

Generated by: LCOV version 1.14