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