Line data Source code
1 : /*
2 : * Copyright (c) 2004, PADL Software Pty Ltd.
3 : * All rights reserved.
4 : *
5 : * Portions Copyright (c) 2009 Apple Inc. All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : *
11 : * 1. Redistributions of source code must retain the above copyright
12 : * notice, this list of conditions and the following disclaimer.
13 : *
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : *
18 : * 3. Neither the name of PADL Software nor the names of its contributors
19 : * may be used to endorse or promote products derived from this software
20 : * without specific prior written permission.
21 : *
22 : * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND
23 : * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 : * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 : * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE
26 : * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 : * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 : * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 : * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 : * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 : * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 : * SUCH DAMAGE.
33 : */
34 :
35 : #include "spnego_locl.h"
36 :
37 : /*
38 : * Apparently Microsoft got the OID wrong, and used
39 : * 1.2.840.48018.1.2.2 instead. We need both this and
40 : * the correct Kerberos OID here in order to deal with
41 : * this. Because this is manifest in SPNEGO only I'd
42 : * prefer to deal with this here rather than inside the
43 : * Kerberos mechanism.
44 : */
45 : gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc =
46 : {9, rk_UNCONST("\x2a\x86\x48\x82\xf7\x12\x01\x02\x02")};
47 :
48 : /*
49 : * Allocate a SPNEGO context handle
50 : */
51 : OM_uint32 GSSAPI_CALLCONV
52 0 : _gss_spnego_alloc_sec_context (OM_uint32 * minor_status,
53 : gss_ctx_id_t *context_handle)
54 : {
55 0 : gssspnego_ctx ctx;
56 :
57 0 : ctx = calloc(1, sizeof(*ctx));
58 0 : if (ctx == NULL) {
59 0 : *minor_status = ENOMEM;
60 0 : return GSS_S_FAILURE;
61 : }
62 :
63 0 : ctx->NegTokenInit_mech_types.value = NULL;
64 0 : ctx->NegTokenInit_mech_types.length = 0;
65 :
66 0 : ctx->preferred_mech_type = GSS_C_NO_OID;
67 0 : ctx->selected_mech_type = GSS_C_NO_OID;
68 0 : ctx->negotiated_mech_type = GSS_C_NO_OID;
69 :
70 0 : ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
71 :
72 0 : ctx->mech_flags = 0;
73 0 : ctx->mech_time_rec = 0;
74 0 : ctx->mech_src_name = GSS_C_NO_NAME;
75 :
76 0 : ctx->flags.open = 0;
77 0 : ctx->flags.local = 0;
78 0 : ctx->flags.peer_require_mic = 0;
79 0 : ctx->flags.require_mic = 0;
80 0 : ctx->flags.verified_mic = 0;
81 :
82 0 : HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex);
83 :
84 0 : ctx->negoex_step = 0;
85 0 : ctx->negoex_transcript = NULL;
86 0 : ctx->negoex_seqnum = 0;
87 0 : HEIM_TAILQ_INIT(&ctx->negoex_mechs);
88 0 : memset(ctx->negoex_conv_id, 0, GUID_LENGTH);
89 :
90 0 : *context_handle = (gss_ctx_id_t)ctx;
91 :
92 0 : return GSS_S_COMPLETE;
93 : }
94 :
95 : /*
96 : * Free a SPNEGO context handle. The caller must have acquired
97 : * the lock before this is called.
98 : */
99 0 : OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context
100 : (OM_uint32 *minor_status,
101 : gss_ctx_id_t *context_handle,
102 : gss_buffer_t output_token
103 : )
104 : {
105 0 : gssspnego_ctx ctx;
106 0 : OM_uint32 ret, minor;
107 :
108 0 : *minor_status = 0;
109 :
110 0 : if (context_handle == NULL) {
111 0 : return GSS_S_NO_CONTEXT;
112 : }
113 :
114 0 : if (output_token != GSS_C_NO_BUFFER) {
115 0 : output_token->length = 0;
116 0 : output_token->value = NULL;
117 : }
118 :
119 0 : ctx = (gssspnego_ctx)*context_handle;
120 0 : *context_handle = GSS_C_NO_CONTEXT;
121 :
122 0 : if (ctx == NULL) {
123 0 : return GSS_S_NO_CONTEXT;
124 : }
125 :
126 0 : if (ctx->NegTokenInit_mech_types.value)
127 0 : free(ctx->NegTokenInit_mech_types.value);
128 :
129 0 : ctx->preferred_mech_type = GSS_C_NO_OID;
130 0 : ctx->negotiated_mech_type = GSS_C_NO_OID;
131 0 : ctx->selected_mech_type = GSS_C_NO_OID;
132 :
133 0 : gss_release_name(&minor, &ctx->target_name);
134 0 : gss_release_name(&minor, &ctx->mech_src_name);
135 :
136 0 : if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) {
137 0 : ret = gss_delete_sec_context(minor_status,
138 : &ctx->negotiated_ctx_id,
139 : output_token);
140 0 : ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT;
141 : } else {
142 0 : ret = GSS_S_COMPLETE;
143 : }
144 :
145 0 : _gss_negoex_release_context(ctx);
146 :
147 0 : HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex);
148 0 : HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex);
149 :
150 0 : free(ctx);
151 :
152 0 : return ret;
153 : }
154 :
155 : static int
156 0 : inq_context_by_oid_bool(gssspnego_ctx ctx, gss_OID oid)
157 : {
158 0 : OM_uint32 major, minor;
159 0 : gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET;
160 0 : uint8_t ret = 0;
161 :
162 0 : major = gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id,
163 : oid, &data_set);
164 0 : if (major != GSS_S_COMPLETE)
165 0 : return FALSE;
166 :
167 0 : if (data_set != GSS_C_NO_BUFFER_SET &&
168 0 : data_set->count == 1 &&
169 0 : data_set->elements[0].length == 1)
170 0 : ret = *((uint8_t *)data_set->elements[0].value);
171 :
172 0 : gss_release_buffer_set(&minor, &data_set);
173 :
174 0 : return ret != 0;
175 : }
176 :
177 : /*
178 : * Returns TRUE if it is safe to omit mechListMIC.
179 : */
180 :
181 : int
182 0 : _gss_spnego_safe_omit_mechlist_mic(gssspnego_ctx ctx)
183 : {
184 0 : int safe_omit = FALSE;
185 :
186 0 : if (ctx->flags.peer_require_mic) {
187 0 : _gss_mg_log(10, "spnego: mechListMIC required by peer");
188 0 : } else if (inq_context_by_oid_bool(ctx, GSS_C_INQ_PEER_HAS_BUGGY_SPNEGO)) {
189 : /* [MS-SPNG] Appendix A <7> Section 3.1.5.1: may be old peer with buggy SPNEGO */
190 0 : safe_omit = TRUE;
191 0 : _gss_mg_log(10, "spnego: mechListMIC omitted for legacy interoperability");
192 0 : } else if (inq_context_by_oid_bool(ctx, GSS_C_INQ_REQUIRE_MECHLIST_MIC)) {
193 : /* [MS-SPNG] Appendix A <7> Section 3.1.5.1: allow NTLM to force MIC */
194 0 : _gss_mg_log(10, "spnego: mechListMIC required by mechanism");
195 0 : } else if (gss_oid_equal(ctx->selected_mech_type, ctx->preferred_mech_type)) {
196 0 : safe_omit = TRUE;
197 0 : _gss_mg_log(10, "spnego: mechListMIC omitted as preferred mechanism selected");
198 : } else {
199 0 : _gss_mg_log(10, "spnego: mechListMIC required by default");
200 : }
201 :
202 0 : return safe_omit;
203 : }
204 :
205 : /*
206 : * A map between a GSS-API flag and a (mechanism attribute, weight)
207 : * tuple. The list of mechanisms is re-ordered by aggregate weight
208 : * (highest weight is more preferred, e.g. if GSS_C_MUTUAL_FLAG and
209 : * GSS_C_ANON_FLAG are set, we prefer a mechanism that supports
210 : * mutual authentication over one that only supports anonymous).
211 : */
212 : static struct {
213 : OM_uint32 flag;
214 : gss_OID ma;
215 : int weight;
216 : } flag_to_ma_map[] = {
217 : { GSS_C_MUTUAL_FLAG, GSS_C_MA_AUTH_TARG, 2 },
218 : { GSS_C_ANON_FLAG, GSS_C_MA_AUTH_INIT_ANON, 1 },
219 : };
220 :
221 : /*
222 : * Returns a bitmask indicating GSS flags we can sort on.
223 : */
224 : static inline OM_uint32
225 0 : mech_flag_mask(void)
226 : {
227 0 : size_t i;
228 0 : OM_uint32 mask = 0;
229 :
230 0 : for (i = 0; i < sizeof(flag_to_ma_map)/sizeof(flag_to_ma_map[0]); i++)
231 0 : mask |= flag_to_ma_map[i].flag;
232 :
233 0 : return mask;
234 : }
235 :
236 : /*
237 : * Returns an integer representing the preference weighting for a
238 : * mechanism, based on the requested GSS flags.
239 : */
240 : static int
241 0 : mech_weight(gss_const_OID mech, OM_uint32 req_flags)
242 : {
243 0 : OM_uint32 major, minor;
244 0 : gss_OID_set mech_attrs = GSS_C_NO_OID_SET;
245 0 : int weight = 0;
246 0 : size_t i, j;
247 :
248 0 : major = gss_inquire_attrs_for_mech(&minor, mech, &mech_attrs, NULL);
249 0 : if (GSS_ERROR(major))
250 0 : return 0;
251 :
252 0 : for (i = 0; i < sizeof(flag_to_ma_map)/sizeof(flag_to_ma_map[0]); i++) {
253 0 : if ((req_flags & flag_to_ma_map[i].flag) == 0)
254 0 : continue;
255 :
256 0 : for (j = 0; j < mech_attrs->count; j++) {
257 0 : if (gss_oid_equal(flag_to_ma_map[i].ma, &mech_attrs->elements[j])) {
258 0 : weight += flag_to_ma_map[i].weight;
259 0 : continue;
260 : }
261 : }
262 : }
263 :
264 0 : gss_release_oid_set(&minor, &mech_attrs);
265 :
266 0 : return weight;
267 : }
268 :
269 : static int
270 0 : mech_compare(const void *mech1, const void *mech2, void *req_flags_p)
271 : {
272 0 : OM_uint32 req_flags = *((OM_uint32 *)req_flags_p);
273 0 : int mech1_weight = mech_weight(mech1, req_flags);
274 0 : int mech2_weight = mech_weight(mech2, req_flags);
275 :
276 0 : return mech2_weight - mech1_weight;
277 : }
278 :
279 : /*
280 : * Order a list of mechanisms by weight based on requested GSS flags.
281 : */
282 : static void
283 0 : order_mechs_by_flags(gss_OID_set mechs, OM_uint32 req_flags)
284 : {
285 0 : if (req_flags & mech_flag_mask()) { /* skip if flags irrelevant */
286 : /*
287 : * NB: must be a stable sort to preserve the existing order
288 : * of mechanisms that are equally weighted.
289 : */
290 0 : mergesort_r(mechs->elements, mechs->count,
291 : sizeof(gss_OID_desc), mech_compare, &req_flags);
292 : }
293 0 : }
294 :
295 : static OM_uint32
296 0 : add_mech_type(OM_uint32 *minor_status,
297 : gss_OID mech_type,
298 : MechTypeList *mechtypelist)
299 : {
300 0 : MechType mech;
301 0 : int ret;
302 :
303 0 : heim_assert(!gss_oid_equal(mech_type, GSS_SPNEGO_MECHANISM),
304 : "SPNEGO mechanism not filtered");
305 :
306 0 : ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL);
307 0 : if (ret == 0) {
308 0 : ret = add_MechTypeList(mechtypelist, &mech);
309 0 : free_MechType(&mech);
310 : }
311 :
312 0 : if (ret) {
313 0 : *minor_status = ret;
314 0 : return GSS_S_FAILURE;
315 : }
316 :
317 0 : return GSS_S_COMPLETE;
318 : }
319 :
320 : static int
321 0 : add_mech_if_approved(OM_uint32 *minor_status,
322 : gss_const_name_t target_name,
323 : OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
324 : void *userptr,
325 : int includeMSCompatOID,
326 : gss_const_cred_id_t cred_handle,
327 : MechTypeList *mechtypelist,
328 : gss_OID mech_oid,
329 : gss_OID *first_mech,
330 : OM_uint32 *first_major,
331 : OM_uint32 *first_minor,
332 : int *added_negoex)
333 : {
334 0 : OM_uint32 major, minor;
335 :
336 : /*
337 : * Unapproved mechanisms are ignored, but we capture their result
338 : * code in case we didn't find any other mechanisms, in which case
339 : * we return that to the caller of _gss_spnego_indicate_mechtypelist().
340 : */
341 0 : major = (*func)(&minor, userptr, target_name, cred_handle, mech_oid);
342 0 : if (major != GSS_S_COMPLETE) {
343 0 : if (*first_mech == GSS_C_NO_OID) {
344 0 : *first_major = major;
345 0 : *first_minor = minor;
346 : }
347 0 : return GSS_S_COMPLETE;
348 : }
349 :
350 0 : if (_gss_negoex_mech_p(mech_oid)) {
351 0 : if (*added_negoex == FALSE) {
352 0 : major = add_mech_type(minor_status, GSS_NEGOEX_MECHANISM, mechtypelist);
353 0 : if (major != GSS_S_COMPLETE)
354 0 : return major;
355 0 : *added_negoex = TRUE;
356 : }
357 :
358 0 : if (*first_mech == GSS_C_NO_OID)
359 0 : *first_mech = GSS_NEGOEX_MECHANISM;
360 :
361 : /* if NegoEx-only mech, we are done */
362 0 : if (!_gss_negoex_and_spnego_mech_p(mech_oid))
363 0 : return GSS_S_COMPLETE;
364 : }
365 :
366 0 : if (includeMSCompatOID && gss_oid_equal(mech_oid, GSS_KRB5_MECHANISM)) {
367 0 : major = add_mech_type(minor_status,
368 : &_gss_spnego_mskrb_mechanism_oid_desc,
369 : mechtypelist);
370 0 : if (major != GSS_S_COMPLETE)
371 0 : return major;
372 : }
373 :
374 0 : major = add_mech_type(minor_status, mech_oid, mechtypelist);
375 0 : if (major != GSS_S_COMPLETE)
376 0 : return major;
377 :
378 0 : if (*first_mech == GSS_C_NO_OID)
379 0 : *first_mech = mech_oid;
380 :
381 0 : return GSS_S_COMPLETE;
382 : }
383 :
384 : OM_uint32 GSSAPI_CALLCONV
385 0 : _gss_spnego_indicate_mechtypelist (OM_uint32 *minor_status,
386 : gss_const_name_t target_name,
387 : OM_uint32 req_flags,
388 : OM_uint32 (*func)(OM_uint32 *, void *, gss_const_name_t, gss_const_cred_id_t, gss_OID),
389 : void *userptr,
390 : int includeMSCompatOID,
391 : gss_const_cred_id_t cred_handle,
392 : MechTypeList *mechtypelist,
393 : gss_OID *preferred_mech)
394 : {
395 0 : gss_OID_set supported_mechs = GSS_C_NO_OID_SET;
396 0 : gss_OID first_mech = GSS_C_NO_OID;
397 0 : OM_uint32 ret, minor;
398 0 : OM_uint32 first_major = GSS_S_BAD_MECH, first_minor = 0;
399 0 : size_t i;
400 0 : int added_negoex = FALSE, canonical_order = FALSE;
401 :
402 0 : mechtypelist->len = 0;
403 0 : mechtypelist->val = NULL;
404 :
405 0 : if (cred_handle != GSS_C_NO_CREDENTIAL)
406 0 : ret = _gss_spnego_inquire_cred_mechs(minor_status, cred_handle,
407 : &supported_mechs, &canonical_order);
408 : else
409 0 : ret = _gss_spnego_indicate_mechs(minor_status, &supported_mechs);
410 0 : if (ret != GSS_S_COMPLETE)
411 0 : return ret;
412 :
413 0 : if (!canonical_order)
414 0 : order_mechs_by_flags(supported_mechs, req_flags);
415 :
416 0 : heim_assert(supported_mechs != GSS_C_NO_OID_SET,
417 : "NULL mech set returned by SPNEGO inquire/indicate mechs");
418 :
419 : /*
420 : * Previously krb5 was tried explicitly, but now the internal mech
421 : * list is reordered so that krb5 is first, this should no longer
422 : * be required. This permits an application to specify another
423 : * mechanism as preferred over krb5 using gss_set_neg_mechs().
424 : */
425 0 : for (i = 0; i < supported_mechs->count; i++) {
426 0 : ret = add_mech_if_approved(minor_status, target_name,
427 : func, userptr, includeMSCompatOID,
428 : cred_handle, mechtypelist,
429 0 : &supported_mechs->elements[i],
430 : &first_mech,
431 : &first_major, &first_minor,
432 : &added_negoex);
433 0 : if (ret != GSS_S_COMPLETE) {
434 0 : gss_release_oid_set(&minor, &supported_mechs);
435 0 : return ret;
436 : }
437 : }
438 :
439 0 : heim_assert(mechtypelist->len == 0 || first_mech != GSS_C_NO_OID,
440 : "mechtypelist non-empty but no mech selected");
441 :
442 0 : if (first_mech != GSS_C_NO_OID)
443 0 : ret = _gss_intern_oid(minor_status, first_mech, &first_mech);
444 0 : else if (GSS_ERROR(first_major)) {
445 0 : ret = first_major;
446 0 : *minor_status = first_minor;
447 : } else
448 0 : ret = GSS_S_BAD_MECH;
449 :
450 0 : if (preferred_mech != NULL)
451 0 : *preferred_mech = first_mech;
452 :
453 0 : gss_release_oid_set(&minor, &supported_mechs);
454 :
455 0 : return ret;
456 : }
457 :
458 : /*
459 : *
460 : */
461 :
462 : OM_uint32
463 0 : _gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status,
464 : gssspnego_ctx ctx,
465 : heim_octet_string *mic)
466 : {
467 0 : gss_buffer_desc mic_buf;
468 0 : OM_uint32 major_status;
469 :
470 0 : if (mic == NULL) {
471 0 : *minor_status = 0;
472 0 : return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
473 : GSS_S_DEFECTIVE_TOKEN, 0,
474 : "SPNEGO peer failed to send mechListMIC");
475 : }
476 :
477 0 : if (ctx->flags.verified_mic) {
478 : /* This doesn't make sense, we've already verified it? */
479 0 : *minor_status = 0;
480 0 : return GSS_S_DUPLICATE_TOKEN;
481 : }
482 :
483 0 : mic_buf.length = mic->length;
484 0 : mic_buf.value = mic->data;
485 :
486 0 : major_status = gss_verify_mic(minor_status,
487 0 : ctx->negotiated_ctx_id,
488 0 : &ctx->NegTokenInit_mech_types,
489 : &mic_buf,
490 : NULL);
491 0 : if (major_status == GSS_S_COMPLETE) {
492 0 : _gss_spnego_ntlm_reset_crypto(minor_status, ctx, TRUE);
493 0 : } else if (major_status == GSS_S_UNAVAILABLE) {
494 0 : _gss_mg_log(10, "mech doesn't support MIC, allowing anyway");
495 0 : } else if (major_status) {
496 0 : return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM,
497 : GSS_S_DEFECTIVE_TOKEN, *minor_status,
498 : "SPNEGO peer sent invalid mechListMIC");
499 : }
500 0 : ctx->flags.verified_mic = 1;
501 :
502 0 : *minor_status = 0;
503 :
504 0 : return GSS_S_COMPLETE;
505 : }
506 :
507 : /*
508 : * According to [MS-SPNG] 3.3.5.1 the crypto state for NTLM is reset
509 : * before the completed context is returned to the application.
510 : */
511 :
512 : OM_uint32
513 0 : _gss_spnego_ntlm_reset_crypto(OM_uint32 *minor_status,
514 : gssspnego_ctx ctx,
515 : OM_uint32 verify)
516 : {
517 0 : if (gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) {
518 0 : gss_buffer_desc value;
519 :
520 0 : value.length = sizeof(verify);
521 0 : value.value = &verify;
522 :
523 0 : return gss_set_sec_context_option(minor_status,
524 : &ctx->negotiated_ctx_id,
525 : GSS_C_NTLM_RESET_CRYPTO,
526 : &value);
527 : }
528 :
529 0 : return GSS_S_COMPLETE;
530 : }
531 :
532 : void
533 0 : _gss_spnego_log_mech(const char *prefix, gss_const_OID oid)
534 : {
535 0 : gss_buffer_desc oidbuf = GSS_C_EMPTY_BUFFER;
536 0 : OM_uint32 junk;
537 0 : const char *name = NULL;
538 :
539 0 : if (!_gss_mg_log_level(10))
540 0 : return;
541 :
542 0 : if (oid == GSS_C_NO_OID ||
543 0 : gss_oid_to_str(&junk, (gss_OID)oid, &oidbuf) != GSS_S_COMPLETE) {
544 0 : _gss_mg_log(10, "spnego: %s (null)", prefix);
545 0 : return;
546 : }
547 :
548 0 : if (gss_oid_equal(oid, GSS_NEGOEX_MECHANISM))
549 0 : name = "negoex"; /* not a real mech */
550 0 : else if (gss_oid_equal(oid, &_gss_spnego_mskrb_mechanism_oid_desc))
551 0 : name = "mskrb";
552 : else {
553 0 : gssapi_mech_interface m = __gss_get_mechanism(oid);
554 0 : if (m)
555 0 : name = m->gm_name;
556 : }
557 :
558 0 : _gss_mg_log(10, "spnego: %s %s { %.*s }",
559 : prefix,
560 : name ? name : "unknown",
561 0 : (int)oidbuf.length, (char *)oidbuf.value);
562 0 : gss_release_buffer(&junk, &oidbuf);
563 : }
564 :
565 : void
566 0 : _gss_spnego_log_mechTypes(MechTypeList *mechTypes)
567 : {
568 0 : size_t i;
569 0 : char mechbuf[64];
570 0 : size_t mech_len;
571 0 : gss_OID_desc oid;
572 0 : int ret;
573 :
574 0 : if (!_gss_mg_log_level(10))
575 0 : return;
576 :
577 0 : for (i = 0; i < mechTypes->len; i++) {
578 0 : ret = der_put_oid ((unsigned char *)mechbuf + sizeof(mechbuf) - 1,
579 : sizeof(mechbuf),
580 0 : &mechTypes->val[i],
581 : &mech_len);
582 0 : if (ret)
583 0 : continue;
584 :
585 0 : oid.length = (OM_uint32)mech_len;
586 0 : oid.elements = mechbuf + sizeof(mechbuf) - mech_len;
587 :
588 0 : _gss_spnego_log_mech("initiator proposed mech", &oid);
589 : }
590 : }
591 :
592 : /*
593 : * Indicate mechs negotiable by SPNEGO
594 : */
595 :
596 : OM_uint32
597 0 : _gss_spnego_indicate_mechs(OM_uint32 *minor_status,
598 : gss_OID_set *mechs_p)
599 : {
600 0 : gss_OID_desc oids[3];
601 0 : gss_OID_set_desc except;
602 :
603 0 : *mechs_p = GSS_C_NO_OID_SET;
604 :
605 0 : oids[0] = *GSS_C_MA_DEPRECATED;
606 0 : oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
607 0 : oids[2] = *GSS_C_MA_MECH_NEGO;
608 :
609 0 : except.count = sizeof(oids) / sizeof(oids[0]);
610 0 : except.elements = oids;
611 :
612 0 : return gss_indicate_mechs_by_attrs(minor_status,
613 : GSS_C_NO_OID_SET,
614 : &except,
615 : GSS_C_NO_OID_SET,
616 : mechs_p);
617 : }
618 :
619 : /*
620 : * Indicate mechs in cred negotiatble by SPNEGO
621 : */
622 :
623 : OM_uint32
624 0 : _gss_spnego_inquire_cred_mechs(OM_uint32 *minor_status,
625 : gss_const_cred_id_t cred,
626 : gss_OID_set *mechs_p,
627 : int *canonical_order)
628 : {
629 0 : OM_uint32 ret, junk;
630 0 : gss_OID_set cred_mechs = GSS_C_NO_OID_SET;
631 0 : gss_OID_set negotiable_mechs = GSS_C_NO_OID_SET;
632 0 : size_t i;
633 :
634 0 : *mechs_p = GSS_C_NO_OID_SET;
635 0 : *canonical_order = FALSE;
636 :
637 0 : heim_assert(cred != GSS_C_NO_CREDENTIAL, "Invalid null credential handle");
638 :
639 0 : ret = gss_get_neg_mechs(minor_status, cred, &cred_mechs);
640 0 : if (ret == GSS_S_COMPLETE) {
641 0 : *canonical_order = TRUE;
642 : } else {
643 0 : ret = gss_inquire_cred(minor_status, cred, NULL, NULL, NULL, &cred_mechs);
644 0 : if (ret != GSS_S_COMPLETE)
645 0 : goto out;
646 : }
647 :
648 0 : heim_assert(cred_mechs != GSS_C_NO_OID_SET && cred_mechs->count > 0,
649 : "gss_inquire_cred succeeded but returned no mechanisms");
650 :
651 0 : ret = _gss_spnego_indicate_mechs(minor_status, &negotiable_mechs);
652 0 : if (ret != GSS_S_COMPLETE)
653 0 : goto out;
654 :
655 0 : heim_assert(negotiable_mechs != GSS_C_NO_OID_SET,
656 : "_gss_spnego_indicate_mechs succeeded but returned null OID set");
657 :
658 0 : ret = gss_create_empty_oid_set(minor_status, mechs_p);
659 0 : if (ret != GSS_S_COMPLETE)
660 0 : goto out;
661 :
662 : /* Filter credential mechs by negotiable mechs, order by credential mechs */
663 0 : for (i = 0; i < cred_mechs->count; i++) {
664 0 : gss_OID cred_mech = &cred_mechs->elements[i];
665 0 : int present = 0;
666 :
667 0 : gss_test_oid_set_member(&junk, cred_mech, negotiable_mechs, &present);
668 0 : if (!present)
669 0 : continue;
670 :
671 0 : ret = gss_add_oid_set_member(minor_status, cred_mech, mechs_p);
672 0 : if (ret != GSS_S_COMPLETE)
673 0 : break;
674 : }
675 :
676 0 : out:
677 0 : if (ret != GSS_S_COMPLETE)
678 0 : gss_release_oid_set(&junk, mechs_p);
679 0 : gss_release_oid_set(&junk, &cred_mechs);
680 0 : gss_release_oid_set(&junk, &negotiable_mechs);
681 :
682 0 : return ret;
683 : }
684 :
|