1 /*
2 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
3 * All rights reserved.
4 *
5 * Export of this software from the United States of America may
6 * require a specific license from the United States Government.
7 * It is the responsibility of any person or organization contemplating
8 * export to obtain such a license before exporting.
9 *
10 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
11 * distribute this software and its documentation for any purpose and
12 * without fee is hereby granted, provided that the above copyright
13 * notice appear in all copies and that both that copyright notice and
14 * this permission notice appear in supporting documentation, and that
15 * the name of M.I.T. not be used in advertising or publicity pertaining
16 * to distribution of the software without specific, written prior
17 * permission. Furthermore if you modify this software you must label
18 * your software as modified software and not distribute it in such a
19 * fashion that it might be confused with the original M.I.T. software.
20 * M.I.T. makes no representations about the suitability of
21 * this software for any purpose. It is provided "as is" without express
22 * or implied warranty.
23 */
24 /*
25 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
26 * Use is subject to license terms.
27 *
28 * A module that implements the spnego security mechanism.
29 * It is used to negotiate the security mechanism between
30 * peers using the GSS-API. SPNEGO is specified in RFC 4178.
31 *
32 */
33 /*
34 * Copyright (c) 2006-2008, Novell, Inc.
35 * All rights reserved.
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions are met:
39 *
40 * * Redistributions of source code must retain the above copyright notice,
41 * this list of conditions and the following disclaimer.
42 * * Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * * The copyright holder's name is not used to endorse or promote products
46 * derived from this software without specific prior written permission.
47 *
48 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
49 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
50 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
51 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
52 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
53 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
54 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
55 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
56 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58 * POSSIBILITY OF SUCH DAMAGE.
59 */
60 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
61
62 #include <k5-int.h>
63 #include <k5-der.h>
64 #include <krb5.h>
65 #include <mglueP.h>
66 #include "gssapiP_spnego.h"
67 #include <gssapi_err_generic.h>
68
69
70 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
71 typedef const gss_OID_desc *gss_OID_const;
72
73 /* private routines for spnego_mechanism */
74 static spnego_token_t make_spnego_token(const char *);
75 static gss_buffer_desc make_err_msg(const char *);
76 static int verify_token_header(struct k5input *, gss_OID_const);
77 static gss_OID get_mech_oid(OM_uint32 *minor_status, struct k5input *);
78 static gss_buffer_t get_octet_string(struct k5input *);
79 static gss_OID_set get_mech_set(OM_uint32 *, struct k5input *);
80 static OM_uint32 get_req_flags(struct k5input *, OM_uint32 *);
81 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, gss_cred_usage_t,
82 gss_const_key_value_set_t,
83 gss_cred_id_t *, gss_OID_set *,
84 OM_uint32 *);
85 static OM_uint32 get_negotiable_mechs(OM_uint32 *, spnego_gss_ctx_id_t,
86 spnego_gss_cred_id_t, gss_cred_usage_t);
87 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
88 static spnego_gss_ctx_id_t create_spnego_ctx(int);
89 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
90
91 static OM_uint32
92 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
93 gss_buffer_t *, OM_uint32 *, send_token_flag *);
94 static OM_uint32
95 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
96 gss_buffer_t *, OM_uint32 *, send_token_flag *);
97
98 static OM_uint32
99 init_ctx_new(OM_uint32 *, spnego_gss_cred_id_t, send_token_flag *,
100 spnego_gss_ctx_id_t *);
101 static OM_uint32
102 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
103 gss_buffer_t *, gss_buffer_t *, send_token_flag *);
104 static OM_uint32
105 init_ctx_cont(OM_uint32 *, spnego_gss_ctx_id_t, gss_buffer_t,
106 gss_buffer_t *, gss_buffer_t *,
107 OM_uint32 *, send_token_flag *);
108 static OM_uint32
109 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
110 gss_OID, gss_buffer_t *, gss_buffer_t *, send_token_flag *);
111 static OM_uint32
112 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
113 OM_uint32, gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
114 gss_channel_bindings_t,
115 gss_buffer_t, OM_uint32 *, send_token_flag *);
116
117 static OM_uint32
118 acc_ctx_new(OM_uint32 *, gss_buffer_t, spnego_gss_cred_id_t, gss_buffer_t *,
119 gss_buffer_t *, OM_uint32 *, send_token_flag *,
120 spnego_gss_ctx_id_t *);
121 static OM_uint32
122 acc_ctx_cont(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, gss_buffer_t *,
123 gss_buffer_t *, OM_uint32 *, send_token_flag *);
124 static OM_uint32
125 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
126 OM_uint32 *, send_token_flag *);
127 static OM_uint32
128 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, spnego_gss_cred_id_t,
129 gss_buffer_t, gss_channel_bindings_t, gss_buffer_t,
130 OM_uint32 *, OM_uint32 *, send_token_flag *);
131
132 static gss_OID
133 negotiate_mech(spnego_gss_ctx_id_t, gss_OID_set, OM_uint32 *);
134
135 static int
136 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
137 int,
138 gss_buffer_t,
139 OM_uint32, gss_buffer_t, send_token_flag,
140 gss_buffer_t);
141 static OM_uint32
142 make_spnego_tokenTarg_msg(uint8_t, gss_OID, gss_buffer_t,
143 gss_buffer_t, send_token_flag,
144 gss_buffer_t);
145
146 static OM_uint32
147 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
148 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
149 gss_buffer_t *);
150 static OM_uint32
151 get_negTokenResp(OM_uint32 *, struct k5input *, OM_uint32 *, gss_OID *,
152 gss_buffer_t *, gss_buffer_t *);
153
154 static int
155 is_kerb_mech(gss_OID oid);
156
157 /* SPNEGO oid structure */
158 static const gss_OID_desc spnego_oids[] = {
159 {SPNEGO_OID_LENGTH, SPNEGO_OID},
160 };
161
162 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
163 static const gss_OID_set_desc spnego_oidsets[] = {
164 {1, (gss_OID) spnego_oids+0},
165 };
166 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
167
168 static gss_OID_desc negoex_mech = { NEGOEX_OID_LENGTH, NEGOEX_OID };
169
170 static int make_NegHints(OM_uint32 *, gss_buffer_t *);
171 static OM_uint32
172 acc_ctx_hints(OM_uint32 *, spnego_gss_cred_id_t, gss_buffer_t *, OM_uint32 *,
173 send_token_flag *, spnego_gss_ctx_id_t *);
174
175 /*
176 * The Mech OID for SPNEGO:
177 * { iso(1) org(3) dod(6) internet(1) security(5)
178 * mechanism(5) spnego(2) }
179 */
180 static struct gss_config spnego_mechanism =
181 {
182 {SPNEGO_OID_LENGTH, SPNEGO_OID},
183 NULL,
184 spnego_gss_acquire_cred,
185 spnego_gss_release_cred,
186 spnego_gss_init_sec_context,
187 #ifndef LEAN_CLIENT
188 spnego_gss_accept_sec_context,
189 #else
190 NULL,
191 #endif /* LEAN_CLIENT */
192 NULL, /* gss_process_context_token */
193 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
194 spnego_gss_context_time, /* gss_context_time */
195 spnego_gss_get_mic, /* gss_get_mic */
196 spnego_gss_verify_mic, /* gss_verify_mic */
197 spnego_gss_wrap, /* gss_wrap */
198 spnego_gss_unwrap, /* gss_unwrap */
199 spnego_gss_display_status,
200 NULL, /* gss_indicate_mechs */
201 spnego_gss_compare_name,
202 spnego_gss_display_name,
203 spnego_gss_import_name,
204 spnego_gss_release_name,
205 spnego_gss_inquire_cred, /* gss_inquire_cred */
206 NULL, /* gss_add_cred */
207 #ifndef LEAN_CLIENT
208 spnego_gss_export_sec_context, /* gss_export_sec_context */
209 spnego_gss_import_sec_context, /* gss_import_sec_context */
210 #else
211 NULL, /* gss_export_sec_context */
212 NULL, /* gss_import_sec_context */
213 #endif /* LEAN_CLIENT */
214 NULL, /* gss_inquire_cred_by_mech */
215 spnego_gss_inquire_names_for_mech,
216 spnego_gss_inquire_context, /* gss_inquire_context */
217 NULL, /* gss_internal_release_oid */
218 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
219 spnego_gss_localname,
220 NULL, /* gss_userok */
221 NULL, /* gss_export_name */
222 spnego_gss_duplicate_name, /* gss_duplicate_name */
223 NULL, /* gss_store_cred */
224 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
225 spnego_gss_inquire_cred_by_oid, /* gss_inquire_cred_by_oid */
226 spnego_gss_set_sec_context_option, /* gss_set_sec_context_option */
227 spnego_gss_set_cred_option, /* gssspi_set_cred_option */
228 NULL, /* gssspi_mech_invoke */
229 spnego_gss_wrap_aead,
230 spnego_gss_unwrap_aead,
231 spnego_gss_wrap_iov,
232 spnego_gss_unwrap_iov,
233 spnego_gss_wrap_iov_length,
234 spnego_gss_complete_auth_token,
235 spnego_gss_acquire_cred_impersonate_name,
236 NULL, /* gss_add_cred_impersonate_name */
237 spnego_gss_display_name_ext,
238 spnego_gss_inquire_name,
239 spnego_gss_get_name_attribute,
240 spnego_gss_set_name_attribute,
241 spnego_gss_delete_name_attribute,
242 spnego_gss_export_name_composite,
243 spnego_gss_map_name_to_any,
244 spnego_gss_release_any_name_mapping,
245 spnego_gss_pseudo_random,
246 spnego_gss_set_neg_mechs,
247 spnego_gss_inquire_saslname_for_mech,
248 spnego_gss_inquire_mech_for_saslname,
249 spnego_gss_inquire_attrs_for_mech,
250 spnego_gss_acquire_cred_from,
251 NULL, /* gss_store_cred_into */
252 spnego_gss_acquire_cred_with_password,
253 spnego_gss_export_cred,
254 spnego_gss_import_cred,
255 NULL, /* gssspi_import_sec_context_by_mech */
256 NULL, /* gssspi_import_name_by_mech */
257 NULL, /* gssspi_import_cred_by_mech */
258 spnego_gss_get_mic_iov,
259 spnego_gss_verify_mic_iov,
260 spnego_gss_get_mic_iov_length
261 };
262
263 #ifdef _GSS_STATIC_LINK
264 #include "mglueP.h"
265
gss_spnegomechglue_init(void)266 static int gss_spnegomechglue_init(void)
267 {
268 struct gss_mech_config mech_spnego;
269
270 memset(&mech_spnego, 0, sizeof(mech_spnego));
271 mech_spnego.mech = &spnego_mechanism;
272 mech_spnego.mechNameStr = "spnego";
273 mech_spnego.mech_type = GSS_C_NO_OID;
274
275 return gssint_register_mechinfo(&mech_spnego);
276 }
277 #else
278 gss_mechanism KRB5_CALLCONV
gss_mech_initialize(void)279 gss_mech_initialize(void)
280 {
281 return (&spnego_mechanism);
282 }
283
284 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
285 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
286 int gss_krb5int_lib_init(void);
287 #endif /* _GSS_STATIC_LINK */
288
gss_spnegoint_lib_init(void)289 int gss_spnegoint_lib_init(void)
290 {
291 int err;
292
293 err = k5_key_register(K5_KEY_GSS_SPNEGO_STATUS, NULL);
294 if (err)
295 return err;
296
297 #ifdef _GSS_STATIC_LINK
298 return gss_spnegomechglue_init();
299 #else
300 return 0;
301 #endif
302 }
303
gss_spnegoint_lib_fini(void)304 void gss_spnegoint_lib_fini(void)
305 {
306 k5_key_delete(K5_KEY_GSS_SPNEGO_STATUS);
307 }
308
309 static OM_uint32
create_spnego_cred(OM_uint32 * minor_status,gss_cred_id_t mcred,spnego_gss_cred_id_t * cred_out)310 create_spnego_cred(OM_uint32 *minor_status, gss_cred_id_t mcred,
311 spnego_gss_cred_id_t *cred_out)
312 {
313 spnego_gss_cred_id_t spcred;
314
315 *cred_out = NULL;
316 spcred = calloc(1, sizeof(*spcred));
317 if (spcred == NULL) {
318 *minor_status = ENOMEM;
319 return GSS_S_FAILURE;
320 }
321 spcred->mcred = mcred;
322 *cred_out = spcred;
323 return GSS_S_COMPLETE;
324 }
325
326 /*ARGSUSED*/
327 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)328 spnego_gss_acquire_cred(OM_uint32 *minor_status,
329 gss_name_t desired_name,
330 OM_uint32 time_req,
331 gss_OID_set desired_mechs,
332 gss_cred_usage_t cred_usage,
333 gss_cred_id_t *output_cred_handle,
334 gss_OID_set *actual_mechs,
335 OM_uint32 *time_rec)
336 {
337 return spnego_gss_acquire_cred_from(minor_status, desired_name, time_req,
338 desired_mechs, cred_usage, NULL,
339 output_cred_handle, actual_mechs,
340 time_rec);
341 }
342
343 /*ARGSUSED*/
344 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_from(OM_uint32 * minor_status,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)345 spnego_gss_acquire_cred_from(OM_uint32 *minor_status,
346 const gss_name_t desired_name,
347 OM_uint32 time_req,
348 const gss_OID_set desired_mechs,
349 gss_cred_usage_t cred_usage,
350 gss_const_key_value_set_t cred_store,
351 gss_cred_id_t *output_cred_handle,
352 gss_OID_set *actual_mechs,
353 OM_uint32 *time_rec)
354 {
355 OM_uint32 status, tmpmin;
356 gss_OID_set amechs;
357 gss_cred_id_t mcred = NULL;
358 spnego_gss_cred_id_t spcred = NULL;
359 dsyslog("Entering spnego_gss_acquire_cred\n");
360
361 if (actual_mechs)
362 *actual_mechs = NULL;
363
364 if (time_rec)
365 *time_rec = 0;
366
367 /* We will obtain a mechglue credential and wrap it in a
368 * spnego_gss_cred_id_rec structure. Allocate the wrapper. */
369 status = create_spnego_cred(minor_status, mcred, &spcred);
370 if (status != GSS_S_COMPLETE)
371 return (status);
372
373 /*
374 * Always use get_available_mechs to collect a list of
375 * mechs for which creds are available.
376 */
377 status = get_available_mechs(minor_status, desired_name,
378 cred_usage, cred_store, &mcred,
379 &amechs, time_rec);
380
381 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
382 (void) gssint_copy_oid_set(&tmpmin, amechs, actual_mechs);
383 }
384 (void) gss_release_oid_set(&tmpmin, &amechs);
385
386 if (status == GSS_S_COMPLETE) {
387 spcred->mcred = mcred;
388 *output_cred_handle = (gss_cred_id_t)spcred;
389 } else {
390 free(spcred);
391 *output_cred_handle = GSS_C_NO_CREDENTIAL;
392 }
393
394 dsyslog("Leaving spnego_gss_acquire_cred\n");
395 return (status);
396 }
397
398 /*ARGSUSED*/
399 OM_uint32 KRB5_CALLCONV
spnego_gss_release_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle)400 spnego_gss_release_cred(OM_uint32 *minor_status,
401 gss_cred_id_t *cred_handle)
402 {
403 spnego_gss_cred_id_t spcred = NULL;
404
405 dsyslog("Entering spnego_gss_release_cred\n");
406
407 if (minor_status == NULL || cred_handle == NULL)
408 return (GSS_S_CALL_INACCESSIBLE_WRITE);
409
410 *minor_status = 0;
411
412 if (*cred_handle == GSS_C_NO_CREDENTIAL)
413 return (GSS_S_COMPLETE);
414
415 spcred = (spnego_gss_cred_id_t)*cred_handle;
416 *cred_handle = GSS_C_NO_CREDENTIAL;
417 gss_release_oid_set(minor_status, &spcred->neg_mechs);
418 gss_release_cred(minor_status, &spcred->mcred);
419 free(spcred);
420
421 dsyslog("Leaving spnego_gss_release_cred\n");
422 return (GSS_S_COMPLETE);
423 }
424
425 static spnego_gss_ctx_id_t
create_spnego_ctx(int initiate)426 create_spnego_ctx(int initiate)
427 {
428 spnego_gss_ctx_id_t spnego_ctx = NULL;
429
430 spnego_ctx = malloc(sizeof(*spnego_ctx));
431 if (spnego_ctx == NULL) {
432 return (NULL);
433 }
434
435 spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
436 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
437 spnego_ctx->mech_set = NULL;
438 spnego_ctx->internal_mech = NULL;
439 spnego_ctx->DER_mechTypes.length = 0;
440 spnego_ctx->DER_mechTypes.value = NULL;
441 spnego_ctx->mic_reqd = 0;
442 spnego_ctx->mic_sent = 0;
443 spnego_ctx->mic_rcvd = 0;
444 spnego_ctx->mech_complete = 0;
445 spnego_ctx->nego_done = 0;
446 spnego_ctx->opened = 0;
447 spnego_ctx->initiate = initiate;
448 spnego_ctx->internal_name = GSS_C_NO_NAME;
449 spnego_ctx->actual_mech = GSS_C_NO_OID;
450 spnego_ctx->deleg_cred = GSS_C_NO_CREDENTIAL;
451 spnego_ctx->negoex_step = 0;
452 memset(&spnego_ctx->negoex_transcript, 0, sizeof(struct k5buf));
453 spnego_ctx->negoex_seqnum = 0;
454 K5_TAILQ_INIT(&spnego_ctx->negoex_mechs);
455 spnego_ctx->kctx = NULL;
456 memset(spnego_ctx->negoex_conv_id, 0, GUID_LENGTH);
457
458 return (spnego_ctx);
459 }
460
461 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
462 * gssntlmssp(655) controls(1) spnego_req_mechlistMIC(2) */
463 static const gss_OID_desc spnego_req_mechlistMIC_oid =
464 { 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x02" };
465
466 /*
467 * Return nonzero if the mechanism has reason to believe that a mechlistMIC
468 * exchange will be required. Microsoft servers erroneously require SPNEGO
469 * mechlistMIC if they see an internal MIC within an NTLMSSP Authenticate
470 * message, even if NTLMSSP was the preferred mechanism.
471 */
472 static int
mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)473 mech_requires_mechlistMIC(spnego_gss_ctx_id_t sc)
474 {
475 OM_uint32 major, minor;
476 gss_ctx_id_t ctx = sc->ctx_handle;
477 gss_OID oid = (gss_OID)&spnego_req_mechlistMIC_oid;
478 gss_buffer_set_t bufs;
479 int result;
480
481 major = gss_inquire_sec_context_by_oid(&minor, ctx, oid, &bufs);
482 if (major != GSS_S_COMPLETE)
483 return 0;
484
485 /* Report true if the mech returns a single buffer containing a single
486 * byte with value 1. */
487 result = (bufs != NULL && bufs->count == 1 &&
488 bufs->elements[0].length == 1 &&
489 memcmp(bufs->elements[0].value, "\1", 1) == 0);
490 (void) gss_release_buffer_set(&minor, &bufs);
491 return result;
492 }
493
494 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) Microsoft(311)
495 * security(2) mechanisms(2) NTLM(10) */
496 static const gss_OID_desc gss_mech_ntlmssp_oid =
497 { 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" };
498
499 /* iso(1) org(3) dod(6) internet(1) private(4) enterprises(1) samba(7165)
500 * gssntlmssp(655) controls(1) ntlmssp_reset_crypto(3) */
501 static const gss_OID_desc ntlmssp_reset_crypto_oid =
502 { 11, "\x2B\x06\x01\x04\x01\xB7\x7D\x85\x0F\x01\x03" };
503
504 /*
505 * MS-SPNG section 3.3.5.1 warns that the NTLM mechanism requires special
506 * handling of the crypto state to interop with Windows. If the mechanism for
507 * sc is SPNEGO, invoke a mechanism-specific operation on the context to reset
508 * the RC4 state after producing or verifying a MIC. Ignore a result of
509 * GSS_S_UNAVAILABLE for compatibility with older versions of the mechanism
510 * that do not support this functionality.
511 */
512 static OM_uint32
ntlmssp_reset_crypto_state(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 verify)513 ntlmssp_reset_crypto_state(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
514 OM_uint32 verify)
515 {
516 OM_uint32 major, minor;
517 gss_buffer_desc value;
518
519 if (!g_OID_equal(sc->internal_mech, &gss_mech_ntlmssp_oid))
520 return GSS_S_COMPLETE;
521
522 value.length = sizeof(verify);
523 value.value = &verify;
524 major = gss_set_sec_context_option(&minor, &sc->ctx_handle,
525 (gss_OID)&ntlmssp_reset_crypto_oid,
526 &value);
527 if (major == GSS_S_UNAVAILABLE)
528 return GSS_S_COMPLETE;
529 *minor_status = minor;
530 return major;
531 }
532
533 /*
534 * Both initiator and acceptor call here to verify and/or create mechListMIC,
535 * and to consistency-check the MIC state. handle_mic is invoked only if the
536 * negotiated mech has completed and supports MICs.
537 */
538 static OM_uint32
handle_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,int send_mechtok,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)539 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
540 int send_mechtok, spnego_gss_ctx_id_t sc,
541 gss_buffer_t *mic_out,
542 OM_uint32 *negState, send_token_flag *tokflag)
543 {
544 OM_uint32 ret;
545
546 ret = GSS_S_FAILURE;
547 *mic_out = GSS_C_NO_BUFFER;
548 if (mic_in != GSS_C_NO_BUFFER) {
549 if (sc->mic_rcvd) {
550 /* Reject MIC if we've already received a MIC. */
551 *negState = REJECT;
552 *tokflag = ERROR_TOKEN_SEND;
553 return GSS_S_DEFECTIVE_TOKEN;
554 }
555 } else if (sc->mic_reqd && !send_mechtok) {
556 /*
557 * If the peer sends the final mechanism token, it
558 * must send the MIC with that token if the
559 * negotiation requires MICs.
560 */
561 *negState = REJECT;
562 *tokflag = ERROR_TOKEN_SEND;
563 return GSS_S_DEFECTIVE_TOKEN;
564 }
565 ret = process_mic(minor_status, mic_in, sc, mic_out,
566 negState, tokflag);
567 if (ret != GSS_S_COMPLETE) {
568 return ret;
569 }
570 if (sc->mic_reqd) {
571 assert(sc->mic_sent || sc->mic_rcvd);
572 }
573 if (sc->mic_sent && sc->mic_rcvd) {
574 ret = GSS_S_COMPLETE;
575 *negState = ACCEPT_COMPLETE;
576 if (*mic_out == GSS_C_NO_BUFFER) {
577 /*
578 * We sent a MIC on the previous pass; we
579 * shouldn't be sending a mechanism token.
580 */
581 assert(!send_mechtok);
582 *tokflag = NO_TOKEN_SEND;
583 } else {
584 *tokflag = CONT_TOKEN_SEND;
585 }
586 } else if (sc->mic_reqd) {
587 *negState = ACCEPT_INCOMPLETE;
588 ret = GSS_S_CONTINUE_NEEDED;
589 } else if (*negState == ACCEPT_COMPLETE) {
590 ret = GSS_S_COMPLETE;
591 } else {
592 ret = GSS_S_CONTINUE_NEEDED;
593 }
594 return ret;
595 }
596
597 /*
598 * Perform the actual verification and/or generation of mechListMIC.
599 */
600 static OM_uint32
process_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)601 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
602 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
603 OM_uint32 *negState, send_token_flag *tokflag)
604 {
605 OM_uint32 ret, tmpmin;
606 gss_qop_t qop_state;
607 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
608
609 ret = GSS_S_FAILURE;
610 if (mic_in != GSS_C_NO_BUFFER) {
611 ret = gss_verify_mic(minor_status, sc->ctx_handle,
612 &sc->DER_mechTypes,
613 mic_in, &qop_state);
614 if (ret == GSS_S_COMPLETE)
615 ret = ntlmssp_reset_crypto_state(minor_status, sc, 1);
616 if (ret != GSS_S_COMPLETE) {
617 *negState = REJECT;
618 *tokflag = ERROR_TOKEN_SEND;
619 return ret;
620 }
621 /* If we got a MIC, we must send a MIC. */
622 sc->mic_reqd = 1;
623 sc->mic_rcvd = 1;
624 }
625 if (sc->mic_reqd && !sc->mic_sent) {
626 ret = gss_get_mic(minor_status, sc->ctx_handle,
627 GSS_C_QOP_DEFAULT,
628 &sc->DER_mechTypes,
629 &tmpmic);
630 if (ret == GSS_S_COMPLETE)
631 ret = ntlmssp_reset_crypto_state(minor_status, sc, 0);
632 if (ret != GSS_S_COMPLETE) {
633 gss_release_buffer(&tmpmin, &tmpmic);
634 *tokflag = NO_TOKEN_SEND;
635 return ret;
636 }
637 *mic_out = malloc(sizeof(gss_buffer_desc));
638 if (*mic_out == GSS_C_NO_BUFFER) {
639 gss_release_buffer(&tmpmin, &tmpmic);
640 *tokflag = NO_TOKEN_SEND;
641 return GSS_S_FAILURE;
642 }
643 **mic_out = tmpmic;
644 sc->mic_sent = 1;
645 }
646 return GSS_S_COMPLETE;
647 }
648
649 /* Create a new SPNEGO context handle for the initial call to
650 * spnego_gss_init_sec_context(). */
651 static OM_uint32
init_ctx_new(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,send_token_flag * tokflag,spnego_gss_ctx_id_t * sc_out)652 init_ctx_new(OM_uint32 *minor_status,
653 spnego_gss_cred_id_t spcred,
654 send_token_flag *tokflag,
655 spnego_gss_ctx_id_t *sc_out)
656 {
657 OM_uint32 ret;
658 spnego_gss_ctx_id_t sc = NULL;
659
660 *sc_out = NULL;
661
662 sc = create_spnego_ctx(1);
663 if (sc == NULL)
664 return GSS_S_FAILURE;
665
666 /* determine negotiation mech set */
667 ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_INITIATE);
668 if (ret != GSS_S_COMPLETE)
669 goto cleanup;
670
671 /* Set an initial internal mech to make the first context token. */
672 sc->internal_mech = &sc->mech_set->elements[0];
673
674 if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
675 ret = GSS_S_FAILURE;
676 goto cleanup;
677 }
678
679 sc->ctx_handle = GSS_C_NO_CONTEXT;
680 *sc_out = sc;
681 sc = NULL;
682 *tokflag = INIT_TOKEN_SEND;
683 ret = GSS_S_COMPLETE;
684
685 cleanup:
686 release_spnego_ctx(&sc);
687 return ret;
688 }
689
690 /*
691 * Called by second and later calls to spnego_gss_init_sec_context()
692 * to decode reply and update state.
693 */
694 static OM_uint32
init_ctx_cont(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_buffer_t buf,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * acc_negState,send_token_flag * tokflag)695 init_ctx_cont(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
696 gss_buffer_t buf, gss_buffer_t *responseToken,
697 gss_buffer_t *mechListMIC, OM_uint32 *acc_negState,
698 send_token_flag *tokflag)
699 {
700 OM_uint32 ret, tmpmin;
701 gss_OID supportedMech = GSS_C_NO_OID;
702 struct k5input in;
703
704 *acc_negState = UNSPECIFIED;
705 *tokflag = ERROR_TOKEN_SEND;
706
707 k5_input_init(&in, buf->value, buf->length);
708 ret = get_negTokenResp(minor_status, &in, acc_negState, &supportedMech,
709 responseToken, mechListMIC);
710 if (ret != GSS_S_COMPLETE)
711 goto cleanup;
712
713 /* Bail out now on a reject with no error token. If we have an error
714 * token, keep going and get a better error status from the mech. */
715 if (*acc_negState == REJECT && *responseToken == GSS_C_NO_BUFFER) {
716 if (!sc->nego_done) {
717 /* RFC 4178 says to return GSS_S_BAD_MECH on a
718 * mechanism negotiation failure. */
719 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
720 map_errcode(minor_status);
721 ret = GSS_S_BAD_MECH;
722 } else {
723 ret = GSS_S_FAILURE;
724 }
725 *tokflag = NO_TOKEN_SEND;
726 goto cleanup;
727 }
728 /*
729 * nego_done is false for the first call to init_ctx_cont()
730 */
731 if (!sc->nego_done) {
732 ret = init_ctx_nego(minor_status, sc, *acc_negState,
733 supportedMech, responseToken, mechListMIC,
734 tokflag);
735 } else if ((!sc->mech_complete && *responseToken == GSS_C_NO_BUFFER) ||
736 (sc->mech_complete && *responseToken != GSS_C_NO_BUFFER)) {
737 /* Missing or spurious token from acceptor. */
738 ret = GSS_S_DEFECTIVE_TOKEN;
739 } else if (!sc->mech_complete ||
740 (sc->mic_reqd &&
741 (sc->ctx_flags & GSS_C_INTEG_FLAG))) {
742 /* Not obviously done; we may decide we're done later in
743 * init_ctx_call_init or handle_mic. */
744 *tokflag = CONT_TOKEN_SEND;
745 ret = GSS_S_COMPLETE;
746 } else {
747 /* mech finished on last pass and no MIC required, so done. */
748 *tokflag = NO_TOKEN_SEND;
749 ret = GSS_S_COMPLETE;
750 }
751 cleanup:
752 if (supportedMech != GSS_C_NO_OID)
753 generic_gss_release_oid(&tmpmin, &supportedMech);
754 return ret;
755 }
756
757 /*
758 * Consistency checking and mechanism negotiation handling for second
759 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
760 * update internal state if acceptor has counter-proposed.
761 */
762 static OM_uint32
init_ctx_nego(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)763 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
764 OM_uint32 acc_negState, gss_OID supportedMech,
765 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
766 send_token_flag *tokflag)
767 {
768 OM_uint32 ret;
769
770 *tokflag = ERROR_TOKEN_SEND;
771 ret = GSS_S_DEFECTIVE_TOKEN;
772
773 /*
774 * According to RFC 4178, both supportedMech and negState must be
775 * present in the first acceptor token. However, some Java
776 * implementations include only a responseToken in the first
777 * NegTokenResp. In this case we can use sc->internal_mech as the
778 * negotiated mechanism. (We do not currently look at acc_negState
779 * when continuing with the optimistic mechanism.)
780 */
781 if (supportedMech == GSS_C_NO_OID)
782 supportedMech = sc->internal_mech;
783
784 /*
785 * If the mechanism we sent is not the mechanism returned from
786 * the server, we need to handle the server's counter
787 * proposal. There is a bug in SAMBA servers that always send
788 * the old Kerberos mech OID, even though we sent the new one.
789 * So we will treat all the Kerberos mech OIDS as the same.
790 */
791 if (!(is_kerb_mech(supportedMech) &&
792 is_kerb_mech(sc->internal_mech)) &&
793 !g_OID_equal(supportedMech, sc->internal_mech)) {
794 ret = init_ctx_reselect(minor_status, sc,
795 acc_negState, supportedMech,
796 responseToken, mechListMIC, tokflag);
797
798 } else if (*responseToken == GSS_C_NO_BUFFER) {
799 if (sc->mech_complete) {
800 /*
801 * Mech completed on first call to its
802 * init_sec_context(). Acceptor sends no mech
803 * token.
804 */
805 *tokflag = NO_TOKEN_SEND;
806 ret = GSS_S_COMPLETE;
807 } else {
808 /*
809 * Reject missing mech token when optimistic
810 * mech selected.
811 */
812 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
813 map_errcode(minor_status);
814 ret = GSS_S_DEFECTIVE_TOKEN;
815 }
816 } else if ((*responseToken)->length == 0 && sc->mech_complete) {
817 /* Handle old IIS servers returning empty token instead of
818 * null tokens in the non-mutual auth case. */
819 *tokflag = NO_TOKEN_SEND;
820 ret = GSS_S_COMPLETE;
821 } else if (sc->mech_complete) {
822 /* Reject spurious mech token. */
823 ret = GSS_S_DEFECTIVE_TOKEN;
824 } else {
825 *tokflag = CONT_TOKEN_SEND;
826 ret = GSS_S_COMPLETE;
827 }
828 sc->nego_done = 1;
829 return ret;
830 }
831
832 /*
833 * Handle acceptor's counter-proposal of an alternative mechanism.
834 */
835 static OM_uint32
init_ctx_reselect(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,send_token_flag * tokflag)836 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
837 OM_uint32 acc_negState, gss_OID supportedMech,
838 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
839 send_token_flag *tokflag)
840 {
841 OM_uint32 tmpmin;
842 size_t i;
843
844 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
845 GSS_C_NO_BUFFER);
846
847 /* Find supportedMech in sc->mech_set. */
848 for (i = 0; i < sc->mech_set->count; i++) {
849 if (g_OID_equal(supportedMech, &sc->mech_set->elements[i]))
850 break;
851 }
852 if (i == sc->mech_set->count)
853 return GSS_S_DEFECTIVE_TOKEN;
854 sc->internal_mech = &sc->mech_set->elements[i];
855
856 /*
857 * A server conforming to RFC4178 MUST set REQUEST_MIC here, but
858 * Windows Server 2003 and earlier implement (roughly) RFC2478 instead,
859 * and send ACCEPT_INCOMPLETE. Tolerate that only if we are falling
860 * back to NTLMSSP.
861 */
862 if (acc_negState == ACCEPT_INCOMPLETE) {
863 if (!g_OID_equal(supportedMech, &gss_mech_ntlmssp_oid))
864 return GSS_S_DEFECTIVE_TOKEN;
865 } else if (acc_negState != REQUEST_MIC) {
866 return GSS_S_DEFECTIVE_TOKEN;
867 }
868
869 sc->mech_complete = 0;
870 sc->mic_reqd = (acc_negState == REQUEST_MIC);
871 *tokflag = CONT_TOKEN_SEND;
872 return GSS_S_COMPLETE;
873 }
874
875 /*
876 * Wrap call to mechanism gss_init_sec_context() and update state
877 * accordingly.
878 */
879 static OM_uint32
init_ctx_call_init(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,OM_uint32 acc_negState,gss_name_t target_name,OM_uint32 req_flags,OM_uint32 time_req,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,send_token_flag * send_token)880 init_ctx_call_init(OM_uint32 *minor_status,
881 spnego_gss_ctx_id_t sc,
882 spnego_gss_cred_id_t spcred,
883 OM_uint32 acc_negState,
884 gss_name_t target_name,
885 OM_uint32 req_flags,
886 OM_uint32 time_req,
887 gss_buffer_t mechtok_in,
888 gss_channel_bindings_t bindings,
889 gss_buffer_t mechtok_out,
890 OM_uint32 *time_rec,
891 send_token_flag *send_token)
892 {
893 OM_uint32 ret, tmpret, tmpmin, mech_req_flags;
894 gss_cred_id_t mcred;
895
896 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
897
898 mech_req_flags = req_flags;
899 if (spcred == NULL || !spcred->no_ask_integ)
900 mech_req_flags |= GSS_C_INTEG_FLAG;
901
902 if (gss_oid_equal(sc->internal_mech, &negoex_mech)) {
903 ret = negoex_init(minor_status, sc, mcred, target_name,
904 mech_req_flags, time_req, mechtok_in,
905 bindings, mechtok_out, time_rec);
906 } else {
907 ret = gss_init_sec_context(minor_status, mcred,
908 &sc->ctx_handle, target_name,
909 sc->internal_mech, mech_req_flags,
910 time_req, bindings, mechtok_in,
911 &sc->actual_mech, mechtok_out,
912 &sc->ctx_flags, time_rec);
913 }
914
915 /* Bail out if the acceptor gave us an error token but the mech didn't
916 * see it as an error. */
917 if (acc_negState == REJECT && !GSS_ERROR(ret)) {
918 ret = GSS_S_DEFECTIVE_TOKEN;
919 goto fail;
920 }
921
922 if (ret == GSS_S_COMPLETE) {
923 sc->mech_complete = 1;
924 /*
925 * Microsoft SPNEGO implementations expect an even number of
926 * token exchanges. So if we're sending a final token, ask for
927 * a zero-length token back from the server. Also ask for a
928 * token back if this is the first token or if a MIC exchange
929 * is required.
930 */
931 if (*send_token == CONT_TOKEN_SEND &&
932 mechtok_out->length == 0 &&
933 (!sc->mic_reqd || !(sc->ctx_flags & GSS_C_INTEG_FLAG)))
934 *send_token = NO_TOKEN_SEND;
935
936 return GSS_S_COMPLETE;
937 }
938
939 if (ret == GSS_S_CONTINUE_NEEDED)
940 return GSS_S_COMPLETE;
941
942 if (*send_token != INIT_TOKEN_SEND) {
943 *send_token = ERROR_TOKEN_SEND;
944 return ret;
945 }
946
947 /*
948 * Since this is the first token, we can fall back to later mechanisms
949 * in the list. Since the mechanism list is expected to be short, we
950 * can do this with recursion. If all mechanisms produce errors, the
951 * caller should get the error from the first mech in the list.
952 */
953 gssalloc_free(sc->mech_set->elements->elements);
954 memmove(sc->mech_set->elements, sc->mech_set->elements + 1,
955 --sc->mech_set->count * sizeof(*sc->mech_set->elements));
956 if (sc->mech_set->count == 0)
957 goto fail;
958 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
959 if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0)
960 goto fail;
961 gss_delete_sec_context(&tmpmin, &sc->ctx_handle, GSS_C_NO_BUFFER);
962 tmpret = init_ctx_call_init(&tmpmin, sc, spcred, acc_negState,
963 target_name, req_flags, time_req,
964 mechtok_in, bindings, mechtok_out,
965 time_rec, send_token);
966 if (HARD_ERROR(tmpret))
967 goto fail;
968 *minor_status = tmpmin;
969 return tmpret;
970
971 fail:
972 /* Don't output token on error from first call. */
973 *send_token = NO_TOKEN_SEND;
974 return ret;
975 }
976
977 /*ARGSUSED*/
978 OM_uint32 KRB5_CALLCONV
spnego_gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)979 spnego_gss_init_sec_context(
980 OM_uint32 *minor_status,
981 gss_cred_id_t claimant_cred_handle,
982 gss_ctx_id_t *context_handle,
983 gss_name_t target_name,
984 gss_OID mech_type,
985 OM_uint32 req_flags,
986 OM_uint32 time_req,
987 gss_channel_bindings_t bindings,
988 gss_buffer_t input_token,
989 gss_OID *actual_mech,
990 gss_buffer_t output_token,
991 OM_uint32 *ret_flags,
992 OM_uint32 *time_rec)
993 {
994 send_token_flag send_token = NO_TOKEN_SEND;
995 OM_uint32 tmpmin, ret, negState = UNSPECIFIED, acc_negState;
996 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
997 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
998 spnego_gss_cred_id_t spcred = NULL;
999 spnego_gss_ctx_id_t spnego_ctx = NULL;
1000
1001 dsyslog("Entering init_sec_context\n");
1002
1003 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1004
1005 /*
1006 * This function works in three steps:
1007 *
1008 * 1. Perform mechanism negotiation.
1009 * 2. Invoke the negotiated or optimistic mech's gss_init_sec_context
1010 * function and examine the results.
1011 * 3. Process or generate MICs if necessary.
1012 *
1013 * The three steps share responsibility for determining when the
1014 * exchange is complete. If the selected mech completed in a previous
1015 * call and no MIC exchange is expected, then step 1 will decide. If
1016 * the selected mech completes in this call and no MIC exchange is
1017 * expected, then step 2 will decide. If a MIC exchange is expected,
1018 * then step 3 will decide. If an error occurs in any step, the
1019 * exchange will be aborted, possibly with an error token.
1020 *
1021 * negState determines the state of the negotiation, and is
1022 * communicated to the acceptor if a continuing token is sent.
1023 * send_token is used to indicate what type of token, if any, should be
1024 * generated.
1025 */
1026
1027 /* Validate arguments. */
1028 if (minor_status != NULL)
1029 *minor_status = 0;
1030 if (output_token != GSS_C_NO_BUFFER) {
1031 output_token->length = 0;
1032 output_token->value = NULL;
1033 }
1034 if (minor_status == NULL ||
1035 output_token == GSS_C_NO_BUFFER ||
1036 context_handle == NULL)
1037 return GSS_S_CALL_INACCESSIBLE_WRITE;
1038
1039 if (actual_mech != NULL)
1040 *actual_mech = GSS_C_NO_OID;
1041 if (time_rec != NULL)
1042 *time_rec = 0;
1043
1044 /* Step 1: perform mechanism negotiation. */
1045 spcred = (spnego_gss_cred_id_t)claimant_cred_handle;
1046 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1047 if (spnego_ctx == NULL) {
1048 ret = init_ctx_new(minor_status, spcred, &send_token,
1049 &spnego_ctx);
1050 if (ret != GSS_S_COMPLETE)
1051 goto cleanup;
1052 *context_handle = (gss_ctx_id_t)spnego_ctx;
1053 acc_negState = UNSPECIFIED;
1054 } else {
1055 ret = init_ctx_cont(minor_status, spnego_ctx, input_token,
1056 &mechtok_in, &mechListMIC_in,
1057 &acc_negState, &send_token);
1058 if (ret != GSS_S_COMPLETE)
1059 goto cleanup;
1060 }
1061
1062 /* Step 2: invoke the selected or optimistic mechanism's
1063 * gss_init_sec_context function, if it didn't complete previously. */
1064 if (!spnego_ctx->mech_complete) {
1065 ret = init_ctx_call_init(minor_status, spnego_ctx, spcred,
1066 acc_negState, target_name, req_flags,
1067 time_req, mechtok_in, bindings,
1068 &mechtok_out, time_rec, &send_token);
1069 if (ret != GSS_S_COMPLETE)
1070 goto cleanup;
1071
1072 /* Give the mechanism a chance to force a mechlistMIC. */
1073 if (mech_requires_mechlistMIC(spnego_ctx))
1074 spnego_ctx->mic_reqd = 1;
1075 }
1076
1077 /* Step 3: process or generate the MIC, if the negotiated mech is
1078 * complete and supports MICs. Also decide the outgoing negState. */
1079 negState = ACCEPT_INCOMPLETE;
1080 if (spnego_ctx->mech_complete &&
1081 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1082
1083 ret = handle_mic(minor_status,
1084 mechListMIC_in,
1085 (mechtok_out.length != 0),
1086 spnego_ctx, &mechListMIC_out,
1087 &negState, &send_token);
1088 if (HARD_ERROR(ret))
1089 goto cleanup;
1090 }
1091
1092 if (ret_flags != NULL)
1093 *ret_flags = spnego_ctx->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1094
1095 ret = (send_token == NO_TOKEN_SEND || negState == ACCEPT_COMPLETE) ?
1096 GSS_S_COMPLETE : GSS_S_CONTINUE_NEEDED;
1097
1098 cleanup:
1099 if (send_token == INIT_TOKEN_SEND) {
1100 if (make_spnego_tokenInit_msg(spnego_ctx,
1101 0,
1102 mechListMIC_out,
1103 req_flags,
1104 &mechtok_out, send_token,
1105 output_token) < 0) {
1106 ret = GSS_S_FAILURE;
1107 }
1108 } else if (send_token != NO_TOKEN_SEND) {
1109 if (send_token == ERROR_TOKEN_SEND)
1110 negState = REJECT;
1111 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1112 &mechtok_out, mechListMIC_out,
1113 send_token,
1114 output_token) < 0) {
1115 ret = GSS_S_FAILURE;
1116 }
1117 }
1118 gss_release_buffer(&tmpmin, &mechtok_out);
1119 if (ret == GSS_S_COMPLETE) {
1120 spnego_ctx->opened = 1;
1121 if (actual_mech != NULL)
1122 *actual_mech = spnego_ctx->actual_mech;
1123 /* Get an updated lifetime if we didn't call into the mech. */
1124 if (time_rec != NULL && *time_rec == 0) {
1125 (void) gss_context_time(&tmpmin,
1126 spnego_ctx->ctx_handle,
1127 time_rec);
1128 }
1129 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1130 if (spnego_ctx != NULL) {
1131 gss_delete_sec_context(&tmpmin,
1132 &spnego_ctx->ctx_handle,
1133 GSS_C_NO_BUFFER);
1134 release_spnego_ctx(&spnego_ctx);
1135 }
1136 *context_handle = GSS_C_NO_CONTEXT;
1137 }
1138 if (mechtok_in != GSS_C_NO_BUFFER) {
1139 gss_release_buffer(&tmpmin, mechtok_in);
1140 free(mechtok_in);
1141 }
1142 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1143 gss_release_buffer(&tmpmin, mechListMIC_in);
1144 free(mechListMIC_in);
1145 }
1146 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1147 gss_release_buffer(&tmpmin, mechListMIC_out);
1148 free(mechListMIC_out);
1149 }
1150 return ret;
1151 } /* init_sec_context */
1152
1153 /* We don't want to import KRB5 headers here */
1154 static const gss_OID_desc gss_mech_krb5_oid =
1155 { 9, "\052\206\110\206\367\022\001\002\002" };
1156 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1157 { 9, "\052\206\110\202\367\022\001\002\002" };
1158
1159 /*
1160 * NegHints ::= SEQUENCE {
1161 * hintName [0] GeneralString OPTIONAL,
1162 * hintAddress [1] OCTET STRING OPTIONAL
1163 * }
1164 */
1165
1166 #define HOST_PREFIX "host@"
1167 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1168
1169 /* Encode the dummy hintname string (as specified in [MS-SPNG]) into a
1170 * DER-encoded [0] tagged GeneralString, and place the result in *outbuf. */
1171 static int
make_NegHints(OM_uint32 * minor_status,gss_buffer_t * outbuf)1172 make_NegHints(OM_uint32 *minor_status, gss_buffer_t *outbuf)
1173 {
1174 OM_uint32 major_status;
1175 size_t hint_len, tlen;
1176 uint8_t *t;
1177 const char *hintname = "not_defined_in_RFC4178@please_ignore";
1178 const size_t hintname_len = strlen(hintname);
1179 struct k5buf buf;
1180
1181 *outbuf = GSS_C_NO_BUFFER;
1182 major_status = GSS_S_FAILURE;
1183
1184 hint_len = k5_der_value_len(hintname_len);
1185 tlen = k5_der_value_len(hint_len);
1186
1187 t = gssalloc_malloc(tlen);
1188 if (t == NULL) {
1189 *minor_status = ENOMEM;
1190 goto errout;
1191 }
1192 k5_buf_init_fixed(&buf, t, tlen);
1193
1194 k5_der_add_taglen(&buf, CONTEXT | 0x00, hint_len);
1195 k5_der_add_value(&buf, GENERAL_STRING, hintname, hintname_len);
1196 assert(buf.len == tlen);
1197
1198 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1199 if (*outbuf == NULL) {
1200 *minor_status = ENOMEM;
1201 goto errout;
1202 }
1203 (*outbuf)->value = (void *)t;
1204 (*outbuf)->length = tlen;
1205
1206 t = NULL; /* don't free */
1207
1208 *minor_status = 0;
1209 major_status = GSS_S_COMPLETE;
1210
1211 errout:
1212 if (t != NULL) {
1213 free(t);
1214 }
1215
1216 return (major_status);
1217 }
1218
1219 /*
1220 * Create a new SPNEGO context handle for the initial call to
1221 * spnego_gss_accept_sec_context() when the request is empty. For empty
1222 * requests, we implement the Microsoft NegHints extension to SPNEGO for
1223 * compatibility with some versions of Samba. See:
1224 * https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-spng/8e71cf53-e867-4b79-b5b5-38c92be3d472
1225 */
1226 static OM_uint32
acc_ctx_hints(OM_uint32 * minor_status,spnego_gss_cred_id_t spcred,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1227 acc_ctx_hints(OM_uint32 *minor_status,
1228 spnego_gss_cred_id_t spcred,
1229 gss_buffer_t *mechListMIC,
1230 OM_uint32 *negState,
1231 send_token_flag *return_token,
1232 spnego_gss_ctx_id_t *sc_out)
1233 {
1234 OM_uint32 ret;
1235 spnego_gss_ctx_id_t sc = NULL;
1236
1237 *mechListMIC = GSS_C_NO_BUFFER;
1238 *return_token = NO_TOKEN_SEND;
1239 *negState = REJECT;
1240 *minor_status = 0;
1241 *sc_out = NULL;
1242
1243 ret = make_NegHints(minor_status, mechListMIC);
1244 if (ret != GSS_S_COMPLETE)
1245 goto cleanup;
1246
1247 sc = create_spnego_ctx(0);
1248 if (sc == NULL) {
1249 ret = GSS_S_FAILURE;
1250 goto cleanup;
1251 }
1252
1253 ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1254 if (ret != GSS_S_COMPLETE)
1255 goto cleanup;
1256
1257 if (put_mech_set(sc->mech_set, &sc->DER_mechTypes) < 0) {
1258 ret = GSS_S_FAILURE;
1259 goto cleanup;
1260 }
1261 sc->internal_mech = GSS_C_NO_OID;
1262
1263 *negState = ACCEPT_INCOMPLETE;
1264 *return_token = INIT_TOKEN_SEND;
1265 sc->firstpass = 1;
1266 *sc_out = sc;
1267 sc = NULL;
1268 ret = GSS_S_COMPLETE;
1269
1270 cleanup:
1271 release_spnego_ctx(&sc);
1272
1273 return ret;
1274 }
1275
1276 /*
1277 * Create a new SPNEGO context handle for the initial call to
1278 * spnego_gss_accept_sec_context(). Set negState to REJECT if the token is
1279 * defective, else ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether
1280 * the initiator's preferred mechanism is supported.
1281 */
1282 static OM_uint32
acc_ctx_new(OM_uint32 * minor_status,gss_buffer_t buf,spnego_gss_cred_id_t spcred,gss_buffer_t * mechToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token,spnego_gss_ctx_id_t * sc_out)1283 acc_ctx_new(OM_uint32 *minor_status,
1284 gss_buffer_t buf,
1285 spnego_gss_cred_id_t spcred,
1286 gss_buffer_t *mechToken,
1287 gss_buffer_t *mechListMIC,
1288 OM_uint32 *negState,
1289 send_token_flag *return_token,
1290 spnego_gss_ctx_id_t *sc_out)
1291 {
1292 OM_uint32 tmpmin, ret, req_flags;
1293 gss_OID_set mechTypes;
1294 gss_buffer_desc der_mechTypes;
1295 gss_OID mech_wanted;
1296 spnego_gss_ctx_id_t sc = NULL;
1297
1298 ret = GSS_S_DEFECTIVE_TOKEN;
1299 der_mechTypes.length = 0;
1300 der_mechTypes.value = NULL;
1301 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1302 mechTypes = GSS_C_NO_OID_SET;
1303 *return_token = ERROR_TOKEN_SEND;
1304 *negState = REJECT;
1305 *minor_status = 0;
1306
1307 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1308 &mechTypes, &req_flags,
1309 mechToken, mechListMIC);
1310 if (ret != GSS_S_COMPLETE) {
1311 goto cleanup;
1312 }
1313
1314 sc = create_spnego_ctx(0);
1315 if (sc == NULL) {
1316 ret = GSS_S_FAILURE;
1317 *return_token = NO_TOKEN_SEND;
1318 goto cleanup;
1319 }
1320
1321 ret = get_negotiable_mechs(minor_status, sc, spcred, GSS_C_ACCEPT);
1322 if (ret != GSS_S_COMPLETE) {
1323 *return_token = NO_TOKEN_SEND;
1324 goto cleanup;
1325 }
1326 /*
1327 * Select the best match between the list of mechs
1328 * that the initiator requested and the list that
1329 * the acceptor will support.
1330 */
1331 mech_wanted = negotiate_mech(sc, mechTypes, negState);
1332 if (*negState == REJECT) {
1333 ret = GSS_S_BAD_MECH;
1334 goto cleanup;
1335 }
1336
1337 sc->internal_mech = mech_wanted;
1338 sc->DER_mechTypes = der_mechTypes;
1339 der_mechTypes.length = 0;
1340 der_mechTypes.value = NULL;
1341
1342 if (*negState == REQUEST_MIC)
1343 sc->mic_reqd = 1;
1344
1345 *return_token = INIT_TOKEN_SEND;
1346 sc->firstpass = 1;
1347 *sc_out = sc;
1348 sc = NULL;
1349 ret = GSS_S_COMPLETE;
1350
1351 cleanup:
1352 release_spnego_ctx(&sc);
1353 gss_release_oid_set(&tmpmin, &mechTypes);
1354 if (der_mechTypes.length != 0)
1355 gss_release_buffer(&tmpmin, &der_mechTypes);
1356
1357 return ret;
1358 }
1359
1360 static OM_uint32
acc_ctx_cont(OM_uint32 * minstat,gss_buffer_t buf,spnego_gss_ctx_id_t sc,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1361 acc_ctx_cont(OM_uint32 *minstat,
1362 gss_buffer_t buf,
1363 spnego_gss_ctx_id_t sc,
1364 gss_buffer_t *responseToken,
1365 gss_buffer_t *mechListMIC,
1366 OM_uint32 *negState,
1367 send_token_flag *return_token)
1368 {
1369 OM_uint32 ret, tmpmin;
1370 gss_OID supportedMech;
1371 struct k5input in;
1372
1373 ret = GSS_S_DEFECTIVE_TOKEN;
1374 *negState = REJECT;
1375 *minstat = 0;
1376 supportedMech = GSS_C_NO_OID;
1377 *return_token = ERROR_TOKEN_SEND;
1378 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1379
1380 k5_input_init(&in, buf->value, buf->length);
1381
1382 /* Attempt to work with old Sun SPNEGO. */
1383 if (in.len > 0 && *in.ptr == HEADER_ID) {
1384 ret = verify_token_header(&in, gss_mech_spnego);
1385 if (ret) {
1386 *minstat = ret;
1387 return GSS_S_DEFECTIVE_TOKEN;
1388 }
1389 }
1390
1391 ret = get_negTokenResp(minstat, &in, negState, &supportedMech,
1392 responseToken, mechListMIC);
1393 if (ret != GSS_S_COMPLETE)
1394 goto cleanup;
1395
1396 if (*responseToken == GSS_C_NO_BUFFER &&
1397 *mechListMIC == GSS_C_NO_BUFFER) {
1398
1399 ret = GSS_S_DEFECTIVE_TOKEN;
1400 goto cleanup;
1401 }
1402 if (supportedMech != GSS_C_NO_OID) {
1403 ret = GSS_S_DEFECTIVE_TOKEN;
1404 goto cleanup;
1405 }
1406 sc->firstpass = 0;
1407 *negState = ACCEPT_INCOMPLETE;
1408 *return_token = CONT_TOKEN_SEND;
1409 cleanup:
1410 if (supportedMech != GSS_C_NO_OID) {
1411 generic_gss_release_oid(&tmpmin, &supportedMech);
1412 }
1413 return ret;
1414 }
1415
1416 /*
1417 * Verify that mech OID is either exactly the same as the negotiated
1418 * mech OID, or is a mech OID supported by the negotiated mech. MS
1419 * implementations can list a most preferred mech using an incorrect
1420 * krb5 OID while emitting a krb5 initiator mech token having the
1421 * correct krb5 mech OID.
1422 */
1423 static OM_uint32
acc_ctx_vfy_oid(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_OID mechoid,OM_uint32 * negState,send_token_flag * tokflag)1424 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1425 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1426 OM_uint32 *negState, send_token_flag *tokflag)
1427 {
1428 OM_uint32 ret, tmpmin;
1429 gss_mechanism mech = NULL;
1430 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1431 int present = 0;
1432
1433 if (g_OID_equal(sc->internal_mech, mechoid))
1434 return GSS_S_COMPLETE;
1435
1436 mech = gssint_get_mechanism(sc->internal_mech);
1437 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1438 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1439 map_errcode(minor_status);
1440 *negState = REJECT;
1441 *tokflag = ERROR_TOKEN_SEND;
1442 return GSS_S_BAD_MECH;
1443 }
1444 ret = mech->gss_indicate_mechs(minor_status, &mech_set);
1445 if (ret != GSS_S_COMPLETE) {
1446 *tokflag = NO_TOKEN_SEND;
1447 map_error(minor_status, mech);
1448 goto cleanup;
1449 }
1450 ret = gss_test_oid_set_member(minor_status, mechoid,
1451 mech_set, &present);
1452 if (ret != GSS_S_COMPLETE)
1453 goto cleanup;
1454 if (!present) {
1455 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1456 map_errcode(minor_status);
1457 *negState = REJECT;
1458 *tokflag = ERROR_TOKEN_SEND;
1459 ret = GSS_S_BAD_MECH;
1460 }
1461 cleanup:
1462 gss_release_oid_set(&tmpmin, &mech_set);
1463 return ret;
1464 }
1465 #ifndef LEAN_CLIENT
1466 /*
1467 * Wrap call to gss_accept_sec_context() and update state
1468 * accordingly.
1469 */
1470 static OM_uint32
acc_ctx_call_acc(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_buffer_t mechtok_in,gss_channel_bindings_t bindings,gss_buffer_t mechtok_out,OM_uint32 * time_rec,OM_uint32 * negState,send_token_flag * tokflag)1471 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1472 spnego_gss_cred_id_t spcred, gss_buffer_t mechtok_in,
1473 gss_channel_bindings_t bindings, gss_buffer_t mechtok_out,
1474 OM_uint32 *time_rec, OM_uint32 *negState,
1475 send_token_flag *tokflag)
1476 {
1477 OM_uint32 ret, tmpmin;
1478 gss_OID_desc mechoid;
1479 gss_cred_id_t mcred;
1480 int negoex = gss_oid_equal(sc->internal_mech, &negoex_mech);
1481
1482 if (sc->ctx_handle == GSS_C_NO_CONTEXT && !negoex) {
1483 /*
1484 * mechoid is an alias; don't free it.
1485 */
1486 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1487 if (ret != GSS_S_COMPLETE) {
1488 *tokflag = NO_TOKEN_SEND;
1489 return ret;
1490 }
1491 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1492 negState, tokflag);
1493 if (ret != GSS_S_COMPLETE)
1494 return ret;
1495 }
1496
1497 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
1498 if (negoex) {
1499 ret = negoex_accept(minor_status, sc, mcred, mechtok_in,
1500 bindings, mechtok_out, time_rec);
1501 } else {
1502 (void) gss_release_name(&tmpmin, &sc->internal_name);
1503 (void) gss_release_cred(&tmpmin, &sc->deleg_cred);
1504 ret = gss_accept_sec_context(minor_status, &sc->ctx_handle,
1505 mcred, mechtok_in, bindings,
1506 &sc->internal_name,
1507 &sc->actual_mech, mechtok_out,
1508 &sc->ctx_flags, time_rec,
1509 &sc->deleg_cred);
1510 }
1511 if (ret == GSS_S_COMPLETE) {
1512 #ifdef MS_BUG_TEST
1513 /*
1514 * Force MIC to be not required even if we previously
1515 * requested a MIC.
1516 */
1517 char *envstr = getenv("MS_FORCE_NO_MIC");
1518
1519 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1520 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1521 sc->mic_reqd) {
1522
1523 sc->mic_reqd = 0;
1524 }
1525 #endif
1526 sc->mech_complete = 1;
1527
1528 if (!sc->mic_reqd ||
1529 !(sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1530 /* No MIC exchange required, so we're done. */
1531 *negState = ACCEPT_COMPLETE;
1532 ret = GSS_S_COMPLETE;
1533 } else {
1534 /* handle_mic will decide if we're done. */
1535 ret = GSS_S_CONTINUE_NEEDED;
1536 }
1537 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1538 *negState = REJECT;
1539 *tokflag = ERROR_TOKEN_SEND;
1540 }
1541 return ret;
1542 }
1543
1544 /*ARGSUSED*/
1545 OM_uint32 KRB5_CALLCONV
spnego_gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)1546 spnego_gss_accept_sec_context(
1547 OM_uint32 *minor_status,
1548 gss_ctx_id_t *context_handle,
1549 gss_cred_id_t verifier_cred_handle,
1550 gss_buffer_t input_token,
1551 gss_channel_bindings_t bindings,
1552 gss_name_t *src_name,
1553 gss_OID *mech_type,
1554 gss_buffer_t output_token,
1555 OM_uint32 *ret_flags,
1556 OM_uint32 *time_rec,
1557 gss_cred_id_t *delegated_cred_handle)
1558 {
1559 OM_uint32 ret, tmpmin, negState;
1560 send_token_flag return_token;
1561 gss_buffer_t mechtok_in, mic_in, mic_out;
1562 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1563 spnego_gss_ctx_id_t sc = NULL;
1564 spnego_gss_cred_id_t spcred = NULL;
1565 int sendTokenInit = 0, tmpret;
1566
1567 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1568
1569 /*
1570 * This function works in three steps:
1571 *
1572 * 1. Perform mechanism negotiation.
1573 * 2. Invoke the negotiated mech's gss_accept_sec_context function
1574 * and examine the results.
1575 * 3. Process or generate MICs if necessary.
1576 *
1577 * Step one determines whether the negotiation requires a MIC exchange,
1578 * while steps two and three share responsibility for determining when
1579 * the exchange is complete. If the selected mech completes in this
1580 * call and no MIC exchange is expected, then step 2 will decide. If a
1581 * MIC exchange is expected, then step 3 will decide. If an error
1582 * occurs in any step, the exchange will be aborted, possibly with an
1583 * error token.
1584 *
1585 * negState determines the state of the negotiation, and is
1586 * communicated to the acceptor if a continuing token is sent.
1587 * return_token is used to indicate what type of token, if any, should
1588 * be generated.
1589 */
1590
1591 /* Validate arguments. */
1592 if (minor_status != NULL)
1593 *minor_status = 0;
1594 if (output_token != GSS_C_NO_BUFFER) {
1595 output_token->length = 0;
1596 output_token->value = NULL;
1597 }
1598 if (src_name != NULL)
1599 *src_name = GSS_C_NO_NAME;
1600 if (mech_type != NULL)
1601 *mech_type = GSS_C_NO_OID;
1602 if (time_rec != NULL)
1603 *time_rec = 0;
1604 if (ret_flags != NULL)
1605 *ret_flags = 0;
1606 if (delegated_cred_handle != NULL)
1607 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1608
1609 if (minor_status == NULL ||
1610 output_token == GSS_C_NO_BUFFER ||
1611 context_handle == NULL)
1612 return GSS_S_CALL_INACCESSIBLE_WRITE;
1613
1614 if (input_token == GSS_C_NO_BUFFER)
1615 return GSS_S_CALL_INACCESSIBLE_READ;
1616
1617 /* Step 1: Perform mechanism negotiation. */
1618 sc = (spnego_gss_ctx_id_t)*context_handle;
1619 spcred = (spnego_gss_cred_id_t)verifier_cred_handle;
1620 if (sc == NULL && input_token->length == 0) {
1621 /* Process a request for NegHints. */
1622 ret = acc_ctx_hints(minor_status, spcred, &mic_out, &negState,
1623 &return_token, &sc);
1624 if (ret != GSS_S_COMPLETE)
1625 goto cleanup;
1626 *context_handle = (gss_ctx_id_t)sc;
1627 sendTokenInit = 1;
1628 ret = GSS_S_CONTINUE_NEEDED;
1629 } else if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1630 if (sc != NULL) {
1631 /* Discard the context from the NegHints request. */
1632 release_spnego_ctx(&sc);
1633 *context_handle = GSS_C_NO_CONTEXT;
1634 }
1635 /* Process an initial token; can set negState to
1636 * REQUEST_MIC. */
1637 ret = acc_ctx_new(minor_status, input_token, spcred,
1638 &mechtok_in, &mic_in, &negState,
1639 &return_token, &sc);
1640 if (ret != GSS_S_COMPLETE)
1641 goto cleanup;
1642 *context_handle = (gss_ctx_id_t)sc;
1643 ret = GSS_S_CONTINUE_NEEDED;
1644 } else {
1645 /* Process a response token. Can set negState to
1646 * ACCEPT_INCOMPLETE. */
1647 ret = acc_ctx_cont(minor_status, input_token, sc, &mechtok_in,
1648 &mic_in, &negState, &return_token);
1649 if (ret != GSS_S_COMPLETE)
1650 goto cleanup;
1651 ret = GSS_S_CONTINUE_NEEDED;
1652 }
1653
1654 /* Step 2: invoke the negotiated mechanism's gss_accept_sec_context
1655 * function. */
1656 /*
1657 * Handle mechtok_in and mic_in only if they are
1658 * present in input_token. If neither is present, whether
1659 * this is an error depends on whether this is the first
1660 * round-trip. RET is set to a default value according to
1661 * whether it is the first round-trip.
1662 */
1663 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1664 ret = acc_ctx_call_acc(minor_status, sc, spcred, mechtok_in,
1665 bindings, &mechtok_out, time_rec,
1666 &negState, &return_token);
1667 }
1668
1669 /* Step 3: process or generate the MIC, if the negotiated mech is
1670 * complete and supports MICs. */
1671 if (!HARD_ERROR(ret) && sc->mech_complete &&
1672 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1673
1674 ret = handle_mic(minor_status, mic_in,
1675 (mechtok_out.length != 0),
1676 sc, &mic_out,
1677 &negState, &return_token);
1678 }
1679
1680 if (!HARD_ERROR(ret) && ret_flags != NULL)
1681 *ret_flags = sc->ctx_flags & ~GSS_C_PROT_READY_FLAG;
1682
1683 cleanup:
1684 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1685 assert(sc != NULL);
1686 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1687 GSS_C_NO_BUFFER,
1688 return_token, output_token);
1689 if (tmpret < 0)
1690 ret = GSS_S_FAILURE;
1691 } else if (return_token != NO_TOKEN_SEND &&
1692 return_token != CHECK_MIC) {
1693 tmpret = make_spnego_tokenTarg_msg(negState,
1694 sc ? sc->internal_mech :
1695 GSS_C_NO_OID,
1696 &mechtok_out, mic_out,
1697 return_token,
1698 output_token);
1699 if (tmpret < 0)
1700 ret = GSS_S_FAILURE;
1701 }
1702 if (ret == GSS_S_COMPLETE) {
1703 sc->opened = 1;
1704 if (sc->internal_name != GSS_C_NO_NAME &&
1705 src_name != NULL) {
1706 *src_name = sc->internal_name;
1707 sc->internal_name = GSS_C_NO_NAME;
1708 }
1709 if (mech_type != NULL)
1710 *mech_type = sc->actual_mech;
1711 /* Get an updated lifetime if we didn't call into the mech. */
1712 if (time_rec != NULL && *time_rec == 0) {
1713 (void) gss_context_time(&tmpmin, sc->ctx_handle,
1714 time_rec);
1715 }
1716 if (delegated_cred_handle != NULL) {
1717 *delegated_cred_handle = sc->deleg_cred;
1718 sc->deleg_cred = GSS_C_NO_CREDENTIAL;
1719 }
1720 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1721 if (sc != NULL) {
1722 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
1723 GSS_C_NO_BUFFER);
1724 release_spnego_ctx(&sc);
1725 }
1726 *context_handle = GSS_C_NO_CONTEXT;
1727 }
1728 gss_release_buffer(&tmpmin, &mechtok_out);
1729 if (mechtok_in != GSS_C_NO_BUFFER) {
1730 gss_release_buffer(&tmpmin, mechtok_in);
1731 free(mechtok_in);
1732 }
1733 if (mic_in != GSS_C_NO_BUFFER) {
1734 gss_release_buffer(&tmpmin, mic_in);
1735 free(mic_in);
1736 }
1737 if (mic_out != GSS_C_NO_BUFFER) {
1738 gss_release_buffer(&tmpmin, mic_out);
1739 free(mic_out);
1740 }
1741 return ret;
1742 }
1743 #endif /* LEAN_CLIENT */
1744
1745 static struct {
1746 OM_uint32 status;
1747 const char *msg;
1748 } msg_table[] = {
1749 { ERR_SPNEGO_NO_MECHS_AVAILABLE,
1750 N_("SPNEGO cannot find mechanisms to negotiate") },
1751 { ERR_SPNEGO_NO_CREDS_ACQUIRED,
1752 N_("SPNEGO failed to acquire creds") },
1753 { ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR,
1754 N_("SPNEGO acceptor did not select a mechanism") },
1755 { ERR_SPNEGO_NEGOTIATION_FAILED,
1756 N_("SPNEGO failed to negotiate a mechanism") },
1757 { ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR,
1758 N_("SPNEGO acceptor did not return a valid token") },
1759 { ERR_NEGOEX_INVALID_MESSAGE_SIGNATURE,
1760 N_("Invalid NegoEx signature") },
1761 { ERR_NEGOEX_INVALID_MESSAGE_TYPE,
1762 N_("Invalid NegoEx message type") },
1763 { ERR_NEGOEX_INVALID_MESSAGE_SIZE,
1764 N_("Invalid NegoEx message size") },
1765 { ERR_NEGOEX_INVALID_CONVERSATION_ID,
1766 N_("Invalid NegoEx conversation ID") },
1767 { ERR_NEGOEX_AUTH_SCHEME_NOT_FOUND,
1768 N_("NegoEx authentication scheme not found") },
1769 { ERR_NEGOEX_MISSING_NEGO_MESSAGE,
1770 N_("Missing NegoEx negotiate message") },
1771 { ERR_NEGOEX_MISSING_AP_REQUEST_MESSAGE,
1772 N_("Missing NegoEx authentication protocol request message") },
1773 { ERR_NEGOEX_NO_AVAILABLE_MECHS,
1774 N_("No mutually supported NegoEx authentication schemes") },
1775 { ERR_NEGOEX_NO_VERIFY_KEY,
1776 N_("No NegoEx verify key") },
1777 { ERR_NEGOEX_UNKNOWN_CHECKSUM_SCHEME,
1778 N_("Unknown NegoEx checksum scheme") },
1779 { ERR_NEGOEX_INVALID_CHECKSUM,
1780 N_("Invalid NegoEx checksum") },
1781 { ERR_NEGOEX_UNSUPPORTED_CRITICAL_EXTENSION,
1782 N_("Unsupported critical NegoEx extension") },
1783 { ERR_NEGOEX_UNSUPPORTED_VERSION,
1784 N_("Unsupported NegoEx version") },
1785 { ERR_NEGOEX_MESSAGE_OUT_OF_SEQUENCE,
1786 N_("NegoEx message out of sequence") },
1787 };
1788
1789 /*ARGSUSED*/
1790 OM_uint32 KRB5_CALLCONV
spnego_gss_display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)1791 spnego_gss_display_status(
1792 OM_uint32 *minor_status,
1793 OM_uint32 status_value,
1794 int status_type,
1795 gss_OID mech_type,
1796 OM_uint32 *message_context,
1797 gss_buffer_t status_string)
1798 {
1799 OM_uint32 maj = GSS_S_COMPLETE;
1800 const char *msg;
1801 size_t i;
1802 int ret;
1803
1804 *message_context = 0;
1805 for (i = 0; i < sizeof(msg_table) / sizeof(*msg_table); i++) {
1806 if (status_value == msg_table[i].status) {
1807 msg = dgettext(KRB5_TEXTDOMAIN, msg_table[i].msg);
1808 *status_string = make_err_msg(msg);
1809 return GSS_S_COMPLETE;
1810 }
1811 }
1812
1813 /* Not one of our minor codes; might be from a mech. Call back
1814 * to gss_display_status, but first check for recursion. */
1815 if (k5_getspecific(K5_KEY_GSS_SPNEGO_STATUS) != NULL) {
1816 /* Perhaps we returned a com_err code like ENOMEM. */
1817 const char *err = error_message(status_value);
1818 *status_string = make_err_msg(err);
1819 return GSS_S_COMPLETE;
1820 }
1821 /* Set a non-null pointer value; doesn't matter which one. */
1822 ret = k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, &ret);
1823 if (ret != 0) {
1824 *minor_status = ret;
1825 return GSS_S_FAILURE;
1826 }
1827
1828 maj = gss_display_status(minor_status, status_value,
1829 status_type, mech_type,
1830 message_context, status_string);
1831 /* This is unlikely to fail; not much we can do if it does. */
1832 (void)k5_setspecific(K5_KEY_GSS_SPNEGO_STATUS, NULL);
1833
1834 return maj;
1835 }
1836
1837
1838 /*ARGSUSED*/
1839 OM_uint32 KRB5_CALLCONV
spnego_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)1840 spnego_gss_import_name(
1841 OM_uint32 *minor_status,
1842 gss_buffer_t input_name_buffer,
1843 gss_OID input_name_type,
1844 gss_name_t *output_name)
1845 {
1846 OM_uint32 status;
1847
1848 dsyslog("Entering import_name\n");
1849
1850 status = gss_import_name(minor_status, input_name_buffer,
1851 input_name_type, output_name);
1852
1853 dsyslog("Leaving import_name\n");
1854 return (status);
1855 }
1856
1857 /*ARGSUSED*/
1858 OM_uint32 KRB5_CALLCONV
spnego_gss_release_name(OM_uint32 * minor_status,gss_name_t * input_name)1859 spnego_gss_release_name(
1860 OM_uint32 *minor_status,
1861 gss_name_t *input_name)
1862 {
1863 OM_uint32 status;
1864
1865 dsyslog("Entering release_name\n");
1866
1867 status = gss_release_name(minor_status, input_name);
1868
1869 dsyslog("Leaving release_name\n");
1870 return (status);
1871 }
1872
1873 /*ARGSUSED*/
1874 OM_uint32 KRB5_CALLCONV
spnego_gss_duplicate_name(OM_uint32 * minor_status,const gss_name_t input_name,gss_name_t * output_name)1875 spnego_gss_duplicate_name(
1876 OM_uint32 *minor_status,
1877 const gss_name_t input_name,
1878 gss_name_t *output_name)
1879 {
1880 OM_uint32 status;
1881
1882 dsyslog("Entering duplicate_name\n");
1883
1884 status = gss_duplicate_name(minor_status, input_name, output_name);
1885
1886 dsyslog("Leaving duplicate_name\n");
1887 return (status);
1888 }
1889
1890 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_name_t * name,OM_uint32 * lifetime,int * cred_usage,gss_OID_set * mechanisms)1891 spnego_gss_inquire_cred(
1892 OM_uint32 *minor_status,
1893 gss_cred_id_t cred_handle,
1894 gss_name_t *name,
1895 OM_uint32 *lifetime,
1896 int *cred_usage,
1897 gss_OID_set *mechanisms)
1898 {
1899 OM_uint32 status;
1900 spnego_gss_cred_id_t spcred = NULL;
1901 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
1902 OM_uint32 tmp_minor_status;
1903 OM_uint32 initiator_lifetime, acceptor_lifetime;
1904
1905 dsyslog("Entering inquire_cred\n");
1906
1907 /*
1908 * To avoid infinite recursion, if GSS_C_NO_CREDENTIAL is
1909 * supplied we call gss_inquire_cred_by_mech() on the
1910 * first non-SPNEGO mechanism.
1911 */
1912 spcred = (spnego_gss_cred_id_t)cred_handle;
1913 if (spcred == NULL) {
1914 status = get_available_mechs(minor_status,
1915 GSS_C_NO_NAME,
1916 GSS_C_BOTH,
1917 GSS_C_NO_CRED_STORE,
1918 &creds,
1919 mechanisms, NULL);
1920 if (status != GSS_S_COMPLETE) {
1921 dsyslog("Leaving inquire_cred\n");
1922 return (status);
1923 }
1924
1925 if ((*mechanisms)->count == 0) {
1926 gss_release_cred(&tmp_minor_status, &creds);
1927 gss_release_oid_set(&tmp_minor_status, mechanisms);
1928 dsyslog("Leaving inquire_cred\n");
1929 return (GSS_S_DEFECTIVE_CREDENTIAL);
1930 }
1931
1932 assert((*mechanisms)->elements != NULL);
1933
1934 status = gss_inquire_cred_by_mech(minor_status,
1935 creds,
1936 &(*mechanisms)->elements[0],
1937 name,
1938 &initiator_lifetime,
1939 &acceptor_lifetime,
1940 cred_usage);
1941 if (status != GSS_S_COMPLETE) {
1942 gss_release_cred(&tmp_minor_status, &creds);
1943 dsyslog("Leaving inquire_cred\n");
1944 return (status);
1945 }
1946
1947 if (lifetime != NULL)
1948 *lifetime = (*cred_usage == GSS_C_ACCEPT) ?
1949 acceptor_lifetime : initiator_lifetime;
1950
1951 gss_release_cred(&tmp_minor_status, &creds);
1952 } else {
1953 status = gss_inquire_cred(minor_status, spcred->mcred,
1954 name, lifetime,
1955 cred_usage, mechanisms);
1956 }
1957
1958 dsyslog("Leaving inquire_cred\n");
1959
1960 return (status);
1961 }
1962
1963 /*ARGSUSED*/
1964 OM_uint32 KRB5_CALLCONV
spnego_gss_compare_name(OM_uint32 * minor_status,const gss_name_t name1,const gss_name_t name2,int * name_equal)1965 spnego_gss_compare_name(
1966 OM_uint32 *minor_status,
1967 const gss_name_t name1,
1968 const gss_name_t name2,
1969 int *name_equal)
1970 {
1971 OM_uint32 status = GSS_S_COMPLETE;
1972 dsyslog("Entering compare_name\n");
1973
1974 status = gss_compare_name(minor_status, name1, name2, name_equal);
1975
1976 dsyslog("Leaving compare_name\n");
1977 return (status);
1978 }
1979
1980 /*ARGSUSED*/
1981 /*ARGSUSED*/
1982 OM_uint32 KRB5_CALLCONV
spnego_gss_display_name(OM_uint32 * minor_status,gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)1983 spnego_gss_display_name(
1984 OM_uint32 *minor_status,
1985 gss_name_t input_name,
1986 gss_buffer_t output_name_buffer,
1987 gss_OID *output_name_type)
1988 {
1989 OM_uint32 status = GSS_S_COMPLETE;
1990 dsyslog("Entering display_name\n");
1991
1992 status = gss_display_name(minor_status, input_name,
1993 output_name_buffer, output_name_type);
1994
1995 dsyslog("Leaving display_name\n");
1996 return (status);
1997 }
1998
1999
2000 /*ARGSUSED*/
2001 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_names_for_mech(OM_uint32 * minor_status,gss_OID mechanism,gss_OID_set * name_types)2002 spnego_gss_inquire_names_for_mech(
2003 OM_uint32 *minor_status,
2004 gss_OID mechanism,
2005 gss_OID_set *name_types)
2006 {
2007 OM_uint32 major, minor;
2008
2009 dsyslog("Entering inquire_names_for_mech\n");
2010 /*
2011 * We only know how to handle our own mechanism.
2012 */
2013 if ((mechanism != GSS_C_NULL_OID) &&
2014 !g_OID_equal(gss_mech_spnego, mechanism)) {
2015 *minor_status = 0;
2016 return (GSS_S_FAILURE);
2017 }
2018
2019 major = gss_create_empty_oid_set(minor_status, name_types);
2020 if (major == GSS_S_COMPLETE) {
2021 /* Now add our members. */
2022 if (((major = gss_add_oid_set_member(minor_status,
2023 (gss_OID) GSS_C_NT_USER_NAME,
2024 name_types)) == GSS_S_COMPLETE) &&
2025 ((major = gss_add_oid_set_member(minor_status,
2026 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2027 name_types)) == GSS_S_COMPLETE) &&
2028 ((major = gss_add_oid_set_member(minor_status,
2029 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2030 name_types)) == GSS_S_COMPLETE)) {
2031 major = gss_add_oid_set_member(minor_status,
2032 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2033 name_types);
2034 }
2035
2036 if (major != GSS_S_COMPLETE)
2037 (void) gss_release_oid_set(&minor, name_types);
2038 }
2039
2040 dsyslog("Leaving inquire_names_for_mech\n");
2041 return (major);
2042 }
2043
2044 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)2045 spnego_gss_unwrap(
2046 OM_uint32 *minor_status,
2047 gss_ctx_id_t context_handle,
2048 gss_buffer_t input_message_buffer,
2049 gss_buffer_t output_message_buffer,
2050 int *conf_state,
2051 gss_qop_t *qop_state)
2052 {
2053 OM_uint32 ret;
2054 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2055
2056 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2057 return (GSS_S_NO_CONTEXT);
2058
2059 ret = gss_unwrap(minor_status,
2060 sc->ctx_handle,
2061 input_message_buffer,
2062 output_message_buffer,
2063 conf_state,
2064 qop_state);
2065
2066 return (ret);
2067 }
2068
2069 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)2070 spnego_gss_wrap(
2071 OM_uint32 *minor_status,
2072 gss_ctx_id_t context_handle,
2073 int conf_req_flag,
2074 gss_qop_t qop_req,
2075 gss_buffer_t input_message_buffer,
2076 int *conf_state,
2077 gss_buffer_t output_message_buffer)
2078 {
2079 OM_uint32 ret;
2080 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2081
2082 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2083 return (GSS_S_NO_CONTEXT);
2084
2085 ret = gss_wrap(minor_status,
2086 sc->ctx_handle,
2087 conf_req_flag,
2088 qop_req,
2089 input_message_buffer,
2090 conf_state,
2091 output_message_buffer);
2092
2093 return (ret);
2094 }
2095
2096 OM_uint32 KRB5_CALLCONV
spnego_gss_process_context_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t token_buffer)2097 spnego_gss_process_context_token(
2098 OM_uint32 *minor_status,
2099 const gss_ctx_id_t context_handle,
2100 const gss_buffer_t token_buffer)
2101 {
2102 OM_uint32 ret;
2103 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2104
2105 /* SPNEGO doesn't have its own context tokens. */
2106 if (!sc->opened)
2107 return (GSS_S_DEFECTIVE_TOKEN);
2108
2109 ret = gss_process_context_token(minor_status,
2110 sc->ctx_handle,
2111 token_buffer);
2112
2113 return (ret);
2114 }
2115
2116 OM_uint32 KRB5_CALLCONV
spnego_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)2117 spnego_gss_delete_sec_context(
2118 OM_uint32 *minor_status,
2119 gss_ctx_id_t *context_handle,
2120 gss_buffer_t output_token)
2121 {
2122 OM_uint32 ret = GSS_S_COMPLETE;
2123 spnego_gss_ctx_id_t *ctx =
2124 (spnego_gss_ctx_id_t *)context_handle;
2125
2126 *minor_status = 0;
2127
2128 if (context_handle == NULL)
2129 return (GSS_S_FAILURE);
2130
2131 if (*ctx == NULL)
2132 return (GSS_S_COMPLETE);
2133
2134 (void) gss_delete_sec_context(minor_status, &(*ctx)->ctx_handle,
2135 output_token);
2136 (void) release_spnego_ctx(ctx);
2137
2138 return (ret);
2139 }
2140
2141 OM_uint32 KRB5_CALLCONV
spnego_gss_context_time(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,OM_uint32 * time_rec)2142 spnego_gss_context_time(
2143 OM_uint32 *minor_status,
2144 const gss_ctx_id_t context_handle,
2145 OM_uint32 *time_rec)
2146 {
2147 OM_uint32 ret;
2148 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2149
2150 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2151 return (GSS_S_NO_CONTEXT);
2152
2153 ret = gss_context_time(minor_status,
2154 sc->ctx_handle,
2155 time_rec);
2156 return (ret);
2157 }
2158 #ifndef LEAN_CLIENT
2159 OM_uint32 KRB5_CALLCONV
spnego_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)2160 spnego_gss_export_sec_context(
2161 OM_uint32 *minor_status,
2162 gss_ctx_id_t *context_handle,
2163 gss_buffer_t interprocess_token)
2164 {
2165 OM_uint32 ret;
2166 spnego_gss_ctx_id_t sc = *(spnego_gss_ctx_id_t *)context_handle;
2167
2168 /* We don't currently support exporting partially established
2169 * contexts. */
2170 if (!sc->opened)
2171 return GSS_S_UNAVAILABLE;
2172
2173 ret = gss_export_sec_context(minor_status,
2174 &sc->ctx_handle,
2175 interprocess_token);
2176 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
2177 release_spnego_ctx(&sc);
2178 *context_handle = GSS_C_NO_CONTEXT;
2179 }
2180 return (ret);
2181 }
2182
2183 OM_uint32 KRB5_CALLCONV
spnego_gss_import_sec_context(OM_uint32 * minor_status,const gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)2184 spnego_gss_import_sec_context(
2185 OM_uint32 *minor_status,
2186 const gss_buffer_t interprocess_token,
2187 gss_ctx_id_t *context_handle)
2188 {
2189 OM_uint32 ret, tmpmin;
2190 gss_ctx_id_t mctx;
2191 spnego_gss_ctx_id_t sc;
2192 int initiate, opened;
2193
2194 ret = gss_import_sec_context(minor_status, interprocess_token, &mctx);
2195 if (ret != GSS_S_COMPLETE)
2196 return ret;
2197
2198 ret = gss_inquire_context(&tmpmin, mctx, NULL, NULL, NULL, NULL, NULL,
2199 &initiate, &opened);
2200 if (ret != GSS_S_COMPLETE || !opened) {
2201 /* We don't currently support importing partially established
2202 * contexts. */
2203 (void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2204 return GSS_S_FAILURE;
2205 }
2206
2207 sc = create_spnego_ctx(initiate);
2208 if (sc == NULL) {
2209 (void) gss_delete_sec_context(&tmpmin, &mctx, GSS_C_NO_BUFFER);
2210 return GSS_S_FAILURE;
2211 }
2212 sc->ctx_handle = mctx;
2213 sc->opened = 1;
2214 *context_handle = (gss_ctx_id_t)sc;
2215 return GSS_S_COMPLETE;
2216 }
2217 #endif /* LEAN_CLIENT */
2218
2219 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * opened)2220 spnego_gss_inquire_context(
2221 OM_uint32 *minor_status,
2222 const gss_ctx_id_t context_handle,
2223 gss_name_t *src_name,
2224 gss_name_t *targ_name,
2225 OM_uint32 *lifetime_rec,
2226 gss_OID *mech_type,
2227 OM_uint32 *ctx_flags,
2228 int *locally_initiated,
2229 int *opened)
2230 {
2231 OM_uint32 ret = GSS_S_COMPLETE;
2232 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2233
2234 if (src_name != NULL)
2235 *src_name = GSS_C_NO_NAME;
2236 if (targ_name != NULL)
2237 *targ_name = GSS_C_NO_NAME;
2238 if (lifetime_rec != NULL)
2239 *lifetime_rec = 0;
2240 if (mech_type != NULL)
2241 *mech_type = (gss_OID)gss_mech_spnego;
2242 if (ctx_flags != NULL)
2243 *ctx_flags = 0;
2244 if (locally_initiated != NULL)
2245 *locally_initiated = sc->initiate;
2246 if (opened != NULL)
2247 *opened = sc->opened;
2248
2249 if (sc->ctx_handle != GSS_C_NO_CONTEXT) {
2250 ret = gss_inquire_context(minor_status, sc->ctx_handle,
2251 src_name, targ_name, lifetime_rec,
2252 mech_type, ctx_flags, NULL, NULL);
2253 }
2254
2255 if (!sc->opened) {
2256 /*
2257 * We are still doing SPNEGO negotiation, so report SPNEGO as
2258 * the OID. After negotiation is complete we will report the
2259 * underlying mechanism OID.
2260 */
2261 if (mech_type != NULL)
2262 *mech_type = (gss_OID)gss_mech_spnego;
2263
2264 /*
2265 * Remove flags we don't support with partially-established
2266 * contexts. (Change this to keep GSS_C_TRANS_FLAG if we add
2267 * support for exporting partial SPNEGO contexts.)
2268 */
2269 if (ctx_flags != NULL) {
2270 *ctx_flags &= ~GSS_C_PROT_READY_FLAG;
2271 *ctx_flags &= ~GSS_C_TRANS_FLAG;
2272 }
2273 }
2274
2275 return (ret);
2276 }
2277
2278 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)2279 spnego_gss_wrap_size_limit(
2280 OM_uint32 *minor_status,
2281 const gss_ctx_id_t context_handle,
2282 int conf_req_flag,
2283 gss_qop_t qop_req,
2284 OM_uint32 req_output_size,
2285 OM_uint32 *max_input_size)
2286 {
2287 OM_uint32 ret;
2288 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2289
2290 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2291 return (GSS_S_NO_CONTEXT);
2292
2293 ret = gss_wrap_size_limit(minor_status,
2294 sc->ctx_handle,
2295 conf_req_flag,
2296 qop_req,
2297 req_output_size,
2298 max_input_size);
2299 return (ret);
2300 }
2301
2302 OM_uint32 KRB5_CALLCONV
spnego_gss_localname(OM_uint32 * minor_status,const gss_name_t pname,const gss_const_OID mech_type,gss_buffer_t localname)2303 spnego_gss_localname(OM_uint32 *minor_status, const gss_name_t pname,
2304 const gss_const_OID mech_type, gss_buffer_t localname)
2305 {
2306 return gss_localname(minor_status, pname, GSS_C_NO_OID, localname);
2307 }
2308
2309 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_qop_t qop_req,const gss_buffer_t message_buffer,gss_buffer_t message_token)2310 spnego_gss_get_mic(
2311 OM_uint32 *minor_status,
2312 const gss_ctx_id_t context_handle,
2313 gss_qop_t qop_req,
2314 const gss_buffer_t message_buffer,
2315 gss_buffer_t message_token)
2316 {
2317 OM_uint32 ret;
2318 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2319
2320 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2321 return (GSS_S_NO_CONTEXT);
2322
2323 ret = gss_get_mic(minor_status,
2324 sc->ctx_handle,
2325 qop_req,
2326 message_buffer,
2327 message_token);
2328 return (ret);
2329 }
2330
2331 OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t msg_buffer,const gss_buffer_t token_buffer,gss_qop_t * qop_state)2332 spnego_gss_verify_mic(
2333 OM_uint32 *minor_status,
2334 const gss_ctx_id_t context_handle,
2335 const gss_buffer_t msg_buffer,
2336 const gss_buffer_t token_buffer,
2337 gss_qop_t *qop_state)
2338 {
2339 OM_uint32 ret;
2340 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2341
2342 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2343 return (GSS_S_NO_CONTEXT);
2344
2345 ret = gss_verify_mic(minor_status,
2346 sc->ctx_handle,
2347 msg_buffer,
2348 token_buffer,
2349 qop_state);
2350 return (ret);
2351 }
2352
2353 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2354 spnego_gss_inquire_sec_context_by_oid(
2355 OM_uint32 *minor_status,
2356 const gss_ctx_id_t context_handle,
2357 const gss_OID desired_object,
2358 gss_buffer_set_t *data_set)
2359 {
2360 OM_uint32 ret;
2361 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2362
2363 /* There are no SPNEGO-specific OIDs for this function. */
2364 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2365 return (GSS_S_UNAVAILABLE);
2366
2367 ret = gss_inquire_sec_context_by_oid(minor_status,
2368 sc->ctx_handle,
2369 desired_object,
2370 data_set);
2371 return (ret);
2372 }
2373
2374 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_cred_by_oid(OM_uint32 * minor_status,const gss_cred_id_t cred_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2375 spnego_gss_inquire_cred_by_oid(
2376 OM_uint32 *minor_status,
2377 const gss_cred_id_t cred_handle,
2378 const gss_OID desired_object,
2379 gss_buffer_set_t *data_set)
2380 {
2381 OM_uint32 ret;
2382 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2383 gss_cred_id_t mcred;
2384 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2385 ret = gss_inquire_cred_by_oid(minor_status,
2386 mcred,
2387 desired_object,
2388 data_set);
2389 return (ret);
2390 }
2391
2392 /* This is the same OID as KRB5_NO_CI_FLAGS_X_OID. */
2393 #define NO_CI_FLAGS_X_OID_LENGTH 6
2394 #define NO_CI_FLAGS_X_OID "\x2a\x85\x70\x2b\x0d\x1d"
2395 static const gss_OID_desc no_ci_flags_oid[] = {
2396 {NO_CI_FLAGS_X_OID_LENGTH, NO_CI_FLAGS_X_OID},
2397 };
2398
2399 OM_uint32 KRB5_CALLCONV
spnego_gss_set_cred_option(OM_uint32 * minor_status,gss_cred_id_t * cred_handle,const gss_OID desired_object,const gss_buffer_t value)2400 spnego_gss_set_cred_option(
2401 OM_uint32 *minor_status,
2402 gss_cred_id_t *cred_handle,
2403 const gss_OID desired_object,
2404 const gss_buffer_t value)
2405 {
2406 OM_uint32 ret;
2407 OM_uint32 tmp_minor_status;
2408 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)*cred_handle;
2409 gss_cred_id_t mcred;
2410
2411 mcred = (spcred == NULL) ? GSS_C_NO_CREDENTIAL : spcred->mcred;
2412 ret = gss_set_cred_option(minor_status,
2413 &mcred,
2414 desired_object,
2415 value);
2416 if (ret == GSS_S_COMPLETE && spcred == NULL) {
2417 /*
2418 * If the mechanism allocated a new credential handle, then
2419 * we need to wrap it up in an SPNEGO credential handle.
2420 */
2421
2422 ret = create_spnego_cred(minor_status, mcred, &spcred);
2423 if (ret != GSS_S_COMPLETE) {
2424 gss_release_cred(&tmp_minor_status, &mcred);
2425 return (ret);
2426 }
2427 *cred_handle = (gss_cred_id_t)spcred;
2428 }
2429
2430 if (ret != GSS_S_COMPLETE)
2431 return (ret);
2432
2433 /* Recognize KRB5_NO_CI_FLAGS_X_OID and avoid asking for integrity. */
2434 if (g_OID_equal(desired_object, no_ci_flags_oid))
2435 spcred->no_ask_integ = 1;
2436
2437 return (GSS_S_COMPLETE);
2438 }
2439
2440 OM_uint32 KRB5_CALLCONV
spnego_gss_set_sec_context_option(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,const gss_OID desired_object,const gss_buffer_t value)2441 spnego_gss_set_sec_context_option(
2442 OM_uint32 *minor_status,
2443 gss_ctx_id_t *context_handle,
2444 const gss_OID desired_object,
2445 const gss_buffer_t value)
2446 {
2447 OM_uint32 ret;
2448 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)*context_handle;
2449
2450 /* There are no SPNEGO-specific OIDs for this function, and we cannot
2451 * construct an empty SPNEGO context with it. */
2452 if (sc == NULL || sc->ctx_handle == GSS_C_NO_CONTEXT)
2453 return (GSS_S_UNAVAILABLE);
2454
2455 ret = gss_set_sec_context_option(minor_status,
2456 &sc->ctx_handle,
2457 desired_object,
2458 value);
2459 return (ret);
2460 }
2461
2462 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_assoc_buffer,gss_buffer_t input_payload_buffer,int * conf_state,gss_buffer_t output_message_buffer)2463 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2464 gss_ctx_id_t context_handle,
2465 int conf_req_flag,
2466 gss_qop_t qop_req,
2467 gss_buffer_t input_assoc_buffer,
2468 gss_buffer_t input_payload_buffer,
2469 int *conf_state,
2470 gss_buffer_t output_message_buffer)
2471 {
2472 OM_uint32 ret;
2473 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2474
2475 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2476 return (GSS_S_NO_CONTEXT);
2477
2478 ret = gss_wrap_aead(minor_status,
2479 sc->ctx_handle,
2480 conf_req_flag,
2481 qop_req,
2482 input_assoc_buffer,
2483 input_payload_buffer,
2484 conf_state,
2485 output_message_buffer);
2486
2487 return (ret);
2488 }
2489
2490 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_aead(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t input_assoc_buffer,gss_buffer_t output_payload_buffer,int * conf_state,gss_qop_t * qop_state)2491 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2492 gss_ctx_id_t context_handle,
2493 gss_buffer_t input_message_buffer,
2494 gss_buffer_t input_assoc_buffer,
2495 gss_buffer_t output_payload_buffer,
2496 int *conf_state,
2497 gss_qop_t *qop_state)
2498 {
2499 OM_uint32 ret;
2500 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2501
2502 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2503 return (GSS_S_NO_CONTEXT);
2504
2505 ret = gss_unwrap_aead(minor_status,
2506 sc->ctx_handle,
2507 input_message_buffer,
2508 input_assoc_buffer,
2509 output_payload_buffer,
2510 conf_state,
2511 qop_state);
2512 return (ret);
2513 }
2514
2515 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2516 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2517 gss_ctx_id_t context_handle,
2518 int conf_req_flag,
2519 gss_qop_t qop_req,
2520 int *conf_state,
2521 gss_iov_buffer_desc *iov,
2522 int iov_count)
2523 {
2524 OM_uint32 ret;
2525 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2526
2527 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2528 return (GSS_S_NO_CONTEXT);
2529
2530 ret = gss_wrap_iov(minor_status,
2531 sc->ctx_handle,
2532 conf_req_flag,
2533 qop_req,
2534 conf_state,
2535 iov,
2536 iov_count);
2537 return (ret);
2538 }
2539
2540 OM_uint32 KRB5_CALLCONV
spnego_gss_unwrap_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int * conf_state,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)2541 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2542 gss_ctx_id_t context_handle,
2543 int *conf_state,
2544 gss_qop_t *qop_state,
2545 gss_iov_buffer_desc *iov,
2546 int iov_count)
2547 {
2548 OM_uint32 ret;
2549 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2550
2551 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2552 return (GSS_S_NO_CONTEXT);
2553
2554 ret = gss_unwrap_iov(minor_status,
2555 sc->ctx_handle,
2556 conf_state,
2557 qop_state,
2558 iov,
2559 iov_count);
2560 return (ret);
2561 }
2562
2563 OM_uint32 KRB5_CALLCONV
spnego_gss_wrap_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,int * conf_state,gss_iov_buffer_desc * iov,int iov_count)2564 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2565 gss_ctx_id_t context_handle,
2566 int conf_req_flag,
2567 gss_qop_t qop_req,
2568 int *conf_state,
2569 gss_iov_buffer_desc *iov,
2570 int iov_count)
2571 {
2572 OM_uint32 ret;
2573 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2574
2575 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2576 return (GSS_S_NO_CONTEXT);
2577
2578 ret = gss_wrap_iov_length(minor_status,
2579 sc->ctx_handle,
2580 conf_req_flag,
2581 qop_req,
2582 conf_state,
2583 iov,
2584 iov_count);
2585 return (ret);
2586 }
2587
2588
2589 OM_uint32 KRB5_CALLCONV
spnego_gss_complete_auth_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer)2590 spnego_gss_complete_auth_token(
2591 OM_uint32 *minor_status,
2592 const gss_ctx_id_t context_handle,
2593 gss_buffer_t input_message_buffer)
2594 {
2595 OM_uint32 ret;
2596 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2597
2598 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2599 return (GSS_S_UNAVAILABLE);
2600
2601 ret = gss_complete_auth_token(minor_status,
2602 sc->ctx_handle,
2603 input_message_buffer);
2604 return (ret);
2605 }
2606
2607 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_impersonate_name(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,const gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2608 spnego_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
2609 const gss_cred_id_t impersonator_cred_handle,
2610 const gss_name_t desired_name,
2611 OM_uint32 time_req,
2612 gss_OID_set desired_mechs,
2613 gss_cred_usage_t cred_usage,
2614 gss_cred_id_t *output_cred_handle,
2615 gss_OID_set *actual_mechs,
2616 OM_uint32 *time_rec)
2617 {
2618 OM_uint32 status, tmpmin;
2619 gss_OID_set amechs = GSS_C_NULL_OID_SET;
2620 spnego_gss_cred_id_t imp_spcred = NULL, out_spcred = NULL;
2621 gss_cred_id_t imp_mcred, out_mcred = GSS_C_NO_CREDENTIAL;
2622
2623 dsyslog("Entering spnego_gss_acquire_cred_impersonate_name\n");
2624
2625 if (actual_mechs)
2626 *actual_mechs = NULL;
2627
2628 if (time_rec)
2629 *time_rec = 0;
2630
2631 imp_spcred = (spnego_gss_cred_id_t)impersonator_cred_handle;
2632 imp_mcred = imp_spcred ? imp_spcred->mcred : GSS_C_NO_CREDENTIAL;
2633 status = gss_inquire_cred(minor_status, imp_mcred, NULL, NULL,
2634 NULL, &amechs);
2635 if (status != GSS_S_COMPLETE)
2636 return status;
2637
2638 status = gss_acquire_cred_impersonate_name(minor_status, imp_mcred,
2639 desired_name, time_req,
2640 amechs, cred_usage,
2641 &out_mcred, actual_mechs,
2642 time_rec);
2643 if (status != GSS_S_COMPLETE)
2644 goto cleanup;
2645
2646 status = create_spnego_cred(minor_status, out_mcred, &out_spcred);
2647 if (status != GSS_S_COMPLETE)
2648 goto cleanup;
2649
2650 out_mcred = GSS_C_NO_CREDENTIAL;
2651 *output_cred_handle = (gss_cred_id_t)out_spcred;
2652
2653 cleanup:
2654 (void) gss_release_oid_set(&tmpmin, &amechs);
2655 (void) gss_release_cred(&tmpmin, &out_mcred);
2656
2657 dsyslog("Leaving spnego_gss_acquire_cred_impersonate_name\n");
2658 return (status);
2659 }
2660
2661 OM_uint32 KRB5_CALLCONV
spnego_gss_acquire_cred_with_password(OM_uint32 * minor_status,const gss_name_t desired_name,const gss_buffer_t password,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)2662 spnego_gss_acquire_cred_with_password(OM_uint32 *minor_status,
2663 const gss_name_t desired_name,
2664 const gss_buffer_t password,
2665 OM_uint32 time_req,
2666 const gss_OID_set desired_mechs,
2667 gss_cred_usage_t cred_usage,
2668 gss_cred_id_t *output_cred_handle,
2669 gss_OID_set *actual_mechs,
2670 OM_uint32 *time_rec)
2671 {
2672 OM_uint32 status, tmpmin;
2673 gss_OID_set amechs = GSS_C_NULL_OID_SET;
2674 gss_cred_id_t mcred = NULL;
2675 spnego_gss_cred_id_t spcred = NULL;
2676
2677 dsyslog("Entering spnego_gss_acquire_cred_with_password\n");
2678
2679 if (actual_mechs)
2680 *actual_mechs = NULL;
2681
2682 if (time_rec)
2683 *time_rec = 0;
2684
2685 status = get_available_mechs(minor_status, desired_name,
2686 cred_usage, GSS_C_NO_CRED_STORE,
2687 NULL, &amechs, NULL);
2688 if (status != GSS_S_COMPLETE)
2689 goto cleanup;
2690
2691 status = gss_acquire_cred_with_password(minor_status, desired_name,
2692 password, time_req, amechs,
2693 cred_usage, &mcred,
2694 actual_mechs, time_rec);
2695 if (status != GSS_S_COMPLETE)
2696 goto cleanup;
2697
2698 status = create_spnego_cred(minor_status, mcred, &spcred);
2699 if (status != GSS_S_COMPLETE)
2700 goto cleanup;
2701
2702 mcred = GSS_C_NO_CREDENTIAL;
2703 *output_cred_handle = (gss_cred_id_t)spcred;
2704
2705 cleanup:
2706
2707 (void) gss_release_oid_set(&tmpmin, &amechs);
2708 (void) gss_release_cred(&tmpmin, &mcred);
2709
2710 dsyslog("Leaving spnego_gss_acquire_cred_with_password\n");
2711 return (status);
2712 }
2713
2714 OM_uint32 KRB5_CALLCONV
spnego_gss_display_name_ext(OM_uint32 * minor_status,gss_name_t name,gss_OID display_as_name_type,gss_buffer_t display_name)2715 spnego_gss_display_name_ext(OM_uint32 *minor_status,
2716 gss_name_t name,
2717 gss_OID display_as_name_type,
2718 gss_buffer_t display_name)
2719 {
2720 OM_uint32 ret;
2721 ret = gss_display_name_ext(minor_status,
2722 name,
2723 display_as_name_type,
2724 display_name);
2725 return (ret);
2726 }
2727
2728
2729 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_name(OM_uint32 * minor_status,gss_name_t name,int * name_is_MN,gss_OID * MN_mech,gss_buffer_set_t * attrs)2730 spnego_gss_inquire_name(OM_uint32 *minor_status,
2731 gss_name_t name,
2732 int *name_is_MN,
2733 gss_OID *MN_mech,
2734 gss_buffer_set_t *attrs)
2735 {
2736 OM_uint32 ret;
2737 ret = gss_inquire_name(minor_status,
2738 name,
2739 name_is_MN,
2740 MN_mech,
2741 attrs);
2742 return (ret);
2743 }
2744
2745 OM_uint32 KRB5_CALLCONV
spnego_gss_get_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr,int * authenticated,int * complete,gss_buffer_t value,gss_buffer_t display_value,int * more)2746 spnego_gss_get_name_attribute(OM_uint32 *minor_status,
2747 gss_name_t name,
2748 gss_buffer_t attr,
2749 int *authenticated,
2750 int *complete,
2751 gss_buffer_t value,
2752 gss_buffer_t display_value,
2753 int *more)
2754 {
2755 OM_uint32 ret;
2756 ret = gss_get_name_attribute(minor_status,
2757 name,
2758 attr,
2759 authenticated,
2760 complete,
2761 value,
2762 display_value,
2763 more);
2764 return (ret);
2765 }
2766
2767 OM_uint32 KRB5_CALLCONV
spnego_gss_set_name_attribute(OM_uint32 * minor_status,gss_name_t name,int complete,gss_buffer_t attr,gss_buffer_t value)2768 spnego_gss_set_name_attribute(OM_uint32 *minor_status,
2769 gss_name_t name,
2770 int complete,
2771 gss_buffer_t attr,
2772 gss_buffer_t value)
2773 {
2774 OM_uint32 ret;
2775 ret = gss_set_name_attribute(minor_status,
2776 name,
2777 complete,
2778 attr,
2779 value);
2780 return (ret);
2781 }
2782
2783 OM_uint32 KRB5_CALLCONV
spnego_gss_delete_name_attribute(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t attr)2784 spnego_gss_delete_name_attribute(OM_uint32 *minor_status,
2785 gss_name_t name,
2786 gss_buffer_t attr)
2787 {
2788 OM_uint32 ret;
2789 ret = gss_delete_name_attribute(minor_status,
2790 name,
2791 attr);
2792 return (ret);
2793 }
2794
2795 OM_uint32 KRB5_CALLCONV
spnego_gss_export_name_composite(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t exp_composite_name)2796 spnego_gss_export_name_composite(OM_uint32 *minor_status,
2797 gss_name_t name,
2798 gss_buffer_t exp_composite_name)
2799 {
2800 OM_uint32 ret;
2801 ret = gss_export_name_composite(minor_status,
2802 name,
2803 exp_composite_name);
2804 return (ret);
2805 }
2806
2807 OM_uint32 KRB5_CALLCONV
spnego_gss_map_name_to_any(OM_uint32 * minor_status,gss_name_t name,int authenticated,gss_buffer_t type_id,gss_any_t * output)2808 spnego_gss_map_name_to_any(OM_uint32 *minor_status,
2809 gss_name_t name,
2810 int authenticated,
2811 gss_buffer_t type_id,
2812 gss_any_t *output)
2813 {
2814 OM_uint32 ret;
2815 ret = gss_map_name_to_any(minor_status,
2816 name,
2817 authenticated,
2818 type_id,
2819 output);
2820 return (ret);
2821 }
2822
2823 OM_uint32 KRB5_CALLCONV
spnego_gss_release_any_name_mapping(OM_uint32 * minor_status,gss_name_t name,gss_buffer_t type_id,gss_any_t * input)2824 spnego_gss_release_any_name_mapping(OM_uint32 *minor_status,
2825 gss_name_t name,
2826 gss_buffer_t type_id,
2827 gss_any_t *input)
2828 {
2829 OM_uint32 ret;
2830 ret = gss_release_any_name_mapping(minor_status,
2831 name,
2832 type_id,
2833 input);
2834 return (ret);
2835 }
2836
2837 OM_uint32 KRB5_CALLCONV
spnego_gss_pseudo_random(OM_uint32 * minor_status,gss_ctx_id_t context,int prf_key,const gss_buffer_t prf_in,ssize_t desired_output_len,gss_buffer_t prf_out)2838 spnego_gss_pseudo_random(OM_uint32 *minor_status,
2839 gss_ctx_id_t context,
2840 int prf_key,
2841 const gss_buffer_t prf_in,
2842 ssize_t desired_output_len,
2843 gss_buffer_t prf_out)
2844 {
2845 OM_uint32 ret;
2846 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context;
2847
2848 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2849 return (GSS_S_NO_CONTEXT);
2850
2851 ret = gss_pseudo_random(minor_status,
2852 sc->ctx_handle,
2853 prf_key,
2854 prf_in,
2855 desired_output_len,
2856 prf_out);
2857 return (ret);
2858 }
2859
2860 OM_uint32 KRB5_CALLCONV
spnego_gss_set_neg_mechs(OM_uint32 * minor_status,gss_cred_id_t cred_handle,const gss_OID_set mech_list)2861 spnego_gss_set_neg_mechs(OM_uint32 *minor_status,
2862 gss_cred_id_t cred_handle,
2863 const gss_OID_set mech_list)
2864 {
2865 OM_uint32 ret;
2866 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2867
2868 /* Store mech_list in spcred for use in negotiation logic. */
2869 gss_release_oid_set(minor_status, &spcred->neg_mechs);
2870 ret = generic_gss_copy_oid_set(minor_status, mech_list,
2871 &spcred->neg_mechs);
2872 if (ret == GSS_S_COMPLETE) {
2873 (void) gss_set_neg_mechs(minor_status,
2874 spcred->mcred,
2875 spcred->neg_mechs);
2876 }
2877
2878 return (ret);
2879 }
2880
2881 #define SPNEGO_SASL_NAME "SPNEGO"
2882 #define SPNEGO_SASL_NAME_LEN (sizeof(SPNEGO_SASL_NAME) - 1)
2883
2884 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_mech_for_saslname(OM_uint32 * minor_status,const gss_buffer_t sasl_mech_name,gss_OID * mech_type)2885 spnego_gss_inquire_mech_for_saslname(OM_uint32 *minor_status,
2886 const gss_buffer_t sasl_mech_name,
2887 gss_OID *mech_type)
2888 {
2889 if (sasl_mech_name->length == SPNEGO_SASL_NAME_LEN &&
2890 memcmp(sasl_mech_name->value, SPNEGO_SASL_NAME,
2891 SPNEGO_SASL_NAME_LEN) == 0) {
2892 if (mech_type != NULL)
2893 *mech_type = (gss_OID)gss_mech_spnego;
2894 return (GSS_S_COMPLETE);
2895 }
2896
2897 return (GSS_S_BAD_MECH);
2898 }
2899
2900 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_saslname_for_mech(OM_uint32 * minor_status,const gss_OID desired_mech,gss_buffer_t sasl_mech_name,gss_buffer_t mech_name,gss_buffer_t mech_description)2901 spnego_gss_inquire_saslname_for_mech(OM_uint32 *minor_status,
2902 const gss_OID desired_mech,
2903 gss_buffer_t sasl_mech_name,
2904 gss_buffer_t mech_name,
2905 gss_buffer_t mech_description)
2906 {
2907 *minor_status = 0;
2908
2909 if (!g_OID_equal(desired_mech, gss_mech_spnego))
2910 return (GSS_S_BAD_MECH);
2911
2912 if (!g_make_string_buffer(SPNEGO_SASL_NAME, sasl_mech_name) ||
2913 !g_make_string_buffer("spnego", mech_name) ||
2914 !g_make_string_buffer("Simple and Protected GSS-API "
2915 "Negotiation Mechanism", mech_description))
2916 goto fail;
2917
2918 return (GSS_S_COMPLETE);
2919
2920 fail:
2921 *minor_status = ENOMEM;
2922 return (GSS_S_FAILURE);
2923 }
2924
2925 OM_uint32 KRB5_CALLCONV
spnego_gss_inquire_attrs_for_mech(OM_uint32 * minor_status,gss_const_OID mech,gss_OID_set * mech_attrs,gss_OID_set * known_mech_attrs)2926 spnego_gss_inquire_attrs_for_mech(OM_uint32 *minor_status,
2927 gss_const_OID mech,
2928 gss_OID_set *mech_attrs,
2929 gss_OID_set *known_mech_attrs)
2930 {
2931 OM_uint32 major, tmpMinor;
2932
2933 /* known_mech_attrs is handled by mechglue */
2934 *minor_status = 0;
2935
2936 if (mech_attrs == NULL)
2937 return (GSS_S_COMPLETE);
2938
2939 major = gss_create_empty_oid_set(minor_status, mech_attrs);
2940 if (GSS_ERROR(major))
2941 goto cleanup;
2942
2943 #define MA_SUPPORTED(ma) do { \
2944 major = gss_add_oid_set_member(minor_status, \
2945 (gss_OID)ma, mech_attrs); \
2946 if (GSS_ERROR(major)) \
2947 goto cleanup; \
2948 } while (0)
2949
2950 MA_SUPPORTED(GSS_C_MA_MECH_NEGO);
2951 MA_SUPPORTED(GSS_C_MA_ITOK_FRAMED);
2952
2953 cleanup:
2954 if (GSS_ERROR(major))
2955 gss_release_oid_set(&tmpMinor, mech_attrs);
2956
2957 return (major);
2958 }
2959
2960 OM_uint32 KRB5_CALLCONV
spnego_gss_export_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_buffer_t token)2961 spnego_gss_export_cred(OM_uint32 *minor_status,
2962 gss_cred_id_t cred_handle,
2963 gss_buffer_t token)
2964 {
2965 spnego_gss_cred_id_t spcred = (spnego_gss_cred_id_t)cred_handle;
2966
2967 return (gss_export_cred(minor_status, spcred->mcred, token));
2968 }
2969
2970 OM_uint32 KRB5_CALLCONV
spnego_gss_import_cred(OM_uint32 * minor_status,gss_buffer_t token,gss_cred_id_t * cred_handle)2971 spnego_gss_import_cred(OM_uint32 *minor_status,
2972 gss_buffer_t token,
2973 gss_cred_id_t *cred_handle)
2974 {
2975 OM_uint32 ret;
2976 spnego_gss_cred_id_t spcred;
2977 gss_cred_id_t mcred;
2978
2979 ret = gss_import_cred(minor_status, token, &mcred);
2980 if (GSS_ERROR(ret))
2981 return (ret);
2982
2983 ret = create_spnego_cred(minor_status, mcred, &spcred);
2984 if (GSS_ERROR(ret))
2985 return (ret);
2986
2987 *cred_handle = (gss_cred_id_t)spcred;
2988 return (ret);
2989 }
2990
2991 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)2992 spnego_gss_get_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
2993 gss_qop_t qop_req, gss_iov_buffer_desc *iov,
2994 int iov_count)
2995 {
2996 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
2997
2998 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
2999 return (GSS_S_NO_CONTEXT);
3000
3001 return gss_get_mic_iov(minor_status, sc->ctx_handle, qop_req, iov,
3002 iov_count);
3003 }
3004
3005 OM_uint32 KRB5_CALLCONV
spnego_gss_verify_mic_iov(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t * qop_state,gss_iov_buffer_desc * iov,int iov_count)3006 spnego_gss_verify_mic_iov(OM_uint32 *minor_status, gss_ctx_id_t context_handle,
3007 gss_qop_t *qop_state, gss_iov_buffer_desc *iov,
3008 int iov_count)
3009 {
3010 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3011
3012 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3013 return (GSS_S_NO_CONTEXT);
3014
3015 return gss_verify_mic_iov(minor_status, sc->ctx_handle, qop_state, iov,
3016 iov_count);
3017 }
3018
3019 OM_uint32 KRB5_CALLCONV
spnego_gss_get_mic_iov_length(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_qop_t qop_req,gss_iov_buffer_desc * iov,int iov_count)3020 spnego_gss_get_mic_iov_length(OM_uint32 *minor_status,
3021 gss_ctx_id_t context_handle, gss_qop_t qop_req,
3022 gss_iov_buffer_desc *iov, int iov_count)
3023 {
3024 spnego_gss_ctx_id_t sc = (spnego_gss_ctx_id_t)context_handle;
3025
3026 if (sc->ctx_handle == GSS_C_NO_CONTEXT)
3027 return (GSS_S_NO_CONTEXT);
3028
3029 return gss_get_mic_iov_length(minor_status, sc->ctx_handle, qop_req, iov,
3030 iov_count);
3031 }
3032
3033 /*
3034 * We will release everything but the ctx_handle so that it
3035 * can be passed back to init/accept context. This routine should
3036 * not be called until after the ctx_handle memory is assigned to
3037 * the supplied context handle from init/accept context.
3038 */
3039 static void
release_spnego_ctx(spnego_gss_ctx_id_t * ctx)3040 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
3041 {
3042 spnego_gss_ctx_id_t context;
3043 OM_uint32 minor_stat;
3044 context = *ctx;
3045
3046 if (context != NULL) {
3047 (void) gss_release_buffer(&minor_stat,
3048 &context->DER_mechTypes);
3049
3050 (void) gss_release_oid_set(&minor_stat, &context->mech_set);
3051
3052 (void) gss_release_name(&minor_stat, &context->internal_name);
3053 (void) gss_release_cred(&minor_stat, &context->deleg_cred);
3054
3055 negoex_release_context(context);
3056
3057 free(context);
3058 *ctx = NULL;
3059 }
3060 }
3061
3062 /*
3063 * Can't use gss_indicate_mechs by itself to get available mechs for
3064 * SPNEGO because it will also return the SPNEGO mech and we do not
3065 * want to consider SPNEGO as an available security mech for
3066 * negotiation. For this reason, get_available_mechs will return
3067 * all available, non-deprecated mechs except SPNEGO and NegoEx-
3068 * only mechanisms.
3069 *
3070 * Note that gss_acquire_cred_from(GSS_C_NO_OID_SET) will filter
3071 * out hidden (GSS_C_MA_NOT_INDICATED) mechanisms such as NegoEx, so
3072 * calling gss_indicate_mechs_by_attrs() also works around that.
3073 *
3074 * If a ptr to a creds list is given, this function will attempt
3075 * to acquire creds for the creds given and trim the list of
3076 * returned mechanisms to only those for which creds are valid.
3077 *
3078 */
3079 static OM_uint32
get_available_mechs(OM_uint32 * minor_status,gss_name_t name,gss_cred_usage_t usage,gss_const_key_value_set_t cred_store,gss_cred_id_t * creds,gss_OID_set * rmechs,OM_uint32 * time_rec)3080 get_available_mechs(OM_uint32 *minor_status,
3081 gss_name_t name, gss_cred_usage_t usage,
3082 gss_const_key_value_set_t cred_store,
3083 gss_cred_id_t *creds, gss_OID_set *rmechs, OM_uint32 *time_rec)
3084 {
3085 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
3086 gss_OID_set mechs, goodmechs;
3087 gss_OID_set_desc except_attrs;
3088 gss_OID_desc attr_oids[3];
3089
3090 *rmechs = GSS_C_NO_OID_SET;
3091
3092 attr_oids[0] = *GSS_C_MA_DEPRECATED;
3093 attr_oids[1] = *GSS_C_MA_NOT_DFLT_MECH;
3094 attr_oids[2] = *GSS_C_MA_MECH_NEGO; /* Exclude ourselves */
3095 except_attrs.count = sizeof(attr_oids) / sizeof(attr_oids[0]);
3096 except_attrs.elements = attr_oids;
3097 major_status = gss_indicate_mechs_by_attrs(minor_status,
3098 GSS_C_NO_OID_SET,
3099 &except_attrs,
3100 GSS_C_NO_OID_SET, &mechs);
3101
3102 /*
3103 * If the caller wanted a list of creds returned,
3104 * trim the list of mechanisms down to only those
3105 * for which the creds are valid.
3106 */
3107 if (mechs->count > 0 && major_status == GSS_S_COMPLETE &&
3108 creds != NULL) {
3109 major_status = gss_acquire_cred_from(minor_status, name,
3110 GSS_C_INDEFINITE,
3111 mechs, usage,
3112 cred_store, creds,
3113 &goodmechs, time_rec);
3114
3115 /*
3116 * Drop the old list in favor of the new
3117 * "trimmed" list.
3118 */
3119 if (major_status == GSS_S_COMPLETE) {
3120 (void) gss_release_oid_set(&tmpmin, &mechs);
3121 mechs = goodmechs;
3122 }
3123 }
3124
3125 if (mechs->count > 0 && major_status == GSS_S_COMPLETE) {
3126 *rmechs = mechs;
3127 } else {
3128 (void) gss_release_oid_set(&tmpmin, &mechs);
3129 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3130 map_errcode(minor_status);
3131 if (major_status == GSS_S_COMPLETE)
3132 major_status = GSS_S_FAILURE;
3133 }
3134
3135 return (major_status);
3136 }
3137
3138 /* Return true if mech asserts the GSS_C_MA_NEGOEX_AND_SPNEGO attribute. */
3139 static int
negoex_and_spnego(gss_OID mech)3140 negoex_and_spnego(gss_OID mech)
3141 {
3142 OM_uint32 ret, minor;
3143 gss_OID_set attrs;
3144 int present;
3145
3146 ret = gss_inquire_attrs_for_mech(&minor, mech, &attrs, NULL);
3147 if (ret != GSS_S_COMPLETE || attrs == GSS_C_NO_OID_SET)
3148 return 0;
3149
3150 (void) generic_gss_test_oid_set_member(&minor,
3151 GSS_C_MA_NEGOEX_AND_SPNEGO,
3152 attrs, &present);
3153 (void) gss_release_oid_set(&minor, &attrs);
3154 return present;
3155 }
3156
3157 /*
3158 * Fill sc->mech_set with the SPNEGO-negotiable mechanism OIDs, and
3159 * sc->negoex_mechs with an entry for each NegoEx-negotiable mechanism. Take
3160 * into account the mech set provided with gss_set_neg_mechs() if it exists.
3161 */
3162 static OM_uint32
get_negotiable_mechs(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,spnego_gss_cred_id_t spcred,gss_cred_usage_t usage)3163 get_negotiable_mechs(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
3164 spnego_gss_cred_id_t spcred, gss_cred_usage_t usage)
3165 {
3166 OM_uint32 ret, tmpmin;
3167 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
3168 gss_OID_set cred_mechs = GSS_C_NULL_OID_SET, mechs;
3169 unsigned int i;
3170 int present, added_negoex = 0;
3171 auth_scheme scheme;
3172
3173 if (spcred != NULL) {
3174 /* Get the list of mechs in the mechglue cred. */
3175 ret = gss_inquire_cred(minor_status, spcred->mcred, NULL,
3176 NULL, NULL, &cred_mechs);
3177 if (ret != GSS_S_COMPLETE)
3178 return (ret);
3179 } else {
3180 /* Start with the list of available mechs. */
3181 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, usage,
3182 GSS_C_NO_CRED_STORE, &creds,
3183 &cred_mechs, NULL);
3184 if (ret != GSS_S_COMPLETE)
3185 return (ret);
3186 gss_release_cred(&tmpmin, &creds);
3187 }
3188
3189 /* If gss_set_neg_mechs() was called, use that to determine the
3190 * iteration order. Otherwise iterate over the credential mechs. */
3191 mechs = (spcred != NULL && spcred->neg_mechs != GSS_C_NULL_OID_SET) ?
3192 spcred->neg_mechs : cred_mechs;
3193
3194 ret = gss_create_empty_oid_set(minor_status, &sc->mech_set);
3195 if (ret != GSS_S_COMPLETE)
3196 goto cleanup;
3197
3198 for (i = 0; i < mechs->count; i++) {
3199 if (mechs != cred_mechs) {
3200 /* Intersect neg_mechs with cred_mechs. */
3201 gss_test_oid_set_member(&tmpmin, &mechs->elements[i],
3202 cred_mechs, &present);
3203 if (!present)
3204 continue;
3205 }
3206
3207 /* Query the auth scheme to see if this is a NegoEx mech. */
3208 ret = gssspi_query_mechanism_info(&tmpmin, &mechs->elements[i],
3209 scheme);
3210 if (ret == GSS_S_COMPLETE) {
3211 /* Add an entry for this mech to the NegoEx list. */
3212 ret = negoex_add_auth_mech(minor_status, sc,
3213 &mechs->elements[i],
3214 scheme);
3215 if (ret != GSS_S_COMPLETE)
3216 goto cleanup;
3217
3218 /* Add the NegoEx OID to the SPNEGO list at the
3219 * position of the first NegoEx mechanism. */
3220 if (!added_negoex) {
3221 ret = gss_add_oid_set_member(minor_status,
3222 &negoex_mech,
3223 &sc->mech_set);
3224 if (ret != GSS_S_COMPLETE)
3225 goto cleanup;
3226 added_negoex = 1;
3227 }
3228
3229 /* Skip this mech in the SPNEGO list unless it asks for
3230 * direct SPNEGO negotiation. */
3231 if (!negoex_and_spnego(&mechs->elements[i]))
3232 continue;
3233 }
3234
3235 /* Add this mech to the SPNEGO list. */
3236 ret = gss_add_oid_set_member(minor_status, &mechs->elements[i],
3237 &sc->mech_set);
3238 if (ret != GSS_S_COMPLETE)
3239 goto cleanup;
3240 }
3241
3242 *minor_status = 0;
3243
3244 cleanup:
3245 if (ret != GSS_S_COMPLETE || sc->mech_set->count == 0) {
3246 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
3247 map_errcode(minor_status);
3248 ret = GSS_S_FAILURE;
3249 }
3250
3251 gss_release_oid_set(&tmpmin, &cred_mechs);
3252 return (ret);
3253 }
3254
3255 /* following are token creation and reading routines */
3256
3257 /*
3258 * If in contains a tagged OID encoding, return a copy of the contents as a
3259 * gss_OID and advance in past the encoding. Otherwise return NULL and do not
3260 * advance in.
3261 */
3262 static gss_OID
get_mech_oid(OM_uint32 * minor_status,struct k5input * in)3263 get_mech_oid(OM_uint32 *minor_status, struct k5input *in)
3264 {
3265 struct k5input oidrep;
3266 OM_uint32 status;
3267 gss_OID_desc oid;
3268 gss_OID mech_out = NULL;
3269
3270 if (!k5_der_get_value(in, MECH_OID, &oidrep))
3271 return (NULL);
3272
3273 oid.length = oidrep.len;
3274 oid.elements = (uint8_t *)oidrep.ptr;
3275 status = generic_gss_copy_oid(minor_status, &oid, &mech_out);
3276 if (status != GSS_S_COMPLETE) {
3277 map_errcode(minor_status);
3278 mech_out = NULL;
3279 }
3280
3281 return (mech_out);
3282 }
3283
3284 /*
3285 * If in contains a tagged octet string encoding, return a copy of the contents
3286 * as a gss_buffer_t and advance in past the encoding. Otherwise return NULL
3287 * and do not advance in.
3288 */
3289 static gss_buffer_t
get_octet_string(struct k5input * in)3290 get_octet_string(struct k5input *in)
3291 {
3292 gss_buffer_t input_token;
3293 struct k5input ostr;
3294
3295 if (!k5_der_get_value(in, OCTET_STRING, &ostr))
3296 return (NULL);
3297
3298 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
3299 if (input_token == NULL)
3300 return (NULL);
3301
3302 input_token->length = ostr.len;
3303 if (input_token->length > 0) {
3304 input_token->value = gssalloc_malloc(input_token->length);
3305 if (input_token->value == NULL) {
3306 free(input_token);
3307 return (NULL);
3308 }
3309
3310 memcpy(input_token->value, ostr.ptr, input_token->length);
3311 } else {
3312 input_token->value = NULL;
3313 }
3314 return (input_token);
3315 }
3316
3317 /*
3318 * verify that buff_in points to a sequence of der encoding. The mech
3319 * set is the only sequence of encoded object in the token, so if it is
3320 * a sequence of encoding, decode the mechset into a gss_OID_set and
3321 * return it, advancing the buffer pointer.
3322 */
3323 static gss_OID_set
get_mech_set(OM_uint32 * minor_status,struct k5input * in)3324 get_mech_set(OM_uint32 *minor_status, struct k5input *in)
3325 {
3326 gss_OID_set returned_mechSet;
3327 OM_uint32 major_status, tmpmin;
3328 struct k5input seq;
3329
3330 if (!k5_der_get_value(in, SEQUENCE_OF, &seq))
3331 return (NULL);
3332
3333 major_status = gss_create_empty_oid_set(minor_status,
3334 &returned_mechSet);
3335 if (major_status != GSS_S_COMPLETE)
3336 return (NULL);
3337
3338 while (!seq.status && seq.len > 0) {
3339 gss_OID_desc *oid = get_mech_oid(minor_status, &seq);
3340
3341 if (oid == NULL) {
3342 gss_release_oid_set(&tmpmin, &returned_mechSet);
3343 return (NULL);
3344 }
3345
3346 major_status = gss_add_oid_set_member(minor_status,
3347 oid, &returned_mechSet);
3348 generic_gss_release_oid(minor_status, &oid);
3349 if (major_status != GSS_S_COMPLETE) {
3350 gss_release_oid_set(&tmpmin, &returned_mechSet);
3351 return (NULL);
3352 }
3353 }
3354
3355 return (returned_mechSet);
3356 }
3357
3358 /*
3359 * Encode mechSet into buf.
3360 */
3361 static int
put_mech_set(gss_OID_set mechSet,gss_buffer_t buffer_out)3362 put_mech_set(gss_OID_set mechSet, gss_buffer_t buffer_out)
3363 {
3364 uint8_t *ptr;
3365 size_t ilen, tlen, i;
3366 struct k5buf buf;
3367
3368 ilen = 0;
3369 for (i = 0; i < mechSet->count; i++)
3370 ilen += k5_der_value_len(mechSet->elements[i].length);
3371 tlen = k5_der_value_len(ilen);
3372
3373 ptr = gssalloc_malloc(tlen);
3374 if (ptr == NULL)
3375 return -1;
3376 k5_buf_init_fixed(&buf, ptr, tlen);
3377
3378 k5_der_add_taglen(&buf, SEQUENCE_OF, ilen);
3379 for (i = 0; i < mechSet->count; i++) {
3380 k5_der_add_value(&buf, MECH_OID,
3381 mechSet->elements[i].elements,
3382 mechSet->elements[i].length);
3383 }
3384 assert(buf.len == tlen);
3385
3386 buffer_out->value = ptr;
3387 buffer_out->length = tlen;
3388 return 0;
3389 }
3390
3391 /* Decode SPNEGO request flags from the DER encoding of a bit string and set
3392 * them in *ret_flags. */
3393 static OM_uint32
get_req_flags(struct k5input * in,OM_uint32 * req_flags)3394 get_req_flags(struct k5input *in, OM_uint32 *req_flags)
3395 {
3396 if (in->status || in->len != 4 ||
3397 k5_input_get_byte(in) != BIT_STRING ||
3398 k5_input_get_byte(in) != BIT_STRING_LENGTH ||
3399 k5_input_get_byte(in) != BIT_STRING_PADDING)
3400 return GSS_S_DEFECTIVE_TOKEN;
3401
3402 *req_flags = k5_input_get_byte(in) >> 1;
3403 return GSS_S_COMPLETE;
3404 }
3405
3406 static OM_uint32
get_negTokenInit(OM_uint32 * minor_status,gss_buffer_t buf,gss_buffer_t der_mechSet,gss_OID_set * mechSet,OM_uint32 * req_flags,gss_buffer_t * mechtok,gss_buffer_t * mechListMIC)3407 get_negTokenInit(OM_uint32 *minor_status,
3408 gss_buffer_t buf,
3409 gss_buffer_t der_mechSet,
3410 gss_OID_set *mechSet,
3411 OM_uint32 *req_flags,
3412 gss_buffer_t *mechtok,
3413 gss_buffer_t *mechListMIC)
3414 {
3415 OM_uint32 err;
3416 struct k5input in, seq, field;
3417
3418 *minor_status = 0;
3419 der_mechSet->length = 0;
3420 der_mechSet->value = NULL;
3421 *mechSet = GSS_C_NO_OID_SET;
3422 *req_flags = 0;
3423 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3424
3425 k5_input_init(&in, buf->value, buf->length);
3426
3427 /* Advance past the framing header. */
3428 err = verify_token_header(&in, gss_mech_spnego);
3429 if (err)
3430 return GSS_S_DEFECTIVE_TOKEN;
3431
3432 /* Advance past the [0] tag for the NegotiationToken choice. */
3433 if (!k5_der_get_value(&in, CONTEXT, &seq))
3434 return GSS_S_DEFECTIVE_TOKEN;
3435
3436 /* Advance past the SEQUENCE tag. */
3437 if (!k5_der_get_value(&seq, SEQUENCE, &seq))
3438 return GSS_S_DEFECTIVE_TOKEN;
3439
3440 /* Get the contents of the mechTypes field. Reject an empty field here
3441 * since we musn't allocate a zero-length buffer in the next step. */
3442 if (!k5_der_get_value(&seq, CONTEXT, &field) || field.len == 0)
3443 return GSS_S_DEFECTIVE_TOKEN;
3444
3445 /* Store a copy of the contents for MIC computation. */
3446 der_mechSet->value = gssalloc_malloc(field.len);
3447 if (der_mechSet->value == NULL)
3448 return GSS_S_FAILURE;
3449 memcpy(der_mechSet->value, field.ptr, field.len);
3450 der_mechSet->length = field.len;
3451
3452 /* Decode the contents into an OID set. */
3453 *mechSet = get_mech_set(minor_status, &field);
3454 if (*mechSet == NULL)
3455 return GSS_S_FAILURE;
3456
3457 if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3458 err = get_req_flags(&field, req_flags);
3459 if (err != GSS_S_COMPLETE)
3460 return err;
3461 }
3462
3463 if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3464 *mechtok = get_octet_string(&field);
3465 if (*mechtok == GSS_C_NO_BUFFER)
3466 return GSS_S_FAILURE;
3467 }
3468
3469 if (k5_der_get_value(&seq, CONTEXT | 0x03, &field)) {
3470 *mechListMIC = get_octet_string(&field);
3471 if (*mechListMIC == GSS_C_NO_BUFFER)
3472 return GSS_S_FAILURE;
3473 }
3474
3475 return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3476 }
3477
3478 /* Decode a NegotiationToken of type negTokenResp. */
3479 static OM_uint32
get_negTokenResp(OM_uint32 * minor_status,struct k5input * in,OM_uint32 * negState,gss_OID * supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC)3480 get_negTokenResp(OM_uint32 *minor_status, struct k5input *in,
3481 OM_uint32 *negState, gss_OID *supportedMech,
3482 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC)
3483 {
3484 struct k5input seq, field, en;
3485
3486 *negState = UNSPECIFIED;
3487 *supportedMech = GSS_C_NO_OID;
3488 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3489
3490 /* Advance past the [1] tag for the NegotiationToken choice. */
3491 if (!k5_der_get_value(in, CONTEXT | 0x01, &seq))
3492 return GSS_S_DEFECTIVE_TOKEN;
3493
3494 /* Advance seq past the SEQUENCE tag (historically this code allows the
3495 * tag to be missing). */
3496 (void)k5_der_get_value(&seq, SEQUENCE, &seq);
3497
3498 if (k5_der_get_value(&seq, CONTEXT, &field)) {
3499 if (!k5_der_get_value(&field, ENUMERATED, &en))
3500 return GSS_S_DEFECTIVE_TOKEN;
3501 if (en.len != ENUMERATION_LENGTH)
3502 return GSS_S_DEFECTIVE_TOKEN;
3503 *negState = *en.ptr;
3504 }
3505
3506 if (k5_der_get_value(&seq, CONTEXT | 0x01, &field)) {
3507 *supportedMech = get_mech_oid(minor_status, &field);
3508 if (*supportedMech == GSS_C_NO_OID)
3509 return GSS_S_DEFECTIVE_TOKEN;
3510 }
3511
3512 if (k5_der_get_value(&seq, CONTEXT | 0x02, &field)) {
3513 *responseToken = get_octet_string(&field);
3514 if (*responseToken == GSS_C_NO_BUFFER)
3515 return GSS_S_DEFECTIVE_TOKEN;
3516 }
3517
3518 if (k5_der_get_value(&seq, CONTEXT | 0x04, &field)) {
3519 *mechListMIC = get_octet_string(&field);
3520
3521 /* Handle Windows 2000 duplicate response token */
3522 if (*responseToken &&
3523 ((*responseToken)->length == (*mechListMIC)->length) &&
3524 !memcmp((*responseToken)->value, (*mechListMIC)->value,
3525 (*responseToken)->length)) {
3526 OM_uint32 tmpmin;
3527
3528 gss_release_buffer(&tmpmin, *mechListMIC);
3529 free(*mechListMIC);
3530 *mechListMIC = NULL;
3531 }
3532 }
3533
3534 return seq.status ? GSS_S_DEFECTIVE_TOKEN : GSS_S_COMPLETE;
3535 }
3536
3537 /*
3538 * This routine compares the received mechset to the mechset that
3539 * this server can support. It looks sequentially through the mechset
3540 * and the first one that matches what the server can support is
3541 * chosen as the negotiated mechanism. If one is found, negResult
3542 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3543 * it's not the first mech, otherwise we return NULL and negResult
3544 * is set to REJECT. The returned pointer is an alias into
3545 * received->elements and should not be freed.
3546 *
3547 * NOTE: There is currently no way to specify a preference order of
3548 * mechanisms supported by the acceptor.
3549 */
3550 static gss_OID
negotiate_mech(spnego_gss_ctx_id_t ctx,gss_OID_set received,OM_uint32 * negResult)3551 negotiate_mech(spnego_gss_ctx_id_t ctx, gss_OID_set received,
3552 OM_uint32 *negResult)
3553 {
3554 size_t i, j;
3555 int wrong_krb5_oid;
3556
3557 for (i = 0; i < received->count; i++) {
3558 gss_OID mech_oid = &received->elements[i];
3559
3560 /* Accept wrong mechanism OID from MS clients */
3561 wrong_krb5_oid = 0;
3562 if (g_OID_equal(mech_oid, &gss_mech_krb5_wrong_oid)) {
3563 mech_oid = (gss_OID)&gss_mech_krb5_oid;
3564 wrong_krb5_oid = 1;
3565 }
3566
3567 for (j = 0; j < ctx->mech_set->count; j++) {
3568 if (g_OID_equal(mech_oid,
3569 &ctx->mech_set->elements[j])) {
3570 *negResult = (i == 0) ? ACCEPT_INCOMPLETE :
3571 REQUEST_MIC;
3572 return wrong_krb5_oid ?
3573 (gss_OID)&gss_mech_krb5_wrong_oid :
3574 &ctx->mech_set->elements[j];
3575 }
3576 }
3577 }
3578 *negResult = REJECT;
3579 return (NULL);
3580 }
3581
3582 /*
3583 * the next two routines make a token buffer suitable for
3584 * spnego_gss_display_status. These currently take the string
3585 * in name and place it in the token. Eventually, if
3586 * spnego_gss_display_status returns valid error messages,
3587 * these routines will be changes to return the error string.
3588 */
3589 static spnego_token_t
make_spnego_token(const char * name)3590 make_spnego_token(const char *name)
3591 {
3592 return (spnego_token_t)gssalloc_strdup(name);
3593 }
3594
3595 static gss_buffer_desc
make_err_msg(const char * name)3596 make_err_msg(const char *name)
3597 {
3598 gss_buffer_desc buffer;
3599
3600 if (name == NULL) {
3601 buffer.length = 0;
3602 buffer.value = NULL;
3603 } else {
3604 buffer.length = strlen(name)+1;
3605 buffer.value = make_spnego_token(name);
3606 }
3607
3608 return (buffer);
3609 }
3610
3611 /*
3612 * Create the client side spnego token passed back to gss_init_sec_context
3613 * and eventually up to the application program and over to the server.
3614 *
3615 * Use DER rules, definite length method per RFC 2478
3616 */
3617 static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,int negHintsCompat,gss_buffer_t mic,OM_uint32 req_flags,gss_buffer_t token,send_token_flag sendtoken,gss_buffer_t outbuf)3618 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, int negHintsCompat,
3619 gss_buffer_t mic, OM_uint32 req_flags,
3620 gss_buffer_t token, send_token_flag sendtoken,
3621 gss_buffer_t outbuf)
3622 {
3623 size_t f0len, f2len, f3len, fields_len, seq_len, choice_len;
3624 size_t mech_len, framed_len;
3625 uint8_t *t;
3626 struct k5buf buf;
3627
3628 if (outbuf == GSS_C_NO_BUFFER)
3629 return (-1);
3630
3631 outbuf->length = 0;
3632 outbuf->value = NULL;
3633
3634 /* Calculate the length of each field and the total fields length. */
3635 fields_len = 0;
3636 /* mechTypes [0] MechTypeList, previously assembled in spnego_ctx */
3637 f0len = spnego_ctx->DER_mechTypes.length;
3638 fields_len += k5_der_value_len(f0len);
3639 if (token != NULL) {
3640 /* mechToken [2] OCTET STRING OPTIONAL */
3641 f2len = k5_der_value_len(token->length);
3642 fields_len += k5_der_value_len(f2len);
3643 }
3644 if (mic != GSS_C_NO_BUFFER) {
3645 /* mechListMIC [3] OCTET STRING OPTIONAL */
3646 f3len = k5_der_value_len(mic->length);
3647 fields_len += k5_der_value_len(f3len);
3648 }
3649
3650 /* Calculate the length of the sequence and choice. */
3651 seq_len = k5_der_value_len(fields_len);
3652 choice_len = k5_der_value_len(seq_len);
3653
3654 /* Calculate the framed token length. */
3655 mech_len = k5_der_value_len(gss_mech_spnego->length);
3656 framed_len = k5_der_value_len(mech_len + choice_len);
3657
3658 /* Allocate space and prepare a buffer. */
3659 t = gssalloc_malloc(framed_len);
3660 if (t == NULL)
3661 return (-1);
3662 k5_buf_init_fixed(&buf, t, framed_len);
3663
3664 /* Add generic token framing. */
3665 k5_der_add_taglen(&buf, HEADER_ID, mech_len + choice_len);
3666 k5_der_add_value(&buf, MECH_OID, gss_mech_spnego->elements,
3667 gss_mech_spnego->length);
3668
3669 /* Add NegotiationToken choice tag and NegTokenInit sequence tag. */
3670 k5_der_add_taglen(&buf, CONTEXT | 0x00, seq_len);
3671 k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3672
3673 /* Add the already-encoded mechanism list as mechTypes. */
3674 k5_der_add_value(&buf, CONTEXT | 0x00, spnego_ctx->DER_mechTypes.value,
3675 spnego_ctx->DER_mechTypes.length);
3676
3677 if (token != NULL) {
3678 k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3679 k5_der_add_value(&buf, OCTET_STRING, token->value,
3680 token->length);
3681 }
3682
3683 if (mic != GSS_C_NO_BUFFER) {
3684 uint8_t id = negHintsCompat ? SEQUENCE : OCTET_STRING;
3685 k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3686 k5_der_add_value(&buf, id, mic->value, mic->length);
3687 }
3688
3689 assert(buf.len == framed_len);
3690 outbuf->length = framed_len;
3691 outbuf->value = t;
3692
3693 return (0);
3694 }
3695
3696 /*
3697 * create the server side spnego token passed back to
3698 * gss_accept_sec_context and eventually up to the application program
3699 * and over to the client.
3700 */
3701 static OM_uint32
make_spnego_tokenTarg_msg(uint8_t status,gss_OID mech_wanted,gss_buffer_t token,gss_buffer_t mic,send_token_flag sendtoken,gss_buffer_t outbuf)3702 make_spnego_tokenTarg_msg(uint8_t status, gss_OID mech_wanted,
3703 gss_buffer_t token, gss_buffer_t mic,
3704 send_token_flag sendtoken,
3705 gss_buffer_t outbuf)
3706 {
3707 size_t f0len, f1len, f2len, f3len, fields_len, seq_len, choice_len;
3708 uint8_t *t;
3709 struct k5buf buf;
3710
3711 if (outbuf == GSS_C_NO_BUFFER)
3712 return (GSS_S_DEFECTIVE_TOKEN);
3713 if (sendtoken == INIT_TOKEN_SEND && mech_wanted == GSS_C_NO_OID)
3714 return (GSS_S_DEFECTIVE_TOKEN);
3715
3716 outbuf->length = 0;
3717 outbuf->value = NULL;
3718
3719 /* Calculate the length of each field and the total fields length. */
3720 fields_len = 0;
3721 /* negState [0] ENUMERATED { ... } OPTIONAL */
3722 f0len = k5_der_value_len(1);
3723 fields_len += k5_der_value_len(f0len);
3724 if (sendtoken == INIT_TOKEN_SEND) {
3725 /* supportedMech [1] MechType OPTIONAL */
3726 f1len = k5_der_value_len(mech_wanted->length);
3727 fields_len += k5_der_value_len(f1len);
3728 }
3729 if (token != NULL && token->length > 0) {
3730 /* mechToken [2] OCTET STRING OPTIONAL */
3731 f2len = k5_der_value_len(token->length);
3732 fields_len += k5_der_value_len(f2len);
3733 }
3734 if (mic != NULL) {
3735 /* mechListMIC [3] OCTET STRING OPTIONAL */
3736 f3len = k5_der_value_len(mic->length);
3737 fields_len += k5_der_value_len(f3len);
3738 }
3739
3740 /* Calculate the length of the sequence and choice. */
3741 seq_len = k5_der_value_len(fields_len);
3742 choice_len = k5_der_value_len(seq_len);
3743
3744 /* Allocate space and prepare a buffer. */
3745 t = gssalloc_malloc(choice_len);
3746 if (t == NULL)
3747 return (GSS_S_DEFECTIVE_TOKEN);
3748 k5_buf_init_fixed(&buf, t, choice_len);
3749
3750 /* Add the choice tag and begin the sequence. */
3751 k5_der_add_taglen(&buf, CONTEXT | 0x01, seq_len);
3752 k5_der_add_taglen(&buf, SEQUENCE, fields_len);
3753
3754 /* Add the negState field. */
3755 k5_der_add_taglen(&buf, CONTEXT | 0x00, f0len);
3756 k5_der_add_value(&buf, ENUMERATED, &status, 1);
3757
3758 if (sendtoken == INIT_TOKEN_SEND) {
3759 /* Add the supportedMech field. */
3760 k5_der_add_taglen(&buf, CONTEXT | 0x01, f1len);
3761 k5_der_add_value(&buf, MECH_OID, mech_wanted->elements,
3762 mech_wanted->length);
3763 }
3764
3765 if (token != NULL && token->length > 0) {
3766 /* Add the mechToken field. */
3767 k5_der_add_taglen(&buf, CONTEXT | 0x02, f2len);
3768 k5_der_add_value(&buf, OCTET_STRING, token->value,
3769 token->length);
3770 }
3771
3772 if (mic != NULL) {
3773 /* Add the mechListMIC field. */
3774 k5_der_add_taglen(&buf, CONTEXT | 0x03, f3len);
3775 k5_der_add_value(&buf, OCTET_STRING, mic->value, mic->length);
3776 }
3777
3778 assert(buf.len == choice_len);
3779 outbuf->length = choice_len;
3780 outbuf->value = t;
3781
3782 return (0);
3783 }
3784
3785 /* Advance in past the [APPLICATION 0] tag and thisMech field of an
3786 * InitialContextToken encoding, checking that thisMech matches mech. */
3787 static int
verify_token_header(struct k5input * in,gss_OID_const mech)3788 verify_token_header(struct k5input *in, gss_OID_const mech)
3789 {
3790 gss_OID_desc oid;
3791 struct k5input field;
3792
3793 if (!k5_der_get_value(in, HEADER_ID, in))
3794 return (G_BAD_TOK_HEADER);
3795 if (!k5_der_get_value(in, MECH_OID, &field))
3796 return (G_BAD_TOK_HEADER);
3797
3798 oid.length = field.len;
3799 oid.elements = (uint8_t *)field.ptr;
3800 return g_OID_equal(&oid, mech) ? 0 : G_WRONG_MECH;
3801 }
3802
3803 /*
3804 * Return non-zero if the oid is one of the kerberos mech oids,
3805 * otherwise return zero.
3806 *
3807 * N.B. There are 3 oids that represent the kerberos mech:
3808 * RFC-specified GSS_MECH_KRB5_OID,
3809 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3810 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3811 */
3812
3813 static int
is_kerb_mech(gss_OID oid)3814 is_kerb_mech(gss_OID oid)
3815 {
3816 int answer = 0;
3817 OM_uint32 minor;
3818 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3819
3820 (void) gss_test_oid_set_member(&minor,
3821 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3822
3823 return (answer);
3824 }
3825