Line data Source code
1 : /*-
2 : * Copyright (c) 2005 Doug Rabson
3 : * All rights reserved.
4 : *
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.
13 : *
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.
25 : *
26 : * $FreeBSD: src/lib/libgssapi/gss_accept_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27 : */
28 :
29 : #include "mech_locl.h"
30 :
31 : /*
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:
35 : *
36 : * [MS-SPNG]: Simple and Protected GSS-API Negotiation
37 : * Mechanism (SPNEGO) Extension
38 : *
39 : * https://winprotocoldoc.blob.core.windows.net/
40 : * productionwindowsarchives/MS-SPNG/%5bMS-SPNG%5d.pdf
41 : *
42 : * Sections 3.1.5.4 to 3.1.5.9.
43 : *
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.
47 : *
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.
52 : *
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.
58 : */
59 :
60 : static OM_uint32
61 74246 : accumulate_token(struct _gss_context *ctx, gss_buffer_t input_token)
62 : {
63 74246 : unsigned char *p = input_token->value;
64 74246 : size_t len = input_token->length;
65 : gss_buffer_t gci;
66 : size_t l;
67 :
68 : /*
69 : * Token must start with [APPLICATION 0] SEQUENCE.
70 : * But if it doesn't assume it is DCE-STYLE Kerberos!
71 : */
72 74246 : if (!ctx->gc_target_len) {
73 74246 : free(ctx->gc_free_this);
74 74246 : ctx->gc_free_this = NULL;
75 74246 : _mg_buffer_zero(&ctx->gc_input);
76 :
77 : /*
78 : * Let's prepare gc_input for the case where
79 : * we aren't accumulating.
80 : */
81 :
82 74246 : ctx->gc_input.length = len;
83 74246 : ctx->gc_input.value = p;
84 :
85 74246 : if (len == 0)
86 0 : return GSS_S_COMPLETE;
87 :
88 : /* Not our DER w/ a length */
89 74246 : if (*p != 0x60)
90 59529 : return GSS_S_COMPLETE;
91 :
92 14717 : if (der_get_length(p+1, len-1, &ctx->gc_target_len, &l) != 0)
93 0 : return GSS_S_DEFECTIVE_TOKEN;
94 :
95 14717 : _gss_mg_log(10, "gss-asc: DER length: %zu",
96 : ctx->gc_target_len);
97 :
98 14717 : ctx->gc_oid_offset = l + 1;
99 14717 : ctx->gc_target_len += ctx->gc_oid_offset;
100 :
101 14717 : _gss_mg_log(10, "gss-asc: total length: %zu",
102 : ctx->gc_target_len);
103 :
104 29434 : if (ctx->gc_target_len == ASN1_INDEFINITE ||
105 14717 : ctx->gc_target_len < len)
106 0 : return GSS_S_DEFECTIVE_TOKEN;
107 :
108 : /* We've got it all, short-circuit the accumulating */
109 14717 : if (ctx->gc_target_len == len)
110 14717 : goto done;
111 :
112 0 : _gss_mg_log(10, "gss-asc: accumulating partial token");
113 :
114 0 : ctx->gc_input.length = 0;
115 0 : ctx->gc_input.value = calloc(ctx->gc_target_len, 1);
116 0 : if (!ctx->gc_input.value)
117 0 : return GSS_S_FAILURE;
118 0 : ctx->gc_free_this = ctx->gc_input.value;
119 : }
120 :
121 0 : if (len == 0)
122 0 : return GSS_S_DEFECTIVE_TOKEN;
123 :
124 0 : gci = &ctx->gc_input;
125 :
126 0 : if (ctx->gc_target_len > gci->length) {
127 0 : if (gci->length + len > ctx->gc_target_len) {
128 0 : _gss_mg_log(10, "gss-asc: accumulation exceeded "
129 : "target length: bailing");
130 0 : return GSS_S_DEFECTIVE_TOKEN;
131 : }
132 0 : memcpy((char *)gci->value + gci->length, p, len);
133 0 : gci->length += len;
134 : }
135 :
136 0 : if (gci->length != ctx->gc_target_len) {
137 0 : _gss_mg_log(10, "gss-asc: collected %zu/%zu bytes",
138 : gci->length, ctx->gc_target_len);
139 0 : return GSS_S_CONTINUE_NEEDED;
140 : }
141 :
142 0 : done:
143 14717 : _gss_mg_log(10, "gss-asc: received complete %zu byte token",
144 : ctx->gc_target_len);
145 14717 : ctx->gc_target_len = 0;
146 :
147 14717 : return GSS_S_COMPLETE;
148 : }
149 :
150 : static void
151 14717 : log_oid(const char *str, gss_OID mech)
152 : {
153 : OM_uint32 maj, min;
154 : gss_buffer_desc buf;
155 :
156 14717 : maj = gss_oid_to_str(&min, mech, &buf);
157 14717 : if (maj == GSS_S_COMPLETE) {
158 14717 : _gss_mg_log(10, "%s: %.*s", str, (int)buf.length,
159 14717 : (char *)buf.value);
160 14717 : gss_release_buffer(&min, &buf);
161 : }
162 14717 : }
163 :
164 : static OM_uint32
165 44495 : choose_mech(struct _gss_context *ctx)
166 : {
167 : gss_OID_desc mech;
168 : gss_OID mech_oid;
169 44495 : unsigned char *p = ctx->gc_input.value;
170 44495 : size_t len = ctx->gc_input.length;
171 :
172 44495 : if (len == 0) {
173 : /*
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.
178 : *
179 : * http://msdn.microsoft.com/en-us/library/cc213114.aspx
180 : * "NegTokenInit2 Variation for Server-Initiation"
181 : */
182 0 : mech_oid = &__gss_spnego_mechanism_oid_desc;
183 0 : goto gss_get_mechanism;
184 : }
185 :
186 44495 : p += ctx->gc_oid_offset;
187 44495 : len -= ctx->gc_oid_offset;
188 :
189 : /*
190 : * Decode the OID for the mechanism. Simplify life by
191 : * assuming that the OID length is less than 128 bytes.
192 : */
193 44495 : if (len < 2 || *p != 0x06)
194 : goto bail;
195 29434 : if ((p[1] & 0x80) || p[1] > (len - 2))
196 : goto bail;
197 14717 : mech.length = p[1];
198 14717 : p += 2;
199 14717 : len -= 2;
200 14717 : mech.elements = p;
201 :
202 14717 : mech_oid = _gss_mg_support_mechanism(&mech);
203 14717 : if (mech_oid == GSS_C_NO_OID)
204 0 : return GSS_S_COMPLETE;
205 :
206 14717 : gss_get_mechanism:
207 : /*
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).
211 : */
212 14717 : if (mech_oid != GSS_C_NO_OID) {
213 14717 : log_oid("mech oid", mech_oid);
214 14717 : ctx->gc_mech = __gss_get_mechanism(mech_oid);
215 14717 : if (!ctx->gc_mech) {
216 0 : _gss_mg_log(10, "mechanism client used is unknown");
217 0 : return (GSS_S_BAD_MECH);
218 : }
219 14717 : _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name);
220 14717 : return GSS_S_COMPLETE;
221 : }
222 :
223 29778 : bail:
224 29778 : _gss_mg_log(10, "no mech oid found");
225 29778 : return GSS_S_COMPLETE;
226 : }
227 :
228 : GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
229 74246 : 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,
235 : gss_OID *mech_type,
236 : gss_buffer_t output_token,
237 : OM_uint32 *ret_flags,
238 : OM_uint32 *time_rec,
239 : gss_cred_id_t *delegated_cred_handle)
240 : {
241 : OM_uint32 major_status, mech_ret_flags, junk;
242 74246 : gssapi_mech_interface m = NULL;
243 74246 : struct _gss_context *ctx = (struct _gss_context *) *context_handle;
244 74246 : 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 74246 : gss_cred_id_t delegated_mc = GSS_C_NO_CREDENTIAL;
249 74246 : gss_name_t src_mn = GSS_C_NO_NAME;
250 74246 : gss_OID mech_ret_type = GSS_C_NO_OID;
251 : int initial;
252 :
253 74246 : defective_token_error.length = 0;
254 74246 : defective_token_error.value = NULL;
255 :
256 74246 : *minor_status = 0;
257 74246 : if (src_name)
258 74246 : *src_name = GSS_C_NO_NAME;
259 74246 : if (mech_type)
260 74246 : *mech_type = GSS_C_NO_OID;
261 74246 : if (ret_flags)
262 74246 : *ret_flags = 0;
263 74246 : if (time_rec)
264 74246 : *time_rec = 0;
265 74246 : if (delegated_cred_handle)
266 74246 : *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
267 74246 : _mg_buffer_zero(output_token);
268 :
269 74246 : if (!*context_handle) {
270 44495 : ctx = calloc(sizeof(*ctx), 1);
271 44495 : if (!ctx) {
272 0 : *minor_status = ENOMEM;
273 0 : return (GSS_S_DEFECTIVE_TOKEN);
274 : }
275 44495 : *context_handle = (gss_ctx_id_t)ctx;
276 44495 : ctx->gc_initial = 1;
277 : }
278 :
279 74246 : major_status = accumulate_token(ctx, input_token);
280 74246 : if (major_status != GSS_S_COMPLETE)
281 0 : return major_status;
282 :
283 : /*
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
286 : *
287 : */
288 :
289 74246 : initial = ctx->gc_initial;
290 74246 : ctx->gc_initial = 0;
291 :
292 74246 : if (major_status == GSS_S_COMPLETE && initial) {
293 44495 : major_status = choose_mech(ctx);
294 44495 : if (major_status != GSS_S_COMPLETE)
295 0 : return major_status;
296 : }
297 74246 : m = ctx->gc_mech;
298 :
299 74246 : if (initial && !m && acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
300 : /*
301 : * No header, not a standard mechanism. Try all the mechanisms
302 : * (because default credential).
303 : */
304 : struct _gss_mech_switch *ms;
305 :
306 0 : _gss_load_mech();
307 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
308 0 : HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
309 0 : m = &ms->gm_mech;
310 0 : mech_ret_flags = 0;
311 0 : major_status = m->gm_accept_sec_context(minor_status,
312 : &ctx->gc_ctx,
313 : acceptor_mc,
314 0 : &ctx->gc_input,
315 : input_chan_bindings,
316 : &src_mn,
317 : &mech_ret_type,
318 : output_token,
319 : &mech_ret_flags,
320 : time_rec,
321 : &delegated_mc);
322 0 : if (major_status == GSS_S_DEFECTIVE_TOKEN) {
323 : /*
324 : * Try to retain and output one error token for
325 : * GSS_S_DEFECTIVE_TOKEN. The first one.
326 : */
327 0 : if (output_token->length &&
328 0 : defective_token_error.length == 0) {
329 0 : defective_token_error = *output_token;
330 0 : output_token->length = 0;
331 0 : output_token->value = NULL;
332 : }
333 0 : gss_release_buffer(&junk, output_token);
334 0 : continue;
335 : }
336 0 : gss_release_buffer(&junk, &defective_token_error);
337 0 : ctx->gc_mech = m;
338 0 : goto got_one;
339 : }
340 0 : m = NULL;
341 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
342 74246 : } else if (initial && !m) {
343 : /*
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.
347 : */
348 59556 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
349 29778 : m = mc->gmc_mech;
350 59556 : acceptor_mc = (m->gm_flags & GM_USE_MG_CRED) ?
351 29778 : acceptor_cred_handle : mc->gmc_cred;
352 29778 : mech_ret_flags = 0;
353 59556 : major_status = m->gm_accept_sec_context(minor_status,
354 : &ctx->gc_ctx,
355 : acceptor_mc,
356 29778 : &ctx->gc_input,
357 : input_chan_bindings,
358 : &src_mn,
359 : &mech_ret_type,
360 : output_token,
361 : &mech_ret_flags,
362 : time_rec,
363 : &delegated_mc);
364 29778 : if (major_status == GSS_S_DEFECTIVE_TOKEN) {
365 0 : if (output_token->length &&
366 0 : defective_token_error.length == 0) {
367 0 : defective_token_error = *output_token;
368 0 : output_token->length = 0;
369 0 : output_token->value = NULL;
370 : }
371 0 : gss_release_buffer(&junk, output_token);
372 0 : continue;
373 : }
374 29778 : gss_release_buffer(&junk, &defective_token_error);
375 29778 : ctx->gc_mech = m;
376 29778 : goto got_one;
377 : }
378 0 : m = NULL;
379 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
380 : }
381 :
382 44468 : if (m == NULL) {
383 0 : gss_delete_sec_context(&junk, context_handle, NULL);
384 0 : _gss_mg_log(10, "No mechanism accepted the non-standard initial security context token");
385 0 : *output_token = defective_token_error;
386 0 : return GSS_S_BAD_MECH;
387 : }
388 :
389 44468 : if (m->gm_flags & GM_USE_MG_CRED) {
390 0 : acceptor_mc = acceptor_cred_handle;
391 44468 : } else if (cred) {
392 44468 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
393 44468 : if (mc->gmc_mech == m)
394 44468 : break;
395 44468 : if (!mc) {
396 0 : gss_delete_sec_context(&junk, context_handle, NULL);
397 0 : _gss_mg_log(10, "gss-asc: client sent mech %s "
398 : "but no credential was matching",
399 : m->gm_name);
400 0 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
401 0 : _gss_mg_log(10, "gss-asc: available creds were %s", mc->gmc_mech->gm_name);
402 0 : return (GSS_S_BAD_MECH);
403 : }
404 44468 : acceptor_mc = mc->gmc_cred;
405 : } else {
406 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
407 : }
408 :
409 44468 : mech_ret_flags = 0;
410 88936 : major_status = m->gm_accept_sec_context(minor_status,
411 : &ctx->gc_ctx,
412 : acceptor_mc,
413 44468 : &ctx->gc_input,
414 : input_chan_bindings,
415 : &src_mn,
416 : &mech_ret_type,
417 : output_token,
418 : &mech_ret_flags,
419 : time_rec,
420 : &delegated_mc);
421 :
422 74246 : got_one:
423 74246 : if (major_status != GSS_S_COMPLETE &&
424 : major_status != GSS_S_CONTINUE_NEEDED)
425 : {
426 29 : _gss_mg_error(m, *minor_status);
427 29 : gss_delete_sec_context(&junk, context_handle, NULL);
428 29 : return (major_status);
429 : }
430 :
431 74217 : if (mech_type)
432 74217 : *mech_type = mech_ret_type;
433 :
434 74217 : if (src_name && src_mn) {
435 148434 : if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
436 : /* Negotiation mechanisms use mechglue names as names */
437 0 : *src_name = src_mn;
438 0 : src_mn = GSS_C_NO_NAME;
439 : } else {
440 : /*
441 : * Make a new name and mark it as an MN.
442 : *
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.
445 : */
446 74217 : struct _gss_name *name = _gss_create_name(src_mn, m);
447 :
448 74217 : if (!name) {
449 0 : m->gm_release_name(minor_status, &src_mn);
450 0 : gss_delete_sec_context(&junk, context_handle, NULL);
451 0 : return (GSS_S_FAILURE);
452 : }
453 74217 : *src_name = (gss_name_t) name;
454 74217 : src_mn = GSS_C_NO_NAME;
455 : }
456 0 : } else if (src_mn) {
457 0 : if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
458 0 : _gss_mg_release_name((struct _gss_name *)src_mn);
459 0 : src_mn = GSS_C_NO_NAME;
460 : } else {
461 0 : m->gm_release_name(minor_status, &src_mn);
462 : }
463 : }
464 :
465 74217 : if (mech_ret_flags & GSS_C_DELEG_FLAG) {
466 41803 : if (!delegated_cred_handle) {
467 0 : if (m->gm_flags & GM_USE_MG_CRED)
468 0 : gss_release_cred(minor_status, &delegated_mc);
469 : else
470 0 : m->gm_release_cred(minor_status, &delegated_mc);
471 0 : mech_ret_flags &=
472 : ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
473 41803 : } else if ((m->gm_flags & GM_USE_MG_CRED) != 0) {
474 : /*
475 : * If credential is uses mechglue cred, assume it
476 : * returns one too.
477 : */
478 0 : *delegated_cred_handle = delegated_mc;
479 41803 : } else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
480 : /*
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
484 : * mech-glue object
485 : */
486 0 : *delegated_cred_handle = delegated_mc;
487 :
488 41803 : } else if (delegated_mc) {
489 : struct _gss_cred *dcred;
490 : struct _gss_mechanism_cred *dmc;
491 :
492 41451 : dcred = _gss_mg_alloc_cred();
493 41451 : if (!dcred) {
494 0 : *minor_status = ENOMEM;
495 0 : gss_delete_sec_context(&junk, context_handle, NULL);
496 0 : return (GSS_S_FAILURE);
497 : }
498 41451 : dmc = malloc(sizeof(struct _gss_mechanism_cred));
499 41451 : if (!dmc) {
500 0 : free(dcred);
501 0 : *minor_status = ENOMEM;
502 0 : gss_delete_sec_context(&junk, context_handle, NULL);
503 0 : return (GSS_S_FAILURE);
504 : }
505 41451 : dmc->gmc_mech = m;
506 41451 : dmc->gmc_mech_oid = &m->gm_mech_oid;
507 41451 : dmc->gmc_cred = delegated_mc;
508 41451 : HEIM_TAILQ_INSERT_TAIL(&dcred->gc_mc, dmc, gmc_link);
509 :
510 41451 : *delegated_cred_handle = (gss_cred_id_t) dcred;
511 : }
512 : }
513 :
514 74217 : _gss_mg_log(10, "gss-asc: return %d/%d", (int)major_status, (int)*minor_status);
515 :
516 74217 : if (ret_flags)
517 74217 : *ret_flags = mech_ret_flags;
518 74217 : return (major_status);
519 : }
|