2 * Copyright (c) 2005 Doug Rabson
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/lib/libgssapi/gss_accept_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
29 #include "mech_locl.h"
32 * accumulate_token() tries to assemble a complete GSS token which may
33 * be fed to it in pieces. Microsoft does this when tokens are too large
34 * in CIFS, e.g. It may occur in other places as well. It is specified in:
36 * [MS-SPNG]: Simple and Protected GSS-API Negotiation
37 * Mechanism (SPNEGO) Extension
39 * https://winprotocoldoc.blob.core.windows.net/
40 * productionwindowsarchives/MS-SPNG/%5bMS-SPNG%5d.pdf
42 * Sections 3.1.5.4 to 3.1.5.9.
44 * We only accumulate if we see the appropriate application tag in the
45 * first byte of 0x60 because in the absence of this, we cannot interpret
46 * the following bytes as a DER length.
48 * We only allocate an accumulating buffer if we detect that the token
49 * is split between multiple packets as this is the uncommon case and
50 * we want to optimise for the common case. If we aren't accumulating,
51 * we simply return success.
53 * Our return value is GSS_S_CONTINUE_NEEDED if we need more input.
54 * We return GSS_S_COMPLETE if we are either finished accumulating or
55 * if we decide that we do not understand this token. We only return
56 * an error if we think that we should understand the token and still
57 * fail to understand it.
61 accumulate_token(struct _gss_context *ctx, gss_buffer_t input_token)
63 unsigned char *p = input_token->value;
64 size_t len = input_token->length;
69 * Token must start with [APPLICATION 0] SEQUENCE.
70 * But if it doesn't assume it is DCE-STYLE Kerberos!
72 if (!ctx->gc_target_len) {
73 free(ctx->gc_free_this);
74 ctx->gc_free_this = NULL;
75 _mg_buffer_zero(&ctx->gc_input);
78 * Let's prepare gc_input for the case where
79 * we aren't accumulating.
82 ctx->gc_input.length = len;
83 ctx->gc_input.value = p;
86 return GSS_S_COMPLETE;
88 /* Not our DER w/ a length */
90 return GSS_S_COMPLETE;
92 if (der_get_length(p+1, len-1, &ctx->gc_target_len, &l) != 0)
93 return GSS_S_DEFECTIVE_TOKEN;
95 _gss_mg_log(10, "gss-asc: DER length: %zu",
98 ctx->gc_oid_offset = l + 1;
99 ctx->gc_target_len += ctx->gc_oid_offset;
101 _gss_mg_log(10, "gss-asc: total length: %zu",
104 if (ctx->gc_target_len == ASN1_INDEFINITE ||
105 ctx->gc_target_len < len)
106 return GSS_S_DEFECTIVE_TOKEN;
108 /* We've got it all, short-circuit the accumulating */
109 if (ctx->gc_target_len == len)
112 _gss_mg_log(10, "gss-asc: accumulating partial token");
114 ctx->gc_input.length = 0;
115 ctx->gc_input.value = calloc(ctx->gc_target_len, 1);
116 if (!ctx->gc_input.value)
117 return GSS_S_FAILURE;
118 ctx->gc_free_this = ctx->gc_input.value;
122 return GSS_S_DEFECTIVE_TOKEN;
124 gci = &ctx->gc_input;
126 if (ctx->gc_target_len > gci->length) {
127 if (gci->length + len > ctx->gc_target_len) {
128 _gss_mg_log(10, "gss-asc: accumulation exceeded "
129 "target length: bailing");
130 return GSS_S_DEFECTIVE_TOKEN;
132 memcpy((char *)gci->value + gci->length, p, len);
136 if (gci->length != ctx->gc_target_len) {
137 _gss_mg_log(10, "gss-asc: collected %zu/%zu bytes",
138 gci->length, ctx->gc_target_len);
139 return GSS_S_CONTINUE_NEEDED;
143 _gss_mg_log(10, "gss-asc: received complete %zu byte token",
145 ctx->gc_target_len = 0;
147 return GSS_S_COMPLETE;
151 log_oid(const char *str, gss_OID mech)
156 maj = gss_oid_to_str(&min, mech, &buf);
157 if (maj == GSS_S_COMPLETE) {
158 _gss_mg_log(10, "%s: %.*s", str, (int)buf.length,
160 gss_release_buffer(&min, &buf);
165 choose_mech(struct _gss_context *ctx)
169 unsigned char *p = ctx->gc_input.value;
170 size_t len = ctx->gc_input.length;
174 * There is the a wierd mode of SPNEGO (in CIFS and
175 * SASL GSS-SPENGO where the first token is zero
176 * length and the acceptor returns a mech_list, lets
177 * hope that is what is happening now.
179 * http://msdn.microsoft.com/en-us/library/cc213114.aspx
180 * "NegTokenInit2 Variation for Server-Initiation"
182 mech_oid = &__gss_spnego_mechanism_oid_desc;
183 goto gss_get_mechanism;
186 p += ctx->gc_oid_offset;
187 len -= ctx->gc_oid_offset;
190 * Decode the OID for the mechanism. Simplify life by
191 * assuming that the OID length is less than 128 bytes.
193 if (len < 2 || *p != 0x06)
195 if ((p[1] & 0x80) || p[1] > (len - 2))
202 mech_oid = _gss_mg_support_mechanism(&mech);
203 if (mech_oid == GSS_C_NO_OID)
204 return GSS_S_COMPLETE;
208 * If mech_oid == GSS_C_NO_OID then the mech is non-standard
209 * and we have to try all mechs (that we have a cred element
210 * for, if we have a cred).
212 if (mech_oid != GSS_C_NO_OID) {
213 log_oid("mech oid", mech_oid);
214 ctx->gc_mech = __gss_get_mechanism(mech_oid);
216 _gss_mg_log(10, "mechanism client used is unknown");
217 return (GSS_S_BAD_MECH);
219 _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name);
220 return GSS_S_COMPLETE;
224 _gss_mg_log(10, "no mech oid found");
225 return GSS_S_COMPLETE;
228 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
229 gss_accept_sec_context(OM_uint32 *minor_status,
230 gss_ctx_id_t *context_handle,
231 gss_const_cred_id_t acceptor_cred_handle,
232 const gss_buffer_t input_token,
233 const gss_channel_bindings_t input_chan_bindings,
234 gss_name_t *src_name,
236 gss_buffer_t output_token,
237 OM_uint32 *ret_flags,
239 gss_cred_id_t *delegated_cred_handle)
241 OM_uint32 major_status, mech_ret_flags, junk;
242 gssapi_mech_interface m = NULL;
243 struct _gss_context *ctx = (struct _gss_context *) *context_handle;
244 struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
245 struct _gss_mechanism_cred *mc;
246 gss_buffer_desc defective_token_error;
247 gss_const_cred_id_t acceptor_mc;
248 gss_cred_id_t delegated_mc = GSS_C_NO_CREDENTIAL;
249 gss_name_t src_mn = GSS_C_NO_NAME;
250 gss_OID mech_ret_type = GSS_C_NO_OID;
253 defective_token_error.length = 0;
254 defective_token_error.value = NULL;
258 *src_name = GSS_C_NO_NAME;
260 *mech_type = GSS_C_NO_OID;
265 if (delegated_cred_handle)
266 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
267 _mg_buffer_zero(output_token);
269 if (!*context_handle) {
270 ctx = calloc(sizeof(*ctx), 1);
272 *minor_status = ENOMEM;
273 return (GSS_S_DEFECTIVE_TOKEN);
275 *context_handle = (gss_ctx_id_t)ctx;
279 major_status = accumulate_token(ctx, input_token);
280 if (major_status != GSS_S_COMPLETE)
284 * If we get here, then we have a complete token. Please note
285 * that we may have a major_status of GSS_S_DEFECTIVE_TOKEN. This
289 initial = ctx->gc_initial;
292 if (major_status == GSS_S_COMPLETE && initial) {
293 major_status = choose_mech(ctx);
294 if (major_status != GSS_S_COMPLETE)
299 if (initial && !m && acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
301 * No header, not a standard mechanism. Try all the mechanisms
302 * (because default credential).
304 struct _gss_mech_switch *ms;
307 acceptor_mc = GSS_C_NO_CREDENTIAL;
308 HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
311 major_status = m->gm_accept_sec_context(minor_status,
322 if (major_status == GSS_S_DEFECTIVE_TOKEN) {
324 * Try to retain and output one error token for
325 * GSS_S_DEFECTIVE_TOKEN. The first one.
327 if (output_token->length &&
328 defective_token_error.length == 0) {
329 defective_token_error = *output_token;
330 output_token->length = 0;
331 output_token->value = NULL;
333 gss_release_buffer(&junk, output_token);
336 gss_release_buffer(&junk, &defective_token_error);
341 acceptor_mc = GSS_C_NO_CREDENTIAL;
342 } else if (initial && !m) {
344 * No header, not a standard mechanism. Try all the mechanisms
345 * that we have a credential element for if we have a
346 * non-default credential.
348 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
350 acceptor_mc = (m->gm_flags & GM_USE_MG_CRED) ?
351 acceptor_cred_handle : mc->gmc_cred;
353 major_status = m->gm_accept_sec_context(minor_status,
364 if (major_status == GSS_S_DEFECTIVE_TOKEN) {
365 if (output_token->length &&
366 defective_token_error.length == 0) {
367 defective_token_error = *output_token;
368 output_token->length = 0;
369 output_token->value = NULL;
371 gss_release_buffer(&junk, output_token);
374 gss_release_buffer(&junk, &defective_token_error);
379 acceptor_mc = GSS_C_NO_CREDENTIAL;
383 gss_delete_sec_context(&junk, context_handle, NULL);
384 _gss_mg_log(10, "No mechanism accepted the non-standard initial security context token");
385 *output_token = defective_token_error;
386 return GSS_S_BAD_MECH;
389 if (m->gm_flags & GM_USE_MG_CRED) {
390 acceptor_mc = acceptor_cred_handle;
392 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
393 if (mc->gmc_mech == m)
396 gss_delete_sec_context(&junk, context_handle, NULL);
397 _gss_mg_log(10, "gss-asc: client sent mech %s "
398 "but no credential was matching",
400 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
401 _gss_mg_log(10, "gss-asc: available creds were %s", mc->gmc_mech->gm_name);
402 return (GSS_S_BAD_MECH);
404 acceptor_mc = mc->gmc_cred;
406 acceptor_mc = GSS_C_NO_CREDENTIAL;
410 major_status = m->gm_accept_sec_context(minor_status,
423 if (major_status != GSS_S_COMPLETE &&
424 major_status != GSS_S_CONTINUE_NEEDED)
426 _gss_mg_error(m, *minor_status);
427 gss_delete_sec_context(&junk, context_handle, NULL);
428 return (major_status);
432 *mech_type = mech_ret_type;
434 if (src_name && src_mn) {
435 if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
436 /* Negotiation mechanisms use mechglue names as names */
438 src_mn = GSS_C_NO_NAME;
441 * Make a new name and mark it as an MN.
443 * Note that _gss_create_name() consumes `src_mn' but doesn't
444 * take a pointer, so it can't set it to GSS_C_NO_NAME.
446 struct _gss_name *name = _gss_create_name(src_mn, m);
449 m->gm_release_name(minor_status, &src_mn);
450 gss_delete_sec_context(&junk, context_handle, NULL);
451 return (GSS_S_FAILURE);
453 *src_name = (gss_name_t) name;
454 src_mn = GSS_C_NO_NAME;
457 if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
458 _gss_mg_release_name((struct _gss_name *)src_mn);
459 src_mn = GSS_C_NO_NAME;
461 m->gm_release_name(minor_status, &src_mn);
465 if (mech_ret_flags & GSS_C_DELEG_FLAG) {
466 if (!delegated_cred_handle) {
467 if (m->gm_flags & GM_USE_MG_CRED)
468 gss_release_cred(minor_status, &delegated_mc);
470 m->gm_release_cred(minor_status, &delegated_mc);
472 ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
473 } else if ((m->gm_flags & GM_USE_MG_CRED) != 0) {
475 * If credential is uses mechglue cred, assume it
478 *delegated_cred_handle = delegated_mc;
479 } else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
481 * If the returned mech_type is not the same
482 * as the mech, assume its pseudo mech type
483 * and the returned type is already a
486 *delegated_cred_handle = delegated_mc;
488 } else if (delegated_mc) {
489 struct _gss_cred *dcred;
490 struct _gss_mechanism_cred *dmc;
492 dcred = _gss_mg_alloc_cred();
494 *minor_status = ENOMEM;
495 gss_delete_sec_context(&junk, context_handle, NULL);
496 return (GSS_S_FAILURE);
498 dmc = malloc(sizeof(struct _gss_mechanism_cred));
501 *minor_status = ENOMEM;
502 gss_delete_sec_context(&junk, context_handle, NULL);
503 return (GSS_S_FAILURE);
506 dmc->gmc_mech_oid = &m->gm_mech_oid;
507 dmc->gmc_cred = delegated_mc;
508 HEIM_TAILQ_INSERT_TAIL(&dcred->gc_mc, dmc, gmc_link);
510 *delegated_cred_handle = (gss_cred_id_t) dcred;
514 _gss_mg_log(10, "gss-asc: return %d/%d", (int)major_status, (int)*minor_status);
517 *ret_flags = mech_ret_flags;
518 return (major_status);