lib/gssapi/krb5: implement GSS_C_CHANNEL_BOUND_FLAG for gss_init_sec_context()
[heimdal.git] / lib / gssapi / mech / gss_accept_sec_context.c
bloba4982a7b66f9c940ec30606022f210c8c1f51747
1 /*-
2 * Copyright (c) 2005 Doug Rabson
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 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
24 * SUCH DAMAGE.
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.
60 static OM_uint32
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;
65 gss_buffer_t gci;
66 size_t l;
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;
85 if (len == 0)
86 return GSS_S_COMPLETE;
88 /* Not our DER w/ a length */
89 if (*p != 0x60)
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",
96 ctx->gc_target_len);
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",
102 ctx->gc_target_len);
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)
110 goto done;
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;
121 if (len == 0)
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);
133 gci->length += 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;
142 done:
143 _gss_mg_log(10, "gss-asc: received complete %zu byte token",
144 ctx->gc_target_len);
145 ctx->gc_target_len = 0;
147 return GSS_S_COMPLETE;
150 static void
151 log_oid(const char *str, gss_OID mech)
153 OM_uint32 maj, min;
154 gss_buffer_desc buf;
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,
159 (char *)buf.value);
160 gss_release_buffer(&min, &buf);
164 static OM_uint32
165 choose_mech(struct _gss_context *ctx)
167 gss_OID_desc mech;
168 gss_OID mech_oid;
169 unsigned char *p = ctx->gc_input.value;
170 size_t len = ctx->gc_input.length;
172 if (len == 0) {
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) {
194 _gss_mg_log(10, "initial context token appears to be for non-standard mechanism");
195 return GSS_S_COMPLETE;
197 len -= 2;
198 if ((p[1] & 0x80) || p[1] > len) {
199 _gss_mg_log(10, "mechanism oid in initial context token is too long");
200 return GSS_S_COMPLETE;
202 mech.length = p[1];
203 p += 2;
204 mech.elements = p;
206 mech_oid = _gss_mg_support_mechanism(&mech);
207 if (mech_oid == GSS_C_NO_OID)
208 return GSS_S_COMPLETE;
210 gss_get_mechanism:
212 * If mech_oid == GSS_C_NO_OID then the mech is non-standard
213 * and we have to try all mechs (that we have a cred element
214 * for, if we have a cred).
216 log_oid("mech oid", mech_oid);
217 ctx->gc_mech = __gss_get_mechanism(mech_oid);
218 if (!ctx->gc_mech) {
219 _gss_mg_log(10, "mechanism client used is unknown");
220 return (GSS_S_BAD_MECH);
222 _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name);
223 return GSS_S_COMPLETE;
226 GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
227 gss_accept_sec_context(OM_uint32 *minor_status,
228 gss_ctx_id_t *context_handle,
229 gss_const_cred_id_t acceptor_cred_handle,
230 const gss_buffer_t input_token,
231 const gss_channel_bindings_t input_chan_bindings,
232 gss_name_t *src_name,
233 gss_OID *mech_type,
234 gss_buffer_t output_token,
235 OM_uint32 *ret_flags,
236 OM_uint32 *time_rec,
237 gss_cred_id_t *delegated_cred_handle)
239 OM_uint32 major_status, mech_ret_flags, junk;
240 gssapi_mech_interface m = NULL;
241 struct _gss_context *ctx = (struct _gss_context *) *context_handle;
242 struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
243 struct _gss_mechanism_cred *mc;
244 gss_buffer_desc defective_token_error;
245 gss_const_cred_id_t acceptor_mc;
246 gss_cred_id_t delegated_mc = GSS_C_NO_CREDENTIAL;
247 gss_name_t src_mn = GSS_C_NO_NAME;
248 gss_OID mech_ret_type = GSS_C_NO_OID;
249 int initial;
251 defective_token_error.length = 0;
252 defective_token_error.value = NULL;
254 *minor_status = 0;
255 if (src_name)
256 *src_name = GSS_C_NO_NAME;
257 if (mech_type)
258 *mech_type = GSS_C_NO_OID;
259 if (ret_flags)
260 *ret_flags = 0;
261 if (time_rec)
262 *time_rec = 0;
263 if (delegated_cred_handle)
264 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
265 _mg_buffer_zero(output_token);
267 if (!*context_handle) {
268 ctx = calloc(1, sizeof(*ctx));
269 if (!ctx) {
270 *minor_status = ENOMEM;
271 return (GSS_S_DEFECTIVE_TOKEN);
273 *context_handle = (gss_ctx_id_t)ctx;
274 ctx->gc_initial = 1;
277 major_status = accumulate_token(ctx, input_token);
278 if (major_status != GSS_S_COMPLETE)
279 return major_status;
282 * If we get here, then we have a complete token. Please note
283 * that we may have a major_status of GSS_S_DEFECTIVE_TOKEN. This
287 initial = ctx->gc_initial;
288 ctx->gc_initial = 0;
290 if (major_status == GSS_S_COMPLETE && initial) {
291 major_status = choose_mech(ctx);
292 if (major_status != GSS_S_COMPLETE)
293 return major_status;
295 m = ctx->gc_mech;
297 if (initial && !m && acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
299 * No header, not a standard mechanism. Try all the mechanisms
300 * (because default credential).
302 struct _gss_mech_switch *ms;
304 _gss_load_mech();
305 acceptor_mc = GSS_C_NO_CREDENTIAL;
306 HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
307 m = &ms->gm_mech;
308 mech_ret_flags = 0;
309 major_status = m->gm_accept_sec_context(minor_status,
310 &ctx->gc_ctx,
311 acceptor_mc,
312 &ctx->gc_input,
313 input_chan_bindings,
314 &src_mn,
315 &mech_ret_type,
316 output_token,
317 &mech_ret_flags,
318 time_rec,
319 &delegated_mc);
320 if (major_status == GSS_S_DEFECTIVE_TOKEN) {
322 * Try to retain and output one error token for
323 * GSS_S_DEFECTIVE_TOKEN. The first one.
325 if (output_token->length &&
326 defective_token_error.length == 0) {
327 defective_token_error = *output_token;
328 output_token->length = 0;
329 output_token->value = NULL;
331 gss_release_buffer(&junk, output_token);
332 continue;
334 gss_release_buffer(&junk, &defective_token_error);
335 ctx->gc_mech = m;
336 goto got_one;
338 m = NULL;
339 acceptor_mc = GSS_C_NO_CREDENTIAL;
340 } else if (initial && !m) {
342 * No header, not a standard mechanism. Try all the mechanisms
343 * that we have a credential element for if we have a
344 * non-default credential.
346 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
347 m = mc->gmc_mech;
348 acceptor_mc = (m->gm_flags & GM_USE_MG_CRED) ?
349 acceptor_cred_handle : mc->gmc_cred;
350 mech_ret_flags = 0;
351 major_status = m->gm_accept_sec_context(minor_status,
352 &ctx->gc_ctx,
353 acceptor_mc,
354 &ctx->gc_input,
355 input_chan_bindings,
356 &src_mn,
357 &mech_ret_type,
358 output_token,
359 &mech_ret_flags,
360 time_rec,
361 &delegated_mc);
362 if (major_status == GSS_S_DEFECTIVE_TOKEN) {
363 if (output_token->length &&
364 defective_token_error.length == 0) {
365 defective_token_error = *output_token;
366 output_token->length = 0;
367 output_token->value = NULL;
369 gss_release_buffer(&junk, output_token);
370 continue;
372 gss_release_buffer(&junk, &defective_token_error);
373 ctx->gc_mech = m;
374 goto got_one;
376 m = NULL;
377 acceptor_mc = GSS_C_NO_CREDENTIAL;
380 if (m == NULL) {
381 gss_delete_sec_context(&junk, context_handle, NULL);
382 _gss_mg_log(10, "No mechanism accepted the non-standard initial security context token");
383 *output_token = defective_token_error;
384 return GSS_S_BAD_MECH;
387 if (m->gm_flags & GM_USE_MG_CRED) {
388 acceptor_mc = acceptor_cred_handle;
389 } else if (cred) {
390 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
391 if (mc->gmc_mech == m)
392 break;
393 if (!mc) {
394 gss_delete_sec_context(&junk, context_handle, NULL);
395 _gss_mg_log(10, "gss-asc: client sent mech %s "
396 "but no credential was matching",
397 m->gm_name);
398 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
399 _gss_mg_log(10, "gss-asc: available creds were %s", mc->gmc_mech->gm_name);
400 return (GSS_S_BAD_MECH);
402 acceptor_mc = mc->gmc_cred;
403 } else {
404 acceptor_mc = GSS_C_NO_CREDENTIAL;
407 mech_ret_flags = 0;
408 major_status = m->gm_accept_sec_context(minor_status,
409 &ctx->gc_ctx,
410 acceptor_mc,
411 &ctx->gc_input,
412 input_chan_bindings,
413 &src_mn,
414 &mech_ret_type,
415 output_token,
416 &mech_ret_flags,
417 time_rec,
418 &delegated_mc);
420 got_one:
421 if (major_status != GSS_S_COMPLETE &&
422 major_status != GSS_S_CONTINUE_NEEDED)
424 _gss_mg_error(m, *minor_status);
425 gss_delete_sec_context(&junk, context_handle, NULL);
426 return (major_status);
429 if (mech_type)
430 *mech_type = mech_ret_type;
432 if (src_name && src_mn) {
433 if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
434 /* Negotiation mechanisms use mechglue names as names */
435 *src_name = src_mn;
436 src_mn = GSS_C_NO_NAME;
437 } else {
439 * Make a new name and mark it as an MN.
441 * Note that _gss_create_name() consumes `src_mn' but doesn't
442 * take a pointer, so it can't set it to GSS_C_NO_NAME.
444 struct _gss_name *name = _gss_create_name(src_mn, m);
446 if (!name) {
447 m->gm_release_name(minor_status, &src_mn);
448 gss_delete_sec_context(&junk, context_handle, NULL);
449 return (GSS_S_FAILURE);
451 *src_name = (gss_name_t) name;
452 src_mn = GSS_C_NO_NAME;
454 } else if (src_mn) {
455 if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
456 _gss_mg_release_name((struct _gss_name *)src_mn);
457 src_mn = GSS_C_NO_NAME;
458 } else {
459 m->gm_release_name(minor_status, &src_mn);
463 if (mech_ret_flags & GSS_C_DELEG_FLAG) {
464 if (!delegated_cred_handle) {
465 if (m->gm_flags & GM_USE_MG_CRED)
466 gss_release_cred(minor_status, &delegated_mc);
467 else
468 m->gm_release_cred(minor_status, &delegated_mc);
469 mech_ret_flags &=
470 ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
471 } else if ((m->gm_flags & GM_USE_MG_CRED) != 0) {
473 * If credential is uses mechglue cred, assume it
474 * returns one too.
476 *delegated_cred_handle = delegated_mc;
477 } else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
479 * If the returned mech_type is not the same
480 * as the mech, assume its pseudo mech type
481 * and the returned type is already a
482 * mech-glue object
484 *delegated_cred_handle = delegated_mc;
486 } else if (delegated_mc) {
487 struct _gss_cred *dcred;
488 struct _gss_mechanism_cred *dmc;
490 dcred = _gss_mg_alloc_cred();
491 if (!dcred) {
492 *minor_status = ENOMEM;
493 gss_delete_sec_context(&junk, context_handle, NULL);
494 return (GSS_S_FAILURE);
496 dmc = malloc(sizeof(struct _gss_mechanism_cred));
497 if (!dmc) {
498 free(dcred);
499 *minor_status = ENOMEM;
500 gss_delete_sec_context(&junk, context_handle, NULL);
501 return (GSS_S_FAILURE);
503 dmc->gmc_mech = m;
504 dmc->gmc_mech_oid = &m->gm_mech_oid;
505 dmc->gmc_cred = delegated_mc;
506 HEIM_TAILQ_INSERT_TAIL(&dcred->gc_mc, dmc, gmc_link);
508 *delegated_cred_handle = (gss_cred_id_t) dcred;
512 _gss_mg_log(10, "gss-asc: return %d/%d", (int)major_status, (int)*minor_status);
514 if (ret_flags)
515 *ret_flags = mech_ret_flags;
516 return (major_status);