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) {
194 _gss_mg_log(10, "initial context token appears to be for non-standard mechanism");
195 return GSS_S_COMPLETE
;
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
;
206 mech_oid
= _gss_mg_support_mechanism(&mech
);
207 if (mech_oid
== GSS_C_NO_OID
)
208 return GSS_S_COMPLETE
;
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
);
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
,
234 gss_buffer_t output_token
,
235 OM_uint32
*ret_flags
,
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
;
251 defective_token_error
.length
= 0;
252 defective_token_error
.value
= NULL
;
256 *src_name
= GSS_C_NO_NAME
;
258 *mech_type
= GSS_C_NO_OID
;
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
));
270 *minor_status
= ENOMEM
;
271 return (GSS_S_DEFECTIVE_TOKEN
);
273 *context_handle
= (gss_ctx_id_t
)ctx
;
277 major_status
= accumulate_token(ctx
, input_token
);
278 if (major_status
!= GSS_S_COMPLETE
)
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
;
290 if (major_status
== GSS_S_COMPLETE
&& initial
) {
291 major_status
= choose_mech(ctx
);
292 if (major_status
!= GSS_S_COMPLETE
)
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
;
305 acceptor_mc
= GSS_C_NO_CREDENTIAL
;
306 HEIM_TAILQ_FOREACH(ms
, &_gss_mechs
, gm_link
) {
309 major_status
= m
->gm_accept_sec_context(minor_status
,
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
);
334 gss_release_buffer(&junk
, &defective_token_error
);
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
) {
348 acceptor_mc
= (m
->gm_flags
& GM_USE_MG_CRED
) ?
349 acceptor_cred_handle
: mc
->gmc_cred
;
351 major_status
= m
->gm_accept_sec_context(minor_status
,
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
);
372 gss_release_buffer(&junk
, &defective_token_error
);
377 acceptor_mc
= GSS_C_NO_CREDENTIAL
;
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
;
390 HEIM_TAILQ_FOREACH(mc
, &cred
->gc_mc
, gmc_link
)
391 if (mc
->gmc_mech
== m
)
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",
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
;
404 acceptor_mc
= GSS_C_NO_CREDENTIAL
;
408 major_status
= m
->gm_accept_sec_context(minor_status
,
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
);
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 */
436 src_mn
= GSS_C_NO_NAME
;
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
);
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
;
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
;
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
);
468 m
->gm_release_cred(minor_status
, &delegated_mc
);
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
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
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();
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
));
499 *minor_status
= ENOMEM
;
500 gss_delete_sec_context(&junk
, context_handle
, NULL
);
501 return (GSS_S_FAILURE
);
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
);
515 *ret_flags
= mech_ret_flags
;
516 return (major_status
);