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

Generated by: LCOV version 1.13