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 84206 : accumulate_token(struct _gss_context *ctx, gss_buffer_t input_token)
62 : {
63 84206 : unsigned char *p = input_token->value;
64 84206 : size_t len = input_token->length;
65 977 : gss_buffer_t gci;
66 977 : 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 84206 : if (!ctx->gc_target_len) {
73 84206 : free(ctx->gc_free_this);
74 84206 : ctx->gc_free_this = NULL;
75 84206 : _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 84206 : ctx->gc_input.length = len;
83 84206 : ctx->gc_input.value = p;
84 :
85 84206 : if (len == 0)
86 0 : return GSS_S_COMPLETE;
87 :
88 : /* Not our DER w/ a length */
89 84206 : if (*p != 0x60)
90 63743 : return GSS_S_COMPLETE;
91 :
92 20271 : if (der_get_length(p+1, len-1, &ctx->gc_target_len, &l) != 0)
93 0 : return GSS_S_DEFECTIVE_TOKEN;
94 :
95 20271 : _gss_mg_log(10, "gss-asc: DER length: %zu",
96 : ctx->gc_target_len);
97 :
98 20271 : ctx->gc_oid_offset = l + 1;
99 20271 : ctx->gc_target_len += ctx->gc_oid_offset;
100 :
101 20271 : _gss_mg_log(10, "gss-asc: total length: %zu",
102 : ctx->gc_target_len);
103 :
104 20271 : if (ctx->gc_target_len == ASN1_INDEFINITE ||
105 19486 : ctx->gc_target_len < len)
106 0 : return GSS_S_DEFECTIVE_TOKEN;
107 :
108 : /* We've got it all, short-circuit the accumulating */
109 20271 : if (ctx->gc_target_len == len)
110 20271 : 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 20271 : _gss_mg_log(10, "gss-asc: received complete %zu byte token",
144 : ctx->gc_target_len);
145 20271 : ctx->gc_target_len = 0;
146 :
147 20271 : return GSS_S_COMPLETE;
148 : }
149 :
150 : static void
151 20271 : log_oid(const char *str, gss_OID mech)
152 : {
153 785 : OM_uint32 maj, min;
154 785 : gss_buffer_desc buf;
155 :
156 20271 : maj = gss_oid_to_str(&min, mech, &buf);
157 20271 : if (maj == GSS_S_COMPLETE) {
158 20271 : _gss_mg_log(10, "%s: %.*s", str, (int)buf.length,
159 20271 : (char *)buf.value);
160 20271 : gss_release_buffer(&min, &buf);
161 : }
162 20271 : }
163 :
164 : static OM_uint32
165 52247 : choose_mech(struct _gss_context *ctx)
166 : {
167 881 : gss_OID_desc mech;
168 881 : gss_OID mech_oid;
169 52247 : unsigned char *p = ctx->gc_input.value;
170 52247 : size_t len = ctx->gc_input.length;
171 :
172 52247 : 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 52247 : p += ctx->gc_oid_offset;
187 52247 : 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 52247 : if (len < 2 || *p != 0x06) {
194 31976 : _gss_mg_log(10, "initial context token appears to be for non-standard mechanism");
195 31976 : return GSS_S_COMPLETE;
196 : }
197 20271 : len -= 2;
198 20271 : if ((p[1] & 0x80) || p[1] > len) {
199 0 : _gss_mg_log(10, "mechanism oid in initial context token is too long");
200 0 : return GSS_S_COMPLETE;
201 : }
202 20271 : mech.length = p[1];
203 20271 : p += 2;
204 20271 : mech.elements = p;
205 :
206 20271 : mech_oid = _gss_mg_support_mechanism(&mech);
207 20271 : if (mech_oid == GSS_C_NO_OID)
208 0 : return GSS_S_COMPLETE;
209 :
210 20271 : gss_get_mechanism:
211 : /*
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).
215 : */
216 20271 : log_oid("mech oid", mech_oid);
217 20271 : ctx->gc_mech = __gss_get_mechanism(mech_oid);
218 20271 : if (!ctx->gc_mech) {
219 0 : _gss_mg_log(10, "mechanism client used is unknown");
220 0 : return (GSS_S_BAD_MECH);
221 : }
222 20271 : _gss_mg_log(10, "using mech \"%s\"", ctx->gc_mech->gm_name);
223 20271 : return GSS_S_COMPLETE;
224 : }
225 :
226 : GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
227 84206 : 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)
238 : {
239 977 : OM_uint32 major_status, mech_ret_flags, junk;
240 84206 : gssapi_mech_interface m = NULL;
241 84206 : struct _gss_context *ctx = (struct _gss_context *) *context_handle;
242 84206 : struct _gss_cred *cred = (struct _gss_cred *) acceptor_cred_handle;
243 977 : struct _gss_mechanism_cred *mc;
244 977 : gss_buffer_desc defective_token_error;
245 977 : gss_const_cred_id_t acceptor_mc;
246 84206 : gss_cred_id_t delegated_mc = GSS_C_NO_CREDENTIAL;
247 84206 : gss_name_t src_mn = GSS_C_NO_NAME;
248 84206 : gss_OID mech_ret_type = GSS_C_NO_OID;
249 977 : int initial;
250 :
251 84206 : defective_token_error.length = 0;
252 84206 : defective_token_error.value = NULL;
253 :
254 84206 : *minor_status = 0;
255 84206 : if (src_name)
256 84206 : *src_name = GSS_C_NO_NAME;
257 84206 : if (mech_type)
258 84206 : *mech_type = GSS_C_NO_OID;
259 84206 : if (ret_flags)
260 84206 : *ret_flags = 0;
261 84206 : if (time_rec)
262 84206 : *time_rec = 0;
263 84206 : if (delegated_cred_handle)
264 84206 : *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
265 84206 : _mg_buffer_zero(output_token);
266 :
267 84206 : if (!*context_handle) {
268 52247 : ctx = calloc(sizeof(*ctx), 1);
269 52247 : if (!ctx) {
270 0 : *minor_status = ENOMEM;
271 0 : return (GSS_S_DEFECTIVE_TOKEN);
272 : }
273 52247 : *context_handle = (gss_ctx_id_t)ctx;
274 52247 : ctx->gc_initial = 1;
275 : }
276 :
277 84206 : major_status = accumulate_token(ctx, input_token);
278 84206 : if (major_status != GSS_S_COMPLETE)
279 0 : return major_status;
280 :
281 : /*
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
284 : *
285 : */
286 :
287 84206 : initial = ctx->gc_initial;
288 84206 : ctx->gc_initial = 0;
289 :
290 84206 : if (major_status == GSS_S_COMPLETE && initial) {
291 52247 : major_status = choose_mech(ctx);
292 52247 : if (major_status != GSS_S_COMPLETE)
293 0 : return major_status;
294 : }
295 84206 : m = ctx->gc_mech;
296 :
297 84206 : if (initial && !m && acceptor_cred_handle == GSS_C_NO_CREDENTIAL) {
298 : /*
299 : * No header, not a standard mechanism. Try all the mechanisms
300 : * (because default credential).
301 : */
302 0 : struct _gss_mech_switch *ms;
303 :
304 0 : _gss_load_mech();
305 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
306 0 : HEIM_TAILQ_FOREACH(ms, &_gss_mechs, gm_link) {
307 0 : m = &ms->gm_mech;
308 0 : mech_ret_flags = 0;
309 0 : major_status = m->gm_accept_sec_context(minor_status,
310 : &ctx->gc_ctx,
311 : acceptor_mc,
312 0 : &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 0 : if (major_status == GSS_S_DEFECTIVE_TOKEN) {
321 : /*
322 : * Try to retain and output one error token for
323 : * GSS_S_DEFECTIVE_TOKEN. The first one.
324 : */
325 0 : if (output_token->length &&
326 0 : defective_token_error.length == 0) {
327 0 : defective_token_error = *output_token;
328 0 : output_token->length = 0;
329 0 : output_token->value = NULL;
330 : }
331 0 : gss_release_buffer(&junk, output_token);
332 0 : continue;
333 : }
334 0 : gss_release_buffer(&junk, &defective_token_error);
335 0 : ctx->gc_mech = m;
336 0 : goto got_one;
337 : }
338 0 : m = NULL;
339 881 : acceptor_mc = GSS_C_NO_CREDENTIAL;
340 84206 : } else if (initial && !m) {
341 : /*
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.
345 : */
346 31976 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
347 31976 : m = mc->gmc_mech;
348 63952 : acceptor_mc = (m->gm_flags & GM_USE_MG_CRED) ?
349 31976 : acceptor_cred_handle : mc->gmc_cred;
350 31976 : mech_ret_flags = 0;
351 32072 : major_status = m->gm_accept_sec_context(minor_status,
352 : &ctx->gc_ctx,
353 : acceptor_mc,
354 31976 : &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 31976 : if (major_status == GSS_S_DEFECTIVE_TOKEN) {
363 0 : if (output_token->length &&
364 0 : defective_token_error.length == 0) {
365 0 : defective_token_error = *output_token;
366 0 : output_token->length = 0;
367 0 : output_token->value = NULL;
368 : }
369 0 : gss_release_buffer(&junk, output_token);
370 0 : continue;
371 : }
372 31976 : gss_release_buffer(&junk, &defective_token_error);
373 31976 : ctx->gc_mech = m;
374 31976 : goto got_one;
375 : }
376 0 : m = NULL;
377 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
378 : }
379 :
380 52230 : if (m == NULL) {
381 0 : gss_delete_sec_context(&junk, context_handle, NULL);
382 0 : _gss_mg_log(10, "No mechanism accepted the non-standard initial security context token");
383 0 : *output_token = defective_token_error;
384 0 : return GSS_S_BAD_MECH;
385 : }
386 :
387 52230 : if (m->gm_flags & GM_USE_MG_CRED) {
388 0 : acceptor_mc = acceptor_cred_handle;
389 52230 : } else if (cred) {
390 52230 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
391 52230 : if (mc->gmc_mech == m)
392 51349 : break;
393 52230 : if (!mc) {
394 0 : gss_delete_sec_context(&junk, context_handle, NULL);
395 0 : _gss_mg_log(10, "gss-asc: client sent mech %s "
396 : "but no credential was matching",
397 : m->gm_name);
398 0 : HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link)
399 0 : _gss_mg_log(10, "gss-asc: available creds were %s", mc->gmc_mech->gm_name);
400 0 : return (GSS_S_BAD_MECH);
401 : }
402 52230 : acceptor_mc = mc->gmc_cred;
403 : } else {
404 0 : acceptor_mc = GSS_C_NO_CREDENTIAL;
405 : }
406 :
407 52230 : mech_ret_flags = 0;
408 52230 : major_status = m->gm_accept_sec_context(minor_status,
409 : &ctx->gc_ctx,
410 : acceptor_mc,
411 52230 : &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);
419 :
420 84206 : got_one:
421 84206 : if (major_status != GSS_S_COMPLETE &&
422 : major_status != GSS_S_CONTINUE_NEEDED)
423 : {
424 19 : _gss_mg_error(m, *minor_status);
425 19 : gss_delete_sec_context(&junk, context_handle, NULL);
426 19 : return (major_status);
427 : }
428 :
429 84187 : if (mech_type)
430 84187 : *mech_type = mech_ret_type;
431 :
432 84187 : if (src_name && src_mn) {
433 84187 : if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
434 : /* Negotiation mechanisms use mechglue names as names */
435 0 : *src_name = src_mn;
436 0 : src_mn = GSS_C_NO_NAME;
437 : } else {
438 : /*
439 : * Make a new name and mark it as an MN.
440 : *
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.
443 : */
444 84187 : struct _gss_name *name = _gss_create_name(src_mn, m);
445 :
446 84187 : if (!name) {
447 0 : m->gm_release_name(minor_status, &src_mn);
448 0 : gss_delete_sec_context(&junk, context_handle, NULL);
449 0 : return (GSS_S_FAILURE);
450 : }
451 84187 : *src_name = (gss_name_t) name;
452 84187 : src_mn = GSS_C_NO_NAME;
453 : }
454 0 : } else if (src_mn) {
455 0 : if (ctx->gc_mech->gm_flags & GM_USE_MG_NAME) {
456 0 : _gss_mg_release_name((struct _gss_name *)src_mn);
457 0 : src_mn = GSS_C_NO_NAME;
458 : } else {
459 0 : m->gm_release_name(minor_status, &src_mn);
460 : }
461 : }
462 :
463 84187 : if (mech_ret_flags & GSS_C_DELEG_FLAG) {
464 49563 : if (!delegated_cred_handle) {
465 0 : if (m->gm_flags & GM_USE_MG_CRED)
466 0 : gss_release_cred(minor_status, &delegated_mc);
467 : else
468 0 : m->gm_release_cred(minor_status, &delegated_mc);
469 0 : mech_ret_flags &=
470 : ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG);
471 49563 : } else if ((m->gm_flags & GM_USE_MG_CRED) != 0) {
472 : /*
473 : * If credential is uses mechglue cred, assume it
474 : * returns one too.
475 : */
476 0 : *delegated_cred_handle = delegated_mc;
477 49563 : } else if (gss_oid_equal(mech_ret_type, &m->gm_mech_oid) == 0) {
478 : /*
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
483 : */
484 0 : *delegated_cred_handle = delegated_mc;
485 :
486 49563 : } else if (delegated_mc) {
487 881 : struct _gss_cred *dcred;
488 881 : struct _gss_mechanism_cred *dmc;
489 :
490 48843 : dcred = _gss_mg_alloc_cred();
491 48843 : if (!dcred) {
492 0 : *minor_status = ENOMEM;
493 0 : gss_delete_sec_context(&junk, context_handle, NULL);
494 0 : return (GSS_S_FAILURE);
495 : }
496 48843 : dmc = malloc(sizeof(struct _gss_mechanism_cred));
497 48843 : if (!dmc) {
498 0 : free(dcred);
499 0 : *minor_status = ENOMEM;
500 0 : gss_delete_sec_context(&junk, context_handle, NULL);
501 0 : return (GSS_S_FAILURE);
502 : }
503 48843 : dmc->gmc_mech = m;
504 48843 : dmc->gmc_mech_oid = &m->gm_mech_oid;
505 48843 : dmc->gmc_cred = delegated_mc;
506 48843 : HEIM_TAILQ_INSERT_TAIL(&dcred->gc_mc, dmc, gmc_link);
507 :
508 48843 : *delegated_cred_handle = (gss_cred_id_t) dcred;
509 : }
510 : }
511 :
512 84187 : _gss_mg_log(10, "gss-asc: return %d/%d", (int)major_status, (int)*minor_status);
513 :
514 84187 : if (ret_flags)
515 84187 : *ret_flags = mech_ret_flags;
516 83210 : return (major_status);
517 : }
|