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