dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_spnego / mech / spnego_mech.c
blob1bf5a2c26fdf1bb9f1e545bb8a87b95786469aca
1 /*
2 * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
3 */
4 /*
5 * Copyright (C) 2006,2008 by the Massachusetts Institute of Technology.
6 * All rights reserved.
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
30 * A module that implements the spnego security mechanism.
31 * It is used to negotiate the security mechanism between
32 * peers using the GSS-API.
37 * Copyright (c) 2006-2008, Novell, Inc.
38 * All rights reserved.
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions are met:
43 * * Redistributions of source code must retain the above copyright notice,
44 * this list of conditions and the following disclaimer.
45 * * Redistributions in binary form must reproduce the above copyright
46 * notice, this list of conditions and the following disclaimer in the
47 * documentation and/or other materials provided with the distribution.
48 * * The copyright holder's name is not used to endorse or promote products
49 * derived from this software without specific prior written permission.
51 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
52 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
53 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
54 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
55 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
56 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
57 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
58 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
59 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
61 * POSSIBILITY OF SUCH DAMAGE.
63 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
65 #include <sys/param.h>
66 #include <unistd.h>
67 #include <assert.h>
68 #include <stdio.h>
69 #include <stdlib.h>
70 #include <string.h>
71 #include <k5-int.h>
72 #include <krb5.h>
73 #include <mglueP.h>
74 #include "gssapiP_spnego.h"
75 #include "gssapiP_generic.h"
76 #include <gssapi_err_generic.h>
77 #include <locale.h>
80 * SUNW17PACresync
81 * MIT has diff names for these GSS utilities. Solaris needs to change
82 * them globally to get in sync w/MIT.
83 * Revisit for full 1.7 resync.
85 #define gssint_get_modOptions __gss_get_modOptions
86 #define gssint_der_length_size der_length_size
87 #define gssint_get_der_length get_der_length
88 #define gssint_put_der_length put_der_length
89 #define gssint_get_mechanism __gss_get_mechanism
90 #define gssint_copy_oid_set gss_copy_oid_set
91 #define gssint_get_mech_type __gss_get_mech_type
94 #undef g_token_size
95 #undef g_verify_token_header
96 #undef g_make_token_header
98 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
99 typedef const gss_OID_desc *gss_OID_const;
101 /* der routines defined in libgss */
102 extern unsigned int gssint_der_length_size(OM_uint32);
103 extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*);
104 extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32);
107 /* private routines for spnego_mechanism */
108 static spnego_token_t make_spnego_token(char *);
109 static gss_buffer_desc make_err_msg(char *);
110 static int g_token_size(gss_OID_const, unsigned int);
111 static int g_make_token_header(gss_OID_const, unsigned int,
112 unsigned char **, unsigned int);
113 static int g_verify_token_header(gss_OID_const, unsigned int *,
114 unsigned char **,
115 int, unsigned int);
116 static int g_verify_neg_token_init(unsigned char **, unsigned int);
117 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t);
118 static gss_buffer_t get_input_token(unsigned char **, unsigned int);
119 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int);
120 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *);
121 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
122 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
123 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
124 static void check_spnego_options(spnego_gss_ctx_id_t);
125 static spnego_gss_ctx_id_t create_spnego_ctx(void);
126 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf);
127 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int);
128 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int);
129 static int put_negResult(unsigned char **, OM_uint32, unsigned int);
131 static OM_uint32
132 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t,
133 gss_buffer_t *, OM_uint32 *, send_token_flag *);
134 static OM_uint32
135 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t,
136 gss_buffer_t *, OM_uint32 *, send_token_flag *);
138 static OM_uint32
139 init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *,
140 gss_OID_set *, send_token_flag *);
141 static OM_uint32
142 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID,
143 gss_buffer_t *, gss_buffer_t *,
144 OM_uint32 *, send_token_flag *);
145 static OM_uint32
146 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t,
147 gss_buffer_t *, gss_buffer_t *,
148 OM_uint32 *, send_token_flag *);
149 static OM_uint32
150 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32,
151 gss_OID, gss_buffer_t *, gss_buffer_t *,
152 OM_uint32 *, send_token_flag *);
153 static OM_uint32
154 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
155 gss_name_t, OM_uint32, OM_uint32, gss_buffer_t,
156 gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *,
157 OM_uint32 *, send_token_flag *);
159 static OM_uint32
160 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
161 gss_cred_id_t, gss_buffer_t *,
162 gss_buffer_t *, OM_uint32 *, send_token_flag *);
163 static OM_uint32
164 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *,
165 gss_buffer_t *, gss_buffer_t *,
166 OM_uint32 *, send_token_flag *);
167 static OM_uint32
168 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID,
169 OM_uint32 *, send_token_flag *);
170 static OM_uint32
171 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t,
172 gss_buffer_t, gss_OID *, gss_buffer_t,
173 OM_uint32 *, OM_uint32 *, gss_cred_id_t *,
174 OM_uint32 *, send_token_flag *);
176 static gss_OID
177 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
178 OM_uint32 *);
179 static int
180 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *);
182 static int
183 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t,
184 int,
185 gss_buffer_t,
186 OM_uint32, gss_buffer_t, send_token_flag,
187 gss_buffer_t);
188 static int
189 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
190 gss_buffer_t, send_token_flag,
191 gss_buffer_t);
193 static OM_uint32
194 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t,
195 gss_OID_set *, OM_uint32 *, gss_buffer_t *,
196 gss_buffer_t *);
197 static OM_uint32
198 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int,
199 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *);
201 static int
202 is_kerb_mech(gss_OID oid);
204 /* SPNEGO oid structure */
205 static const gss_OID_desc spnego_oids[] = {
206 {SPNEGO_OID_LENGTH, SPNEGO_OID},
209 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0;
210 static const gss_OID_set_desc spnego_oidsets[] = {
211 {1, (gss_OID) spnego_oids+0},
213 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
215 static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *);
216 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int);
217 static OM_uint32
218 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t,
219 gss_buffer_t *, OM_uint32 *, send_token_flag *);
221 #ifdef _GSS_STATIC_LINK
222 int gss_spnegoint_lib_init(void);
223 void gss_spnegoint_lib_fini(void);
224 #else
225 gss_mechanism gss_mech_initialize(void);
226 #endif /* _GSS_STATIC_LINK */
229 * The Mech OID for SPNEGO:
230 * { iso(1) org(3) dod(6) internet(1) security(5)
231 * mechanism(5) spnego(2) }
233 static struct gss_config spnego_mechanism =
235 {SPNEGO_OID_LENGTH, SPNEGO_OID},
236 NULL,
237 glue_spnego_gss_acquire_cred,
238 glue_spnego_gss_release_cred,
239 glue_spnego_gss_init_sec_context,
240 #ifndef LEAN_CLIENT
241 glue_spnego_gss_accept_sec_context,
242 #else
243 NULL,
244 #endif /* LEAN_CLIENT */
245 NULL, /* unseal */
246 NULL, /* gss_process_context_token */
247 glue_spnego_gss_delete_sec_context, /* gss_delete_sec_context */
248 glue_spnego_gss_context_time,
249 glue_spnego_gss_display_status,
250 NULL, /* gss_indicate_mechs */
251 glue_spnego_gss_compare_name,
252 glue_spnego_gss_display_name,
253 glue_spnego_gss_import_name, /* glue */
254 glue_spnego_gss_release_name,
255 NULL, /* gss_inquire_cred */
256 NULL, /* gss_add_cred */
257 NULL, /* seal */
258 #ifndef LEAN_CLIENT
259 glue_spnego_gss_export_sec_context, /* gss_export_sec_context */
260 glue_spnego_gss_import_sec_context, /* gss_import_sec_context */
261 #else
262 NULL, /* gss_export_sec_context */
263 NULL, /* gss_import_sec_context */
264 #endif /* LEAN_CLIENT */
265 NULL, /* gss_inquire_cred_by_mech */
266 glue_spnego_gss_inquire_names_for_mech,
267 glue_spnego_gss_inquire_context,
268 NULL, /* gss_internal_release_oid */
269 glue_spnego_gss_wrap_size_limit,
270 NULL, /* pname */
271 NULL, /* userok */
272 NULL, /* gss_export_name */
273 NULL, /* sign */
274 NULL, /* verify */
275 NULL, /* gss_store_cred */
276 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
279 #ifdef _GSS_STATIC_LINK
280 #include "mglueP.h"
282 static
283 int gss_spnegomechglue_init(void)
285 struct gss_mech_config mech_spnego;
287 memset(&mech_spnego, 0, sizeof(mech_spnego));
288 mech_spnego.mech = &spnego_mechanism;
289 mech_spnego.mechNameStr = "spnego";
290 mech_spnego.mech_type = GSS_C_NO_OID;
292 return gssint_register_mechinfo(&mech_spnego);
294 #else
295 /* Entry point for libgss */
296 gss_mechanism KRB5_CALLCONV
297 gss_mech_initialize(void)
299 int err;
301 err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
302 spnego_gss_delete_error_info);
303 if (err) {
304 syslog(LOG_NOTICE,
305 "SPNEGO gss_mech_initialize: error message TSD key register fail");
306 return (NULL);
309 return (&spnego_mechanism);
312 #if 0 /* SUNW17PACresync */
313 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
314 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
315 int gss_krb5int_lib_init(void)
316 #endif
318 #endif /* _GSS_STATIC_LINK */
320 static
321 int gss_spnegoint_lib_init(void)
323 #ifdef _GSS_STATIC_LINK
324 return gss_spnegomechglue_init();
325 #else
326 int err;
328 err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
329 spnego_gss_delete_error_info);
330 if (err) {
331 syslog(LOG_NOTICE,
332 "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
333 err);
334 return err;
337 return 0;
338 #endif
341 static void gss_spnegoint_lib_fini(void)
345 /*ARGSUSED*/
346 OM_uint32
347 glue_spnego_gss_acquire_cred(
348 void *context,
349 OM_uint32 *minor_status,
350 gss_name_t desired_name,
351 OM_uint32 time_req,
352 gss_OID_set desired_mechs,
353 gss_cred_usage_t cred_usage,
354 gss_cred_id_t *output_cred_handle,
355 gss_OID_set *actual_mechs,
356 OM_uint32 *time_rec)
358 return(spnego_gss_acquire_cred(minor_status,
359 desired_name,
360 time_req,
361 desired_mechs,
362 cred_usage,
363 output_cred_handle,
364 actual_mechs,
365 time_rec));
368 /*ARGSUSED*/
369 OM_uint32
370 spnego_gss_acquire_cred(OM_uint32 *minor_status,
371 gss_name_t desired_name,
372 OM_uint32 time_req,
373 gss_OID_set desired_mechs,
374 gss_cred_usage_t cred_usage,
375 gss_cred_id_t *output_cred_handle,
376 gss_OID_set *actual_mechs,
377 OM_uint32 *time_rec)
379 OM_uint32 status;
380 gss_OID_set amechs;
381 dsyslog("Entering spnego_gss_acquire_cred\n");
383 if (actual_mechs)
384 *actual_mechs = NULL;
386 if (time_rec)
387 *time_rec = 0;
390 * If the user did not specify a list of mechs,
391 * use get_available_mechs to collect a list of
392 * mechs for which creds are available.
394 if (desired_mechs == GSS_C_NULL_OID_SET) {
395 status = get_available_mechs(minor_status,
396 desired_name, cred_usage,
397 output_cred_handle, &amechs);
398 } else {
400 * The caller gave a specific list of mechanisms,
401 * so just get whatever creds are available.
402 * gss_acquire_creds will return the subset of mechs for
403 * which the given 'output_cred_handle' is valid.
405 status = gss_acquire_cred(minor_status,
406 desired_name, time_req,
407 desired_mechs, cred_usage,
408 output_cred_handle, &amechs,
409 time_rec);
412 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
413 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
415 (void) gss_release_oid_set(minor_status, &amechs);
417 dsyslog("Leaving spnego_gss_acquire_cred\n");
418 return (status);
421 /*ARGSUSED*/
422 OM_uint32
423 glue_spnego_gss_release_cred(void *context,
424 OM_uint32 *minor_status,
425 gss_cred_id_t *cred_handle)
427 return( spnego_gss_release_cred(minor_status, cred_handle));
430 /*ARGSUSED*/
431 OM_uint32
432 spnego_gss_release_cred(OM_uint32 *minor_status,
433 gss_cred_id_t *cred_handle)
435 OM_uint32 status;
437 dsyslog("Entering spnego_gss_release_cred\n");
439 if (minor_status == NULL || cred_handle == NULL)
440 return (GSS_S_CALL_INACCESSIBLE_WRITE);
442 *minor_status = 0;
444 if (*cred_handle == GSS_C_NO_CREDENTIAL)
445 return (GSS_S_COMPLETE);
447 status = gss_release_cred(minor_status, cred_handle);
449 dsyslog("Leaving spnego_gss_release_cred\n");
450 return (status);
453 static void
454 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
456 spnego_ctx->optionStr = gssint_get_modOptions(
457 (const gss_OID)&spnego_oids[0]);
460 static spnego_gss_ctx_id_t
461 create_spnego_ctx(void)
463 spnego_gss_ctx_id_t spnego_ctx = NULL;
464 spnego_ctx = (spnego_gss_ctx_id_t)
465 malloc(sizeof (spnego_gss_ctx_id_rec));
467 if (spnego_ctx == NULL) {
468 return (NULL);
471 spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
472 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
473 spnego_ctx->internal_mech = NULL;
474 spnego_ctx->optionStr = NULL;
475 spnego_ctx->DER_mechTypes.length = 0;
476 spnego_ctx->DER_mechTypes.value = NULL;
477 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
478 spnego_ctx->mic_reqd = 0;
479 spnego_ctx->mic_sent = 0;
480 spnego_ctx->mic_rcvd = 0;
481 spnego_ctx->mech_complete = 0;
482 spnego_ctx->nego_done = 0;
483 spnego_ctx->internal_name = GSS_C_NO_NAME;
484 spnego_ctx->actual_mech = GSS_C_NO_OID;
485 spnego_ctx->err.msg = NULL;
486 spnego_ctx->err.scratch_buf[0] = 0;
487 check_spnego_options(spnego_ctx);
489 return (spnego_ctx);
493 * Both initiator and acceptor call here to verify and/or create
494 * mechListMIC, and to consistency-check the MIC state.
496 static OM_uint32
497 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
498 int send_mechtok, spnego_gss_ctx_id_t sc,
499 gss_buffer_t *mic_out,
500 OM_uint32 *negState, send_token_flag *tokflag)
502 OM_uint32 ret;
504 ret = GSS_S_FAILURE;
505 *mic_out = GSS_C_NO_BUFFER;
506 if (mic_in != GSS_C_NO_BUFFER) {
507 if (sc->mic_rcvd) {
508 /* Reject MIC if we've already received a MIC. */
509 *negState = REJECT;
510 *tokflag = ERROR_TOKEN_SEND;
511 return GSS_S_DEFECTIVE_TOKEN;
513 } else if (sc->mic_reqd && !send_mechtok) {
515 * If the peer sends the final mechanism token, it
516 * must send the MIC with that token if the
517 * negotiation requires MICs.
519 *negState = REJECT;
520 *tokflag = ERROR_TOKEN_SEND;
521 return GSS_S_DEFECTIVE_TOKEN;
523 ret = process_mic(minor_status, mic_in, sc, mic_out,
524 negState, tokflag);
525 if (ret != GSS_S_COMPLETE) {
526 return ret;
528 if (sc->mic_reqd) {
529 assert(sc->mic_sent || sc->mic_rcvd);
531 if (sc->mic_sent && sc->mic_rcvd) {
532 ret = GSS_S_COMPLETE;
533 *negState = ACCEPT_COMPLETE;
534 if (*mic_out == GSS_C_NO_BUFFER) {
536 * We sent a MIC on the previous pass; we
537 * shouldn't be sending a mechanism token.
539 assert(!send_mechtok);
540 *tokflag = NO_TOKEN_SEND;
541 } else {
542 *tokflag = CONT_TOKEN_SEND;
544 } else if (sc->mic_reqd) {
545 *negState = ACCEPT_INCOMPLETE;
546 ret = GSS_S_CONTINUE_NEEDED;
547 } else if (*negState == ACCEPT_COMPLETE) {
548 ret = GSS_S_COMPLETE;
549 } else {
550 ret = GSS_S_CONTINUE_NEEDED;
552 return ret;
556 * Perform the actual verification and/or generation of mechListMIC.
558 static OM_uint32
559 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
560 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
561 OM_uint32 *negState, send_token_flag *tokflag)
563 OM_uint32 ret, tmpmin;
564 gss_qop_t qop_state;
565 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
567 ret = GSS_S_FAILURE;
568 if (mic_in != GSS_C_NO_BUFFER) {
569 ret = gss_verify_mic(minor_status, sc->ctx_handle,
570 &sc->DER_mechTypes,
571 mic_in, &qop_state);
572 if (ret != GSS_S_COMPLETE) {
573 *negState = REJECT;
574 *tokflag = ERROR_TOKEN_SEND;
575 return ret;
577 /* If we got a MIC, we must send a MIC. */
578 sc->mic_reqd = 1;
579 sc->mic_rcvd = 1;
581 if (sc->mic_reqd && !sc->mic_sent) {
582 ret = gss_get_mic(minor_status, sc->ctx_handle,
583 GSS_C_QOP_DEFAULT,
584 &sc->DER_mechTypes,
585 &tmpmic);
586 if (ret != GSS_S_COMPLETE) {
587 gss_release_buffer(&tmpmin, &tmpmic);
588 *tokflag = NO_TOKEN_SEND;
589 return ret;
591 *mic_out = malloc(sizeof(gss_buffer_desc));
592 if (*mic_out == GSS_C_NO_BUFFER) {
593 gss_release_buffer(&tmpmin, &tmpmic);
594 *tokflag = NO_TOKEN_SEND;
595 return GSS_S_FAILURE;
597 **mic_out = tmpmic;
598 sc->mic_sent = 1;
600 return GSS_S_COMPLETE;
604 * Initial call to spnego_gss_init_sec_context().
606 static OM_uint32
607 init_ctx_new(OM_uint32 *minor_status,
608 gss_cred_id_t cred,
609 gss_ctx_id_t *ctx,
610 gss_OID_set *mechSet,
611 send_token_flag *tokflag)
613 OM_uint32 ret, tmpmin;
614 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
615 spnego_gss_ctx_id_t sc = NULL;
617 /* determine negotiation mech set */
618 if (cred == GSS_C_NO_CREDENTIAL) {
619 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
620 GSS_C_INITIATE, &creds, mechSet);
621 gss_release_cred(&tmpmin, &creds);
622 } else {
624 * Use the list of mechs included in the cred that we
625 * were given.
627 ret = gss_inquire_cred(minor_status, cred,
628 NULL, NULL, NULL, mechSet);
630 if (ret != GSS_S_COMPLETE)
631 return ret;
633 sc = create_spnego_ctx();
634 if (sc == NULL)
635 return GSS_S_FAILURE;
638 * need to pull the first mech from mechSet to do first
639 * gss_init_sec_context()
641 ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
642 &sc->internal_mech);
643 if (ret != GSS_S_COMPLETE) {
644 map_errcode(minor_status);
645 goto cleanup;
648 if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
649 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
650 ret = GSS_S_FAILURE;
651 goto cleanup;
654 * The actual context is not yet determined, set the output
655 * context handle to refer to the spnego context itself.
657 sc->ctx_handle = GSS_C_NO_CONTEXT;
658 *ctx = (gss_ctx_id_t)sc;
659 *tokflag = INIT_TOKEN_SEND;
660 ret = GSS_S_CONTINUE_NEEDED;
662 cleanup:
663 gss_release_oid_set(&tmpmin, mechSet);
664 return ret;
668 * Called by second and later calls to spnego_gss_init_sec_context()
669 * to decode reply and update state.
671 static OM_uint32
672 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
673 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
674 OM_uint32 *negState, send_token_flag *tokflag)
676 OM_uint32 ret, tmpmin, acc_negState;
677 unsigned char *ptr;
678 spnego_gss_ctx_id_t sc;
679 gss_OID supportedMech = GSS_C_NO_OID;
681 sc = (spnego_gss_ctx_id_t)*ctx;
682 *negState = REJECT;
683 *tokflag = ERROR_TOKEN_SEND;
685 ptr = buf->value;
686 ret = get_negTokenResp(minor_status, ptr, buf->length,
687 &acc_negState, &supportedMech,
688 responseToken, mechListMIC);
689 if (ret != GSS_S_COMPLETE)
690 goto cleanup;
691 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
692 supportedMech == GSS_C_NO_OID &&
693 *responseToken == GSS_C_NO_BUFFER &&
694 *mechListMIC == GSS_C_NO_BUFFER) {
695 /* Reject "empty" token. */
696 ret = GSS_S_DEFECTIVE_TOKEN;
698 if (acc_negState == REJECT) {
699 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
700 /* Solaris SPNEGO */
701 spnego_set_error_message(sc, *minor_status,
702 dgettext(TEXT_DOMAIN,
703 "SPNEGO failed to negotiate a mechanism: server rejected request"));
704 map_errcode(minor_status);
705 *tokflag = NO_TOKEN_SEND;
706 ret = GSS_S_FAILURE;
707 goto cleanup;
710 * nego_done is false for the first call to init_ctx_cont()
712 if (!sc->nego_done) {
713 ret = init_ctx_nego(minor_status, sc,
714 acc_negState,
715 supportedMech, responseToken,
716 mechListMIC,
717 negState, tokflag);
718 } else if (!sc->mech_complete &&
719 *responseToken == GSS_C_NO_BUFFER) {
721 * mech not finished and mech token missing
723 ret = GSS_S_DEFECTIVE_TOKEN;
724 } else if (sc->mic_reqd &&
725 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
726 *negState = ACCEPT_INCOMPLETE;
727 *tokflag = CONT_TOKEN_SEND;
728 ret = GSS_S_CONTINUE_NEEDED;
729 } else {
730 *negState = ACCEPT_COMPLETE;
731 *tokflag = NO_TOKEN_SEND;
732 ret = GSS_S_COMPLETE;
734 cleanup:
735 if (supportedMech != GSS_C_NO_OID)
736 generic_gss_release_oid(&tmpmin, &supportedMech);
737 return ret;
741 * Consistency checking and mechanism negotiation handling for second
742 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
743 * update internal state if acceptor has counter-proposed.
745 static OM_uint32
746 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
747 OM_uint32 acc_negState, gss_OID supportedMech,
748 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
749 OM_uint32 *negState, send_token_flag *tokflag)
751 OM_uint32 ret;
753 *negState = REJECT;
754 *tokflag = ERROR_TOKEN_SEND;
755 ret = GSS_S_DEFECTIVE_TOKEN;
757 * Both supportedMech and negState must be present in first
758 * acceptor token.
760 if (supportedMech == GSS_C_NO_OID) {
761 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
762 map_errcode(minor_status);
763 return GSS_S_DEFECTIVE_TOKEN;
765 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
766 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
767 /* Solaris SPNEGO */
768 spnego_set_error_message(sc, *minor_status,
769 dgettext(TEXT_DOMAIN,
770 "SPNEGO failed to negotiate a mechanism: defective token"));
771 map_errcode(minor_status);
772 return GSS_S_DEFECTIVE_TOKEN;
776 * If the mechanism we sent is not the mechanism returned from
777 * the server, we need to handle the server's counter
778 * proposal. There is a bug in SAMBA servers that always send
779 * the old Kerberos mech OID, even though we sent the new one.
780 * So we will treat all the Kerberos mech OIDS as the same.
782 if (!(is_kerb_mech(supportedMech) &&
783 is_kerb_mech(sc->internal_mech)) &&
784 !g_OID_equal(supportedMech, sc->internal_mech)) {
785 ret = init_ctx_reselect(minor_status, sc,
786 acc_negState, supportedMech,
787 responseToken, mechListMIC,
788 negState, tokflag);
790 } else if (*responseToken == GSS_C_NO_BUFFER) {
791 if (sc->mech_complete) {
793 * Mech completed on first call to its
794 * init_sec_context(). Acceptor sends no mech
795 * token.
797 *negState = ACCEPT_COMPLETE;
798 *tokflag = NO_TOKEN_SEND;
799 ret = GSS_S_COMPLETE;
800 } else {
802 * Reject missing mech token when optimistic
803 * mech selected.
805 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
806 map_errcode(minor_status);
807 ret = GSS_S_DEFECTIVE_TOKEN;
809 } else if (sc->mech_complete) {
810 /* Reject spurious mech token. */
811 ret = GSS_S_DEFECTIVE_TOKEN;
812 } else {
813 *negState = ACCEPT_INCOMPLETE;
814 *tokflag = CONT_TOKEN_SEND;
815 ret = GSS_S_CONTINUE_NEEDED;
817 sc->nego_done = 1;
818 return ret;
822 * Handle acceptor's counter-proposal of an alternative mechanism.
824 static OM_uint32
825 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
826 OM_uint32 acc_negState, gss_OID supportedMech,
827 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
828 OM_uint32 *negState, send_token_flag *tokflag)
830 OM_uint32 ret, tmpmin;
832 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
833 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
834 GSS_C_NO_BUFFER);
836 ret = generic_gss_copy_oid(minor_status, supportedMech,
837 &sc->internal_mech);
838 if (ret != GSS_S_COMPLETE) {
839 map_errcode(minor_status);
840 sc->internal_mech = GSS_C_NO_OID;
841 *tokflag = NO_TOKEN_SEND;
842 return ret;
844 if (*responseToken != GSS_C_NO_BUFFER) {
845 /* Reject spurious mech token. */
846 return GSS_S_DEFECTIVE_TOKEN;
849 * Windows 2003 and earlier don't correctly send a
850 * negState of request-mic when counter-proposing a
851 * mechanism. They probably don't handle mechListMICs
852 * properly either.
854 if (acc_negState != REQUEST_MIC)
855 return GSS_S_DEFECTIVE_TOKEN;
857 sc->mech_complete = 0;
858 sc->mic_reqd = 1;
859 *negState = REQUEST_MIC;
860 *tokflag = CONT_TOKEN_SEND;
861 return GSS_S_CONTINUE_NEEDED;
865 * Wrap call to mechanism gss_init_sec_context() and update state
866 * accordingly.
868 static OM_uint32
869 init_ctx_call_init(OM_uint32 *minor_status,
870 spnego_gss_ctx_id_t sc,
871 gss_cred_id_t claimant_cred_handle,
872 gss_name_t target_name,
873 OM_uint32 req_flags,
874 OM_uint32 time_req,
875 gss_buffer_t mechtok_in,
876 gss_OID *actual_mech,
877 gss_buffer_t mechtok_out,
878 OM_uint32 *ret_flags,
879 OM_uint32 *time_rec,
880 OM_uint32 *negState,
881 send_token_flag *send_token)
883 OM_uint32 ret;
885 ret = gss_init_sec_context(minor_status,
886 claimant_cred_handle,
887 &sc->ctx_handle,
888 target_name,
889 sc->internal_mech,
890 (req_flags | GSS_C_INTEG_FLAG),
891 time_req,
892 GSS_C_NO_CHANNEL_BINDINGS,
893 mechtok_in,
894 &sc->actual_mech,
895 mechtok_out,
896 &sc->ctx_flags,
897 time_rec);
898 if (ret == GSS_S_COMPLETE) {
899 sc->mech_complete = 1;
900 if (ret_flags != NULL)
901 *ret_flags = sc->ctx_flags;
903 * If this isn't the first time we've been called,
904 * we're done unless a MIC needs to be
905 * generated/handled.
907 if (*send_token == CONT_TOKEN_SEND &&
908 mechtok_out->length == 0 &&
909 (!sc->mic_reqd ||
910 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
912 *negState = ACCEPT_COMPLETE;
913 ret = GSS_S_COMPLETE;
914 if (mechtok_out->length == 0) {
915 *send_token = NO_TOKEN_SEND;
917 } else {
918 *negState = ACCEPT_INCOMPLETE;
919 ret = GSS_S_CONTINUE_NEEDED;
921 } else if (ret != GSS_S_CONTINUE_NEEDED) {
922 if (*send_token == INIT_TOKEN_SEND) {
923 /* Don't output token on error if first call. */
924 *send_token = NO_TOKEN_SEND;
925 } else {
926 *send_token = ERROR_TOKEN_SEND;
928 *negState = REJECT;
930 return ret;
933 /*ARGSUSED*/
934 OM_uint32
935 glue_spnego_gss_init_sec_context(
936 void *context,
937 OM_uint32 *minor_status,
938 gss_cred_id_t claimant_cred_handle,
939 gss_ctx_id_t *context_handle,
940 gss_name_t target_name,
941 gss_OID mech_type,
942 OM_uint32 req_flags,
943 OM_uint32 time_req,
944 gss_channel_bindings_t input_chan_bindings,
945 gss_buffer_t input_token,
946 gss_OID *actual_mech,
947 gss_buffer_t output_token,
948 OM_uint32 *ret_flags,
949 OM_uint32 *time_rec)
951 return(spnego_gss_init_sec_context(
952 minor_status,
953 claimant_cred_handle,
954 context_handle,
955 target_name,
956 mech_type,
957 req_flags,
958 time_req,
959 input_chan_bindings,
960 input_token,
961 actual_mech,
962 output_token,
963 ret_flags,
964 time_rec));
967 /*ARGSUSED*/
968 OM_uint32
969 spnego_gss_init_sec_context(
970 OM_uint32 *minor_status,
971 gss_cred_id_t claimant_cred_handle,
972 gss_ctx_id_t *context_handle,
973 gss_name_t target_name,
974 gss_OID mech_type,
975 OM_uint32 req_flags,
976 OM_uint32 time_req,
977 gss_channel_bindings_t input_chan_bindings,
978 gss_buffer_t input_token,
979 gss_OID *actual_mech,
980 gss_buffer_t output_token,
981 OM_uint32 *ret_flags,
982 OM_uint32 *time_rec)
985 * send_token is used to indicate in later steps
986 * what type of token, if any should be sent or processed.
987 * NO_TOKEN_SEND = no token should be sent
988 * INIT_TOKEN_SEND = initial token will be sent
989 * CONT_TOKEN_SEND = continuing tokens to be sent
990 * CHECK_MIC = no token to be sent, but have a MIC to check.
992 send_token_flag send_token = NO_TOKEN_SEND;
993 OM_uint32 tmpmin, ret, negState;
994 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
995 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
996 gss_OID_set mechSet = GSS_C_NO_OID_SET;
997 spnego_gss_ctx_id_t spnego_ctx = NULL;
999 dsyslog("Entering init_sec_context\n");
1001 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1002 negState = REJECT;
1004 if (minor_status != NULL)
1005 *minor_status = 0;
1006 if (output_token != GSS_C_NO_BUFFER) {
1007 output_token->length = 0;
1008 output_token->value = NULL;
1010 if (minor_status == NULL ||
1011 output_token == GSS_C_NO_BUFFER ||
1012 context_handle == NULL)
1013 return GSS_S_CALL_INACCESSIBLE_WRITE;
1015 if (actual_mech != NULL)
1016 *actual_mech = GSS_C_NO_OID;
1018 if (*context_handle == GSS_C_NO_CONTEXT) {
1019 ret = init_ctx_new(minor_status, claimant_cred_handle,
1020 context_handle, &mechSet, &send_token);
1021 if (ret != GSS_S_CONTINUE_NEEDED) {
1022 goto cleanup;
1024 } else {
1025 ret = init_ctx_cont(minor_status, context_handle,
1026 input_token, &mechtok_in,
1027 &mechListMIC_in, &negState, &send_token);
1028 if (HARD_ERROR(ret)) {
1029 goto cleanup;
1032 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1034 /* Solaris SPNEGO */
1035 if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1036 spnego_gss_save_error_info(*minor_status, spnego_ctx);
1038 if (!spnego_ctx->mech_complete) {
1039 ret = init_ctx_call_init(
1040 minor_status, spnego_ctx,
1041 claimant_cred_handle,
1042 target_name, req_flags,
1043 time_req, mechtok_in,
1044 actual_mech, &mechtok_out,
1045 ret_flags, time_rec,
1046 &negState, &send_token);
1048 /* create mic/check mic */
1049 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
1050 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1052 ret = handle_mic(minor_status,
1053 mechListMIC_in,
1054 (mechtok_out.length != 0),
1055 spnego_ctx, &mechListMIC_out,
1056 &negState, &send_token);
1058 cleanup:
1059 if (send_token == INIT_TOKEN_SEND) {
1060 if (make_spnego_tokenInit_msg(spnego_ctx,
1062 mechListMIC_out,
1063 req_flags,
1064 &mechtok_out, send_token,
1065 output_token) < 0) {
1066 ret = GSS_S_FAILURE;
1068 } else if (send_token != NO_TOKEN_SEND) {
1069 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1070 &mechtok_out, mechListMIC_out,
1071 send_token,
1072 output_token) < 0) {
1073 ret = GSS_S_FAILURE;
1076 gss_release_buffer(&tmpmin, &mechtok_out);
1077 if (ret == GSS_S_COMPLETE) {
1079 * Now, switch the output context to refer to the
1080 * negotiated mechanism's context.
1082 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1083 if (actual_mech != NULL)
1084 *actual_mech = spnego_ctx->actual_mech;
1085 if (ret_flags != NULL)
1086 *ret_flags = spnego_ctx->ctx_flags;
1087 release_spnego_ctx(&spnego_ctx);
1088 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1089 if (spnego_ctx != NULL) {
1090 gss_delete_sec_context(&tmpmin,
1091 &spnego_ctx->ctx_handle,
1092 GSS_C_NO_BUFFER);
1093 release_spnego_ctx(&spnego_ctx);
1095 *context_handle = GSS_C_NO_CONTEXT;
1097 if (mechtok_in != GSS_C_NO_BUFFER) {
1098 gss_release_buffer(&tmpmin, mechtok_in);
1099 free(mechtok_in);
1101 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1102 gss_release_buffer(&tmpmin, mechListMIC_in);
1103 free(mechListMIC_in);
1105 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1106 gss_release_buffer(&tmpmin, mechListMIC_out);
1107 free(mechListMIC_out);
1109 if (mechSet != GSS_C_NO_OID_SET) {
1110 gss_release_oid_set(&tmpmin, &mechSet);
1112 return ret;
1113 } /* init_sec_context */
1115 /* We don't want to import KRB5 headers here */
1116 static const gss_OID_desc gss_mech_krb5_oid =
1117 { 9, "\052\206\110\206\367\022\001\002\002" };
1118 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1119 { 9, "\052\206\110\202\367\022\001\002\002" };
1122 * verify that the input token length is not 0. If it is, just return.
1123 * If the token length is greater than 0, der encode as a sequence
1124 * and place in buf_out, advancing buf_out.
1127 static int
1128 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1129 unsigned int buflen)
1131 int ret;
1133 /* if token length is 0, we do not want to send */
1134 if (input_token->length == 0)
1135 return (0);
1137 if (input_token->length > buflen)
1138 return (-1);
1140 *(*buf_out)++ = SEQUENCE;
1141 if ((ret = gssint_put_der_length(input_token->length, buf_out,
1142 input_token->length)))
1143 return (ret);
1144 TWRITE_STR(*buf_out, input_token->value, input_token->length);
1145 return (0);
1149 * NegHints ::= SEQUENCE {
1150 * hintName [0] GeneralString OPTIONAL,
1151 * hintAddress [1] OCTET STRING OPTIONAL
1155 #define HOST_PREFIX "host@"
1156 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1158 static int
1159 make_NegHints(OM_uint32 *minor_status,
1160 gss_cred_id_t cred, gss_buffer_t *outbuf)
1162 gss_buffer_desc hintNameBuf;
1163 gss_name_t hintName = GSS_C_NO_NAME;
1164 gss_name_t hintKerberosName;
1165 gss_OID hintNameType;
1166 OM_uint32 major_status;
1167 OM_uint32 minor;
1168 unsigned int tlen = 0;
1169 unsigned int hintNameSize = 0;
1170 unsigned int negHintsSize = 0;
1171 unsigned char *ptr;
1172 unsigned char *t;
1174 *outbuf = GSS_C_NO_BUFFER;
1176 if (cred != GSS_C_NO_CREDENTIAL) {
1177 major_status = gss_inquire_cred(minor_status,
1178 cred,
1179 &hintName,
1180 NULL,
1181 NULL,
1182 NULL);
1183 if (major_status != GSS_S_COMPLETE)
1184 return (major_status);
1187 if (hintName == GSS_C_NO_NAME) {
1188 krb5_error_code code;
1189 krb5int_access kaccess;
1190 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1192 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1193 if (code != 0) {
1194 *minor_status = code;
1195 return (GSS_S_FAILURE);
1198 /* this breaks mutual authentication but Samba relies on it */
1199 code = (*kaccess.clean_hostname)(NULL, NULL,
1200 &hostname[HOST_PREFIX_LEN],
1201 MAXHOSTNAMELEN);
1202 if (code != 0) {
1203 *minor_status = code;
1204 return (GSS_S_FAILURE);
1207 hintNameBuf.value = hostname;
1208 hintNameBuf.length = strlen(hostname);
1210 major_status = gss_import_name(minor_status,
1211 &hintNameBuf,
1212 GSS_C_NT_HOSTBASED_SERVICE,
1213 &hintName);
1214 if (major_status != GSS_S_COMPLETE) {
1215 return (major_status);
1219 hintNameBuf.value = NULL;
1220 hintNameBuf.length = 0;
1222 major_status = gss_canonicalize_name(minor_status,
1223 hintName,
1224 (gss_OID)&gss_mech_krb5_oid,
1225 &hintKerberosName);
1226 if (major_status != GSS_S_COMPLETE) {
1227 gss_release_name(&minor, &hintName);
1228 return (major_status);
1230 gss_release_name(&minor, &hintName);
1232 major_status = gss_display_name(minor_status,
1233 hintKerberosName,
1234 &hintNameBuf,
1235 &hintNameType);
1236 if (major_status != GSS_S_COMPLETE) {
1237 gss_release_name(&minor, &hintKerberosName);
1238 return (major_status);
1240 gss_release_name(&minor, &hintKerberosName);
1243 * Now encode the name hint into a NegHints ASN.1 type
1245 major_status = GSS_S_FAILURE;
1247 /* Length of DER encoded GeneralString */
1248 tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1249 hintNameBuf.length;
1250 hintNameSize = tlen;
1252 /* Length of DER encoded hintName */
1253 tlen += 1 + gssint_der_length_size(hintNameSize);
1254 negHintsSize = tlen;
1256 t = (unsigned char *)malloc(tlen);
1257 if (t == NULL) {
1258 *minor_status = ENOMEM;
1259 goto errout;
1262 ptr = t;
1264 *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1265 if (gssint_put_der_length(hintNameSize,
1266 &ptr, tlen - (int)(ptr-t)))
1267 goto errout;
1269 *ptr++ = GENERAL_STRING;
1270 if (gssint_put_der_length(hintNameBuf.length,
1271 &ptr, tlen - (int)(ptr-t)))
1272 goto errout;
1274 memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1275 ptr += hintNameBuf.length;
1277 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1278 if (*outbuf == NULL) {
1279 *minor_status = ENOMEM;
1280 goto errout;
1282 (*outbuf)->value = (void *)t;
1283 (*outbuf)->length = ptr - t;
1285 t = NULL; /* don't free */
1287 *minor_status = 0;
1288 major_status = GSS_S_COMPLETE;
1290 errout:
1291 if (t != NULL) {
1292 free(t);
1295 gss_release_buffer(&minor, &hintNameBuf);
1296 return (major_status);
1299 static OM_uint32
1300 acc_ctx_hints(OM_uint32 *minor_status,
1301 gss_ctx_id_t *ctx,
1302 gss_cred_id_t cred,
1303 gss_buffer_t *mechListMIC,
1304 OM_uint32 *negState,
1305 send_token_flag *return_token)
1307 OM_uint32 tmpmin, ret;
1308 gss_OID_set supported_mechSet;
1309 spnego_gss_ctx_id_t sc = NULL;
1311 *mechListMIC = GSS_C_NO_BUFFER;
1312 supported_mechSet = GSS_C_NO_OID_SET;
1313 *return_token = ERROR_TOKEN_SEND;
1314 *negState = REJECT;
1315 *minor_status = 0;
1317 *ctx = GSS_C_NO_CONTEXT;
1318 ret = GSS_S_DEFECTIVE_TOKEN;
1320 if (cred != GSS_C_NO_CREDENTIAL) {
1321 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1322 NULL, &supported_mechSet);
1323 if (ret != GSS_S_COMPLETE) {
1324 *return_token = NO_TOKEN_SEND;
1325 goto cleanup;
1327 } else {
1328 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1329 GSS_C_ACCEPT, NULL,
1330 &supported_mechSet);
1331 if (ret != GSS_S_COMPLETE) {
1332 *return_token = NO_TOKEN_SEND;
1333 goto cleanup;
1337 ret = make_NegHints(minor_status, cred, mechListMIC);
1338 if (ret != GSS_S_COMPLETE) {
1339 *return_token = NO_TOKEN_SEND;
1340 goto cleanup;
1344 * Select the best match between the list of mechs
1345 * that the initiator requested and the list that
1346 * the acceptor will support.
1348 sc = create_spnego_ctx();
1349 if (sc == NULL) {
1350 ret = GSS_S_FAILURE;
1351 *return_token = NO_TOKEN_SEND;
1352 goto cleanup;
1354 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1355 ret = GSS_S_FAILURE;
1356 *return_token = NO_TOKEN_SEND;
1357 goto cleanup;
1359 sc->internal_mech = GSS_C_NO_OID;
1361 *negState = ACCEPT_INCOMPLETE;
1362 *return_token = INIT_TOKEN_SEND;
1363 sc->firstpass = 1;
1364 *ctx = (gss_ctx_id_t)sc;
1365 ret = GSS_S_COMPLETE;
1367 cleanup:
1368 gss_release_oid_set(&tmpmin, &supported_mechSet);
1369 return ret;
1373 * Solaris SPNEGO
1374 * mechoidset2str()
1375 * Input an OID set of mechs and output a string like so:
1376 * '{ x y z } (mechname0), { a b c } (mechname1) ...'.
1377 * On error return NULL.
1378 * Caller needs to free returned string.
1380 static const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
1381 static const char *oid_no_map = "Can't map OID to string";
1382 static char *
1383 mechoidset2str(gss_OID_set mechset)
1385 int i, l;
1386 char buf[256] = {0};
1387 char *s = NULL;
1389 if (!mechset)
1390 return NULL;
1392 for (i = 0; i < mechset->count; i++) {
1393 OM_uint32 maj, min;
1394 gss_buffer_desc oidstr;
1395 gss_buffer_t oidstrp = &oidstr;
1396 gss_OID mech_oid = &mechset->elements[i];
1397 /* No need to free mech_name. */
1398 const char *mech_name = __gss_oid_to_mech(mech_oid);
1400 if (i > 0)
1401 if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
1402 if (oidstrp->value)
1403 gss_release_buffer(&min, oidstrp);
1404 break;
1407 /* Add '{ x y x ... }'. */
1408 maj = gss_oid_to_str(&min, mech_oid, oidstrp);
1409 if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
1410 sizeof (buf)) >= sizeof (buf)) {
1411 if (oidstrp->value)
1412 gss_release_buffer(&min, oidstrp);
1413 break;
1415 if (oidstrp->value)
1416 gss_release_buffer(&min, oidstrp);
1418 /* Add '(mech name)'. */
1419 if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
1420 break;
1421 if (strlcat(buf, mech_name ? mech_name : mech_no_map,
1422 sizeof (buf)) >= sizeof (buf))
1423 break;
1424 if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
1425 break;
1428 /* Even if we have buf overflow, let's output what we got so far. */
1429 if (mechset->count) {
1430 l = strlen(buf);
1431 if (l > 0) {
1432 s = malloc(l + 1);
1433 if (!s)
1434 return NULL;
1435 (void) strlcpy(s, buf, l);
1439 return s ? s : NULL;
1443 * Set negState to REJECT if the token is defective, else
1444 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1445 * preferred mechanism is supported.
1447 static OM_uint32
1448 acc_ctx_new(OM_uint32 *minor_status,
1449 gss_buffer_t buf,
1450 gss_ctx_id_t *ctx,
1451 gss_cred_id_t cred,
1452 gss_buffer_t *mechToken,
1453 gss_buffer_t *mechListMIC,
1454 OM_uint32 *negState,
1455 send_token_flag *return_token)
1457 OM_uint32 tmpmin, ret, req_flags;
1458 gss_OID_set supported_mechSet, mechTypes;
1459 gss_buffer_desc der_mechTypes;
1460 gss_OID mech_wanted;
1461 spnego_gss_ctx_id_t sc = NULL;
1463 ret = GSS_S_DEFECTIVE_TOKEN;
1464 der_mechTypes.length = 0;
1465 der_mechTypes.value = NULL;
1466 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1467 supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1468 *return_token = ERROR_TOKEN_SEND;
1469 *negState = REJECT;
1470 *minor_status = 0;
1472 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1473 &mechTypes, &req_flags,
1474 mechToken, mechListMIC);
1475 if (ret != GSS_S_COMPLETE) {
1476 goto cleanup;
1478 if (cred != GSS_C_NO_CREDENTIAL) {
1479 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1480 NULL, &supported_mechSet);
1481 if (ret != GSS_S_COMPLETE) {
1482 *return_token = NO_TOKEN_SEND;
1483 goto cleanup;
1485 } else {
1486 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1487 GSS_C_ACCEPT, NULL,
1488 &supported_mechSet);
1489 if (ret != GSS_S_COMPLETE) {
1490 *return_token = NO_TOKEN_SEND;
1491 goto cleanup;
1495 * Select the best match between the list of mechs
1496 * that the initiator requested and the list that
1497 * the acceptor will support.
1499 mech_wanted = negotiate_mech_type(minor_status,
1500 supported_mechSet,
1501 mechTypes,
1502 negState);
1503 if (*negState == REJECT) {
1504 /* Solaris SPNEGO: Spruce-up error msg */
1505 char *mechTypesStr = mechoidset2str(mechTypes);
1506 spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
1507 if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
1508 spnego_set_error_message(tmpsc, *minor_status,
1509 dgettext(TEXT_DOMAIN,
1510 "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
1511 mechTypesStr ? mechTypesStr : "<null>");
1513 free(mechTypesStr);
1516 * We save error here cuz the tmp ctx goes away (very) soon.
1517 * So callers of acc_ctx_new() should NOT call it again.
1519 spnego_gss_save_error_info(*minor_status, tmpsc);
1520 if (tmpsc)
1521 release_spnego_ctx(&tmpsc);
1522 ret = GSS_S_BAD_MECH;
1523 goto cleanup;
1526 sc = (spnego_gss_ctx_id_t)*ctx;
1527 if (sc != NULL) {
1528 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1529 assert(mech_wanted != GSS_C_NO_OID);
1530 } else
1531 sc = create_spnego_ctx();
1532 if (sc == NULL) {
1533 ret = GSS_S_FAILURE;
1534 *return_token = NO_TOKEN_SEND;
1535 generic_gss_release_oid(&tmpmin, &mech_wanted);
1536 goto cleanup;
1538 sc->internal_mech = mech_wanted;
1539 sc->DER_mechTypes = der_mechTypes;
1540 der_mechTypes.length = 0;
1541 der_mechTypes.value = NULL;
1543 if (*negState == REQUEST_MIC)
1544 sc->mic_reqd = 1;
1546 *return_token = INIT_TOKEN_SEND;
1547 sc->firstpass = 1;
1548 *ctx = (gss_ctx_id_t)sc;
1549 ret = GSS_S_COMPLETE;
1550 cleanup:
1551 gss_release_oid_set(&tmpmin, &mechTypes);
1552 gss_release_oid_set(&tmpmin, &supported_mechSet);
1553 if (der_mechTypes.length != 0)
1554 gss_release_buffer(&tmpmin, &der_mechTypes);
1555 return ret;
1558 static OM_uint32
1559 acc_ctx_cont(OM_uint32 *minstat,
1560 gss_buffer_t buf,
1561 gss_ctx_id_t *ctx,
1562 gss_buffer_t *responseToken,
1563 gss_buffer_t *mechListMIC,
1564 OM_uint32 *negState,
1565 send_token_flag *return_token)
1567 OM_uint32 ret, tmpmin;
1568 gss_OID supportedMech;
1569 spnego_gss_ctx_id_t sc;
1570 unsigned int len;
1571 unsigned char *ptr, *bufstart;
1573 sc = (spnego_gss_ctx_id_t)*ctx;
1574 ret = GSS_S_DEFECTIVE_TOKEN;
1575 *negState = REJECT;
1576 *minstat = 0;
1577 supportedMech = GSS_C_NO_OID;
1578 *return_token = ERROR_TOKEN_SEND;
1579 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1581 ptr = bufstart = buf->value;
1582 #define REMAIN (buf->length - (ptr - bufstart))
1583 if (REMAIN > INT_MAX)
1584 return GSS_S_DEFECTIVE_TOKEN;
1587 * Attempt to work with old Sun SPNEGO.
1589 if (*ptr == HEADER_ID) {
1590 ret = g_verify_token_header(gss_mech_spnego,
1591 &len, &ptr, 0, REMAIN);
1592 if (ret) {
1593 *minstat = ret;
1594 return GSS_S_DEFECTIVE_TOKEN;
1597 if (*ptr != (CONTEXT | 0x01)) {
1598 return GSS_S_DEFECTIVE_TOKEN;
1600 ret = get_negTokenResp(minstat, ptr, REMAIN,
1601 negState, &supportedMech,
1602 responseToken, mechListMIC);
1603 if (ret != GSS_S_COMPLETE)
1604 goto cleanup;
1606 if (*responseToken == GSS_C_NO_BUFFER &&
1607 *mechListMIC == GSS_C_NO_BUFFER) {
1609 ret = GSS_S_DEFECTIVE_TOKEN;
1610 goto cleanup;
1612 if (supportedMech != GSS_C_NO_OID) {
1613 ret = GSS_S_DEFECTIVE_TOKEN;
1614 goto cleanup;
1616 sc->firstpass = 0;
1617 *negState = ACCEPT_INCOMPLETE;
1618 *return_token = CONT_TOKEN_SEND;
1619 cleanup:
1620 if (supportedMech != GSS_C_NO_OID) {
1621 generic_gss_release_oid(&tmpmin, &supportedMech);
1623 return ret;
1624 #undef REMAIN
1628 * Verify that mech OID is either exactly the same as the negotiated
1629 * mech OID, or is a mech OID supported by the negotiated mech. MS
1630 * implementations can list a most preferred mech using an incorrect
1631 * krb5 OID while emitting a krb5 initiator mech token having the
1632 * correct krb5 mech OID.
1634 static OM_uint32
1635 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1636 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1637 OM_uint32 *negState, send_token_flag *tokflag)
1639 OM_uint32 ret, tmpmin;
1640 gss_mechanism mech = NULL;
1641 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1642 int present = 0;
1644 if (g_OID_equal(sc->internal_mech, mechoid))
1645 return GSS_S_COMPLETE;
1648 * SUNW17PACresync
1649 * If both mechs are kerb, we are done.
1651 if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
1652 return GSS_S_COMPLETE;
1655 mech = gssint_get_mechanism(mechoid);
1656 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1657 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1660 * Solaris SPNEGO
1661 * Spruce-up error msg.
1663 OM_uint32 maj, maj_sc, min;
1664 gss_buffer_desc oidstr, oidstr_sc;
1665 /* No need to free mnamestr. */
1666 const char *mnamestr = __gss_oid_to_mech(
1667 sc->internal_mech);
1668 maj_sc = gss_oid_to_str(&min,
1669 sc->internal_mech,
1670 &oidstr_sc);
1671 maj = gss_oid_to_str(&min, mechoid, &oidstr);
1672 spnego_set_error_message(sc, *minor_status,
1673 dgettext(TEXT_DOMAIN,
1674 "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1675 maj ? oid_no_map: oidstr.value,
1676 maj_sc ? oid_no_map: oidstr_sc.value,
1677 mnamestr ? mnamestr : mech_no_map);
1678 if (!maj)
1679 (void) gss_release_buffer(&min, &oidstr);
1680 if (!maj_sc)
1681 (void) gss_release_buffer(&min, &oidstr_sc);
1683 map_errcode(minor_status);
1684 *negState = REJECT;
1685 *tokflag = ERROR_TOKEN_SEND;
1686 return GSS_S_BAD_MECH;
1688 ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
1689 if (ret != GSS_S_COMPLETE) {
1690 *tokflag = NO_TOKEN_SEND;
1691 map_error(minor_status, mech);
1692 goto cleanup;
1694 ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
1695 mech_set, &present);
1696 if (ret != GSS_S_COMPLETE)
1697 goto cleanup;
1698 if (!present) {
1701 * Solaris SPNEGO
1702 * Spruce-up error msg.
1704 OM_uint32 maj, min;
1705 gss_buffer_desc oidstr;
1706 char *mech_set_str = mechoidset2str(mech_set);
1707 /* No need to free mnamestr. */
1708 const char *mnamestr =
1709 __gss_oid_to_mech(sc->internal_mech);
1710 maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
1711 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1712 spnego_set_error_message(sc, *minor_status,
1713 dgettext(TEXT_DOMAIN,
1714 "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1715 maj ? oid_no_map: oidstr.value,
1716 mnamestr ? mnamestr : mech_no_map,
1717 mech_set_str ? mech_set_str : "<null>");
1718 if (!maj)
1719 (void) gss_release_buffer(&min, &oidstr);
1720 free(mech_set_str);
1722 map_errcode(minor_status);
1723 *negState = REJECT;
1724 *tokflag = ERROR_TOKEN_SEND;
1725 ret = GSS_S_BAD_MECH;
1727 cleanup:
1728 gss_release_oid_set(&tmpmin, &mech_set);
1729 return ret;
1731 #ifndef LEAN_CLIENT
1733 * Wrap call to gss_accept_sec_context() and update state
1734 * accordingly.
1736 static OM_uint32
1737 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1738 gss_cred_id_t cred, gss_buffer_t mechtok_in,
1739 gss_OID *mech_type, gss_buffer_t mechtok_out,
1740 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1741 gss_cred_id_t *delegated_cred_handle,
1742 OM_uint32 *negState, send_token_flag *tokflag)
1744 OM_uint32 ret;
1745 gss_OID_desc mechoid;
1747 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1749 * mechoid is an alias; don't free it.
1751 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1752 if (ret != GSS_S_COMPLETE) {
1753 *tokflag = NO_TOKEN_SEND;
1754 return ret;
1756 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1757 negState, tokflag);
1758 if (ret != GSS_S_COMPLETE)
1759 return ret;
1762 ret = gss_accept_sec_context(minor_status,
1763 &sc->ctx_handle,
1764 cred,
1765 mechtok_in,
1766 GSS_C_NO_CHANNEL_BINDINGS,
1767 &sc->internal_name,
1768 mech_type,
1769 mechtok_out,
1770 &sc->ctx_flags,
1771 time_rec,
1772 delegated_cred_handle);
1774 if (ret == GSS_S_COMPLETE) {
1775 #ifdef MS_BUG_TEST
1777 * Force MIC to be not required even if we previously
1778 * requested a MIC.
1780 char *envstr = getenv("MS_FORCE_NO_MIC");
1782 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1783 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1784 sc->mic_reqd) {
1786 sc->mic_reqd = 0;
1788 #endif
1789 sc->mech_complete = 1;
1790 if (ret_flags != NULL)
1791 *ret_flags = sc->ctx_flags;
1793 if (!sc->mic_reqd) {
1794 *negState = ACCEPT_COMPLETE;
1795 ret = GSS_S_COMPLETE;
1796 } else {
1797 ret = GSS_S_CONTINUE_NEEDED;
1799 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1800 *negState = REJECT;
1801 *tokflag = ERROR_TOKEN_SEND;
1803 return ret;
1806 /*ARGSUSED*/
1807 OM_uint32
1808 glue_spnego_gss_accept_sec_context(
1809 void *context,
1810 OM_uint32 *minor_status,
1811 gss_ctx_id_t *context_handle,
1812 gss_cred_id_t verifier_cred_handle,
1813 gss_buffer_t input_token,
1814 gss_channel_bindings_t input_chan_bindings,
1815 gss_name_t *src_name,
1816 gss_OID *mech_type,
1817 gss_buffer_t output_token,
1818 OM_uint32 *ret_flags,
1819 OM_uint32 *time_rec,
1820 gss_cred_id_t *delegated_cred_handle)
1822 return(spnego_gss_accept_sec_context(
1823 minor_status,
1824 context_handle,
1825 verifier_cred_handle,
1826 input_token,
1827 input_chan_bindings,
1828 src_name,
1829 mech_type,
1830 output_token,
1831 ret_flags,
1832 time_rec,
1833 delegated_cred_handle));
1836 /*ARGSUSED*/
1837 OM_uint32
1838 spnego_gss_accept_sec_context(
1839 OM_uint32 *minor_status,
1840 gss_ctx_id_t *context_handle,
1841 gss_cred_id_t verifier_cred_handle,
1842 gss_buffer_t input_token,
1843 gss_channel_bindings_t input_chan_bindings,
1844 gss_name_t *src_name,
1845 gss_OID *mech_type,
1846 gss_buffer_t output_token,
1847 OM_uint32 *ret_flags,
1848 OM_uint32 *time_rec,
1849 gss_cred_id_t *delegated_cred_handle)
1851 OM_uint32 ret, tmpmin, negState;
1852 send_token_flag return_token;
1853 gss_buffer_t mechtok_in, mic_in, mic_out;
1854 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1855 spnego_gss_ctx_id_t sc = NULL;
1856 OM_uint32 mechstat = GSS_S_FAILURE;
1857 int sendTokenInit = 0, tmpret;
1859 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1861 if (minor_status != NULL)
1862 *minor_status = 0;
1863 if (output_token != GSS_C_NO_BUFFER) {
1864 output_token->length = 0;
1865 output_token->value = NULL;
1869 if (minor_status == NULL ||
1870 output_token == GSS_C_NO_BUFFER ||
1871 context_handle == NULL) {
1872 return GSS_S_CALL_INACCESSIBLE_WRITE;
1875 if (input_token == GSS_C_NO_BUFFER) {
1876 return GSS_S_CALL_INACCESSIBLE_READ;
1879 sc = (spnego_gss_ctx_id_t)*context_handle;
1880 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1881 if (src_name != NULL)
1882 *src_name = GSS_C_NO_NAME;
1883 if (mech_type != NULL)
1884 *mech_type = GSS_C_NO_OID;
1885 if (time_rec != NULL)
1886 *time_rec = 0;
1887 if (ret_flags != NULL)
1888 *ret_flags = 0;
1889 if (delegated_cred_handle != NULL)
1890 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1891 if (input_token->length == 0) {
1892 ret = acc_ctx_hints(minor_status,
1893 context_handle,
1894 verifier_cred_handle,
1895 &mic_out,
1896 &negState,
1897 &return_token);
1898 if (ret != GSS_S_COMPLETE)
1899 goto cleanup;
1900 sendTokenInit = 1;
1901 ret = GSS_S_CONTINUE_NEEDED;
1902 } else {
1903 /* Can set negState to REQUEST_MIC */
1904 ret = acc_ctx_new(minor_status, input_token,
1905 context_handle, verifier_cred_handle,
1906 &mechtok_in, &mic_in,
1907 &negState, &return_token);
1908 if (ret != GSS_S_COMPLETE)
1909 goto cleanup;
1910 ret = GSS_S_CONTINUE_NEEDED;
1912 } else {
1913 /* Can set negState to ACCEPT_INCOMPLETE */
1914 ret = acc_ctx_cont(minor_status, input_token,
1915 context_handle, &mechtok_in,
1916 &mic_in, &negState, &return_token);
1917 if (ret != GSS_S_COMPLETE)
1918 goto cleanup;
1919 ret = GSS_S_CONTINUE_NEEDED;
1922 sc = (spnego_gss_ctx_id_t)*context_handle;
1924 * Handle mechtok_in and mic_in only if they are
1925 * present in input_token. If neither is present, whether
1926 * this is an error depends on whether this is the first
1927 * round-trip. RET is set to a default value according to
1928 * whether it is the first round-trip.
1930 mechstat = GSS_S_FAILURE;
1931 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1932 ret = acc_ctx_call_acc(minor_status, sc,
1933 verifier_cred_handle, mechtok_in,
1934 mech_type, &mechtok_out,
1935 ret_flags, time_rec,
1936 delegated_cred_handle,
1937 &negState, &return_token);
1938 } else if (negState == REQUEST_MIC) {
1939 mechstat = GSS_S_CONTINUE_NEEDED;
1942 /* Solaris SPNEGO */
1943 if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1944 spnego_gss_save_error_info(*minor_status, sc);
1946 if (!HARD_ERROR(ret) && sc->mech_complete &&
1947 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1949 ret = handle_mic(minor_status, mic_in,
1950 (mechtok_out.length != 0),
1951 sc, &mic_out,
1952 &negState, &return_token);
1955 cleanup:
1956 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1957 assert(sc != NULL);
1958 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1959 GSS_C_NO_BUFFER,
1960 return_token, output_token);
1961 if (tmpret < 0)
1962 ret = GSS_S_FAILURE;
1963 } else if (return_token != NO_TOKEN_SEND &&
1964 return_token != CHECK_MIC) {
1965 tmpret = make_spnego_tokenTarg_msg(negState,
1966 sc ? sc->internal_mech :
1967 GSS_C_NO_OID,
1968 &mechtok_out, mic_out,
1969 return_token,
1970 output_token);
1971 if (tmpret < 0)
1972 ret = GSS_S_FAILURE;
1974 if (ret == GSS_S_COMPLETE) {
1975 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1976 if (sc->internal_name != GSS_C_NO_NAME &&
1977 src_name != NULL) {
1978 *src_name = sc->internal_name;
1980 release_spnego_ctx(&sc);
1982 gss_release_buffer(&tmpmin, &mechtok_out);
1983 if (mechtok_in != GSS_C_NO_BUFFER) {
1984 gss_release_buffer(&tmpmin, mechtok_in);
1985 free(mechtok_in);
1987 if (mic_in != GSS_C_NO_BUFFER) {
1988 gss_release_buffer(&tmpmin, mic_in);
1989 free(mic_in);
1991 if (mic_out != GSS_C_NO_BUFFER) {
1992 gss_release_buffer(&tmpmin, mic_out);
1993 free(mic_out);
1995 return ret;
1997 #endif /* LEAN_CLIENT */
1999 /*ARGSUSED*/
2000 OM_uint32
2001 glue_spnego_gss_display_status(
2002 void *context,
2003 OM_uint32 *minor_status,
2004 OM_uint32 status_value,
2005 int status_type,
2006 gss_OID mech_type,
2007 OM_uint32 *message_context,
2008 gss_buffer_t status_string)
2010 return (spnego_gss_display_status(minor_status,
2011 status_value,
2012 status_type,
2013 mech_type,
2014 message_context,
2015 status_string));
2018 /*ARGSUSED*/
2019 OM_uint32
2020 spnego_gss_display_status(
2021 OM_uint32 *minor_status,
2022 OM_uint32 status_value,
2023 int status_type,
2024 gss_OID mech_type,
2025 OM_uint32 *message_context,
2026 gss_buffer_t status_string)
2028 dsyslog("Entering display_status\n");
2030 *message_context = 0;
2031 switch (status_value) {
2032 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
2033 /* CSTYLED */
2034 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2035 break;
2036 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
2037 /* CSTYLED */
2038 *status_string = make_err_msg("SPNEGO failed to acquire creds");
2039 break;
2040 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
2041 /* CSTYLED */
2042 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
2043 break;
2044 case ERR_SPNEGO_NEGOTIATION_FAILED:
2045 /* CSTYLED */
2046 return(spnego_gss_display_status2(minor_status,
2047 status_value,
2048 status_type,
2049 mech_type,
2050 message_context,
2051 status_string));
2052 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
2053 /* CSTYLED */
2054 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
2055 break;
2056 default:
2058 * Solaris SPNEGO
2059 * If mech_spnego calls mech_krb5 (via libgss) and an
2060 * error occurs there, give it a shot.
2062 /* CSTYLED */
2063 return(krb5_gss_display_status2(minor_status,
2064 status_value,
2065 status_type,
2066 (gss_OID)&gss_mech_krb5_oid,
2067 message_context,
2068 status_string));
2072 dsyslog("Leaving display_status\n");
2073 return (GSS_S_COMPLETE);
2076 /*ARGSUSED*/
2077 OM_uint32
2078 glue_spnego_gss_import_name(
2079 void *context,
2080 OM_uint32 *minor_status,
2081 gss_buffer_t input_name_buffer,
2082 gss_OID input_name_type,
2083 gss_name_t *output_name)
2085 return(spnego_gss_import_name(minor_status,
2086 input_name_buffer,
2087 input_name_type,
2088 output_name));
2091 /*ARGSUSED*/
2092 OM_uint32
2093 spnego_gss_import_name(
2094 OM_uint32 *minor_status,
2095 gss_buffer_t input_name_buffer,
2096 gss_OID input_name_type,
2097 gss_name_t *output_name)
2099 OM_uint32 status;
2101 dsyslog("Entering import_name\n");
2103 status = gss_import_name(minor_status, input_name_buffer,
2104 input_name_type, output_name);
2106 dsyslog("Leaving import_name\n");
2107 return (status);
2110 /*ARGSUSED*/
2111 OM_uint32
2112 glue_spnego_gss_release_name(
2113 void *context,
2114 OM_uint32 *minor_status,
2115 gss_name_t *input_name)
2117 return(spnego_gss_release_name(minor_status, input_name));
2120 /*ARGSUSED*/
2121 OM_uint32
2122 spnego_gss_release_name(
2123 OM_uint32 *minor_status,
2124 gss_name_t *input_name)
2126 OM_uint32 status;
2128 dsyslog("Entering release_name\n");
2130 status = gss_release_name(minor_status, input_name);
2132 dsyslog("Leaving release_name\n");
2133 return (status);
2136 /*ARGSUSED*/
2137 OM_uint32
2138 glue_spnego_gss_compare_name(
2139 void *context,
2140 OM_uint32 *minor_status,
2141 const gss_name_t name1,
2142 const gss_name_t name2,
2143 int *name_equal)
2145 return(spnego_gss_compare_name(minor_status,
2146 name1,
2147 name2,
2148 name_equal));
2150 /*ARGSUSED*/
2151 OM_uint32
2152 spnego_gss_compare_name(
2153 OM_uint32 *minor_status,
2154 const gss_name_t name1,
2155 const gss_name_t name2,
2156 int *name_equal)
2158 OM_uint32 status = GSS_S_COMPLETE;
2159 dsyslog("Entering compare_name\n");
2161 status = gss_compare_name(minor_status, name1, name2, name_equal);
2163 dsyslog("Leaving compare_name\n");
2164 return (status);
2167 /*ARGSUSED*/
2168 OM_uint32
2169 glue_spnego_gss_display_name(
2170 void *context,
2171 OM_uint32 *minor_status,
2172 gss_name_t input_name,
2173 gss_buffer_t output_name_buffer,
2174 gss_OID *output_name_type)
2176 return(spnego_gss_display_name(
2177 minor_status,
2178 input_name,
2179 output_name_buffer,
2180 output_name_type));
2183 /*ARGSUSED*/
2184 OM_uint32
2185 spnego_gss_display_name(
2186 OM_uint32 *minor_status,
2187 gss_name_t input_name,
2188 gss_buffer_t output_name_buffer,
2189 gss_OID *output_name_type)
2191 OM_uint32 status = GSS_S_COMPLETE;
2192 dsyslog("Entering display_name\n");
2194 status = gss_display_name(minor_status, input_name,
2195 output_name_buffer, output_name_type);
2197 dsyslog("Leaving display_name\n");
2198 return (status);
2202 /*ARGSUSED*/
2203 OM_uint32
2204 glue_spnego_gss_inquire_names_for_mech(
2205 void *context,
2206 OM_uint32 *minor_status,
2207 gss_OID mechanism,
2208 gss_OID_set *name_types)
2210 return(spnego_gss_inquire_names_for_mech(minor_status,
2211 mechanism,
2212 name_types));
2214 /*ARGSUSED*/
2215 OM_uint32
2216 spnego_gss_inquire_names_for_mech(
2217 OM_uint32 *minor_status,
2218 gss_OID mechanism,
2219 gss_OID_set *name_types)
2221 OM_uint32 major, minor;
2223 dsyslog("Entering inquire_names_for_mech\n");
2225 * We only know how to handle our own mechanism.
2227 if ((mechanism != GSS_C_NULL_OID) &&
2228 !g_OID_equal(gss_mech_spnego, mechanism)) {
2229 *minor_status = 0;
2230 return (GSS_S_FAILURE);
2233 major = gss_create_empty_oid_set(minor_status, name_types);
2234 if (major == GSS_S_COMPLETE) {
2235 /* Now add our members. */
2236 if (((major = gss_add_oid_set_member(minor_status,
2237 (gss_OID) GSS_C_NT_USER_NAME,
2238 name_types)) == GSS_S_COMPLETE) &&
2239 ((major = gss_add_oid_set_member(minor_status,
2240 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2241 name_types)) == GSS_S_COMPLETE) &&
2242 ((major = gss_add_oid_set_member(minor_status,
2243 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2244 name_types)) == GSS_S_COMPLETE)) {
2245 major = gss_add_oid_set_member(minor_status,
2246 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2247 name_types);
2250 if (major != GSS_S_COMPLETE)
2251 (void) gss_release_oid_set(&minor, name_types);
2254 dsyslog("Leaving inquire_names_for_mech\n");
2255 return (major);
2258 OM_uint32
2259 spnego_gss_unwrap(
2260 OM_uint32 *minor_status,
2261 gss_ctx_id_t context_handle,
2262 gss_buffer_t input_message_buffer,
2263 gss_buffer_t output_message_buffer,
2264 int *conf_state,
2265 gss_qop_t *qop_state)
2267 OM_uint32 ret;
2268 ret = gss_unwrap(minor_status,
2269 context_handle,
2270 input_message_buffer,
2271 output_message_buffer,
2272 conf_state,
2273 qop_state);
2275 return (ret);
2278 OM_uint32
2279 spnego_gss_wrap(
2280 OM_uint32 *minor_status,
2281 gss_ctx_id_t context_handle,
2282 int conf_req_flag,
2283 gss_qop_t qop_req,
2284 gss_buffer_t input_message_buffer,
2285 int *conf_state,
2286 gss_buffer_t output_message_buffer)
2288 OM_uint32 ret;
2289 ret = gss_wrap(minor_status,
2290 context_handle,
2291 conf_req_flag,
2292 qop_req,
2293 input_message_buffer,
2294 conf_state,
2295 output_message_buffer);
2297 return (ret);
2300 OM_uint32
2301 spnego_gss_process_context_token(
2302 OM_uint32 *minor_status,
2303 const gss_ctx_id_t context_handle,
2304 const gss_buffer_t token_buffer)
2306 OM_uint32 ret;
2307 ret = gss_process_context_token(minor_status,
2308 context_handle,
2309 token_buffer);
2311 return (ret);
2314 OM_uint32
2315 glue_spnego_gss_delete_sec_context(
2316 void *context,
2317 OM_uint32 *minor_status,
2318 gss_ctx_id_t *context_handle,
2319 gss_buffer_t output_token)
2321 return(spnego_gss_delete_sec_context(minor_status,
2322 context_handle, output_token));
2325 OM_uint32
2326 spnego_gss_delete_sec_context(
2327 OM_uint32 *minor_status,
2328 gss_ctx_id_t *context_handle,
2329 gss_buffer_t output_token)
2331 OM_uint32 ret = GSS_S_COMPLETE;
2332 spnego_gss_ctx_id_t *ctx =
2333 (spnego_gss_ctx_id_t *)context_handle;
2335 if (context_handle == NULL)
2336 return (GSS_S_FAILURE);
2339 * If this is still an SPNEGO mech, release it locally.
2341 if (*ctx != NULL &&
2342 (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2343 (void) release_spnego_ctx(ctx);
2344 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2345 if (output_token) {
2346 output_token->length = 0;
2347 output_token->value = NULL;
2349 } else {
2350 ret = gss_delete_sec_context(minor_status,
2351 context_handle,
2352 output_token);
2355 return (ret);
2358 OM_uint32
2359 glue_spnego_gss_context_time(
2360 void *context,
2361 OM_uint32 *minor_status,
2362 const gss_ctx_id_t context_handle,
2363 OM_uint32 *time_rec)
2365 return(spnego_gss_context_time(minor_status,
2366 context_handle,
2367 time_rec));
2370 OM_uint32
2371 spnego_gss_context_time(
2372 OM_uint32 *minor_status,
2373 const gss_ctx_id_t context_handle,
2374 OM_uint32 *time_rec)
2376 OM_uint32 ret;
2377 ret = gss_context_time(minor_status,
2378 context_handle,
2379 time_rec);
2380 return (ret);
2383 #ifndef LEAN_CLIENT
2384 OM_uint32
2385 glue_spnego_gss_export_sec_context(
2386 void *context,
2387 OM_uint32 *minor_status,
2388 gss_ctx_id_t *context_handle,
2389 gss_buffer_t interprocess_token)
2391 return(spnego_gss_export_sec_context(minor_status,
2392 context_handle,
2393 interprocess_token));
2395 OM_uint32
2396 spnego_gss_export_sec_context(
2397 OM_uint32 *minor_status,
2398 gss_ctx_id_t *context_handle,
2399 gss_buffer_t interprocess_token)
2401 OM_uint32 ret;
2402 ret = gss_export_sec_context(minor_status,
2403 context_handle,
2404 interprocess_token);
2405 return (ret);
2408 OM_uint32
2409 glue_spnego_gss_import_sec_context(
2410 void *context,
2411 OM_uint32 *minor_status,
2412 const gss_buffer_t interprocess_token,
2413 gss_ctx_id_t *context_handle)
2415 return(spnego_gss_import_sec_context(minor_status,
2416 interprocess_token,
2417 context_handle));
2419 OM_uint32
2420 spnego_gss_import_sec_context(
2421 OM_uint32 *minor_status,
2422 const gss_buffer_t interprocess_token,
2423 gss_ctx_id_t *context_handle)
2425 OM_uint32 ret;
2426 ret = gss_import_sec_context(minor_status,
2427 interprocess_token,
2428 context_handle);
2429 return (ret);
2431 #endif /* LEAN_CLIENT */
2433 OM_uint32
2434 glue_spnego_gss_inquire_context(
2435 void *context,
2436 OM_uint32 *minor_status,
2437 const gss_ctx_id_t context_handle,
2438 gss_name_t *src_name,
2439 gss_name_t *targ_name,
2440 OM_uint32 *lifetime_rec,
2441 gss_OID *mech_type,
2442 OM_uint32 *ctx_flags,
2443 int *locally_initiated,
2444 int *opened)
2446 return(spnego_gss_inquire_context(
2447 minor_status,
2448 context_handle,
2449 src_name,
2450 targ_name,
2451 lifetime_rec,
2452 mech_type,
2453 ctx_flags,
2454 locally_initiated,
2455 opened));
2458 OM_uint32
2459 spnego_gss_inquire_context(
2460 OM_uint32 *minor_status,
2461 const gss_ctx_id_t context_handle,
2462 gss_name_t *src_name,
2463 gss_name_t *targ_name,
2464 OM_uint32 *lifetime_rec,
2465 gss_OID *mech_type,
2466 OM_uint32 *ctx_flags,
2467 int *locally_initiated,
2468 int *opened)
2470 OM_uint32 ret = GSS_S_COMPLETE;
2472 ret = gss_inquire_context(minor_status,
2473 context_handle,
2474 src_name,
2475 targ_name,
2476 lifetime_rec,
2477 mech_type,
2478 ctx_flags,
2479 locally_initiated,
2480 opened);
2482 return (ret);
2485 OM_uint32
2486 glue_spnego_gss_wrap_size_limit(
2487 void *context,
2488 OM_uint32 *minor_status,
2489 const gss_ctx_id_t context_handle,
2490 int conf_req_flag,
2491 gss_qop_t qop_req,
2492 OM_uint32 req_output_size,
2493 OM_uint32 *max_input_size)
2495 return(spnego_gss_wrap_size_limit(minor_status,
2496 context_handle,
2497 conf_req_flag,
2498 qop_req,
2499 req_output_size,
2500 max_input_size));
2503 OM_uint32
2504 spnego_gss_wrap_size_limit(
2505 OM_uint32 *minor_status,
2506 const gss_ctx_id_t context_handle,
2507 int conf_req_flag,
2508 gss_qop_t qop_req,
2509 OM_uint32 req_output_size,
2510 OM_uint32 *max_input_size)
2512 OM_uint32 ret;
2513 ret = gss_wrap_size_limit(minor_status,
2514 context_handle,
2515 conf_req_flag,
2516 qop_req,
2517 req_output_size,
2518 max_input_size);
2519 return (ret);
2522 #if 0 /* SUNW17PACresync */
2523 OM_uint32
2524 spnego_gss_get_mic(
2525 OM_uint32 *minor_status,
2526 const gss_ctx_id_t context_handle,
2527 gss_qop_t qop_req,
2528 const gss_buffer_t message_buffer,
2529 gss_buffer_t message_token)
2531 OM_uint32 ret;
2532 ret = gss_get_mic(minor_status,
2533 context_handle,
2534 qop_req,
2535 message_buffer,
2536 message_token);
2537 return (ret);
2539 #endif
2541 OM_uint32
2542 spnego_gss_verify_mic(
2543 OM_uint32 *minor_status,
2544 const gss_ctx_id_t context_handle,
2545 const gss_buffer_t msg_buffer,
2546 const gss_buffer_t token_buffer,
2547 gss_qop_t *qop_state)
2549 OM_uint32 ret;
2550 ret = gss_verify_mic(minor_status,
2551 context_handle,
2552 msg_buffer,
2553 token_buffer,
2554 qop_state);
2555 return (ret);
2558 OM_uint32
2559 spnego_gss_inquire_sec_context_by_oid(
2560 OM_uint32 *minor_status,
2561 const gss_ctx_id_t context_handle,
2562 const gss_OID desired_object,
2563 gss_buffer_set_t *data_set)
2565 OM_uint32 ret;
2566 ret = gss_inquire_sec_context_by_oid(minor_status,
2567 context_handle,
2568 desired_object,
2569 data_set);
2570 return (ret);
2574 * SUNW17PACresync
2575 * These GSS funcs not needed yet, so disable them.
2576 * Revisit for full 1.7 resync.
2578 #if 0
2579 OM_uint32
2580 spnego_gss_set_sec_context_option(
2581 OM_uint32 *minor_status,
2582 gss_ctx_id_t *context_handle,
2583 const gss_OID desired_object,
2584 const gss_buffer_t value)
2586 OM_uint32 ret;
2587 ret = gss_set_sec_context_option(minor_status,
2588 context_handle,
2589 desired_object,
2590 value);
2591 return (ret);
2594 OM_uint32
2595 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2596 gss_ctx_id_t context_handle,
2597 int conf_req_flag,
2598 gss_qop_t qop_req,
2599 gss_buffer_t input_assoc_buffer,
2600 gss_buffer_t input_payload_buffer,
2601 int *conf_state,
2602 gss_buffer_t output_message_buffer)
2604 OM_uint32 ret;
2605 ret = gss_wrap_aead(minor_status,
2606 context_handle,
2607 conf_req_flag,
2608 qop_req,
2609 input_assoc_buffer,
2610 input_payload_buffer,
2611 conf_state,
2612 output_message_buffer);
2614 return (ret);
2617 OM_uint32
2618 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2619 gss_ctx_id_t context_handle,
2620 gss_buffer_t input_message_buffer,
2621 gss_buffer_t input_assoc_buffer,
2622 gss_buffer_t output_payload_buffer,
2623 int *conf_state,
2624 gss_qop_t *qop_state)
2626 OM_uint32 ret;
2627 ret = gss_unwrap_aead(minor_status,
2628 context_handle,
2629 input_message_buffer,
2630 input_assoc_buffer,
2631 output_payload_buffer,
2632 conf_state,
2633 qop_state);
2634 return (ret);
2637 OM_uint32
2638 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2639 gss_ctx_id_t context_handle,
2640 int conf_req_flag,
2641 gss_qop_t qop_req,
2642 int *conf_state,
2643 gss_iov_buffer_desc *iov,
2644 int iov_count)
2646 OM_uint32 ret;
2647 ret = gss_wrap_iov(minor_status,
2648 context_handle,
2649 conf_req_flag,
2650 qop_req,
2651 conf_state,
2652 iov,
2653 iov_count);
2654 return (ret);
2657 OM_uint32
2658 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2659 gss_ctx_id_t context_handle,
2660 int *conf_state,
2661 gss_qop_t *qop_state,
2662 gss_iov_buffer_desc *iov,
2663 int iov_count)
2665 OM_uint32 ret;
2666 ret = gss_unwrap_iov(minor_status,
2667 context_handle,
2668 conf_state,
2669 qop_state,
2670 iov,
2671 iov_count);
2672 return (ret);
2675 OM_uint32
2676 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2677 gss_ctx_id_t context_handle,
2678 int conf_req_flag,
2679 gss_qop_t qop_req,
2680 int *conf_state,
2681 gss_iov_buffer_desc *iov,
2682 int iov_count)
2684 OM_uint32 ret;
2685 ret = gss_wrap_iov_length(minor_status,
2686 context_handle,
2687 conf_req_flag,
2688 qop_req,
2689 conf_state,
2690 iov,
2691 iov_count);
2692 return (ret);
2696 OM_uint32
2697 spnego_gss_complete_auth_token(
2698 OM_uint32 *minor_status,
2699 const gss_ctx_id_t context_handle,
2700 gss_buffer_t input_message_buffer)
2702 OM_uint32 ret;
2703 ret = gss_complete_auth_token(minor_status,
2704 context_handle,
2705 input_message_buffer);
2706 return (ret);
2708 #endif /* 0 */
2711 * We will release everything but the ctx_handle so that it
2712 * can be passed back to init/accept context. This routine should
2713 * not be called until after the ctx_handle memory is assigned to
2714 * the supplied context handle from init/accept context.
2716 static void
2717 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2719 spnego_gss_ctx_id_t context;
2720 OM_uint32 minor_stat;
2721 context = *ctx;
2723 if (context != NULL) {
2724 (void) gss_release_buffer(&minor_stat,
2725 &context->DER_mechTypes);
2727 (void) generic_gss_release_oid(&minor_stat,
2728 &context->internal_mech);
2730 if (context->optionStr != NULL) {
2731 free(context->optionStr);
2732 context->optionStr = NULL;
2734 free(context);
2735 *ctx = NULL;
2740 * Can't use gss_indicate_mechs by itself to get available mechs for
2741 * SPNEGO because it will also return the SPNEGO mech and we do not
2742 * want to consider SPNEGO as an available security mech for
2743 * negotiation. For this reason, get_available_mechs will return
2744 * all available mechs except SPNEGO.
2746 * If a ptr to a creds list is given, this function will attempt
2747 * to acquire creds for the creds given and trim the list of
2748 * returned mechanisms to only those for which creds are valid.
2751 static OM_uint32
2752 get_available_mechs(OM_uint32 *minor_status,
2753 gss_name_t name, gss_cred_usage_t usage,
2754 gss_cred_id_t *creds, gss_OID_set *rmechs)
2756 unsigned int i;
2757 int found = 0;
2758 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2759 gss_OID_set mechs, goodmechs;
2761 major_status = gss_indicate_mechs(minor_status, &mechs);
2763 if (major_status != GSS_S_COMPLETE) {
2764 return (major_status);
2767 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2769 if (major_status != GSS_S_COMPLETE) {
2770 (void) gss_release_oid_set(minor_status, &mechs);
2771 return (major_status);
2774 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2775 if ((mechs->elements[i].length
2776 != spnego_mechanism.mech_type.length) ||
2777 memcmp(mechs->elements[i].elements,
2778 spnego_mechanism.mech_type.elements,
2779 spnego_mechanism.mech_type.length)) {
2781 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2782 * it never inferences any of the related OIDs of the
2783 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2784 * We add KRB5_WRONG here so that old MS clients can
2785 * negotiate this mechanism, which allows extensions
2786 * in Kerberos (clock skew adjustment, refresh ccache).
2788 if (is_kerb_mech(&mechs->elements[i])) {
2789 extern gss_OID_desc * const gss_mech_krb5_wrong;
2791 major_status =
2792 gss_add_oid_set_member(minor_status,
2793 gss_mech_krb5_wrong, rmechs);
2796 major_status = gss_add_oid_set_member(minor_status,
2797 &mechs->elements[i],
2798 rmechs);
2799 if (major_status == GSS_S_COMPLETE)
2800 found++;
2805 * If the caller wanted a list of creds returned,
2806 * trim the list of mechanisms down to only those
2807 * for which the creds are valid.
2809 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2810 major_status = gss_acquire_cred(minor_status,
2811 name, GSS_C_INDEFINITE,
2812 *rmechs, usage, creds,
2813 &goodmechs, NULL);
2816 * Drop the old list in favor of the new
2817 * "trimmed" list.
2819 (void) gss_release_oid_set(&tmpmin, rmechs);
2820 if (major_status == GSS_S_COMPLETE) {
2821 (void) gssint_copy_oid_set(&tmpmin,
2822 goodmechs, rmechs);
2823 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2827 (void) gss_release_oid_set(&tmpmin, &mechs);
2828 if (found == 0 || major_status != GSS_S_COMPLETE) {
2829 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2830 map_errcode(minor_status);
2831 if (major_status == GSS_S_COMPLETE)
2832 major_status = GSS_S_FAILURE;
2835 return (major_status);
2838 /* following are token creation and reading routines */
2841 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2842 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2843 * place in gss_OID.
2845 static gss_OID
2846 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2848 OM_uint32 status;
2849 gss_OID_desc toid;
2850 gss_OID mech_out = NULL;
2851 unsigned char *start, *end;
2853 if (length < 1 || **buff_in != MECH_OID)
2854 return (NULL);
2856 start = *buff_in;
2857 end = start + length;
2859 (*buff_in)++;
2860 toid.length = *(*buff_in)++;
2862 if ((*buff_in + toid.length) > end)
2863 return (NULL);
2865 toid.elements = *buff_in;
2866 *buff_in += toid.length;
2868 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2870 if (status != GSS_S_COMPLETE) {
2871 map_errcode(minor_status);
2872 mech_out = NULL;
2875 return (mech_out);
2879 * der encode the given mechanism oid into buf_out, advancing the
2880 * buffer pointer.
2883 static int
2884 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2886 if (buflen < mech->length + 2)
2887 return (-1);
2888 *(*buf_out)++ = MECH_OID;
2889 *(*buf_out)++ = (unsigned char) mech->length;
2890 memcpy((void *)(*buf_out), mech->elements, mech->length);
2891 *buf_out += mech->length;
2892 return (0);
2896 * verify that buff_in points to an octet string, if it does not,
2897 * return NULL and don't advance the pointer. If it is an octet string
2898 * decode buff_in into a gss_buffer_t and return it, advancing the
2899 * buffer pointer.
2901 static gss_buffer_t
2902 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2904 gss_buffer_t input_token;
2905 unsigned int bytes;
2907 if (**buff_in != OCTET_STRING)
2908 return (NULL);
2910 (*buff_in)++;
2911 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2913 if (input_token == NULL)
2914 return (NULL);
2916 input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2917 if ((int)input_token->length == -1) {
2918 free(input_token);
2919 return (NULL);
2921 input_token->value = malloc(input_token->length);
2923 if (input_token->value == NULL) {
2924 free(input_token);
2925 return (NULL);
2928 (void) memcpy(input_token->value, *buff_in, input_token->length);
2929 *buff_in += input_token->length;
2930 return (input_token);
2934 * verify that the input token length is not 0. If it is, just return.
2935 * If the token length is greater than 0, der encode as an octet string
2936 * and place in buf_out, advancing buf_out.
2939 static int
2940 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2941 unsigned int buflen)
2943 int ret;
2945 /* if token length is 0, we do not want to send */
2946 if (input_token->length == 0)
2947 return (0);
2949 if (input_token->length > buflen)
2950 return (-1);
2952 *(*buf_out)++ = OCTET_STRING;
2953 if ((ret = gssint_put_der_length(input_token->length, buf_out,
2954 input_token->length)))
2955 return (ret);
2956 TWRITE_STR(*buf_out, input_token->value, input_token->length);
2957 return (0);
2961 * verify that buff_in points to a sequence of der encoding. The mech
2962 * set is the only sequence of encoded object in the token, so if it is
2963 * a sequence of encoding, decode the mechset into a gss_OID_set and
2964 * return it, advancing the buffer pointer.
2966 static gss_OID_set
2967 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2968 unsigned int buff_length)
2970 gss_OID_set returned_mechSet;
2971 OM_uint32 major_status;
2972 int length; /* SUNW17PACresync */
2973 OM_uint32 bytes;
2974 OM_uint32 set_length;
2975 unsigned char *start;
2976 int i;
2978 if (**buff_in != SEQUENCE_OF)
2979 return (NULL);
2981 start = *buff_in;
2982 (*buff_in)++;
2984 length = gssint_get_der_length(buff_in, buff_length, &bytes);
2985 if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
2986 return (NULL);
2988 major_status = gss_create_empty_oid_set(minor_status,
2989 &returned_mechSet);
2990 if (major_status != GSS_S_COMPLETE)
2991 return (NULL);
2993 for (set_length = 0, i = 0; set_length < length; i++) {
2994 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2995 buff_length - (*buff_in - start));
2996 if (temp != NULL) {
2997 major_status = gss_add_oid_set_member(minor_status,
2998 temp, &returned_mechSet);
2999 if (major_status == GSS_S_COMPLETE) {
3000 set_length += returned_mechSet->elements[i].length +2;
3001 if (generic_gss_release_oid(minor_status, &temp))
3002 map_errcode(minor_status);
3007 return (returned_mechSet);
3011 * Encode mechSet into buf.
3013 static int
3014 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3016 unsigned char *ptr;
3017 unsigned int i;
3018 unsigned int tlen, ilen;
3020 tlen = ilen = 0;
3021 for (i = 0; i < mechSet->count; i++) {
3023 * 0x06 [DER LEN] [OID]
3025 ilen += 1 +
3026 gssint_der_length_size(mechSet->elements[i].length) +
3027 mechSet->elements[i].length;
3030 * 0x30 [DER LEN]
3032 tlen = 1 + gssint_der_length_size(ilen) + ilen;
3033 ptr = malloc(tlen);
3034 if (ptr == NULL)
3035 return -1;
3037 buf->value = ptr;
3038 buf->length = tlen;
3039 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3041 *ptr++ = SEQUENCE_OF;
3042 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3043 return -1;
3044 for (i = 0; i < mechSet->count; i++) {
3045 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3046 return -1;
3049 return 0;
3050 #undef REMAIN
3054 * Verify that buff_in is pointing to a BIT_STRING with the correct
3055 * length and padding for the req_flags. If it is, decode req_flags
3056 * and return them, otherwise, return NULL.
3058 static OM_uint32
3059 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3060 OM_uint32 *req_flags)
3062 unsigned int len;
3064 if (**buff_in != (CONTEXT | 0x01))
3065 return (0);
3067 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3068 bodysize, &len) < 0)
3069 return GSS_S_DEFECTIVE_TOKEN;
3071 if (*(*buff_in)++ != BIT_STRING)
3072 return GSS_S_DEFECTIVE_TOKEN;
3074 if (*(*buff_in)++ != BIT_STRING_LENGTH)
3075 return GSS_S_DEFECTIVE_TOKEN;
3077 if (*(*buff_in)++ != BIT_STRING_PADDING)
3078 return GSS_S_DEFECTIVE_TOKEN;
3080 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3081 return (0);
3084 static OM_uint32
3085 get_negTokenInit(OM_uint32 *minor_status,
3086 gss_buffer_t buf,
3087 gss_buffer_t der_mechSet,
3088 gss_OID_set *mechSet,
3089 OM_uint32 *req_flags,
3090 gss_buffer_t *mechtok,
3091 gss_buffer_t *mechListMIC)
3093 OM_uint32 err;
3094 unsigned char *ptr, *bufstart;
3095 unsigned int len;
3096 gss_buffer_desc tmpbuf;
3098 *minor_status = 0;
3099 der_mechSet->length = 0;
3100 der_mechSet->value = NULL;
3101 *mechSet = GSS_C_NO_OID_SET;
3102 *req_flags = 0;
3103 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3105 ptr = bufstart = buf->value;
3106 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3107 return GSS_S_FAILURE;
3108 #define REMAIN (buf->length - (ptr - bufstart))
3110 err = g_verify_token_header(gss_mech_spnego,
3111 &len, &ptr, 0, REMAIN);
3112 if (err) {
3113 *minor_status = err;
3114 map_errcode(minor_status);
3115 return GSS_S_FAILURE;
3117 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3118 if (*minor_status) {
3119 map_errcode(minor_status);
3120 return GSS_S_FAILURE;
3123 /* alias into input_token */
3124 tmpbuf.value = ptr;
3125 tmpbuf.length = REMAIN;
3126 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3127 if (*mechSet == NULL)
3128 return GSS_S_FAILURE;
3130 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3131 der_mechSet->value = malloc(tmpbuf.length);
3132 if (der_mechSet->value == NULL)
3133 return GSS_S_FAILURE;
3134 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3135 der_mechSet->length = tmpbuf.length;
3137 err = get_req_flags(&ptr, REMAIN, req_flags);
3138 if (err != GSS_S_COMPLETE) {
3139 return err;
3141 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3142 REMAIN, &len) >= 0) {
3143 *mechtok = get_input_token(&ptr, len);
3144 if (*mechtok == GSS_C_NO_BUFFER) {
3145 return GSS_S_FAILURE;
3148 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3149 REMAIN, &len) >= 0) {
3150 *mechListMIC = get_input_token(&ptr, len);
3151 if (*mechListMIC == GSS_C_NO_BUFFER) {
3152 return GSS_S_FAILURE;
3155 return GSS_S_COMPLETE;
3156 #undef REMAIN
3159 static OM_uint32
3160 get_negTokenResp(OM_uint32 *minor_status,
3161 unsigned char *buf, unsigned int buflen,
3162 OM_uint32 *negState,
3163 gss_OID *supportedMech,
3164 gss_buffer_t *responseToken,
3165 gss_buffer_t *mechListMIC)
3167 unsigned char *ptr, *bufstart;
3168 unsigned int len;
3169 int tmplen;
3170 unsigned int tag, bytes;
3172 *negState = ACCEPT_DEFECTIVE_TOKEN;
3173 *supportedMech = GSS_C_NO_OID;
3174 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3175 ptr = bufstart = buf;
3176 #define REMAIN (buflen - (ptr - bufstart))
3178 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3179 return GSS_S_DEFECTIVE_TOKEN;
3180 if (*ptr++ == SEQUENCE) {
3181 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3182 if (tmplen < 0)
3183 return GSS_S_DEFECTIVE_TOKEN;
3185 if (REMAIN < 1)
3186 tag = 0;
3187 else
3188 tag = *ptr++;
3190 if (tag == CONTEXT) {
3191 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3192 if (tmplen < 0)
3193 return GSS_S_DEFECTIVE_TOKEN;
3195 if (g_get_tag_and_length(&ptr, ENUMERATED,
3196 REMAIN, &len) < 0)
3197 return GSS_S_DEFECTIVE_TOKEN;
3199 if (len != ENUMERATION_LENGTH)
3200 return GSS_S_DEFECTIVE_TOKEN;
3202 if (REMAIN < 1)
3203 return GSS_S_DEFECTIVE_TOKEN;
3204 *negState = *ptr++;
3206 if (REMAIN < 1)
3207 tag = 0;
3208 else
3209 tag = *ptr++;
3211 if (tag == (CONTEXT | 0x01)) {
3212 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3213 if (tmplen < 0)
3214 return GSS_S_DEFECTIVE_TOKEN;
3216 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3217 if (*supportedMech == GSS_C_NO_OID)
3218 return GSS_S_DEFECTIVE_TOKEN;
3220 if (REMAIN < 1)
3221 tag = 0;
3222 else
3223 tag = *ptr++;
3225 if (tag == (CONTEXT | 0x02)) {
3226 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3227 if (tmplen < 0)
3228 return GSS_S_DEFECTIVE_TOKEN;
3230 *responseToken = get_input_token(&ptr, REMAIN);
3231 if (*responseToken == GSS_C_NO_BUFFER)
3232 return GSS_S_DEFECTIVE_TOKEN;
3234 if (REMAIN < 1)
3235 tag = 0;
3236 else
3237 tag = *ptr++;
3239 if (tag == (CONTEXT | 0x03)) {
3240 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3241 if (tmplen < 0)
3242 return GSS_S_DEFECTIVE_TOKEN;
3244 *mechListMIC = get_input_token(&ptr, REMAIN);
3245 if (*mechListMIC == GSS_C_NO_BUFFER)
3246 return GSS_S_DEFECTIVE_TOKEN;
3248 return GSS_S_COMPLETE;
3249 #undef REMAIN
3253 * der encode the passed negResults as an ENUMERATED type and
3254 * place it in buf_out, advancing the buffer.
3257 static int
3258 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3259 unsigned int buflen)
3261 if (buflen < 3)
3262 return (-1);
3263 *(*buf_out)++ = ENUMERATED;
3264 *(*buf_out)++ = ENUMERATION_LENGTH;
3265 *(*buf_out)++ = (unsigned char) negResult;
3266 return (0);
3270 * This routine compares the recieved mechset to the mechset that
3271 * this server can support. It looks sequentially through the mechset
3272 * and the first one that matches what the server can support is
3273 * chosen as the negotiated mechanism. If one is found, negResult
3274 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3275 * it's not the first mech, otherwise we return NULL and negResult
3276 * is set to REJECT.
3278 * NOTE: There is currently no way to specify a preference order of
3279 * mechanisms supported by the acceptor.
3281 static gss_OID
3282 negotiate_mech_type(OM_uint32 *minor_status,
3283 gss_OID_set supported_mechSet,
3284 gss_OID_set mechset,
3285 OM_uint32 *negResult)
3287 gss_OID returned_mech;
3288 OM_uint32 status;
3289 int present;
3290 unsigned int i;
3292 for (i = 0; i < mechset->count; i++) {
3293 gss_OID mech_oid = &mechset->elements[i];
3296 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3297 * we actually want to select it if the client supports, as this
3298 * will enable features on MS clients that allow credential
3299 * refresh on rekeying and caching system times from servers.
3301 #if 0
3302 /* Accept wrong mechanism OID from MS clients */
3303 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3304 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3305 mech_oid = (gss_OID)&gss_mech_krb5_oid;
3306 #endif
3308 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3309 if (!present)
3310 continue;
3312 if (i == 0)
3313 *negResult = ACCEPT_INCOMPLETE;
3314 else
3315 *negResult = REQUEST_MIC;
3317 status = generic_gss_copy_oid(minor_status,
3318 &mechset->elements[i],
3319 &returned_mech);
3320 if (status != GSS_S_COMPLETE) {
3321 *negResult = REJECT;
3322 map_errcode(minor_status);
3323 return (NULL);
3325 return (returned_mech);
3327 /* Solaris SPNEGO */
3328 *minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
3330 *negResult = REJECT;
3331 return (NULL);
3335 * the next two routines make a token buffer suitable for
3336 * spnego_gss_display_status. These currently take the string
3337 * in name and place it in the token. Eventually, if
3338 * spnego_gss_display_status returns valid error messages,
3339 * these routines will be changes to return the error string.
3341 static spnego_token_t
3342 make_spnego_token(char *name)
3344 return (spnego_token_t)strdup(name);
3347 static gss_buffer_desc
3348 make_err_msg(char *name)
3350 gss_buffer_desc buffer;
3352 if (name == NULL) {
3353 buffer.length = 0;
3354 buffer.value = NULL;
3355 } else {
3356 buffer.length = strlen(name)+1;
3357 buffer.value = make_spnego_token(name);
3360 return (buffer);
3364 * Create the client side spnego token passed back to gss_init_sec_context
3365 * and eventually up to the application program and over to the server.
3367 * Use DER rules, definite length method per RFC 2478
3369 static int
3370 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3371 int negHintsCompat,
3372 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3373 gss_buffer_t data, send_token_flag sendtoken,
3374 gss_buffer_t outbuf)
3376 int ret = 0;
3377 unsigned int tlen, dataLen = 0;
3378 unsigned int negTokenInitSize = 0;
3379 unsigned int negTokenInitSeqSize = 0;
3380 unsigned int negTokenInitContSize = 0;
3381 unsigned int rspTokenSize = 0;
3382 unsigned int mechListTokenSize = 0;
3383 unsigned int micTokenSize = 0;
3384 unsigned char *t;
3385 unsigned char *ptr;
3387 if (outbuf == GSS_C_NO_BUFFER)
3388 return (-1);
3390 outbuf->length = 0;
3391 outbuf->value = NULL;
3393 /* calculate the data length */
3396 * 0xa0 [DER LEN] [mechTypes]
3398 mechListTokenSize = 1 +
3399 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3400 spnego_ctx->DER_mechTypes.length;
3401 dataLen += mechListTokenSize;
3404 * If a token from gss_init_sec_context exists,
3405 * add the length of the token + the ASN.1 overhead
3407 if (data != NULL) {
3409 * Encoded in final output as:
3410 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3411 * -----s--------|--------s2----------
3413 rspTokenSize = 1 +
3414 gssint_der_length_size(data->length) +
3415 data->length;
3416 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3417 rspTokenSize;
3420 if (mechListMIC) {
3422 * Encoded in final output as:
3423 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3424 * --s-- -----tlen------------
3426 micTokenSize = 1 +
3427 gssint_der_length_size(mechListMIC->length) +
3428 mechListMIC->length;
3429 dataLen += 1 +
3430 gssint_der_length_size(micTokenSize) +
3431 micTokenSize;
3435 * Add size of DER encoding
3436 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3437 * 0x30 [DER_LEN] [data]
3440 negTokenInitContSize = dataLen;
3441 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3442 dataLen = negTokenInitSeqSize;
3445 * negTokenInitSize indicates the bytes needed to
3446 * hold the ASN.1 encoding of the entire NegTokenInit
3447 * SEQUENCE.
3448 * 0xa0 [DER_LEN] + data
3451 negTokenInitSize = 1 +
3452 gssint_der_length_size(negTokenInitSeqSize) +
3453 negTokenInitSeqSize;
3455 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3457 t = (unsigned char *) malloc(tlen);
3459 if (t == NULL) {
3460 return (-1);
3463 ptr = t;
3465 /* create the message */
3466 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3467 &ptr, tlen)))
3468 goto errout;
3470 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3471 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3472 goto errout;
3474 *ptr++ = SEQUENCE;
3475 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3476 tlen - (int)(ptr-t))))
3477 goto errout;
3479 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3480 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3481 &ptr, tlen - (int)(ptr-t))))
3482 goto errout;
3484 /* We already encoded the MechSetList */
3485 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3486 spnego_ctx->DER_mechTypes.length);
3488 ptr += spnego_ctx->DER_mechTypes.length;
3490 if (data != NULL) {
3491 *ptr++ = CONTEXT | 0x02;
3492 if ((ret = gssint_put_der_length(rspTokenSize,
3493 &ptr, tlen - (int)(ptr - t))))
3494 goto errout;
3496 if ((ret = put_input_token(&ptr, data,
3497 tlen - (int)(ptr - t))))
3498 goto errout;
3501 if (mechListMIC != GSS_C_NO_BUFFER) {
3502 *ptr++ = CONTEXT | 0x03;
3503 if ((ret = gssint_put_der_length(micTokenSize,
3504 &ptr, tlen - (int)(ptr - t))))
3505 goto errout;
3507 if (negHintsCompat) {
3508 ret = put_neg_hints(&ptr, mechListMIC,
3509 tlen - (int)(ptr - t));
3510 if (ret)
3511 goto errout;
3512 } else if ((ret = put_input_token(&ptr, mechListMIC,
3513 tlen - (int)(ptr - t))))
3514 goto errout;
3517 errout:
3518 if (ret != 0) {
3519 free(t);
3520 t = NULL;
3521 tlen = 0;
3523 outbuf->length = tlen;
3524 outbuf->value = (void *) t;
3526 return (ret);
3530 * create the server side spnego token passed back to
3531 * gss_accept_sec_context and eventually up to the application program
3532 * and over to the client.
3534 static int
3535 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3536 gss_buffer_t data, gss_buffer_t mechListMIC,
3537 send_token_flag sendtoken,
3538 gss_buffer_t outbuf)
3540 unsigned int tlen = 0;
3541 unsigned int ret = 0;
3542 unsigned int NegTokenTargSize = 0;
3543 unsigned int NegTokenSize = 0;
3544 unsigned int rspTokenSize = 0;
3545 unsigned int micTokenSize = 0;
3546 unsigned int dataLen = 0;
3547 unsigned char *t;
3548 unsigned char *ptr;
3550 if (outbuf == GSS_C_NO_BUFFER)
3551 return (GSS_S_DEFECTIVE_TOKEN);
3553 outbuf->length = 0;
3554 outbuf->value = NULL;
3557 * ASN.1 encoding of the negResult
3558 * ENUMERATED type is 3 bytes
3559 * ENUMERATED TAG, Length, Value,
3560 * Plus 2 bytes for the CONTEXT id and length.
3562 dataLen = 5;
3565 * calculate data length
3567 * If this is the initial token, include length of
3568 * mech_type and the negotiation result fields.
3570 if (sendtoken == INIT_TOKEN_SEND) {
3571 int mechlistTokenSize;
3573 * 1 byte for the CONTEXT ID(0xa0),
3574 * 1 byte for the OID ID(0x06)
3575 * 1 byte for OID Length field
3576 * Plus the rest... (OID Length, OID value)
3578 mechlistTokenSize = 3 + mech_wanted->length +
3579 gssint_der_length_size(mech_wanted->length);
3581 dataLen += mechlistTokenSize;
3583 if (data != NULL && data->length > 0) {
3584 /* Length of the inner token */
3585 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3586 data->length;
3588 dataLen += rspTokenSize;
3590 /* Length of the outer token */
3591 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3593 if (mechListMIC != NULL) {
3595 /* Length of the inner token */
3596 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3597 mechListMIC->length;
3599 dataLen += micTokenSize;
3601 /* Length of the outer token */
3602 dataLen += 1 + gssint_der_length_size(micTokenSize);
3605 * Add size of DER encoded:
3606 * NegTokenTarg [ SEQUENCE ] of
3607 * NegResult[0] ENUMERATED {
3608 * accept_completed(0),
3609 * accept_incomplete(1),
3610 * reject(2) }
3611 * supportedMech [1] MechType OPTIONAL,
3612 * responseToken [2] OCTET STRING OPTIONAL,
3613 * mechListMIC [3] OCTET STRING OPTIONAL
3615 * size = data->length + MechListMic + SupportedMech len +
3616 * Result Length + ASN.1 overhead
3618 NegTokenTargSize = dataLen;
3619 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3622 * NegotiationToken [ CHOICE ]{
3623 * negTokenInit [0] NegTokenInit,
3624 * negTokenTarg [1] NegTokenTarg }
3626 NegTokenSize = dataLen;
3627 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3629 tlen = dataLen;
3630 t = (unsigned char *) malloc(tlen);
3632 if (t == NULL) {
3633 ret = GSS_S_DEFECTIVE_TOKEN;
3634 goto errout;
3637 ptr = t;
3640 * Indicate that we are sending CHOICE 1
3641 * (NegTokenTarg)
3643 *ptr++ = CONTEXT | 0x01;
3644 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3645 ret = GSS_S_DEFECTIVE_TOKEN;
3646 goto errout;
3648 *ptr++ = SEQUENCE;
3649 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3650 tlen - (int)(ptr-t)) < 0) {
3651 ret = GSS_S_DEFECTIVE_TOKEN;
3652 goto errout;
3656 * First field of the NegTokenTarg SEQUENCE
3657 * is the ENUMERATED NegResult.
3659 *ptr++ = CONTEXT;
3660 if (gssint_put_der_length(3, &ptr,
3661 tlen - (int)(ptr-t)) < 0) {
3662 ret = GSS_S_DEFECTIVE_TOKEN;
3663 goto errout;
3665 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3666 ret = GSS_S_DEFECTIVE_TOKEN;
3667 goto errout;
3669 if (sendtoken == INIT_TOKEN_SEND) {
3671 * Next, is the Supported MechType
3673 *ptr++ = CONTEXT | 0x01;
3674 if (gssint_put_der_length(mech_wanted->length + 2,
3675 &ptr,
3676 tlen - (int)(ptr - t)) < 0) {
3677 ret = GSS_S_DEFECTIVE_TOKEN;
3678 goto errout;
3680 if (put_mech_oid(&ptr, mech_wanted,
3681 tlen - (int)(ptr - t)) < 0) {
3682 ret = GSS_S_DEFECTIVE_TOKEN;
3683 goto errout;
3686 if (data != NULL && data->length > 0) {
3687 *ptr++ = CONTEXT | 0x02;
3688 if (gssint_put_der_length(rspTokenSize, &ptr,
3689 tlen - (int)(ptr - t)) < 0) {
3690 ret = GSS_S_DEFECTIVE_TOKEN;
3691 goto errout;
3693 if (put_input_token(&ptr, data,
3694 tlen - (int)(ptr - t)) < 0) {
3695 ret = GSS_S_DEFECTIVE_TOKEN;
3696 goto errout;
3699 if (mechListMIC != NULL) {
3700 *ptr++ = CONTEXT | 0x03;
3701 if (gssint_put_der_length(micTokenSize, &ptr,
3702 tlen - (int)(ptr - t)) < 0) {
3703 ret = GSS_S_DEFECTIVE_TOKEN;
3704 goto errout;
3706 if (put_input_token(&ptr, mechListMIC,
3707 tlen - (int)(ptr - t)) < 0) {
3708 ret = GSS_S_DEFECTIVE_TOKEN;
3709 goto errout;
3712 ret = GSS_S_COMPLETE;
3713 errout:
3714 if (ret != GSS_S_COMPLETE) {
3715 free(t);
3716 } else {
3717 outbuf->length = ptr - t;
3718 outbuf->value = (void *) t;
3721 return (ret);
3724 /* determine size of token */
3725 static int
3726 g_token_size(gss_OID_const mech, unsigned int body_size)
3728 int hdrsize;
3731 * Initialize the header size to the
3732 * MECH_OID byte + the bytes needed to indicate the
3733 * length of the OID + the OID itself.
3735 * 0x06 [MECHLENFIELD] MECHDATA
3737 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3740 * Now add the bytes needed for the initial header
3741 * token bytes:
3742 * 0x60 + [DER_LEN] + HDRSIZE
3744 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3746 return (hdrsize + body_size);
3750 * generate token header.
3752 * Use DER Definite Length method per RFC2478
3753 * Use of indefinite length encoding will not be compatible
3754 * with Microsoft or others that actually follow the spec.
3756 static int
3757 g_make_token_header(gss_OID_const mech,
3758 unsigned int body_size,
3759 unsigned char **buf,
3760 unsigned int totallen)
3762 int ret = 0;
3763 unsigned int hdrsize;
3764 unsigned char *p = *buf;
3766 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3768 *(*buf)++ = HEADER_ID;
3769 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3770 return (ret);
3772 *(*buf)++ = MECH_OID;
3773 if ((ret = gssint_put_der_length(mech->length, buf,
3774 totallen - (int)(p - *buf))))
3775 return (ret);
3776 TWRITE_STR(*buf, mech->elements, mech->length);
3777 return (0);
3781 * NOTE: This checks that the length returned by
3782 * gssint_get_der_length() is not greater than the number of octets
3783 * remaining, even though gssint_get_der_length() already checks, in
3784 * theory.
3786 static int
3787 g_get_tag_and_length(unsigned char **buf, int tag,
3788 unsigned int buflen, unsigned int *outlen)
3790 unsigned char *ptr = *buf;
3791 int ret = -1; /* pessimists, assume failure ! */
3792 unsigned int encoded_len;
3793 unsigned int tmplen = 0;
3795 *outlen = 0;
3796 if (buflen > 1 && *ptr == tag) {
3797 ptr++;
3798 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3799 &encoded_len);
3800 if (tmplen < 0) {
3801 ret = -1;
3802 } else if (tmplen > buflen - (ptr - *buf)) {
3803 ret = -1;
3804 } else
3805 ret = 0;
3807 *outlen = tmplen;
3808 *buf = ptr;
3809 return (ret);
3812 static int
3813 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3815 unsigned char *buf = *buf_in;
3816 unsigned char *endptr = buf + cur_size;
3817 unsigned int seqsize;
3818 int ret = 0;
3819 unsigned int bytes;
3822 * Verify this is a NegotiationToken type token
3823 * - check for a0(context specific identifier)
3824 * - get length and verify that enoughd ata exists
3826 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3827 return (G_BAD_TOK_HEADER);
3829 cur_size = seqsize; /* should indicate bytes remaining */
3832 * Verify the next piece, it should identify this as
3833 * a strucure of type NegTokenInit.
3835 if (*buf++ == SEQUENCE) {
3836 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3837 return (G_BAD_TOK_HEADER);
3839 * Make sure we have the entire buffer as described
3841 if (buf + seqsize > endptr)
3842 return (G_BAD_TOK_HEADER);
3843 } else {
3844 return (G_BAD_TOK_HEADER);
3847 cur_size = seqsize; /* should indicate bytes remaining */
3850 * Verify that the first blob is a sequence of mechTypes
3852 if (*buf++ == CONTEXT) {
3853 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3854 return (G_BAD_TOK_HEADER);
3856 * Make sure we have the entire buffer as described
3858 if (buf + bytes > endptr)
3859 return (G_BAD_TOK_HEADER);
3860 } else {
3861 return (G_BAD_TOK_HEADER);
3865 * At this point, *buf should be at the beginning of the
3866 * DER encoded list of mech types that are to be negotiated.
3868 *buf_in = buf;
3870 return (ret);
3874 /* verify token header. */
3875 static int
3876 g_verify_token_header(gss_OID_const mech,
3877 unsigned int *body_size,
3878 unsigned char **buf_in,
3879 int tok_type,
3880 unsigned int toksize)
3882 unsigned char *buf = *buf_in;
3883 int seqsize;
3884 gss_OID_desc toid;
3885 int ret = 0;
3886 unsigned int bytes;
3888 if (toksize-- < 1)
3889 return (G_BAD_TOK_HEADER);
3891 if (*buf++ != HEADER_ID)
3892 return (G_BAD_TOK_HEADER);
3894 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3895 return (G_BAD_TOK_HEADER);
3897 if ((seqsize + bytes) != toksize)
3898 return (G_BAD_TOK_HEADER);
3900 if (toksize-- < 1)
3901 return (G_BAD_TOK_HEADER);
3904 if (*buf++ != MECH_OID)
3905 return (G_BAD_TOK_HEADER);
3907 if (toksize-- < 1)
3908 return (G_BAD_TOK_HEADER);
3910 toid.length = *buf++;
3912 if (toksize < toid.length)
3913 return (G_BAD_TOK_HEADER);
3914 else
3915 toksize -= toid.length;
3917 toid.elements = buf;
3918 buf += toid.length;
3920 if (!g_OID_equal(&toid, mech))
3921 ret = G_WRONG_MECH;
3924 * G_WRONG_MECH is not returned immediately because it's more important
3925 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3927 if (toksize < 2)
3928 return (G_BAD_TOK_HEADER);
3929 else
3930 toksize -= 2;
3932 if (!ret) {
3933 *buf_in = buf;
3934 *body_size = toksize;
3937 return (ret);
3941 * Return non-zero if the oid is one of the kerberos mech oids,
3942 * otherwise return zero.
3944 * N.B. There are 3 oids that represent the kerberos mech:
3945 * RFC-specified GSS_MECH_KRB5_OID,
3946 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3947 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3950 static int
3951 is_kerb_mech(gss_OID oid)
3953 int answer = 0;
3954 OM_uint32 minor;
3955 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3957 (void) gss_test_oid_set_member(&minor,
3958 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3960 return (answer);