xref: /freebsd/crypto/krb5/src/lib/gssapi/spnego/negoex_util.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1*7f2fe78bSCy Schubert /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2*7f2fe78bSCy Schubert /*
3*7f2fe78bSCy Schubert  * Copyright (C) 2011-2018 PADL Software Pty Ltd.
4*7f2fe78bSCy Schubert  * All rights reserved.
5*7f2fe78bSCy Schubert  *
6*7f2fe78bSCy Schubert  * Redistribution and use in source and binary forms, with or without
7*7f2fe78bSCy Schubert  * modification, are permitted provided that the following conditions
8*7f2fe78bSCy Schubert  * are met:
9*7f2fe78bSCy Schubert  *
10*7f2fe78bSCy Schubert  * * Redistributions of source code must retain the above copyright
11*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer.
12*7f2fe78bSCy Schubert  *
13*7f2fe78bSCy Schubert  * * Redistributions in binary form must reproduce the above copyright
14*7f2fe78bSCy Schubert  *   notice, this list of conditions and the following disclaimer in
15*7f2fe78bSCy Schubert  *   the documentation and/or other materials provided with the
16*7f2fe78bSCy Schubert  *   distribution.
17*7f2fe78bSCy Schubert  *
18*7f2fe78bSCy Schubert  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19*7f2fe78bSCy Schubert  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20*7f2fe78bSCy Schubert  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
21*7f2fe78bSCy Schubert  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22*7f2fe78bSCy Schubert  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23*7f2fe78bSCy Schubert  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24*7f2fe78bSCy Schubert  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25*7f2fe78bSCy Schubert  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26*7f2fe78bSCy Schubert  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
27*7f2fe78bSCy Schubert  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28*7f2fe78bSCy Schubert  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
29*7f2fe78bSCy Schubert  * OF THE POSSIBILITY OF SUCH DAMAGE.
30*7f2fe78bSCy Schubert  */
31*7f2fe78bSCy Schubert 
32*7f2fe78bSCy Schubert #include "gssapiP_spnego.h"
33*7f2fe78bSCy Schubert #include <generic/gssapiP_generic.h>
34*7f2fe78bSCy Schubert #include "k5-input.h"
35*7f2fe78bSCy Schubert 
36*7f2fe78bSCy Schubert static void
37*7f2fe78bSCy Schubert release_auth_mech(struct negoex_auth_mech *mech);
38*7f2fe78bSCy Schubert 
39*7f2fe78bSCy Schubert OM_uint32
negoex_random(OM_uint32 * minor,spnego_gss_ctx_id_t ctx,uint8_t * data,size_t length)40*7f2fe78bSCy Schubert negoex_random(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
41*7f2fe78bSCy Schubert               uint8_t *data, size_t length)
42*7f2fe78bSCy Schubert {
43*7f2fe78bSCy Schubert     krb5_data d = make_data(data, length);
44*7f2fe78bSCy Schubert 
45*7f2fe78bSCy Schubert     *minor = krb5_c_random_make_octets(ctx->kctx, &d);
46*7f2fe78bSCy Schubert     return *minor ? GSS_S_FAILURE : GSS_S_COMPLETE;
47*7f2fe78bSCy Schubert }
48*7f2fe78bSCy Schubert 
49*7f2fe78bSCy Schubert /*
50*7f2fe78bSCy Schubert  * SPNEGO functions expect to find the active mech context in ctx->ctx_handle,
51*7f2fe78bSCy Schubert  * but the metadata exchange APIs force us to have one mech context per mech
52*7f2fe78bSCy Schubert  * entry.  To address this mismatch, move the active mech context (if we have
53*7f2fe78bSCy Schubert  * one) to ctx->ctx_handle at the end of NegoEx processing.
54*7f2fe78bSCy Schubert  */
55*7f2fe78bSCy Schubert void
negoex_prep_context_for_spnego(spnego_gss_ctx_id_t ctx)56*7f2fe78bSCy Schubert negoex_prep_context_for_spnego(spnego_gss_ctx_id_t ctx)
57*7f2fe78bSCy Schubert {
58*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
59*7f2fe78bSCy Schubert 
60*7f2fe78bSCy Schubert     mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
61*7f2fe78bSCy Schubert     if (mech == NULL || mech->mech_context == GSS_C_NO_CONTEXT)
62*7f2fe78bSCy Schubert         return;
63*7f2fe78bSCy Schubert 
64*7f2fe78bSCy Schubert     assert(ctx->ctx_handle == GSS_C_NO_CONTEXT);
65*7f2fe78bSCy Schubert     ctx->ctx_handle = mech->mech_context;
66*7f2fe78bSCy Schubert     mech->mech_context = GSS_C_NO_CONTEXT;
67*7f2fe78bSCy Schubert }
68*7f2fe78bSCy Schubert 
69*7f2fe78bSCy Schubert OM_uint32
negoex_prep_context_for_negoex(OM_uint32 * minor,spnego_gss_ctx_id_t ctx)70*7f2fe78bSCy Schubert negoex_prep_context_for_negoex(OM_uint32 *minor, spnego_gss_ctx_id_t ctx)
71*7f2fe78bSCy Schubert {
72*7f2fe78bSCy Schubert     krb5_error_code ret;
73*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
74*7f2fe78bSCy Schubert 
75*7f2fe78bSCy Schubert     if (ctx->kctx != NULL) {
76*7f2fe78bSCy Schubert         /* The context is already initialized for NegoEx.  Undo what
77*7f2fe78bSCy Schubert          * negoex_prep_for_spnego() did, if applicable. */
78*7f2fe78bSCy Schubert         if (ctx->ctx_handle != GSS_C_NO_CONTEXT) {
79*7f2fe78bSCy Schubert             mech = K5_TAILQ_FIRST(&ctx->negoex_mechs);
80*7f2fe78bSCy Schubert             assert(mech != NULL && mech->mech_context == GSS_C_NO_CONTEXT);
81*7f2fe78bSCy Schubert             mech->mech_context = ctx->ctx_handle;
82*7f2fe78bSCy Schubert             ctx->ctx_handle = GSS_C_NO_CONTEXT;
83*7f2fe78bSCy Schubert         }
84*7f2fe78bSCy Schubert         return GSS_S_COMPLETE;
85*7f2fe78bSCy Schubert     }
86*7f2fe78bSCy Schubert 
87*7f2fe78bSCy Schubert     /* Initialize the NegoEX context fields.  (negoex_mechs is already set up
88*7f2fe78bSCy Schubert      * by SPNEGO.) */
89*7f2fe78bSCy Schubert     ret = krb5_init_context(&ctx->kctx);
90*7f2fe78bSCy Schubert     if (ret) {
91*7f2fe78bSCy Schubert         *minor = ret;
92*7f2fe78bSCy Schubert         return GSS_S_FAILURE;
93*7f2fe78bSCy Schubert     }
94*7f2fe78bSCy Schubert 
95*7f2fe78bSCy Schubert     k5_buf_init_dynamic(&ctx->negoex_transcript);
96*7f2fe78bSCy Schubert 
97*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
98*7f2fe78bSCy Schubert }
99*7f2fe78bSCy Schubert 
100*7f2fe78bSCy Schubert static void
release_all_mechs(spnego_gss_ctx_id_t ctx)101*7f2fe78bSCy Schubert release_all_mechs(spnego_gss_ctx_id_t ctx)
102*7f2fe78bSCy Schubert {
103*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech, *next;
104*7f2fe78bSCy Schubert 
105*7f2fe78bSCy Schubert     K5_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next)
106*7f2fe78bSCy Schubert         release_auth_mech(mech);
107*7f2fe78bSCy Schubert     K5_TAILQ_INIT(&ctx->negoex_mechs);
108*7f2fe78bSCy Schubert }
109*7f2fe78bSCy Schubert 
110*7f2fe78bSCy Schubert void
negoex_release_context(spnego_gss_ctx_id_t ctx)111*7f2fe78bSCy Schubert negoex_release_context(spnego_gss_ctx_id_t ctx)
112*7f2fe78bSCy Schubert {
113*7f2fe78bSCy Schubert     k5_buf_free(&ctx->negoex_transcript);
114*7f2fe78bSCy Schubert     release_all_mechs(ctx);
115*7f2fe78bSCy Schubert     krb5_free_context(ctx->kctx);
116*7f2fe78bSCy Schubert     ctx->kctx = NULL;
117*7f2fe78bSCy Schubert }
118*7f2fe78bSCy Schubert 
119*7f2fe78bSCy Schubert static const char *
typestr(enum message_type type)120*7f2fe78bSCy Schubert typestr(enum message_type type)
121*7f2fe78bSCy Schubert {
122*7f2fe78bSCy Schubert     if (type == INITIATOR_NEGO)
123*7f2fe78bSCy Schubert         return "INITIATOR_NEGO";
124*7f2fe78bSCy Schubert     else if (type == ACCEPTOR_NEGO)
125*7f2fe78bSCy Schubert         return "ACCEPTOR_NEGO";
126*7f2fe78bSCy Schubert     else if (type == INITIATOR_META_DATA)
127*7f2fe78bSCy Schubert         return "INITIATOR_META_DATA";
128*7f2fe78bSCy Schubert     else if (type == ACCEPTOR_META_DATA)
129*7f2fe78bSCy Schubert         return "ACCEPTOR_META_DATA";
130*7f2fe78bSCy Schubert     else if (type == CHALLENGE)
131*7f2fe78bSCy Schubert         return "CHALLENGE";
132*7f2fe78bSCy Schubert     else if (type == AP_REQUEST)
133*7f2fe78bSCy Schubert         return "AP_REQUEST";
134*7f2fe78bSCy Schubert     else if (type == VERIFY)
135*7f2fe78bSCy Schubert         return "VERIFY";
136*7f2fe78bSCy Schubert     else if (type == ALERT)
137*7f2fe78bSCy Schubert         return "ALERT";
138*7f2fe78bSCy Schubert     else
139*7f2fe78bSCy Schubert         return "UNKNOWN";
140*7f2fe78bSCy Schubert }
141*7f2fe78bSCy Schubert 
142*7f2fe78bSCy Schubert static void
add_guid(struct k5buf * buf,const uint8_t guid[GUID_LENGTH])143*7f2fe78bSCy Schubert add_guid(struct k5buf *buf, const uint8_t guid[GUID_LENGTH])
144*7f2fe78bSCy Schubert {
145*7f2fe78bSCy Schubert     uint32_t data1 = load_32_le(guid);
146*7f2fe78bSCy Schubert     uint16_t data2 = load_16_le(guid + 4), data3 = load_16_le(guid + 6);
147*7f2fe78bSCy Schubert 
148*7f2fe78bSCy Schubert     k5_buf_add_fmt(buf, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
149*7f2fe78bSCy Schubert                    data1, data2, data3, guid[8], guid[9], guid[10], guid[11],
150*7f2fe78bSCy Schubert                    guid[12], guid[13], guid[14], guid[15]);
151*7f2fe78bSCy Schubert }
152*7f2fe78bSCy Schubert 
153*7f2fe78bSCy Schubert static char *
guid_to_string(const uint8_t guid[GUID_LENGTH])154*7f2fe78bSCy Schubert guid_to_string(const uint8_t guid[GUID_LENGTH])
155*7f2fe78bSCy Schubert {
156*7f2fe78bSCy Schubert     struct k5buf buf;
157*7f2fe78bSCy Schubert 
158*7f2fe78bSCy Schubert     k5_buf_init_dynamic(&buf);
159*7f2fe78bSCy Schubert     add_guid(&buf, guid);
160*7f2fe78bSCy Schubert     return k5_buf_cstring(&buf);
161*7f2fe78bSCy Schubert }
162*7f2fe78bSCy Schubert 
163*7f2fe78bSCy Schubert /* Check that the described vector lies within the message, and return a
164*7f2fe78bSCy Schubert  * pointer to its first element. */
165*7f2fe78bSCy Schubert static inline const uint8_t *
vector_base(size_t offset,size_t count,size_t width,const uint8_t * msg_base,size_t msg_len)166*7f2fe78bSCy Schubert vector_base(size_t offset, size_t count, size_t width,
167*7f2fe78bSCy Schubert             const uint8_t *msg_base, size_t msg_len)
168*7f2fe78bSCy Schubert {
169*7f2fe78bSCy Schubert     if (offset > msg_len || count > (msg_len - offset) / width)
170*7f2fe78bSCy Schubert         return NULL;
171*7f2fe78bSCy Schubert     return msg_base + offset;
172*7f2fe78bSCy Schubert }
173*7f2fe78bSCy Schubert 
174*7f2fe78bSCy Schubert /* Trace a received message.  Call after the context sequence number is
175*7f2fe78bSCy Schubert  * incremented. */
176*7f2fe78bSCy Schubert static void
trace_received_message(spnego_gss_ctx_id_t ctx,const struct negoex_message * msg)177*7f2fe78bSCy Schubert trace_received_message(spnego_gss_ctx_id_t ctx,
178*7f2fe78bSCy Schubert                        const struct negoex_message *msg)
179*7f2fe78bSCy Schubert {
180*7f2fe78bSCy Schubert     struct k5buf buf;
181*7f2fe78bSCy Schubert     uint16_t i;
182*7f2fe78bSCy Schubert     char *info = NULL;
183*7f2fe78bSCy Schubert 
184*7f2fe78bSCy Schubert     if (msg->type == INITIATOR_NEGO || msg->type == ACCEPTOR_NEGO) {
185*7f2fe78bSCy Schubert         k5_buf_init_dynamic(&buf);
186*7f2fe78bSCy Schubert         for (i = 0; i < msg->u.n.nschemes; i++) {
187*7f2fe78bSCy Schubert             add_guid(&buf, msg->u.n.schemes + i * GUID_LENGTH);
188*7f2fe78bSCy Schubert             if (i + 1 < msg->u.n.nschemes)
189*7f2fe78bSCy Schubert                 k5_buf_add(&buf, " ");
190*7f2fe78bSCy Schubert         }
191*7f2fe78bSCy Schubert         info = k5_buf_cstring(&buf);
192*7f2fe78bSCy Schubert     } else if (msg->type == INITIATOR_META_DATA ||
193*7f2fe78bSCy Schubert                msg->type == ACCEPTOR_META_DATA ||
194*7f2fe78bSCy Schubert                msg->type == CHALLENGE || msg->type == AP_REQUEST) {
195*7f2fe78bSCy Schubert         info = guid_to_string(msg->u.e.scheme);
196*7f2fe78bSCy Schubert     } else if (msg->type == VERIFY) {
197*7f2fe78bSCy Schubert         info = guid_to_string(msg->u.v.scheme);
198*7f2fe78bSCy Schubert     } else if (msg->type == ALERT) {
199*7f2fe78bSCy Schubert         info = guid_to_string(msg->u.a.scheme);
200*7f2fe78bSCy Schubert     }
201*7f2fe78bSCy Schubert 
202*7f2fe78bSCy Schubert     if (info == NULL)
203*7f2fe78bSCy Schubert         return;
204*7f2fe78bSCy Schubert 
205*7f2fe78bSCy Schubert     TRACE_NEGOEX_INCOMING(ctx->kctx, ctx->negoex_seqnum - 1,
206*7f2fe78bSCy Schubert                           typestr(msg->type), info);
207*7f2fe78bSCy Schubert     free(info);
208*7f2fe78bSCy Schubert }
209*7f2fe78bSCy Schubert 
210*7f2fe78bSCy Schubert /* Trace an outgoing message with a GUID info string.  Call after the context
211*7f2fe78bSCy Schubert  * sequence number is incremented. */
212*7f2fe78bSCy Schubert static void
trace_outgoing_message(spnego_gss_ctx_id_t ctx,enum message_type type,const uint8_t guid[GUID_LENGTH])213*7f2fe78bSCy Schubert trace_outgoing_message(spnego_gss_ctx_id_t ctx, enum message_type type,
214*7f2fe78bSCy Schubert                        const uint8_t guid[GUID_LENGTH])
215*7f2fe78bSCy Schubert {
216*7f2fe78bSCy Schubert     char *info = guid_to_string(guid);
217*7f2fe78bSCy Schubert 
218*7f2fe78bSCy Schubert     if (info == NULL)
219*7f2fe78bSCy Schubert         return;
220*7f2fe78bSCy Schubert     TRACE_NEGOEX_OUTGOING(ctx->kctx, ctx->negoex_seqnum - 1, typestr(type),
221*7f2fe78bSCy Schubert                           info);
222*7f2fe78bSCy Schubert     free(info);
223*7f2fe78bSCy Schubert }
224*7f2fe78bSCy Schubert 
225*7f2fe78bSCy Schubert static OM_uint32
parse_nego_message(OM_uint32 * minor,struct k5input * in,const uint8_t * msg_base,size_t msg_len,struct nego_message * msg)226*7f2fe78bSCy Schubert parse_nego_message(OM_uint32 *minor, struct k5input *in,
227*7f2fe78bSCy Schubert                    const uint8_t *msg_base, size_t msg_len,
228*7f2fe78bSCy Schubert                    struct nego_message *msg)
229*7f2fe78bSCy Schubert {
230*7f2fe78bSCy Schubert     const uint8_t *p;
231*7f2fe78bSCy Schubert     uint64_t protocol_version;
232*7f2fe78bSCy Schubert     uint32_t extension_type;
233*7f2fe78bSCy Schubert     size_t offset, count, i;
234*7f2fe78bSCy Schubert 
235*7f2fe78bSCy Schubert     p = k5_input_get_bytes(in, sizeof(msg->random));
236*7f2fe78bSCy Schubert     if (p != NULL)
237*7f2fe78bSCy Schubert         memcpy(msg->random, p, sizeof(msg->random));
238*7f2fe78bSCy Schubert     protocol_version = k5_input_get_uint64_le(in);
239*7f2fe78bSCy Schubert     if (protocol_version != 0) {
240*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_UNSUPPORTED_VERSION;
241*7f2fe78bSCy Schubert         return GSS_S_UNAVAILABLE;
242*7f2fe78bSCy Schubert     }
243*7f2fe78bSCy Schubert 
244*7f2fe78bSCy Schubert     offset = k5_input_get_uint32_le(in);
245*7f2fe78bSCy Schubert     count = k5_input_get_uint16_le(in);
246*7f2fe78bSCy Schubert     msg->schemes = vector_base(offset, count, GUID_LENGTH, msg_base, msg_len);
247*7f2fe78bSCy Schubert     msg->nschemes = count;
248*7f2fe78bSCy Schubert     if (msg->schemes == NULL) {
249*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
250*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
251*7f2fe78bSCy Schubert     }
252*7f2fe78bSCy Schubert 
253*7f2fe78bSCy Schubert     offset = k5_input_get_uint32_le(in);
254*7f2fe78bSCy Schubert     count = k5_input_get_uint16_le(in);
255*7f2fe78bSCy Schubert     p = vector_base(offset, count, EXTENSION_LENGTH, msg_base, msg_len);
256*7f2fe78bSCy Schubert     for (i = 0; i < count; i++) {
257*7f2fe78bSCy Schubert         extension_type = load_32_le(p + i * EXTENSION_LENGTH);
258*7f2fe78bSCy Schubert         if (extension_type & EXTENSION_FLAG_CRITICAL) {
259*7f2fe78bSCy Schubert             *minor = ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION;
260*7f2fe78bSCy Schubert             return GSS_S_UNAVAILABLE;
261*7f2fe78bSCy Schubert         }
262*7f2fe78bSCy Schubert     }
263*7f2fe78bSCy Schubert 
264*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
265*7f2fe78bSCy Schubert }
266*7f2fe78bSCy Schubert 
267*7f2fe78bSCy Schubert static OM_uint32
parse_exchange_message(OM_uint32 * minor,struct k5input * in,const uint8_t * msg_base,size_t msg_len,struct exchange_message * msg)268*7f2fe78bSCy Schubert parse_exchange_message(OM_uint32 *minor, struct k5input *in,
269*7f2fe78bSCy Schubert                        const uint8_t *msg_base, size_t msg_len,
270*7f2fe78bSCy Schubert                        struct exchange_message *msg)
271*7f2fe78bSCy Schubert {
272*7f2fe78bSCy Schubert     const uint8_t *p;
273*7f2fe78bSCy Schubert     size_t offset, len;
274*7f2fe78bSCy Schubert 
275*7f2fe78bSCy Schubert     p = k5_input_get_bytes(in, GUID_LENGTH);
276*7f2fe78bSCy Schubert     if (p != NULL)
277*7f2fe78bSCy Schubert         memcpy(msg->scheme, p, GUID_LENGTH);
278*7f2fe78bSCy Schubert 
279*7f2fe78bSCy Schubert     offset = k5_input_get_uint32_le(in);
280*7f2fe78bSCy Schubert     len = k5_input_get_uint32_le(in);
281*7f2fe78bSCy Schubert     p = vector_base(offset, len, 1, msg_base, msg_len);
282*7f2fe78bSCy Schubert     if (p == NULL) {
283*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
284*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
285*7f2fe78bSCy Schubert     }
286*7f2fe78bSCy Schubert     msg->token.value = (void *)p;
287*7f2fe78bSCy Schubert     msg->token.length = len;
288*7f2fe78bSCy Schubert 
289*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
290*7f2fe78bSCy Schubert }
291*7f2fe78bSCy Schubert 
292*7f2fe78bSCy Schubert static OM_uint32
parse_verify_message(OM_uint32 * minor,struct k5input * in,const uint8_t * msg_base,size_t msg_len,size_t token_offset,struct verify_message * msg)293*7f2fe78bSCy Schubert parse_verify_message(OM_uint32 *minor, struct k5input *in,
294*7f2fe78bSCy Schubert                      const uint8_t *msg_base, size_t msg_len,
295*7f2fe78bSCy Schubert                      size_t token_offset, struct verify_message *msg)
296*7f2fe78bSCy Schubert {
297*7f2fe78bSCy Schubert     const uint8_t *p;
298*7f2fe78bSCy Schubert     size_t offset, len;
299*7f2fe78bSCy Schubert     uint32_t hdrlen, cksum_scheme;
300*7f2fe78bSCy Schubert 
301*7f2fe78bSCy Schubert     p = k5_input_get_bytes(in, GUID_LENGTH);
302*7f2fe78bSCy Schubert     if (p != NULL)
303*7f2fe78bSCy Schubert         memcpy(msg->scheme, p, GUID_LENGTH);
304*7f2fe78bSCy Schubert 
305*7f2fe78bSCy Schubert     hdrlen = k5_input_get_uint32_le(in);
306*7f2fe78bSCy Schubert     if (hdrlen != CHECKSUM_HEADER_LENGTH) {
307*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
308*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
309*7f2fe78bSCy Schubert     }
310*7f2fe78bSCy Schubert     cksum_scheme = k5_input_get_uint32_le(in);
311*7f2fe78bSCy Schubert     if (cksum_scheme != CHECKSUM_SCHEME_RFC3961) {
312*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME;
313*7f2fe78bSCy Schubert         return GSS_S_UNAVAILABLE;
314*7f2fe78bSCy Schubert     }
315*7f2fe78bSCy Schubert     msg->cksum_type = k5_input_get_uint32_le(in);
316*7f2fe78bSCy Schubert 
317*7f2fe78bSCy Schubert     offset = k5_input_get_uint32_le(in);
318*7f2fe78bSCy Schubert     len = k5_input_get_uint32_le(in);
319*7f2fe78bSCy Schubert     msg->cksum = vector_base(offset, len, 1, msg_base, msg_len);
320*7f2fe78bSCy Schubert     msg->cksum_len = len;
321*7f2fe78bSCy Schubert     if (msg->cksum == NULL) {
322*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
323*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
324*7f2fe78bSCy Schubert     }
325*7f2fe78bSCy Schubert 
326*7f2fe78bSCy Schubert     msg->offset_in_token = token_offset;
327*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
328*7f2fe78bSCy Schubert }
329*7f2fe78bSCy Schubert 
330*7f2fe78bSCy Schubert static OM_uint32
parse_alert_message(OM_uint32 * minor,struct k5input * in,const uint8_t * msg_base,size_t msg_len,struct alert_message * msg)331*7f2fe78bSCy Schubert parse_alert_message(OM_uint32 *minor, struct k5input *in,
332*7f2fe78bSCy Schubert                     const uint8_t *msg_base, size_t msg_len,
333*7f2fe78bSCy Schubert                     struct alert_message *msg)
334*7f2fe78bSCy Schubert {
335*7f2fe78bSCy Schubert     const uint8_t *p;
336*7f2fe78bSCy Schubert     uint32_t atype, reason;
337*7f2fe78bSCy Schubert     size_t alerts_offset, nalerts, value_offset, value_len, i;
338*7f2fe78bSCy Schubert     struct k5input alerts_in, pulse_in;
339*7f2fe78bSCy Schubert 
340*7f2fe78bSCy Schubert     p = k5_input_get_bytes(in, GUID_LENGTH);
341*7f2fe78bSCy Schubert     if (p != NULL)
342*7f2fe78bSCy Schubert         memcpy(msg->scheme, p, GUID_LENGTH);
343*7f2fe78bSCy Schubert     (void)k5_input_get_uint32_le(in);  /* skip over ErrorCode */
344*7f2fe78bSCy Schubert     alerts_offset = k5_input_get_uint32_le(in);
345*7f2fe78bSCy Schubert     nalerts = k5_input_get_uint32_le(in);
346*7f2fe78bSCy Schubert     p = vector_base(alerts_offset, nalerts, ALERT_LENGTH, msg_base, msg_len);
347*7f2fe78bSCy Schubert     if (p == NULL) {
348*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
349*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
350*7f2fe78bSCy Schubert     }
351*7f2fe78bSCy Schubert 
352*7f2fe78bSCy Schubert     /* Look for a VERIFY_NO_KEY pulse alert in the alerts vector. */
353*7f2fe78bSCy Schubert     msg->verify_no_key = FALSE;
354*7f2fe78bSCy Schubert     k5_input_init(&alerts_in, p, nalerts * ALERT_LENGTH);
355*7f2fe78bSCy Schubert     for (i = 0; i < nalerts; i++) {
356*7f2fe78bSCy Schubert         atype = k5_input_get_uint32_le(&alerts_in);
357*7f2fe78bSCy Schubert         value_offset = k5_input_get_uint32_le(&alerts_in);
358*7f2fe78bSCy Schubert         value_len = k5_input_get_uint32_le(&alerts_in);
359*7f2fe78bSCy Schubert         p = vector_base(value_offset, value_len, 1, msg_base, msg_len);
360*7f2fe78bSCy Schubert         if (p == NULL) {
361*7f2fe78bSCy Schubert             *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
362*7f2fe78bSCy Schubert             return GSS_S_DEFECTIVE_TOKEN;
363*7f2fe78bSCy Schubert         }
364*7f2fe78bSCy Schubert 
365*7f2fe78bSCy Schubert         if (atype == ALERT_TYPE_PULSE && value_len >= ALERT_PULSE_LENGTH) {
366*7f2fe78bSCy Schubert             k5_input_init(&pulse_in, p, value_len);
367*7f2fe78bSCy Schubert             (void)k5_input_get_uint32_le(&pulse_in);  /* skip header length */
368*7f2fe78bSCy Schubert             reason = k5_input_get_uint32_le(&pulse_in);
369*7f2fe78bSCy Schubert             if (reason == ALERT_VERIFY_NO_KEY)
370*7f2fe78bSCy Schubert                 msg->verify_no_key = TRUE;
371*7f2fe78bSCy Schubert         }
372*7f2fe78bSCy Schubert     }
373*7f2fe78bSCy Schubert 
374*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
375*7f2fe78bSCy Schubert }
376*7f2fe78bSCy Schubert 
377*7f2fe78bSCy Schubert static OM_uint32
parse_message(OM_uint32 * minor,spnego_gss_ctx_id_t ctx,struct k5input * in,const uint8_t * token_base,struct negoex_message * msg)378*7f2fe78bSCy Schubert parse_message(OM_uint32 *minor, spnego_gss_ctx_id_t ctx, struct k5input *in,
379*7f2fe78bSCy Schubert               const uint8_t *token_base, struct negoex_message *msg)
380*7f2fe78bSCy Schubert {
381*7f2fe78bSCy Schubert     OM_uint32 major;
382*7f2fe78bSCy Schubert     const uint8_t *msg_base = in->ptr, *conv_id;
383*7f2fe78bSCy Schubert     size_t token_remaining = in->len, header_len, msg_len;
384*7f2fe78bSCy Schubert     uint64_t signature;
385*7f2fe78bSCy Schubert     uint32_t type, seqnum;
386*7f2fe78bSCy Schubert 
387*7f2fe78bSCy Schubert     signature = k5_input_get_uint64_le(in);
388*7f2fe78bSCy Schubert     type = k5_input_get_uint32_le(in);
389*7f2fe78bSCy Schubert     seqnum = k5_input_get_uint32_le(in);
390*7f2fe78bSCy Schubert     header_len = k5_input_get_uint32_le(in);
391*7f2fe78bSCy Schubert     msg_len = k5_input_get_uint32_le(in);
392*7f2fe78bSCy Schubert     conv_id = k5_input_get_bytes(in, GUID_LENGTH);
393*7f2fe78bSCy Schubert 
394*7f2fe78bSCy Schubert     if (in->status || msg_len > token_remaining || header_len > msg_len) {
395*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
396*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
397*7f2fe78bSCy Schubert     }
398*7f2fe78bSCy Schubert     if (signature != MESSAGE_SIGNATURE) {
399*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE;
400*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
401*7f2fe78bSCy Schubert     }
402*7f2fe78bSCy Schubert     if (seqnum != ctx->negoex_seqnum) {
403*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE;
404*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
405*7f2fe78bSCy Schubert     }
406*7f2fe78bSCy Schubert     if (seqnum == 0) {
407*7f2fe78bSCy Schubert         memcpy(ctx->negoex_conv_id, conv_id, GUID_LENGTH);
408*7f2fe78bSCy Schubert     } else if (!GUID_EQ(conv_id, ctx->negoex_conv_id)) {
409*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_CONVERSATION_ID;
410*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
411*7f2fe78bSCy Schubert     }
412*7f2fe78bSCy Schubert 
413*7f2fe78bSCy Schubert     /* Restrict the input region to the header. */
414*7f2fe78bSCy Schubert     in->len = header_len - (in->ptr - msg_base);
415*7f2fe78bSCy Schubert 
416*7f2fe78bSCy Schubert     msg->type = type;
417*7f2fe78bSCy Schubert     if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO) {
418*7f2fe78bSCy Schubert         major = parse_nego_message(minor, in, msg_base, msg_len, &msg->u.n);
419*7f2fe78bSCy Schubert     } else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||
420*7f2fe78bSCy Schubert                type == CHALLENGE || type == AP_REQUEST) {
421*7f2fe78bSCy Schubert         major = parse_exchange_message(minor, in, msg_base, msg_len,
422*7f2fe78bSCy Schubert                                        &msg->u.e);
423*7f2fe78bSCy Schubert     } else if (type == VERIFY) {
424*7f2fe78bSCy Schubert         major = parse_verify_message(minor, in, msg_base, msg_len,
425*7f2fe78bSCy Schubert                                      msg_base - token_base, &msg->u.v);
426*7f2fe78bSCy Schubert     } else if (type == ALERT) {
427*7f2fe78bSCy Schubert         major = parse_alert_message(minor, in, msg_base, msg_len, &msg->u.a);
428*7f2fe78bSCy Schubert     } else {
429*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_TYPE;
430*7f2fe78bSCy Schubert         return GSS_S_DEFECTIVE_TOKEN;
431*7f2fe78bSCy Schubert     }
432*7f2fe78bSCy Schubert     if (major != GSS_S_COMPLETE)
433*7f2fe78bSCy Schubert         return major;
434*7f2fe78bSCy Schubert 
435*7f2fe78bSCy Schubert     /* Reset the input buffer to the remainder of the token. */
436*7f2fe78bSCy Schubert     if (!in->status)
437*7f2fe78bSCy Schubert         k5_input_init(in, msg_base + msg_len, token_remaining - msg_len);
438*7f2fe78bSCy Schubert 
439*7f2fe78bSCy Schubert     ctx->negoex_seqnum++;
440*7f2fe78bSCy Schubert     trace_received_message(ctx, msg);
441*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
442*7f2fe78bSCy Schubert }
443*7f2fe78bSCy Schubert 
444*7f2fe78bSCy Schubert /*
445*7f2fe78bSCy Schubert  * Parse token into an array of negoex_message structures.  All pointer fields
446*7f2fe78bSCy Schubert  * within the parsed messages are aliases into token, so the result can be
447*7f2fe78bSCy Schubert  * freed with free().  An unknown protocol version, a critical extension, or an
448*7f2fe78bSCy Schubert  * unknown checksum scheme will cause a parsing failure.  Increment the
449*7f2fe78bSCy Schubert  * sequence number in ctx for each message, and record and check the
450*7f2fe78bSCy Schubert  * conversation ID in ctx as appropriate.
451*7f2fe78bSCy Schubert  */
452*7f2fe78bSCy Schubert OM_uint32
negoex_parse_token(OM_uint32 * minor,spnego_gss_ctx_id_t ctx,gss_const_buffer_t token,struct negoex_message ** messages_out,size_t * count_out)453*7f2fe78bSCy Schubert negoex_parse_token(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
454*7f2fe78bSCy Schubert                    gss_const_buffer_t token,
455*7f2fe78bSCy Schubert                    struct negoex_message **messages_out, size_t *count_out)
456*7f2fe78bSCy Schubert {
457*7f2fe78bSCy Schubert     OM_uint32 major = GSS_S_COMPLETE;
458*7f2fe78bSCy Schubert     size_t count = 0;
459*7f2fe78bSCy Schubert     struct k5input in;
460*7f2fe78bSCy Schubert     struct negoex_message *messages = NULL, *newptr;
461*7f2fe78bSCy Schubert 
462*7f2fe78bSCy Schubert     *messages_out = NULL;
463*7f2fe78bSCy Schubert     *count_out = 0;
464*7f2fe78bSCy Schubert     assert(token != GSS_C_NO_BUFFER);
465*7f2fe78bSCy Schubert     k5_input_init(&in, token->value, token->length);
466*7f2fe78bSCy Schubert 
467*7f2fe78bSCy Schubert     while (in.status == 0 && in.len > 0) {
468*7f2fe78bSCy Schubert         newptr = realloc(messages, (count + 1) * sizeof(*newptr));
469*7f2fe78bSCy Schubert         if (newptr == NULL) {
470*7f2fe78bSCy Schubert             free(messages);
471*7f2fe78bSCy Schubert             *minor = ENOMEM;
472*7f2fe78bSCy Schubert             return GSS_S_FAILURE;
473*7f2fe78bSCy Schubert         }
474*7f2fe78bSCy Schubert         messages = newptr;
475*7f2fe78bSCy Schubert 
476*7f2fe78bSCy Schubert         major = parse_message(minor, ctx, &in, token->value, &messages[count]);
477*7f2fe78bSCy Schubert         if (major != GSS_S_COMPLETE)
478*7f2fe78bSCy Schubert             break;
479*7f2fe78bSCy Schubert 
480*7f2fe78bSCy Schubert         count++;
481*7f2fe78bSCy Schubert     }
482*7f2fe78bSCy Schubert 
483*7f2fe78bSCy Schubert     if (in.status) {
484*7f2fe78bSCy Schubert         *minor = ERR_NEGOEX_INVALID_MESSAGE_SIZE;
485*7f2fe78bSCy Schubert         major = GSS_S_DEFECTIVE_TOKEN;
486*7f2fe78bSCy Schubert     }
487*7f2fe78bSCy Schubert     if (major != GSS_S_COMPLETE) {
488*7f2fe78bSCy Schubert         free(messages);
489*7f2fe78bSCy Schubert         return major;
490*7f2fe78bSCy Schubert     }
491*7f2fe78bSCy Schubert 
492*7f2fe78bSCy Schubert     *messages_out = messages;
493*7f2fe78bSCy Schubert     *count_out = count;
494*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
495*7f2fe78bSCy Schubert }
496*7f2fe78bSCy Schubert 
497*7f2fe78bSCy Schubert static struct negoex_message *
locate_message(struct negoex_message * messages,size_t nmessages,enum message_type type)498*7f2fe78bSCy Schubert locate_message(struct negoex_message *messages, size_t nmessages,
499*7f2fe78bSCy Schubert                enum message_type type)
500*7f2fe78bSCy Schubert {
501*7f2fe78bSCy Schubert     uint32_t i;
502*7f2fe78bSCy Schubert 
503*7f2fe78bSCy Schubert     for (i = 0; i < nmessages; i++) {
504*7f2fe78bSCy Schubert         if (messages[i].type == type)
505*7f2fe78bSCy Schubert             return &messages[i];
506*7f2fe78bSCy Schubert     }
507*7f2fe78bSCy Schubert 
508*7f2fe78bSCy Schubert     return NULL;
509*7f2fe78bSCy Schubert }
510*7f2fe78bSCy Schubert 
511*7f2fe78bSCy Schubert struct nego_message *
negoex_locate_nego_message(struct negoex_message * messages,size_t nmessages,enum message_type type)512*7f2fe78bSCy Schubert negoex_locate_nego_message(struct negoex_message *messages, size_t nmessages,
513*7f2fe78bSCy Schubert                            enum message_type type)
514*7f2fe78bSCy Schubert {
515*7f2fe78bSCy Schubert     struct negoex_message *msg = locate_message(messages, nmessages, type);
516*7f2fe78bSCy Schubert 
517*7f2fe78bSCy Schubert     return (msg == NULL) ? NULL : &msg->u.n;
518*7f2fe78bSCy Schubert }
519*7f2fe78bSCy Schubert 
520*7f2fe78bSCy Schubert struct exchange_message *
negoex_locate_exchange_message(struct negoex_message * messages,size_t nmessages,enum message_type type)521*7f2fe78bSCy Schubert negoex_locate_exchange_message(struct negoex_message *messages,
522*7f2fe78bSCy Schubert                                size_t nmessages, enum message_type type)
523*7f2fe78bSCy Schubert {
524*7f2fe78bSCy Schubert     struct negoex_message *msg = locate_message(messages, nmessages, type);
525*7f2fe78bSCy Schubert 
526*7f2fe78bSCy Schubert     return (msg == NULL) ? NULL : &msg->u.e;
527*7f2fe78bSCy Schubert }
528*7f2fe78bSCy Schubert 
529*7f2fe78bSCy Schubert struct verify_message *
negoex_locate_verify_message(struct negoex_message * messages,size_t nmessages)530*7f2fe78bSCy Schubert negoex_locate_verify_message(struct negoex_message *messages,
531*7f2fe78bSCy Schubert                              size_t nmessages)
532*7f2fe78bSCy Schubert {
533*7f2fe78bSCy Schubert     struct negoex_message *msg = locate_message(messages, nmessages, VERIFY);
534*7f2fe78bSCy Schubert 
535*7f2fe78bSCy Schubert     return (msg == NULL) ? NULL : &msg->u.v;
536*7f2fe78bSCy Schubert }
537*7f2fe78bSCy Schubert 
538*7f2fe78bSCy Schubert struct alert_message *
negoex_locate_alert_message(struct negoex_message * messages,size_t nmessages)539*7f2fe78bSCy Schubert negoex_locate_alert_message(struct negoex_message *messages, size_t nmessages)
540*7f2fe78bSCy Schubert {
541*7f2fe78bSCy Schubert     struct negoex_message *msg = locate_message(messages, nmessages, ALERT);
542*7f2fe78bSCy Schubert 
543*7f2fe78bSCy Schubert     return (msg == NULL) ? NULL : &msg->u.a;
544*7f2fe78bSCy Schubert }
545*7f2fe78bSCy Schubert 
546*7f2fe78bSCy Schubert /*
547*7f2fe78bSCy Schubert  * Add the encoding of a MESSAGE_HEADER structure to buf, given the number of
548*7f2fe78bSCy Schubert  * bytes of the payload following the full header.  Increment the sequence
549*7f2fe78bSCy Schubert  * number in ctx.  Set *payload_start_out to the position of the payload within
550*7f2fe78bSCy Schubert  * the message.
551*7f2fe78bSCy Schubert  */
552*7f2fe78bSCy Schubert static void
put_message_header(spnego_gss_ctx_id_t ctx,enum message_type type,uint32_t payload_len,uint32_t * payload_start_out)553*7f2fe78bSCy Schubert put_message_header(spnego_gss_ctx_id_t ctx, enum message_type type,
554*7f2fe78bSCy Schubert                    uint32_t payload_len, uint32_t *payload_start_out)
555*7f2fe78bSCy Schubert {
556*7f2fe78bSCy Schubert     size_t header_len;
557*7f2fe78bSCy Schubert 
558*7f2fe78bSCy Schubert     if (type == INITIATOR_NEGO || type == ACCEPTOR_NEGO)
559*7f2fe78bSCy Schubert         header_len = NEGO_MESSAGE_HEADER_LENGTH;
560*7f2fe78bSCy Schubert     else if (type == INITIATOR_META_DATA || type == ACCEPTOR_META_DATA ||
561*7f2fe78bSCy Schubert              type == CHALLENGE || type == AP_REQUEST)
562*7f2fe78bSCy Schubert         header_len = EXCHANGE_MESSAGE_HEADER_LENGTH;
563*7f2fe78bSCy Schubert     else if (type == VERIFY)
564*7f2fe78bSCy Schubert         header_len = VERIFY_MESSAGE_HEADER_LENGTH;
565*7f2fe78bSCy Schubert     else if (type == ALERT)
566*7f2fe78bSCy Schubert         header_len = ALERT_MESSAGE_HEADER_LENGTH;
567*7f2fe78bSCy Schubert     else
568*7f2fe78bSCy Schubert         abort();
569*7f2fe78bSCy Schubert 
570*7f2fe78bSCy Schubert     k5_buf_add_uint64_le(&ctx->negoex_transcript, MESSAGE_SIGNATURE);
571*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, type);
572*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, ctx->negoex_seqnum++);
573*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, header_len);
574*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, header_len + payload_len);
575*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, ctx->negoex_conv_id, GUID_LENGTH);
576*7f2fe78bSCy Schubert 
577*7f2fe78bSCy Schubert     *payload_start_out = header_len;
578*7f2fe78bSCy Schubert }
579*7f2fe78bSCy Schubert 
580*7f2fe78bSCy Schubert void
negoex_add_nego_message(spnego_gss_ctx_id_t ctx,enum message_type type,uint8_t random[32])581*7f2fe78bSCy Schubert negoex_add_nego_message(spnego_gss_ctx_id_t ctx, enum message_type type,
582*7f2fe78bSCy Schubert                         uint8_t random[32])
583*7f2fe78bSCy Schubert {
584*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
585*7f2fe78bSCy Schubert     uint32_t payload_start, seqnum = ctx->negoex_seqnum;
586*7f2fe78bSCy Schubert     uint16_t nschemes;
587*7f2fe78bSCy Schubert     struct k5buf buf;
588*7f2fe78bSCy Schubert 
589*7f2fe78bSCy Schubert     nschemes = 0;
590*7f2fe78bSCy Schubert     K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links)
591*7f2fe78bSCy Schubert         nschemes++;
592*7f2fe78bSCy Schubert 
593*7f2fe78bSCy Schubert     put_message_header(ctx, type, nschemes * GUID_LENGTH, &payload_start);
594*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, random, 32);
595*7f2fe78bSCy Schubert     /* ProtocolVersion */
596*7f2fe78bSCy Schubert     k5_buf_add_uint64_le(&ctx->negoex_transcript, 0);
597*7f2fe78bSCy Schubert     /* AuthSchemes vector */
598*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);
599*7f2fe78bSCy Schubert     k5_buf_add_uint16_le(&ctx->negoex_transcript, nschemes);
600*7f2fe78bSCy Schubert     /* Extensions vector */
601*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);
602*7f2fe78bSCy Schubert     k5_buf_add_uint16_le(&ctx->negoex_transcript, 0);
603*7f2fe78bSCy Schubert     /* Four bytes of padding to reach a multiple of 8 bytes. */
604*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0", 4);
605*7f2fe78bSCy Schubert 
606*7f2fe78bSCy Schubert     /* Payload (auth schemes); also build guid string for tracing. */
607*7f2fe78bSCy Schubert     k5_buf_init_dynamic(&buf);
608*7f2fe78bSCy Schubert     K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
609*7f2fe78bSCy Schubert         k5_buf_add_len(&ctx->negoex_transcript, mech->scheme, GUID_LENGTH);
610*7f2fe78bSCy Schubert         add_guid(&buf, mech->scheme);
611*7f2fe78bSCy Schubert         k5_buf_add(&buf, " ");
612*7f2fe78bSCy Schubert     }
613*7f2fe78bSCy Schubert 
614*7f2fe78bSCy Schubert     if (buf.len > 0) {
615*7f2fe78bSCy Schubert         k5_buf_truncate(&buf, buf.len - 1);
616*7f2fe78bSCy Schubert         TRACE_NEGOEX_OUTGOING(ctx->kctx, seqnum, typestr(type),
617*7f2fe78bSCy Schubert                               k5_buf_cstring(&buf));
618*7f2fe78bSCy Schubert         k5_buf_free(&buf);
619*7f2fe78bSCy Schubert     }
620*7f2fe78bSCy Schubert }
621*7f2fe78bSCy Schubert 
622*7f2fe78bSCy Schubert void
negoex_add_exchange_message(spnego_gss_ctx_id_t ctx,enum message_type type,const auth_scheme scheme,gss_buffer_t token)623*7f2fe78bSCy Schubert negoex_add_exchange_message(spnego_gss_ctx_id_t ctx, enum message_type type,
624*7f2fe78bSCy Schubert                             const auth_scheme scheme, gss_buffer_t token)
625*7f2fe78bSCy Schubert {
626*7f2fe78bSCy Schubert     uint32_t payload_start;
627*7f2fe78bSCy Schubert 
628*7f2fe78bSCy Schubert     put_message_header(ctx, type, token->length, &payload_start);
629*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);
630*7f2fe78bSCy Schubert     /* Exchange byte vector */
631*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);
632*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, token->length);
633*7f2fe78bSCy Schubert     /* Payload (token) */
634*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, token->value, token->length);
635*7f2fe78bSCy Schubert 
636*7f2fe78bSCy Schubert     trace_outgoing_message(ctx, type, scheme);
637*7f2fe78bSCy Schubert }
638*7f2fe78bSCy Schubert 
639*7f2fe78bSCy Schubert void
negoex_add_verify_message(spnego_gss_ctx_id_t ctx,const auth_scheme scheme,uint32_t cksum_type,const uint8_t * cksum,uint32_t cksum_len)640*7f2fe78bSCy Schubert negoex_add_verify_message(spnego_gss_ctx_id_t ctx, const auth_scheme scheme,
641*7f2fe78bSCy Schubert                           uint32_t cksum_type, const uint8_t *cksum,
642*7f2fe78bSCy Schubert                           uint32_t cksum_len)
643*7f2fe78bSCy Schubert {
644*7f2fe78bSCy Schubert     uint32_t payload_start;
645*7f2fe78bSCy Schubert 
646*7f2fe78bSCy Schubert     put_message_header(ctx, VERIFY, cksum_len, &payload_start);
647*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);
648*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, CHECKSUM_HEADER_LENGTH);
649*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, CHECKSUM_SCHEME_RFC3961);
650*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, cksum_type);
651*7f2fe78bSCy Schubert     /* ChecksumValue vector */
652*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);
653*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, cksum_len);
654*7f2fe78bSCy Schubert     /* Four bytes of padding to reach a multiple of 8 bytes. */
655*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0", 4);
656*7f2fe78bSCy Schubert     /* Payload (checksum contents) */
657*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, cksum, cksum_len);
658*7f2fe78bSCy Schubert 
659*7f2fe78bSCy Schubert     trace_outgoing_message(ctx, VERIFY, scheme);
660*7f2fe78bSCy Schubert }
661*7f2fe78bSCy Schubert 
662*7f2fe78bSCy Schubert /* Add an ALERT_MESSAGE containing a single ALERT_TYPE_PULSE alert with the
663*7f2fe78bSCy Schubert  * reason ALERT_VERIFY_NO_KEY. */
664*7f2fe78bSCy Schubert void
negoex_add_verify_no_key_alert(spnego_gss_ctx_id_t ctx,const auth_scheme scheme)665*7f2fe78bSCy Schubert negoex_add_verify_no_key_alert(spnego_gss_ctx_id_t ctx,
666*7f2fe78bSCy Schubert                                const auth_scheme scheme)
667*7f2fe78bSCy Schubert {
668*7f2fe78bSCy Schubert     uint32_t payload_start;
669*7f2fe78bSCy Schubert 
670*7f2fe78bSCy Schubert     put_message_header(ctx, ALERT, ALERT_LENGTH + ALERT_PULSE_LENGTH,
671*7f2fe78bSCy Schubert                        &payload_start);
672*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, scheme, GUID_LENGTH);
673*7f2fe78bSCy Schubert     /* ErrorCode */
674*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, 0);
675*7f2fe78bSCy Schubert     /* Alerts vector */
676*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, payload_start);
677*7f2fe78bSCy Schubert     k5_buf_add_uint16_le(&ctx->negoex_transcript, 1);
678*7f2fe78bSCy Schubert     /* Six bytes of padding to reach a multiple of 8 bytes. */
679*7f2fe78bSCy Schubert     k5_buf_add_len(&ctx->negoex_transcript, "\0\0\0\0\0\0", 6);
680*7f2fe78bSCy Schubert     /* Payload part 1: a single ALERT element */
681*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_TYPE_PULSE);
682*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript,
683*7f2fe78bSCy Schubert                          payload_start + ALERT_LENGTH);
684*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_PULSE_LENGTH);
685*7f2fe78bSCy Schubert     /* Payload part 2: ALERT_PULSE */
686*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_PULSE_LENGTH);
687*7f2fe78bSCy Schubert     k5_buf_add_uint32_le(&ctx->negoex_transcript, ALERT_VERIFY_NO_KEY);
688*7f2fe78bSCy Schubert 
689*7f2fe78bSCy Schubert     trace_outgoing_message(ctx, ALERT, scheme);
690*7f2fe78bSCy Schubert }
691*7f2fe78bSCy Schubert 
692*7f2fe78bSCy Schubert static void
release_auth_mech(struct negoex_auth_mech * mech)693*7f2fe78bSCy Schubert release_auth_mech(struct negoex_auth_mech *mech)
694*7f2fe78bSCy Schubert {
695*7f2fe78bSCy Schubert     OM_uint32 tmpmin;
696*7f2fe78bSCy Schubert 
697*7f2fe78bSCy Schubert     if (mech == NULL)
698*7f2fe78bSCy Schubert         return;
699*7f2fe78bSCy Schubert 
700*7f2fe78bSCy Schubert     gss_delete_sec_context(&tmpmin, &mech->mech_context, NULL);
701*7f2fe78bSCy Schubert     generic_gss_release_oid(&tmpmin, &mech->oid);
702*7f2fe78bSCy Schubert     gss_release_buffer(&tmpmin, &mech->metadata);
703*7f2fe78bSCy Schubert     krb5_free_keyblock_contents(NULL, &mech->key);
704*7f2fe78bSCy Schubert     krb5_free_keyblock_contents(NULL, &mech->verify_key);
705*7f2fe78bSCy Schubert 
706*7f2fe78bSCy Schubert     free(mech);
707*7f2fe78bSCy Schubert }
708*7f2fe78bSCy Schubert 
709*7f2fe78bSCy Schubert void
negoex_delete_auth_mech(spnego_gss_ctx_id_t ctx,struct negoex_auth_mech * mech)710*7f2fe78bSCy Schubert negoex_delete_auth_mech(spnego_gss_ctx_id_t ctx,
711*7f2fe78bSCy Schubert                         struct negoex_auth_mech *mech)
712*7f2fe78bSCy Schubert {
713*7f2fe78bSCy Schubert     K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
714*7f2fe78bSCy Schubert     release_auth_mech(mech);
715*7f2fe78bSCy Schubert }
716*7f2fe78bSCy Schubert 
717*7f2fe78bSCy Schubert /* Remove all auth mech entries except for mech from ctx->mechs. */
718*7f2fe78bSCy Schubert void
negoex_select_auth_mech(spnego_gss_ctx_id_t ctx,struct negoex_auth_mech * mech)719*7f2fe78bSCy Schubert negoex_select_auth_mech(spnego_gss_ctx_id_t ctx,
720*7f2fe78bSCy Schubert                         struct negoex_auth_mech *mech)
721*7f2fe78bSCy Schubert {
722*7f2fe78bSCy Schubert     assert(mech != NULL);
723*7f2fe78bSCy Schubert     K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
724*7f2fe78bSCy Schubert     release_all_mechs(ctx);
725*7f2fe78bSCy Schubert     K5_TAILQ_INSERT_HEAD(&ctx->negoex_mechs, mech, links);
726*7f2fe78bSCy Schubert }
727*7f2fe78bSCy Schubert 
728*7f2fe78bSCy Schubert OM_uint32
negoex_add_auth_mech(OM_uint32 * minor,spnego_gss_ctx_id_t ctx,gss_const_OID oid,auth_scheme scheme)729*7f2fe78bSCy Schubert negoex_add_auth_mech(OM_uint32 *minor, spnego_gss_ctx_id_t ctx,
730*7f2fe78bSCy Schubert                      gss_const_OID oid, auth_scheme scheme)
731*7f2fe78bSCy Schubert {
732*7f2fe78bSCy Schubert     OM_uint32 major;
733*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
734*7f2fe78bSCy Schubert 
735*7f2fe78bSCy Schubert     mech = calloc(1, sizeof(*mech));
736*7f2fe78bSCy Schubert     if (mech == NULL) {
737*7f2fe78bSCy Schubert         *minor = ENOMEM;
738*7f2fe78bSCy Schubert         return GSS_S_FAILURE;
739*7f2fe78bSCy Schubert     }
740*7f2fe78bSCy Schubert 
741*7f2fe78bSCy Schubert     major = generic_gss_copy_oid(minor, (gss_OID)oid, &mech->oid);
742*7f2fe78bSCy Schubert     if (major != GSS_S_COMPLETE) {
743*7f2fe78bSCy Schubert         free(mech);
744*7f2fe78bSCy Schubert         return major;
745*7f2fe78bSCy Schubert     }
746*7f2fe78bSCy Schubert 
747*7f2fe78bSCy Schubert     memcpy(mech->scheme, scheme, GUID_LENGTH);
748*7f2fe78bSCy Schubert 
749*7f2fe78bSCy Schubert     K5_TAILQ_INSERT_TAIL(&ctx->negoex_mechs, mech, links);
750*7f2fe78bSCy Schubert 
751*7f2fe78bSCy Schubert     *minor = 0;
752*7f2fe78bSCy Schubert     return GSS_S_COMPLETE;
753*7f2fe78bSCy Schubert }
754*7f2fe78bSCy Schubert 
755*7f2fe78bSCy Schubert struct negoex_auth_mech *
negoex_locate_auth_scheme(spnego_gss_ctx_id_t ctx,const auth_scheme scheme)756*7f2fe78bSCy Schubert negoex_locate_auth_scheme(spnego_gss_ctx_id_t ctx, const auth_scheme scheme)
757*7f2fe78bSCy Schubert {
758*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
759*7f2fe78bSCy Schubert 
760*7f2fe78bSCy Schubert     K5_TAILQ_FOREACH(mech, &ctx->negoex_mechs, links) {
761*7f2fe78bSCy Schubert         if (GUID_EQ(mech->scheme, scheme))
762*7f2fe78bSCy Schubert             return mech;
763*7f2fe78bSCy Schubert     }
764*7f2fe78bSCy Schubert 
765*7f2fe78bSCy Schubert     return NULL;
766*7f2fe78bSCy Schubert }
767*7f2fe78bSCy Schubert 
768*7f2fe78bSCy Schubert /* Prune ctx->mechs to the schemes present in schemes, and reorder them to
769*7f2fe78bSCy Schubert  * match its order. */
770*7f2fe78bSCy Schubert void
negoex_common_auth_schemes(spnego_gss_ctx_id_t ctx,const uint8_t * schemes,uint16_t nschemes)771*7f2fe78bSCy Schubert negoex_common_auth_schemes(spnego_gss_ctx_id_t ctx,
772*7f2fe78bSCy Schubert                            const uint8_t *schemes, uint16_t nschemes)
773*7f2fe78bSCy Schubert {
774*7f2fe78bSCy Schubert     struct negoex_mech_list list;
775*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech;
776*7f2fe78bSCy Schubert     uint16_t i;
777*7f2fe78bSCy Schubert 
778*7f2fe78bSCy Schubert     /* Construct a new list in the order of schemes. */
779*7f2fe78bSCy Schubert     K5_TAILQ_INIT(&list);
780*7f2fe78bSCy Schubert     for (i = 0; i < nschemes; i++) {
781*7f2fe78bSCy Schubert         mech = negoex_locate_auth_scheme(ctx, schemes + i * GUID_LENGTH);
782*7f2fe78bSCy Schubert         if (mech == NULL)
783*7f2fe78bSCy Schubert             continue;
784*7f2fe78bSCy Schubert         K5_TAILQ_REMOVE(&ctx->negoex_mechs, mech, links);
785*7f2fe78bSCy Schubert         K5_TAILQ_INSERT_TAIL(&list, mech, links);
786*7f2fe78bSCy Schubert     }
787*7f2fe78bSCy Schubert 
788*7f2fe78bSCy Schubert     /* Release any leftover entries and replace the context list. */
789*7f2fe78bSCy Schubert     release_all_mechs(ctx);
790*7f2fe78bSCy Schubert     K5_TAILQ_CONCAT(&ctx->negoex_mechs, &list, links);
791*7f2fe78bSCy Schubert }
792*7f2fe78bSCy Schubert 
793*7f2fe78bSCy Schubert /* Prune ctx->mechs to the schemes present in schemes, but do not change
794*7f2fe78bSCy Schubert  * their order. */
795*7f2fe78bSCy Schubert void
negoex_restrict_auth_schemes(spnego_gss_ctx_id_t ctx,const uint8_t * schemes,uint16_t nschemes)796*7f2fe78bSCy Schubert negoex_restrict_auth_schemes(spnego_gss_ctx_id_t ctx,
797*7f2fe78bSCy Schubert                              const uint8_t *schemes, uint16_t nschemes)
798*7f2fe78bSCy Schubert {
799*7f2fe78bSCy Schubert     struct negoex_auth_mech *mech, *next;
800*7f2fe78bSCy Schubert     uint16_t i;
801*7f2fe78bSCy Schubert     int found;
802*7f2fe78bSCy Schubert 
803*7f2fe78bSCy Schubert     K5_TAILQ_FOREACH_SAFE(mech, &ctx->negoex_mechs, links, next) {
804*7f2fe78bSCy Schubert         found = FALSE;
805*7f2fe78bSCy Schubert         for (i = 0; i < nschemes && !found; i++) {
806*7f2fe78bSCy Schubert             if (GUID_EQ(mech->scheme, schemes + i * GUID_LENGTH))
807*7f2fe78bSCy Schubert                 found = TRUE;
808*7f2fe78bSCy Schubert         }
809*7f2fe78bSCy Schubert 
810*7f2fe78bSCy Schubert         if (!found)
811*7f2fe78bSCy Schubert             negoex_delete_auth_mech(ctx, mech);
812*7f2fe78bSCy Schubert     }
813*7f2fe78bSCy Schubert }
814