8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_spnego / mech / spnego_mech.c
blob8faac2d3a1af9dd6873c15fe851130beddb17021
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 if (mechTypesStr)
1514 free(mechTypesStr);
1517 * We save error here cuz the tmp ctx goes away (very) soon.
1518 * So callers of acc_ctx_new() should NOT call it again.
1520 spnego_gss_save_error_info(*minor_status, tmpsc);
1521 if (tmpsc)
1522 release_spnego_ctx(&tmpsc);
1523 ret = GSS_S_BAD_MECH;
1524 goto cleanup;
1527 sc = (spnego_gss_ctx_id_t)*ctx;
1528 if (sc != NULL) {
1529 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1530 assert(mech_wanted != GSS_C_NO_OID);
1531 } else
1532 sc = create_spnego_ctx();
1533 if (sc == NULL) {
1534 ret = GSS_S_FAILURE;
1535 *return_token = NO_TOKEN_SEND;
1536 generic_gss_release_oid(&tmpmin, &mech_wanted);
1537 goto cleanup;
1539 sc->internal_mech = mech_wanted;
1540 sc->DER_mechTypes = der_mechTypes;
1541 der_mechTypes.length = 0;
1542 der_mechTypes.value = NULL;
1544 if (*negState == REQUEST_MIC)
1545 sc->mic_reqd = 1;
1547 *return_token = INIT_TOKEN_SEND;
1548 sc->firstpass = 1;
1549 *ctx = (gss_ctx_id_t)sc;
1550 ret = GSS_S_COMPLETE;
1551 cleanup:
1552 gss_release_oid_set(&tmpmin, &mechTypes);
1553 gss_release_oid_set(&tmpmin, &supported_mechSet);
1554 if (der_mechTypes.length != 0)
1555 gss_release_buffer(&tmpmin, &der_mechTypes);
1556 return ret;
1559 static OM_uint32
1560 acc_ctx_cont(OM_uint32 *minstat,
1561 gss_buffer_t buf,
1562 gss_ctx_id_t *ctx,
1563 gss_buffer_t *responseToken,
1564 gss_buffer_t *mechListMIC,
1565 OM_uint32 *negState,
1566 send_token_flag *return_token)
1568 OM_uint32 ret, tmpmin;
1569 gss_OID supportedMech;
1570 spnego_gss_ctx_id_t sc;
1571 unsigned int len;
1572 unsigned char *ptr, *bufstart;
1574 sc = (spnego_gss_ctx_id_t)*ctx;
1575 ret = GSS_S_DEFECTIVE_TOKEN;
1576 *negState = REJECT;
1577 *minstat = 0;
1578 supportedMech = GSS_C_NO_OID;
1579 *return_token = ERROR_TOKEN_SEND;
1580 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1582 ptr = bufstart = buf->value;
1583 #define REMAIN (buf->length - (ptr - bufstart))
1584 if (REMAIN > INT_MAX)
1585 return GSS_S_DEFECTIVE_TOKEN;
1588 * Attempt to work with old Sun SPNEGO.
1590 if (*ptr == HEADER_ID) {
1591 ret = g_verify_token_header(gss_mech_spnego,
1592 &len, &ptr, 0, REMAIN);
1593 if (ret) {
1594 *minstat = ret;
1595 return GSS_S_DEFECTIVE_TOKEN;
1598 if (*ptr != (CONTEXT | 0x01)) {
1599 return GSS_S_DEFECTIVE_TOKEN;
1601 ret = get_negTokenResp(minstat, ptr, REMAIN,
1602 negState, &supportedMech,
1603 responseToken, mechListMIC);
1604 if (ret != GSS_S_COMPLETE)
1605 goto cleanup;
1607 if (*responseToken == GSS_C_NO_BUFFER &&
1608 *mechListMIC == GSS_C_NO_BUFFER) {
1610 ret = GSS_S_DEFECTIVE_TOKEN;
1611 goto cleanup;
1613 if (supportedMech != GSS_C_NO_OID) {
1614 ret = GSS_S_DEFECTIVE_TOKEN;
1615 goto cleanup;
1617 sc->firstpass = 0;
1618 *negState = ACCEPT_INCOMPLETE;
1619 *return_token = CONT_TOKEN_SEND;
1620 cleanup:
1621 if (supportedMech != GSS_C_NO_OID) {
1622 generic_gss_release_oid(&tmpmin, &supportedMech);
1624 return ret;
1625 #undef REMAIN
1629 * Verify that mech OID is either exactly the same as the negotiated
1630 * mech OID, or is a mech OID supported by the negotiated mech. MS
1631 * implementations can list a most preferred mech using an incorrect
1632 * krb5 OID while emitting a krb5 initiator mech token having the
1633 * correct krb5 mech OID.
1635 static OM_uint32
1636 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1637 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1638 OM_uint32 *negState, send_token_flag *tokflag)
1640 OM_uint32 ret, tmpmin;
1641 gss_mechanism mech = NULL;
1642 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1643 int present = 0;
1645 if (g_OID_equal(sc->internal_mech, mechoid))
1646 return GSS_S_COMPLETE;
1649 * SUNW17PACresync
1650 * If both mechs are kerb, we are done.
1652 if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
1653 return GSS_S_COMPLETE;
1656 mech = gssint_get_mechanism(mechoid);
1657 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1658 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1661 * Solaris SPNEGO
1662 * Spruce-up error msg.
1664 OM_uint32 maj, maj_sc, min;
1665 gss_buffer_desc oidstr, oidstr_sc;
1666 /* No need to free mnamestr. */
1667 const char *mnamestr = __gss_oid_to_mech(
1668 sc->internal_mech);
1669 maj_sc = gss_oid_to_str(&min,
1670 sc->internal_mech,
1671 &oidstr_sc);
1672 maj = gss_oid_to_str(&min, mechoid, &oidstr);
1673 spnego_set_error_message(sc, *minor_status,
1674 dgettext(TEXT_DOMAIN,
1675 "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1676 maj ? oid_no_map: oidstr.value,
1677 maj_sc ? oid_no_map: oidstr_sc.value,
1678 mnamestr ? mnamestr : mech_no_map);
1679 if (!maj)
1680 (void) gss_release_buffer(&min, &oidstr);
1681 if (!maj_sc)
1682 (void) gss_release_buffer(&min, &oidstr_sc);
1684 map_errcode(minor_status);
1685 *negState = REJECT;
1686 *tokflag = ERROR_TOKEN_SEND;
1687 return GSS_S_BAD_MECH;
1689 ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
1690 if (ret != GSS_S_COMPLETE) {
1691 *tokflag = NO_TOKEN_SEND;
1692 map_error(minor_status, mech);
1693 goto cleanup;
1695 ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
1696 mech_set, &present);
1697 if (ret != GSS_S_COMPLETE)
1698 goto cleanup;
1699 if (!present) {
1702 * Solaris SPNEGO
1703 * Spruce-up error msg.
1705 OM_uint32 maj, min;
1706 gss_buffer_desc oidstr;
1707 char *mech_set_str = mechoidset2str(mech_set);
1708 /* No need to free mnamestr. */
1709 const char *mnamestr =
1710 __gss_oid_to_mech(sc->internal_mech);
1711 maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
1712 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1713 spnego_set_error_message(sc, *minor_status,
1714 dgettext(TEXT_DOMAIN,
1715 "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1716 maj ? oid_no_map: oidstr.value,
1717 mnamestr ? mnamestr : mech_no_map,
1718 mech_set_str ? mech_set_str : "<null>");
1719 if (!maj)
1720 (void) gss_release_buffer(&min, &oidstr);
1721 if (mech_set_str)
1722 free(mech_set_str);
1724 map_errcode(minor_status);
1725 *negState = REJECT;
1726 *tokflag = ERROR_TOKEN_SEND;
1727 ret = GSS_S_BAD_MECH;
1729 cleanup:
1730 gss_release_oid_set(&tmpmin, &mech_set);
1731 return ret;
1733 #ifndef LEAN_CLIENT
1735 * Wrap call to gss_accept_sec_context() and update state
1736 * accordingly.
1738 static OM_uint32
1739 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1740 gss_cred_id_t cred, gss_buffer_t mechtok_in,
1741 gss_OID *mech_type, gss_buffer_t mechtok_out,
1742 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1743 gss_cred_id_t *delegated_cred_handle,
1744 OM_uint32 *negState, send_token_flag *tokflag)
1746 OM_uint32 ret;
1747 gss_OID_desc mechoid;
1749 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1751 * mechoid is an alias; don't free it.
1753 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1754 if (ret != GSS_S_COMPLETE) {
1755 *tokflag = NO_TOKEN_SEND;
1756 return ret;
1758 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1759 negState, tokflag);
1760 if (ret != GSS_S_COMPLETE)
1761 return ret;
1764 ret = gss_accept_sec_context(minor_status,
1765 &sc->ctx_handle,
1766 cred,
1767 mechtok_in,
1768 GSS_C_NO_CHANNEL_BINDINGS,
1769 &sc->internal_name,
1770 mech_type,
1771 mechtok_out,
1772 &sc->ctx_flags,
1773 time_rec,
1774 delegated_cred_handle);
1776 if (ret == GSS_S_COMPLETE) {
1777 #ifdef MS_BUG_TEST
1779 * Force MIC to be not required even if we previously
1780 * requested a MIC.
1782 char *envstr = getenv("MS_FORCE_NO_MIC");
1784 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1785 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1786 sc->mic_reqd) {
1788 sc->mic_reqd = 0;
1790 #endif
1791 sc->mech_complete = 1;
1792 if (ret_flags != NULL)
1793 *ret_flags = sc->ctx_flags;
1795 if (!sc->mic_reqd) {
1796 *negState = ACCEPT_COMPLETE;
1797 ret = GSS_S_COMPLETE;
1798 } else {
1799 ret = GSS_S_CONTINUE_NEEDED;
1801 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1802 *negState = REJECT;
1803 *tokflag = ERROR_TOKEN_SEND;
1805 return ret;
1808 /*ARGSUSED*/
1809 OM_uint32
1810 glue_spnego_gss_accept_sec_context(
1811 void *context,
1812 OM_uint32 *minor_status,
1813 gss_ctx_id_t *context_handle,
1814 gss_cred_id_t verifier_cred_handle,
1815 gss_buffer_t input_token,
1816 gss_channel_bindings_t input_chan_bindings,
1817 gss_name_t *src_name,
1818 gss_OID *mech_type,
1819 gss_buffer_t output_token,
1820 OM_uint32 *ret_flags,
1821 OM_uint32 *time_rec,
1822 gss_cred_id_t *delegated_cred_handle)
1824 return(spnego_gss_accept_sec_context(
1825 minor_status,
1826 context_handle,
1827 verifier_cred_handle,
1828 input_token,
1829 input_chan_bindings,
1830 src_name,
1831 mech_type,
1832 output_token,
1833 ret_flags,
1834 time_rec,
1835 delegated_cred_handle));
1838 /*ARGSUSED*/
1839 OM_uint32
1840 spnego_gss_accept_sec_context(
1841 OM_uint32 *minor_status,
1842 gss_ctx_id_t *context_handle,
1843 gss_cred_id_t verifier_cred_handle,
1844 gss_buffer_t input_token,
1845 gss_channel_bindings_t input_chan_bindings,
1846 gss_name_t *src_name,
1847 gss_OID *mech_type,
1848 gss_buffer_t output_token,
1849 OM_uint32 *ret_flags,
1850 OM_uint32 *time_rec,
1851 gss_cred_id_t *delegated_cred_handle)
1853 OM_uint32 ret, tmpmin, negState;
1854 send_token_flag return_token;
1855 gss_buffer_t mechtok_in, mic_in, mic_out;
1856 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1857 spnego_gss_ctx_id_t sc = NULL;
1858 OM_uint32 mechstat = GSS_S_FAILURE;
1859 int sendTokenInit = 0, tmpret;
1861 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1863 if (minor_status != NULL)
1864 *minor_status = 0;
1865 if (output_token != GSS_C_NO_BUFFER) {
1866 output_token->length = 0;
1867 output_token->value = NULL;
1871 if (minor_status == NULL ||
1872 output_token == GSS_C_NO_BUFFER ||
1873 context_handle == NULL) {
1874 return GSS_S_CALL_INACCESSIBLE_WRITE;
1877 if (input_token == GSS_C_NO_BUFFER) {
1878 return GSS_S_CALL_INACCESSIBLE_READ;
1881 sc = (spnego_gss_ctx_id_t)*context_handle;
1882 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1883 if (src_name != NULL)
1884 *src_name = GSS_C_NO_NAME;
1885 if (mech_type != NULL)
1886 *mech_type = GSS_C_NO_OID;
1887 if (time_rec != NULL)
1888 *time_rec = 0;
1889 if (ret_flags != NULL)
1890 *ret_flags = 0;
1891 if (delegated_cred_handle != NULL)
1892 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1893 if (input_token->length == 0) {
1894 ret = acc_ctx_hints(minor_status,
1895 context_handle,
1896 verifier_cred_handle,
1897 &mic_out,
1898 &negState,
1899 &return_token);
1900 if (ret != GSS_S_COMPLETE)
1901 goto cleanup;
1902 sendTokenInit = 1;
1903 ret = GSS_S_CONTINUE_NEEDED;
1904 } else {
1905 /* Can set negState to REQUEST_MIC */
1906 ret = acc_ctx_new(minor_status, input_token,
1907 context_handle, verifier_cred_handle,
1908 &mechtok_in, &mic_in,
1909 &negState, &return_token);
1910 if (ret != GSS_S_COMPLETE)
1911 goto cleanup;
1912 ret = GSS_S_CONTINUE_NEEDED;
1914 } else {
1915 /* Can set negState to ACCEPT_INCOMPLETE */
1916 ret = acc_ctx_cont(minor_status, input_token,
1917 context_handle, &mechtok_in,
1918 &mic_in, &negState, &return_token);
1919 if (ret != GSS_S_COMPLETE)
1920 goto cleanup;
1921 ret = GSS_S_CONTINUE_NEEDED;
1924 sc = (spnego_gss_ctx_id_t)*context_handle;
1926 * Handle mechtok_in and mic_in only if they are
1927 * present in input_token. If neither is present, whether
1928 * this is an error depends on whether this is the first
1929 * round-trip. RET is set to a default value according to
1930 * whether it is the first round-trip.
1932 mechstat = GSS_S_FAILURE;
1933 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1934 ret = acc_ctx_call_acc(minor_status, sc,
1935 verifier_cred_handle, mechtok_in,
1936 mech_type, &mechtok_out,
1937 ret_flags, time_rec,
1938 delegated_cred_handle,
1939 &negState, &return_token);
1940 } else if (negState == REQUEST_MIC) {
1941 mechstat = GSS_S_CONTINUE_NEEDED;
1944 /* Solaris SPNEGO */
1945 if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1946 spnego_gss_save_error_info(*minor_status, sc);
1948 if (!HARD_ERROR(ret) && sc->mech_complete &&
1949 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1951 ret = handle_mic(minor_status, mic_in,
1952 (mechtok_out.length != 0),
1953 sc, &mic_out,
1954 &negState, &return_token);
1957 cleanup:
1958 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1959 assert(sc != NULL);
1960 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1961 GSS_C_NO_BUFFER,
1962 return_token, output_token);
1963 if (tmpret < 0)
1964 ret = GSS_S_FAILURE;
1965 } else if (return_token != NO_TOKEN_SEND &&
1966 return_token != CHECK_MIC) {
1967 tmpret = make_spnego_tokenTarg_msg(negState,
1968 sc ? sc->internal_mech :
1969 GSS_C_NO_OID,
1970 &mechtok_out, mic_out,
1971 return_token,
1972 output_token);
1973 if (tmpret < 0)
1974 ret = GSS_S_FAILURE;
1976 if (ret == GSS_S_COMPLETE) {
1977 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1978 if (sc->internal_name != GSS_C_NO_NAME &&
1979 src_name != NULL) {
1980 *src_name = sc->internal_name;
1982 release_spnego_ctx(&sc);
1984 gss_release_buffer(&tmpmin, &mechtok_out);
1985 if (mechtok_in != GSS_C_NO_BUFFER) {
1986 gss_release_buffer(&tmpmin, mechtok_in);
1987 free(mechtok_in);
1989 if (mic_in != GSS_C_NO_BUFFER) {
1990 gss_release_buffer(&tmpmin, mic_in);
1991 free(mic_in);
1993 if (mic_out != GSS_C_NO_BUFFER) {
1994 gss_release_buffer(&tmpmin, mic_out);
1995 free(mic_out);
1997 return ret;
1999 #endif /* LEAN_CLIENT */
2001 /*ARGSUSED*/
2002 OM_uint32
2003 glue_spnego_gss_display_status(
2004 void *context,
2005 OM_uint32 *minor_status,
2006 OM_uint32 status_value,
2007 int status_type,
2008 gss_OID mech_type,
2009 OM_uint32 *message_context,
2010 gss_buffer_t status_string)
2012 return (spnego_gss_display_status(minor_status,
2013 status_value,
2014 status_type,
2015 mech_type,
2016 message_context,
2017 status_string));
2020 /*ARGSUSED*/
2021 OM_uint32
2022 spnego_gss_display_status(
2023 OM_uint32 *minor_status,
2024 OM_uint32 status_value,
2025 int status_type,
2026 gss_OID mech_type,
2027 OM_uint32 *message_context,
2028 gss_buffer_t status_string)
2030 dsyslog("Entering display_status\n");
2032 *message_context = 0;
2033 switch (status_value) {
2034 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
2035 /* CSTYLED */
2036 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2037 break;
2038 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
2039 /* CSTYLED */
2040 *status_string = make_err_msg("SPNEGO failed to acquire creds");
2041 break;
2042 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
2043 /* CSTYLED */
2044 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
2045 break;
2046 case ERR_SPNEGO_NEGOTIATION_FAILED:
2047 /* CSTYLED */
2048 return(spnego_gss_display_status2(minor_status,
2049 status_value,
2050 status_type,
2051 mech_type,
2052 message_context,
2053 status_string));
2054 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
2055 /* CSTYLED */
2056 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
2057 break;
2058 default:
2060 * Solaris SPNEGO
2061 * If mech_spnego calls mech_krb5 (via libgss) and an
2062 * error occurs there, give it a shot.
2064 /* CSTYLED */
2065 return(krb5_gss_display_status2(minor_status,
2066 status_value,
2067 status_type,
2068 (gss_OID)&gss_mech_krb5_oid,
2069 message_context,
2070 status_string));
2074 dsyslog("Leaving display_status\n");
2075 return (GSS_S_COMPLETE);
2078 /*ARGSUSED*/
2079 OM_uint32
2080 glue_spnego_gss_import_name(
2081 void *context,
2082 OM_uint32 *minor_status,
2083 gss_buffer_t input_name_buffer,
2084 gss_OID input_name_type,
2085 gss_name_t *output_name)
2087 return(spnego_gss_import_name(minor_status,
2088 input_name_buffer,
2089 input_name_type,
2090 output_name));
2093 /*ARGSUSED*/
2094 OM_uint32
2095 spnego_gss_import_name(
2096 OM_uint32 *minor_status,
2097 gss_buffer_t input_name_buffer,
2098 gss_OID input_name_type,
2099 gss_name_t *output_name)
2101 OM_uint32 status;
2103 dsyslog("Entering import_name\n");
2105 status = gss_import_name(minor_status, input_name_buffer,
2106 input_name_type, output_name);
2108 dsyslog("Leaving import_name\n");
2109 return (status);
2112 /*ARGSUSED*/
2113 OM_uint32
2114 glue_spnego_gss_release_name(
2115 void *context,
2116 OM_uint32 *minor_status,
2117 gss_name_t *input_name)
2119 return(spnego_gss_release_name(minor_status, input_name));
2122 /*ARGSUSED*/
2123 OM_uint32
2124 spnego_gss_release_name(
2125 OM_uint32 *minor_status,
2126 gss_name_t *input_name)
2128 OM_uint32 status;
2130 dsyslog("Entering release_name\n");
2132 status = gss_release_name(minor_status, input_name);
2134 dsyslog("Leaving release_name\n");
2135 return (status);
2138 /*ARGSUSED*/
2139 OM_uint32
2140 glue_spnego_gss_compare_name(
2141 void *context,
2142 OM_uint32 *minor_status,
2143 const gss_name_t name1,
2144 const gss_name_t name2,
2145 int *name_equal)
2147 return(spnego_gss_compare_name(minor_status,
2148 name1,
2149 name2,
2150 name_equal));
2152 /*ARGSUSED*/
2153 OM_uint32
2154 spnego_gss_compare_name(
2155 OM_uint32 *minor_status,
2156 const gss_name_t name1,
2157 const gss_name_t name2,
2158 int *name_equal)
2160 OM_uint32 status = GSS_S_COMPLETE;
2161 dsyslog("Entering compare_name\n");
2163 status = gss_compare_name(minor_status, name1, name2, name_equal);
2165 dsyslog("Leaving compare_name\n");
2166 return (status);
2169 /*ARGSUSED*/
2170 OM_uint32
2171 glue_spnego_gss_display_name(
2172 void *context,
2173 OM_uint32 *minor_status,
2174 gss_name_t input_name,
2175 gss_buffer_t output_name_buffer,
2176 gss_OID *output_name_type)
2178 return(spnego_gss_display_name(
2179 minor_status,
2180 input_name,
2181 output_name_buffer,
2182 output_name_type));
2185 /*ARGSUSED*/
2186 OM_uint32
2187 spnego_gss_display_name(
2188 OM_uint32 *minor_status,
2189 gss_name_t input_name,
2190 gss_buffer_t output_name_buffer,
2191 gss_OID *output_name_type)
2193 OM_uint32 status = GSS_S_COMPLETE;
2194 dsyslog("Entering display_name\n");
2196 status = gss_display_name(minor_status, input_name,
2197 output_name_buffer, output_name_type);
2199 dsyslog("Leaving display_name\n");
2200 return (status);
2204 /*ARGSUSED*/
2205 OM_uint32
2206 glue_spnego_gss_inquire_names_for_mech(
2207 void *context,
2208 OM_uint32 *minor_status,
2209 gss_OID mechanism,
2210 gss_OID_set *name_types)
2212 return(spnego_gss_inquire_names_for_mech(minor_status,
2213 mechanism,
2214 name_types));
2216 /*ARGSUSED*/
2217 OM_uint32
2218 spnego_gss_inquire_names_for_mech(
2219 OM_uint32 *minor_status,
2220 gss_OID mechanism,
2221 gss_OID_set *name_types)
2223 OM_uint32 major, minor;
2225 dsyslog("Entering inquire_names_for_mech\n");
2227 * We only know how to handle our own mechanism.
2229 if ((mechanism != GSS_C_NULL_OID) &&
2230 !g_OID_equal(gss_mech_spnego, mechanism)) {
2231 *minor_status = 0;
2232 return (GSS_S_FAILURE);
2235 major = gss_create_empty_oid_set(minor_status, name_types);
2236 if (major == GSS_S_COMPLETE) {
2237 /* Now add our members. */
2238 if (((major = gss_add_oid_set_member(minor_status,
2239 (gss_OID) GSS_C_NT_USER_NAME,
2240 name_types)) == GSS_S_COMPLETE) &&
2241 ((major = gss_add_oid_set_member(minor_status,
2242 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2243 name_types)) == GSS_S_COMPLETE) &&
2244 ((major = gss_add_oid_set_member(minor_status,
2245 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2246 name_types)) == GSS_S_COMPLETE)) {
2247 major = gss_add_oid_set_member(minor_status,
2248 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2249 name_types);
2252 if (major != GSS_S_COMPLETE)
2253 (void) gss_release_oid_set(&minor, name_types);
2256 dsyslog("Leaving inquire_names_for_mech\n");
2257 return (major);
2260 OM_uint32
2261 spnego_gss_unwrap(
2262 OM_uint32 *minor_status,
2263 gss_ctx_id_t context_handle,
2264 gss_buffer_t input_message_buffer,
2265 gss_buffer_t output_message_buffer,
2266 int *conf_state,
2267 gss_qop_t *qop_state)
2269 OM_uint32 ret;
2270 ret = gss_unwrap(minor_status,
2271 context_handle,
2272 input_message_buffer,
2273 output_message_buffer,
2274 conf_state,
2275 qop_state);
2277 return (ret);
2280 OM_uint32
2281 spnego_gss_wrap(
2282 OM_uint32 *minor_status,
2283 gss_ctx_id_t context_handle,
2284 int conf_req_flag,
2285 gss_qop_t qop_req,
2286 gss_buffer_t input_message_buffer,
2287 int *conf_state,
2288 gss_buffer_t output_message_buffer)
2290 OM_uint32 ret;
2291 ret = gss_wrap(minor_status,
2292 context_handle,
2293 conf_req_flag,
2294 qop_req,
2295 input_message_buffer,
2296 conf_state,
2297 output_message_buffer);
2299 return (ret);
2302 OM_uint32
2303 spnego_gss_process_context_token(
2304 OM_uint32 *minor_status,
2305 const gss_ctx_id_t context_handle,
2306 const gss_buffer_t token_buffer)
2308 OM_uint32 ret;
2309 ret = gss_process_context_token(minor_status,
2310 context_handle,
2311 token_buffer);
2313 return (ret);
2316 OM_uint32
2317 glue_spnego_gss_delete_sec_context(
2318 void *context,
2319 OM_uint32 *minor_status,
2320 gss_ctx_id_t *context_handle,
2321 gss_buffer_t output_token)
2323 return(spnego_gss_delete_sec_context(minor_status,
2324 context_handle, output_token));
2327 OM_uint32
2328 spnego_gss_delete_sec_context(
2329 OM_uint32 *minor_status,
2330 gss_ctx_id_t *context_handle,
2331 gss_buffer_t output_token)
2333 OM_uint32 ret = GSS_S_COMPLETE;
2334 spnego_gss_ctx_id_t *ctx =
2335 (spnego_gss_ctx_id_t *)context_handle;
2337 if (context_handle == NULL)
2338 return (GSS_S_FAILURE);
2341 * If this is still an SPNEGO mech, release it locally.
2343 if (*ctx != NULL &&
2344 (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2345 (void) release_spnego_ctx(ctx);
2346 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2347 if (output_token) {
2348 output_token->length = 0;
2349 output_token->value = NULL;
2351 } else {
2352 ret = gss_delete_sec_context(minor_status,
2353 context_handle,
2354 output_token);
2357 return (ret);
2360 OM_uint32
2361 glue_spnego_gss_context_time(
2362 void *context,
2363 OM_uint32 *minor_status,
2364 const gss_ctx_id_t context_handle,
2365 OM_uint32 *time_rec)
2367 return(spnego_gss_context_time(minor_status,
2368 context_handle,
2369 time_rec));
2372 OM_uint32
2373 spnego_gss_context_time(
2374 OM_uint32 *minor_status,
2375 const gss_ctx_id_t context_handle,
2376 OM_uint32 *time_rec)
2378 OM_uint32 ret;
2379 ret = gss_context_time(minor_status,
2380 context_handle,
2381 time_rec);
2382 return (ret);
2385 #ifndef LEAN_CLIENT
2386 OM_uint32
2387 glue_spnego_gss_export_sec_context(
2388 void *context,
2389 OM_uint32 *minor_status,
2390 gss_ctx_id_t *context_handle,
2391 gss_buffer_t interprocess_token)
2393 return(spnego_gss_export_sec_context(minor_status,
2394 context_handle,
2395 interprocess_token));
2397 OM_uint32
2398 spnego_gss_export_sec_context(
2399 OM_uint32 *minor_status,
2400 gss_ctx_id_t *context_handle,
2401 gss_buffer_t interprocess_token)
2403 OM_uint32 ret;
2404 ret = gss_export_sec_context(minor_status,
2405 context_handle,
2406 interprocess_token);
2407 return (ret);
2410 OM_uint32
2411 glue_spnego_gss_import_sec_context(
2412 void *context,
2413 OM_uint32 *minor_status,
2414 const gss_buffer_t interprocess_token,
2415 gss_ctx_id_t *context_handle)
2417 return(spnego_gss_import_sec_context(minor_status,
2418 interprocess_token,
2419 context_handle));
2421 OM_uint32
2422 spnego_gss_import_sec_context(
2423 OM_uint32 *minor_status,
2424 const gss_buffer_t interprocess_token,
2425 gss_ctx_id_t *context_handle)
2427 OM_uint32 ret;
2428 ret = gss_import_sec_context(minor_status,
2429 interprocess_token,
2430 context_handle);
2431 return (ret);
2433 #endif /* LEAN_CLIENT */
2435 OM_uint32
2436 glue_spnego_gss_inquire_context(
2437 void *context,
2438 OM_uint32 *minor_status,
2439 const gss_ctx_id_t context_handle,
2440 gss_name_t *src_name,
2441 gss_name_t *targ_name,
2442 OM_uint32 *lifetime_rec,
2443 gss_OID *mech_type,
2444 OM_uint32 *ctx_flags,
2445 int *locally_initiated,
2446 int *opened)
2448 return(spnego_gss_inquire_context(
2449 minor_status,
2450 context_handle,
2451 src_name,
2452 targ_name,
2453 lifetime_rec,
2454 mech_type,
2455 ctx_flags,
2456 locally_initiated,
2457 opened));
2460 OM_uint32
2461 spnego_gss_inquire_context(
2462 OM_uint32 *minor_status,
2463 const gss_ctx_id_t context_handle,
2464 gss_name_t *src_name,
2465 gss_name_t *targ_name,
2466 OM_uint32 *lifetime_rec,
2467 gss_OID *mech_type,
2468 OM_uint32 *ctx_flags,
2469 int *locally_initiated,
2470 int *opened)
2472 OM_uint32 ret = GSS_S_COMPLETE;
2474 ret = gss_inquire_context(minor_status,
2475 context_handle,
2476 src_name,
2477 targ_name,
2478 lifetime_rec,
2479 mech_type,
2480 ctx_flags,
2481 locally_initiated,
2482 opened);
2484 return (ret);
2487 OM_uint32
2488 glue_spnego_gss_wrap_size_limit(
2489 void *context,
2490 OM_uint32 *minor_status,
2491 const gss_ctx_id_t context_handle,
2492 int conf_req_flag,
2493 gss_qop_t qop_req,
2494 OM_uint32 req_output_size,
2495 OM_uint32 *max_input_size)
2497 return(spnego_gss_wrap_size_limit(minor_status,
2498 context_handle,
2499 conf_req_flag,
2500 qop_req,
2501 req_output_size,
2502 max_input_size));
2505 OM_uint32
2506 spnego_gss_wrap_size_limit(
2507 OM_uint32 *minor_status,
2508 const gss_ctx_id_t context_handle,
2509 int conf_req_flag,
2510 gss_qop_t qop_req,
2511 OM_uint32 req_output_size,
2512 OM_uint32 *max_input_size)
2514 OM_uint32 ret;
2515 ret = gss_wrap_size_limit(minor_status,
2516 context_handle,
2517 conf_req_flag,
2518 qop_req,
2519 req_output_size,
2520 max_input_size);
2521 return (ret);
2524 #if 0 /* SUNW17PACresync */
2525 OM_uint32
2526 spnego_gss_get_mic(
2527 OM_uint32 *minor_status,
2528 const gss_ctx_id_t context_handle,
2529 gss_qop_t qop_req,
2530 const gss_buffer_t message_buffer,
2531 gss_buffer_t message_token)
2533 OM_uint32 ret;
2534 ret = gss_get_mic(minor_status,
2535 context_handle,
2536 qop_req,
2537 message_buffer,
2538 message_token);
2539 return (ret);
2541 #endif
2543 OM_uint32
2544 spnego_gss_verify_mic(
2545 OM_uint32 *minor_status,
2546 const gss_ctx_id_t context_handle,
2547 const gss_buffer_t msg_buffer,
2548 const gss_buffer_t token_buffer,
2549 gss_qop_t *qop_state)
2551 OM_uint32 ret;
2552 ret = gss_verify_mic(minor_status,
2553 context_handle,
2554 msg_buffer,
2555 token_buffer,
2556 qop_state);
2557 return (ret);
2560 OM_uint32
2561 spnego_gss_inquire_sec_context_by_oid(
2562 OM_uint32 *minor_status,
2563 const gss_ctx_id_t context_handle,
2564 const gss_OID desired_object,
2565 gss_buffer_set_t *data_set)
2567 OM_uint32 ret;
2568 ret = gss_inquire_sec_context_by_oid(minor_status,
2569 context_handle,
2570 desired_object,
2571 data_set);
2572 return (ret);
2576 * SUNW17PACresync
2577 * These GSS funcs not needed yet, so disable them.
2578 * Revisit for full 1.7 resync.
2580 #if 0
2581 OM_uint32
2582 spnego_gss_set_sec_context_option(
2583 OM_uint32 *minor_status,
2584 gss_ctx_id_t *context_handle,
2585 const gss_OID desired_object,
2586 const gss_buffer_t value)
2588 OM_uint32 ret;
2589 ret = gss_set_sec_context_option(minor_status,
2590 context_handle,
2591 desired_object,
2592 value);
2593 return (ret);
2596 OM_uint32
2597 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2598 gss_ctx_id_t context_handle,
2599 int conf_req_flag,
2600 gss_qop_t qop_req,
2601 gss_buffer_t input_assoc_buffer,
2602 gss_buffer_t input_payload_buffer,
2603 int *conf_state,
2604 gss_buffer_t output_message_buffer)
2606 OM_uint32 ret;
2607 ret = gss_wrap_aead(minor_status,
2608 context_handle,
2609 conf_req_flag,
2610 qop_req,
2611 input_assoc_buffer,
2612 input_payload_buffer,
2613 conf_state,
2614 output_message_buffer);
2616 return (ret);
2619 OM_uint32
2620 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2621 gss_ctx_id_t context_handle,
2622 gss_buffer_t input_message_buffer,
2623 gss_buffer_t input_assoc_buffer,
2624 gss_buffer_t output_payload_buffer,
2625 int *conf_state,
2626 gss_qop_t *qop_state)
2628 OM_uint32 ret;
2629 ret = gss_unwrap_aead(minor_status,
2630 context_handle,
2631 input_message_buffer,
2632 input_assoc_buffer,
2633 output_payload_buffer,
2634 conf_state,
2635 qop_state);
2636 return (ret);
2639 OM_uint32
2640 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2641 gss_ctx_id_t context_handle,
2642 int conf_req_flag,
2643 gss_qop_t qop_req,
2644 int *conf_state,
2645 gss_iov_buffer_desc *iov,
2646 int iov_count)
2648 OM_uint32 ret;
2649 ret = gss_wrap_iov(minor_status,
2650 context_handle,
2651 conf_req_flag,
2652 qop_req,
2653 conf_state,
2654 iov,
2655 iov_count);
2656 return (ret);
2659 OM_uint32
2660 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2661 gss_ctx_id_t context_handle,
2662 int *conf_state,
2663 gss_qop_t *qop_state,
2664 gss_iov_buffer_desc *iov,
2665 int iov_count)
2667 OM_uint32 ret;
2668 ret = gss_unwrap_iov(minor_status,
2669 context_handle,
2670 conf_state,
2671 qop_state,
2672 iov,
2673 iov_count);
2674 return (ret);
2677 OM_uint32
2678 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2679 gss_ctx_id_t context_handle,
2680 int conf_req_flag,
2681 gss_qop_t qop_req,
2682 int *conf_state,
2683 gss_iov_buffer_desc *iov,
2684 int iov_count)
2686 OM_uint32 ret;
2687 ret = gss_wrap_iov_length(minor_status,
2688 context_handle,
2689 conf_req_flag,
2690 qop_req,
2691 conf_state,
2692 iov,
2693 iov_count);
2694 return (ret);
2698 OM_uint32
2699 spnego_gss_complete_auth_token(
2700 OM_uint32 *minor_status,
2701 const gss_ctx_id_t context_handle,
2702 gss_buffer_t input_message_buffer)
2704 OM_uint32 ret;
2705 ret = gss_complete_auth_token(minor_status,
2706 context_handle,
2707 input_message_buffer);
2708 return (ret);
2710 #endif /* 0 */
2713 * We will release everything but the ctx_handle so that it
2714 * can be passed back to init/accept context. This routine should
2715 * not be called until after the ctx_handle memory is assigned to
2716 * the supplied context handle from init/accept context.
2718 static void
2719 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2721 spnego_gss_ctx_id_t context;
2722 OM_uint32 minor_stat;
2723 context = *ctx;
2725 if (context != NULL) {
2726 (void) gss_release_buffer(&minor_stat,
2727 &context->DER_mechTypes);
2729 (void) generic_gss_release_oid(&minor_stat,
2730 &context->internal_mech);
2732 if (context->optionStr != NULL) {
2733 free(context->optionStr);
2734 context->optionStr = NULL;
2736 free(context);
2737 *ctx = NULL;
2742 * Can't use gss_indicate_mechs by itself to get available mechs for
2743 * SPNEGO because it will also return the SPNEGO mech and we do not
2744 * want to consider SPNEGO as an available security mech for
2745 * negotiation. For this reason, get_available_mechs will return
2746 * all available mechs except SPNEGO.
2748 * If a ptr to a creds list is given, this function will attempt
2749 * to acquire creds for the creds given and trim the list of
2750 * returned mechanisms to only those for which creds are valid.
2753 static OM_uint32
2754 get_available_mechs(OM_uint32 *minor_status,
2755 gss_name_t name, gss_cred_usage_t usage,
2756 gss_cred_id_t *creds, gss_OID_set *rmechs)
2758 unsigned int i;
2759 int found = 0;
2760 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2761 gss_OID_set mechs, goodmechs;
2763 major_status = gss_indicate_mechs(minor_status, &mechs);
2765 if (major_status != GSS_S_COMPLETE) {
2766 return (major_status);
2769 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2771 if (major_status != GSS_S_COMPLETE) {
2772 (void) gss_release_oid_set(minor_status, &mechs);
2773 return (major_status);
2776 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2777 if ((mechs->elements[i].length
2778 != spnego_mechanism.mech_type.length) ||
2779 memcmp(mechs->elements[i].elements,
2780 spnego_mechanism.mech_type.elements,
2781 spnego_mechanism.mech_type.length)) {
2783 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2784 * it never inferences any of the related OIDs of the
2785 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2786 * We add KRB5_WRONG here so that old MS clients can
2787 * negotiate this mechanism, which allows extensions
2788 * in Kerberos (clock skew adjustment, refresh ccache).
2790 if (is_kerb_mech(&mechs->elements[i])) {
2791 extern gss_OID_desc * const gss_mech_krb5_wrong;
2793 major_status =
2794 gss_add_oid_set_member(minor_status,
2795 gss_mech_krb5_wrong, rmechs);
2798 major_status = gss_add_oid_set_member(minor_status,
2799 &mechs->elements[i],
2800 rmechs);
2801 if (major_status == GSS_S_COMPLETE)
2802 found++;
2807 * If the caller wanted a list of creds returned,
2808 * trim the list of mechanisms down to only those
2809 * for which the creds are valid.
2811 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2812 major_status = gss_acquire_cred(minor_status,
2813 name, GSS_C_INDEFINITE,
2814 *rmechs, usage, creds,
2815 &goodmechs, NULL);
2818 * Drop the old list in favor of the new
2819 * "trimmed" list.
2821 (void) gss_release_oid_set(&tmpmin, rmechs);
2822 if (major_status == GSS_S_COMPLETE) {
2823 (void) gssint_copy_oid_set(&tmpmin,
2824 goodmechs, rmechs);
2825 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2829 (void) gss_release_oid_set(&tmpmin, &mechs);
2830 if (found == 0 || major_status != GSS_S_COMPLETE) {
2831 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2832 map_errcode(minor_status);
2833 if (major_status == GSS_S_COMPLETE)
2834 major_status = GSS_S_FAILURE;
2837 return (major_status);
2840 /* following are token creation and reading routines */
2843 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2844 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2845 * place in gss_OID.
2847 static gss_OID
2848 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2850 OM_uint32 status;
2851 gss_OID_desc toid;
2852 gss_OID mech_out = NULL;
2853 unsigned char *start, *end;
2855 if (length < 1 || **buff_in != MECH_OID)
2856 return (NULL);
2858 start = *buff_in;
2859 end = start + length;
2861 (*buff_in)++;
2862 toid.length = *(*buff_in)++;
2864 if ((*buff_in + toid.length) > end)
2865 return (NULL);
2867 toid.elements = *buff_in;
2868 *buff_in += toid.length;
2870 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2872 if (status != GSS_S_COMPLETE) {
2873 map_errcode(minor_status);
2874 mech_out = NULL;
2877 return (mech_out);
2881 * der encode the given mechanism oid into buf_out, advancing the
2882 * buffer pointer.
2885 static int
2886 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2888 if (buflen < mech->length + 2)
2889 return (-1);
2890 *(*buf_out)++ = MECH_OID;
2891 *(*buf_out)++ = (unsigned char) mech->length;
2892 memcpy((void *)(*buf_out), mech->elements, mech->length);
2893 *buf_out += mech->length;
2894 return (0);
2898 * verify that buff_in points to an octet string, if it does not,
2899 * return NULL and don't advance the pointer. If it is an octet string
2900 * decode buff_in into a gss_buffer_t and return it, advancing the
2901 * buffer pointer.
2903 static gss_buffer_t
2904 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2906 gss_buffer_t input_token;
2907 unsigned int bytes;
2909 if (**buff_in != OCTET_STRING)
2910 return (NULL);
2912 (*buff_in)++;
2913 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2915 if (input_token == NULL)
2916 return (NULL);
2918 input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2919 if ((int)input_token->length == -1) {
2920 free(input_token);
2921 return (NULL);
2923 input_token->value = malloc(input_token->length);
2925 if (input_token->value == NULL) {
2926 free(input_token);
2927 return (NULL);
2930 (void) memcpy(input_token->value, *buff_in, input_token->length);
2931 *buff_in += input_token->length;
2932 return (input_token);
2936 * verify that the input token length is not 0. If it is, just return.
2937 * If the token length is greater than 0, der encode as an octet string
2938 * and place in buf_out, advancing buf_out.
2941 static int
2942 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2943 unsigned int buflen)
2945 int ret;
2947 /* if token length is 0, we do not want to send */
2948 if (input_token->length == 0)
2949 return (0);
2951 if (input_token->length > buflen)
2952 return (-1);
2954 *(*buf_out)++ = OCTET_STRING;
2955 if ((ret = gssint_put_der_length(input_token->length, buf_out,
2956 input_token->length)))
2957 return (ret);
2958 TWRITE_STR(*buf_out, input_token->value, input_token->length);
2959 return (0);
2963 * verify that buff_in points to a sequence of der encoding. The mech
2964 * set is the only sequence of encoded object in the token, so if it is
2965 * a sequence of encoding, decode the mechset into a gss_OID_set and
2966 * return it, advancing the buffer pointer.
2968 static gss_OID_set
2969 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2970 unsigned int buff_length)
2972 gss_OID_set returned_mechSet;
2973 OM_uint32 major_status;
2974 int length; /* SUNW17PACresync */
2975 OM_uint32 bytes;
2976 OM_uint32 set_length;
2977 unsigned char *start;
2978 int i;
2980 if (**buff_in != SEQUENCE_OF)
2981 return (NULL);
2983 start = *buff_in;
2984 (*buff_in)++;
2986 length = gssint_get_der_length(buff_in, buff_length, &bytes);
2987 if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
2988 return (NULL);
2990 major_status = gss_create_empty_oid_set(minor_status,
2991 &returned_mechSet);
2992 if (major_status != GSS_S_COMPLETE)
2993 return (NULL);
2995 for (set_length = 0, i = 0; set_length < length; i++) {
2996 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
2997 buff_length - (*buff_in - start));
2998 if (temp != NULL) {
2999 major_status = gss_add_oid_set_member(minor_status,
3000 temp, &returned_mechSet);
3001 if (major_status == GSS_S_COMPLETE) {
3002 set_length += returned_mechSet->elements[i].length +2;
3003 if (generic_gss_release_oid(minor_status, &temp))
3004 map_errcode(minor_status);
3009 return (returned_mechSet);
3013 * Encode mechSet into buf.
3015 static int
3016 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3018 unsigned char *ptr;
3019 unsigned int i;
3020 unsigned int tlen, ilen;
3022 tlen = ilen = 0;
3023 for (i = 0; i < mechSet->count; i++) {
3025 * 0x06 [DER LEN] [OID]
3027 ilen += 1 +
3028 gssint_der_length_size(mechSet->elements[i].length) +
3029 mechSet->elements[i].length;
3032 * 0x30 [DER LEN]
3034 tlen = 1 + gssint_der_length_size(ilen) + ilen;
3035 ptr = malloc(tlen);
3036 if (ptr == NULL)
3037 return -1;
3039 buf->value = ptr;
3040 buf->length = tlen;
3041 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3043 *ptr++ = SEQUENCE_OF;
3044 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3045 return -1;
3046 for (i = 0; i < mechSet->count; i++) {
3047 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3048 return -1;
3051 return 0;
3052 #undef REMAIN
3056 * Verify that buff_in is pointing to a BIT_STRING with the correct
3057 * length and padding for the req_flags. If it is, decode req_flags
3058 * and return them, otherwise, return NULL.
3060 static OM_uint32
3061 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3062 OM_uint32 *req_flags)
3064 unsigned int len;
3066 if (**buff_in != (CONTEXT | 0x01))
3067 return (0);
3069 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3070 bodysize, &len) < 0)
3071 return GSS_S_DEFECTIVE_TOKEN;
3073 if (*(*buff_in)++ != BIT_STRING)
3074 return GSS_S_DEFECTIVE_TOKEN;
3076 if (*(*buff_in)++ != BIT_STRING_LENGTH)
3077 return GSS_S_DEFECTIVE_TOKEN;
3079 if (*(*buff_in)++ != BIT_STRING_PADDING)
3080 return GSS_S_DEFECTIVE_TOKEN;
3082 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3083 return (0);
3086 static OM_uint32
3087 get_negTokenInit(OM_uint32 *minor_status,
3088 gss_buffer_t buf,
3089 gss_buffer_t der_mechSet,
3090 gss_OID_set *mechSet,
3091 OM_uint32 *req_flags,
3092 gss_buffer_t *mechtok,
3093 gss_buffer_t *mechListMIC)
3095 OM_uint32 err;
3096 unsigned char *ptr, *bufstart;
3097 unsigned int len;
3098 gss_buffer_desc tmpbuf;
3100 *minor_status = 0;
3101 der_mechSet->length = 0;
3102 der_mechSet->value = NULL;
3103 *mechSet = GSS_C_NO_OID_SET;
3104 *req_flags = 0;
3105 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3107 ptr = bufstart = buf->value;
3108 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3109 return GSS_S_FAILURE;
3110 #define REMAIN (buf->length - (ptr - bufstart))
3112 err = g_verify_token_header(gss_mech_spnego,
3113 &len, &ptr, 0, REMAIN);
3114 if (err) {
3115 *minor_status = err;
3116 map_errcode(minor_status);
3117 return GSS_S_FAILURE;
3119 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3120 if (*minor_status) {
3121 map_errcode(minor_status);
3122 return GSS_S_FAILURE;
3125 /* alias into input_token */
3126 tmpbuf.value = ptr;
3127 tmpbuf.length = REMAIN;
3128 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3129 if (*mechSet == NULL)
3130 return GSS_S_FAILURE;
3132 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3133 der_mechSet->value = malloc(tmpbuf.length);
3134 if (der_mechSet->value == NULL)
3135 return GSS_S_FAILURE;
3136 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3137 der_mechSet->length = tmpbuf.length;
3139 err = get_req_flags(&ptr, REMAIN, req_flags);
3140 if (err != GSS_S_COMPLETE) {
3141 return err;
3143 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3144 REMAIN, &len) >= 0) {
3145 *mechtok = get_input_token(&ptr, len);
3146 if (*mechtok == GSS_C_NO_BUFFER) {
3147 return GSS_S_FAILURE;
3150 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3151 REMAIN, &len) >= 0) {
3152 *mechListMIC = get_input_token(&ptr, len);
3153 if (*mechListMIC == GSS_C_NO_BUFFER) {
3154 return GSS_S_FAILURE;
3157 return GSS_S_COMPLETE;
3158 #undef REMAIN
3161 static OM_uint32
3162 get_negTokenResp(OM_uint32 *minor_status,
3163 unsigned char *buf, unsigned int buflen,
3164 OM_uint32 *negState,
3165 gss_OID *supportedMech,
3166 gss_buffer_t *responseToken,
3167 gss_buffer_t *mechListMIC)
3169 unsigned char *ptr, *bufstart;
3170 unsigned int len;
3171 int tmplen;
3172 unsigned int tag, bytes;
3174 *negState = ACCEPT_DEFECTIVE_TOKEN;
3175 *supportedMech = GSS_C_NO_OID;
3176 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3177 ptr = bufstart = buf;
3178 #define REMAIN (buflen - (ptr - bufstart))
3180 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3181 return GSS_S_DEFECTIVE_TOKEN;
3182 if (*ptr++ == SEQUENCE) {
3183 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3184 if (tmplen < 0)
3185 return GSS_S_DEFECTIVE_TOKEN;
3187 if (REMAIN < 1)
3188 tag = 0;
3189 else
3190 tag = *ptr++;
3192 if (tag == CONTEXT) {
3193 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3194 if (tmplen < 0)
3195 return GSS_S_DEFECTIVE_TOKEN;
3197 if (g_get_tag_and_length(&ptr, ENUMERATED,
3198 REMAIN, &len) < 0)
3199 return GSS_S_DEFECTIVE_TOKEN;
3201 if (len != ENUMERATION_LENGTH)
3202 return GSS_S_DEFECTIVE_TOKEN;
3204 if (REMAIN < 1)
3205 return GSS_S_DEFECTIVE_TOKEN;
3206 *negState = *ptr++;
3208 if (REMAIN < 1)
3209 tag = 0;
3210 else
3211 tag = *ptr++;
3213 if (tag == (CONTEXT | 0x01)) {
3214 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3215 if (tmplen < 0)
3216 return GSS_S_DEFECTIVE_TOKEN;
3218 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3219 if (*supportedMech == GSS_C_NO_OID)
3220 return GSS_S_DEFECTIVE_TOKEN;
3222 if (REMAIN < 1)
3223 tag = 0;
3224 else
3225 tag = *ptr++;
3227 if (tag == (CONTEXT | 0x02)) {
3228 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3229 if (tmplen < 0)
3230 return GSS_S_DEFECTIVE_TOKEN;
3232 *responseToken = get_input_token(&ptr, REMAIN);
3233 if (*responseToken == GSS_C_NO_BUFFER)
3234 return GSS_S_DEFECTIVE_TOKEN;
3236 if (REMAIN < 1)
3237 tag = 0;
3238 else
3239 tag = *ptr++;
3241 if (tag == (CONTEXT | 0x03)) {
3242 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3243 if (tmplen < 0)
3244 return GSS_S_DEFECTIVE_TOKEN;
3246 *mechListMIC = get_input_token(&ptr, REMAIN);
3247 if (*mechListMIC == GSS_C_NO_BUFFER)
3248 return GSS_S_DEFECTIVE_TOKEN;
3250 return GSS_S_COMPLETE;
3251 #undef REMAIN
3255 * der encode the passed negResults as an ENUMERATED type and
3256 * place it in buf_out, advancing the buffer.
3259 static int
3260 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3261 unsigned int buflen)
3263 if (buflen < 3)
3264 return (-1);
3265 *(*buf_out)++ = ENUMERATED;
3266 *(*buf_out)++ = ENUMERATION_LENGTH;
3267 *(*buf_out)++ = (unsigned char) negResult;
3268 return (0);
3272 * This routine compares the recieved mechset to the mechset that
3273 * this server can support. It looks sequentially through the mechset
3274 * and the first one that matches what the server can support is
3275 * chosen as the negotiated mechanism. If one is found, negResult
3276 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3277 * it's not the first mech, otherwise we return NULL and negResult
3278 * is set to REJECT.
3280 * NOTE: There is currently no way to specify a preference order of
3281 * mechanisms supported by the acceptor.
3283 static gss_OID
3284 negotiate_mech_type(OM_uint32 *minor_status,
3285 gss_OID_set supported_mechSet,
3286 gss_OID_set mechset,
3287 OM_uint32 *negResult)
3289 gss_OID returned_mech;
3290 OM_uint32 status;
3291 int present;
3292 unsigned int i;
3294 for (i = 0; i < mechset->count; i++) {
3295 gss_OID mech_oid = &mechset->elements[i];
3298 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3299 * we actually want to select it if the client supports, as this
3300 * will enable features on MS clients that allow credential
3301 * refresh on rekeying and caching system times from servers.
3303 #if 0
3304 /* Accept wrong mechanism OID from MS clients */
3305 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3306 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3307 mech_oid = (gss_OID)&gss_mech_krb5_oid;
3308 #endif
3310 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3311 if (!present)
3312 continue;
3314 if (i == 0)
3315 *negResult = ACCEPT_INCOMPLETE;
3316 else
3317 *negResult = REQUEST_MIC;
3319 status = generic_gss_copy_oid(minor_status,
3320 &mechset->elements[i],
3321 &returned_mech);
3322 if (status != GSS_S_COMPLETE) {
3323 *negResult = REJECT;
3324 map_errcode(minor_status);
3325 return (NULL);
3327 return (returned_mech);
3329 /* Solaris SPNEGO */
3330 *minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
3332 *negResult = REJECT;
3333 return (NULL);
3337 * the next two routines make a token buffer suitable for
3338 * spnego_gss_display_status. These currently take the string
3339 * in name and place it in the token. Eventually, if
3340 * spnego_gss_display_status returns valid error messages,
3341 * these routines will be changes to return the error string.
3343 static spnego_token_t
3344 make_spnego_token(char *name)
3346 return (spnego_token_t)strdup(name);
3349 static gss_buffer_desc
3350 make_err_msg(char *name)
3352 gss_buffer_desc buffer;
3354 if (name == NULL) {
3355 buffer.length = 0;
3356 buffer.value = NULL;
3357 } else {
3358 buffer.length = strlen(name)+1;
3359 buffer.value = make_spnego_token(name);
3362 return (buffer);
3366 * Create the client side spnego token passed back to gss_init_sec_context
3367 * and eventually up to the application program and over to the server.
3369 * Use DER rules, definite length method per RFC 2478
3371 static int
3372 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3373 int negHintsCompat,
3374 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3375 gss_buffer_t data, send_token_flag sendtoken,
3376 gss_buffer_t outbuf)
3378 int ret = 0;
3379 unsigned int tlen, dataLen = 0;
3380 unsigned int negTokenInitSize = 0;
3381 unsigned int negTokenInitSeqSize = 0;
3382 unsigned int negTokenInitContSize = 0;
3383 unsigned int rspTokenSize = 0;
3384 unsigned int mechListTokenSize = 0;
3385 unsigned int micTokenSize = 0;
3386 unsigned char *t;
3387 unsigned char *ptr;
3389 if (outbuf == GSS_C_NO_BUFFER)
3390 return (-1);
3392 outbuf->length = 0;
3393 outbuf->value = NULL;
3395 /* calculate the data length */
3398 * 0xa0 [DER LEN] [mechTypes]
3400 mechListTokenSize = 1 +
3401 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3402 spnego_ctx->DER_mechTypes.length;
3403 dataLen += mechListTokenSize;
3406 * If a token from gss_init_sec_context exists,
3407 * add the length of the token + the ASN.1 overhead
3409 if (data != NULL) {
3411 * Encoded in final output as:
3412 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3413 * -----s--------|--------s2----------
3415 rspTokenSize = 1 +
3416 gssint_der_length_size(data->length) +
3417 data->length;
3418 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3419 rspTokenSize;
3422 if (mechListMIC) {
3424 * Encoded in final output as:
3425 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3426 * --s-- -----tlen------------
3428 micTokenSize = 1 +
3429 gssint_der_length_size(mechListMIC->length) +
3430 mechListMIC->length;
3431 dataLen += 1 +
3432 gssint_der_length_size(micTokenSize) +
3433 micTokenSize;
3437 * Add size of DER encoding
3438 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3439 * 0x30 [DER_LEN] [data]
3442 negTokenInitContSize = dataLen;
3443 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3444 dataLen = negTokenInitSeqSize;
3447 * negTokenInitSize indicates the bytes needed to
3448 * hold the ASN.1 encoding of the entire NegTokenInit
3449 * SEQUENCE.
3450 * 0xa0 [DER_LEN] + data
3453 negTokenInitSize = 1 +
3454 gssint_der_length_size(negTokenInitSeqSize) +
3455 negTokenInitSeqSize;
3457 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3459 t = (unsigned char *) malloc(tlen);
3461 if (t == NULL) {
3462 return (-1);
3465 ptr = t;
3467 /* create the message */
3468 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3469 &ptr, tlen)))
3470 goto errout;
3472 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3473 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3474 goto errout;
3476 *ptr++ = SEQUENCE;
3477 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3478 tlen - (int)(ptr-t))))
3479 goto errout;
3481 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3482 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3483 &ptr, tlen - (int)(ptr-t))))
3484 goto errout;
3486 /* We already encoded the MechSetList */
3487 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3488 spnego_ctx->DER_mechTypes.length);
3490 ptr += spnego_ctx->DER_mechTypes.length;
3492 if (data != NULL) {
3493 *ptr++ = CONTEXT | 0x02;
3494 if ((ret = gssint_put_der_length(rspTokenSize,
3495 &ptr, tlen - (int)(ptr - t))))
3496 goto errout;
3498 if ((ret = put_input_token(&ptr, data,
3499 tlen - (int)(ptr - t))))
3500 goto errout;
3503 if (mechListMIC != GSS_C_NO_BUFFER) {
3504 *ptr++ = CONTEXT | 0x03;
3505 if ((ret = gssint_put_der_length(micTokenSize,
3506 &ptr, tlen - (int)(ptr - t))))
3507 goto errout;
3509 if (negHintsCompat) {
3510 ret = put_neg_hints(&ptr, mechListMIC,
3511 tlen - (int)(ptr - t));
3512 if (ret)
3513 goto errout;
3514 } else if ((ret = put_input_token(&ptr, mechListMIC,
3515 tlen - (int)(ptr - t))))
3516 goto errout;
3519 errout:
3520 if (ret != 0) {
3521 if (t)
3522 free(t);
3523 t = NULL;
3524 tlen = 0;
3526 outbuf->length = tlen;
3527 outbuf->value = (void *) t;
3529 return (ret);
3533 * create the server side spnego token passed back to
3534 * gss_accept_sec_context and eventually up to the application program
3535 * and over to the client.
3537 static int
3538 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3539 gss_buffer_t data, gss_buffer_t mechListMIC,
3540 send_token_flag sendtoken,
3541 gss_buffer_t outbuf)
3543 unsigned int tlen = 0;
3544 unsigned int ret = 0;
3545 unsigned int NegTokenTargSize = 0;
3546 unsigned int NegTokenSize = 0;
3547 unsigned int rspTokenSize = 0;
3548 unsigned int micTokenSize = 0;
3549 unsigned int dataLen = 0;
3550 unsigned char *t;
3551 unsigned char *ptr;
3553 if (outbuf == GSS_C_NO_BUFFER)
3554 return (GSS_S_DEFECTIVE_TOKEN);
3556 outbuf->length = 0;
3557 outbuf->value = NULL;
3560 * ASN.1 encoding of the negResult
3561 * ENUMERATED type is 3 bytes
3562 * ENUMERATED TAG, Length, Value,
3563 * Plus 2 bytes for the CONTEXT id and length.
3565 dataLen = 5;
3568 * calculate data length
3570 * If this is the initial token, include length of
3571 * mech_type and the negotiation result fields.
3573 if (sendtoken == INIT_TOKEN_SEND) {
3574 int mechlistTokenSize;
3576 * 1 byte for the CONTEXT ID(0xa0),
3577 * 1 byte for the OID ID(0x06)
3578 * 1 byte for OID Length field
3579 * Plus the rest... (OID Length, OID value)
3581 mechlistTokenSize = 3 + mech_wanted->length +
3582 gssint_der_length_size(mech_wanted->length);
3584 dataLen += mechlistTokenSize;
3586 if (data != NULL && data->length > 0) {
3587 /* Length of the inner token */
3588 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3589 data->length;
3591 dataLen += rspTokenSize;
3593 /* Length of the outer token */
3594 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3596 if (mechListMIC != NULL) {
3598 /* Length of the inner token */
3599 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3600 mechListMIC->length;
3602 dataLen += micTokenSize;
3604 /* Length of the outer token */
3605 dataLen += 1 + gssint_der_length_size(micTokenSize);
3608 * Add size of DER encoded:
3609 * NegTokenTarg [ SEQUENCE ] of
3610 * NegResult[0] ENUMERATED {
3611 * accept_completed(0),
3612 * accept_incomplete(1),
3613 * reject(2) }
3614 * supportedMech [1] MechType OPTIONAL,
3615 * responseToken [2] OCTET STRING OPTIONAL,
3616 * mechListMIC [3] OCTET STRING OPTIONAL
3618 * size = data->length + MechListMic + SupportedMech len +
3619 * Result Length + ASN.1 overhead
3621 NegTokenTargSize = dataLen;
3622 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3625 * NegotiationToken [ CHOICE ]{
3626 * negTokenInit [0] NegTokenInit,
3627 * negTokenTarg [1] NegTokenTarg }
3629 NegTokenSize = dataLen;
3630 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3632 tlen = dataLen;
3633 t = (unsigned char *) malloc(tlen);
3635 if (t == NULL) {
3636 ret = GSS_S_DEFECTIVE_TOKEN;
3637 goto errout;
3640 ptr = t;
3643 * Indicate that we are sending CHOICE 1
3644 * (NegTokenTarg)
3646 *ptr++ = CONTEXT | 0x01;
3647 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3648 ret = GSS_S_DEFECTIVE_TOKEN;
3649 goto errout;
3651 *ptr++ = SEQUENCE;
3652 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3653 tlen - (int)(ptr-t)) < 0) {
3654 ret = GSS_S_DEFECTIVE_TOKEN;
3655 goto errout;
3659 * First field of the NegTokenTarg SEQUENCE
3660 * is the ENUMERATED NegResult.
3662 *ptr++ = CONTEXT;
3663 if (gssint_put_der_length(3, &ptr,
3664 tlen - (int)(ptr-t)) < 0) {
3665 ret = GSS_S_DEFECTIVE_TOKEN;
3666 goto errout;
3668 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3669 ret = GSS_S_DEFECTIVE_TOKEN;
3670 goto errout;
3672 if (sendtoken == INIT_TOKEN_SEND) {
3674 * Next, is the Supported MechType
3676 *ptr++ = CONTEXT | 0x01;
3677 if (gssint_put_der_length(mech_wanted->length + 2,
3678 &ptr,
3679 tlen - (int)(ptr - t)) < 0) {
3680 ret = GSS_S_DEFECTIVE_TOKEN;
3681 goto errout;
3683 if (put_mech_oid(&ptr, mech_wanted,
3684 tlen - (int)(ptr - t)) < 0) {
3685 ret = GSS_S_DEFECTIVE_TOKEN;
3686 goto errout;
3689 if (data != NULL && data->length > 0) {
3690 *ptr++ = CONTEXT | 0x02;
3691 if (gssint_put_der_length(rspTokenSize, &ptr,
3692 tlen - (int)(ptr - t)) < 0) {
3693 ret = GSS_S_DEFECTIVE_TOKEN;
3694 goto errout;
3696 if (put_input_token(&ptr, data,
3697 tlen - (int)(ptr - t)) < 0) {
3698 ret = GSS_S_DEFECTIVE_TOKEN;
3699 goto errout;
3702 if (mechListMIC != NULL) {
3703 *ptr++ = CONTEXT | 0x03;
3704 if (gssint_put_der_length(micTokenSize, &ptr,
3705 tlen - (int)(ptr - t)) < 0) {
3706 ret = GSS_S_DEFECTIVE_TOKEN;
3707 goto errout;
3709 if (put_input_token(&ptr, mechListMIC,
3710 tlen - (int)(ptr - t)) < 0) {
3711 ret = GSS_S_DEFECTIVE_TOKEN;
3712 goto errout;
3715 ret = GSS_S_COMPLETE;
3716 errout:
3717 if (ret != GSS_S_COMPLETE) {
3718 if (t)
3719 free(t);
3720 } else {
3721 outbuf->length = ptr - t;
3722 outbuf->value = (void *) t;
3725 return (ret);
3728 /* determine size of token */
3729 static int
3730 g_token_size(gss_OID_const mech, unsigned int body_size)
3732 int hdrsize;
3735 * Initialize the header size to the
3736 * MECH_OID byte + the bytes needed to indicate the
3737 * length of the OID + the OID itself.
3739 * 0x06 [MECHLENFIELD] MECHDATA
3741 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3744 * Now add the bytes needed for the initial header
3745 * token bytes:
3746 * 0x60 + [DER_LEN] + HDRSIZE
3748 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3750 return (hdrsize + body_size);
3754 * generate token header.
3756 * Use DER Definite Length method per RFC2478
3757 * Use of indefinite length encoding will not be compatible
3758 * with Microsoft or others that actually follow the spec.
3760 static int
3761 g_make_token_header(gss_OID_const mech,
3762 unsigned int body_size,
3763 unsigned char **buf,
3764 unsigned int totallen)
3766 int ret = 0;
3767 unsigned int hdrsize;
3768 unsigned char *p = *buf;
3770 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3772 *(*buf)++ = HEADER_ID;
3773 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3774 return (ret);
3776 *(*buf)++ = MECH_OID;
3777 if ((ret = gssint_put_der_length(mech->length, buf,
3778 totallen - (int)(p - *buf))))
3779 return (ret);
3780 TWRITE_STR(*buf, mech->elements, mech->length);
3781 return (0);
3785 * NOTE: This checks that the length returned by
3786 * gssint_get_der_length() is not greater than the number of octets
3787 * remaining, even though gssint_get_der_length() already checks, in
3788 * theory.
3790 static int
3791 g_get_tag_and_length(unsigned char **buf, int tag,
3792 unsigned int buflen, unsigned int *outlen)
3794 unsigned char *ptr = *buf;
3795 int ret = -1; /* pessimists, assume failure ! */
3796 unsigned int encoded_len;
3797 unsigned int tmplen = 0;
3799 *outlen = 0;
3800 if (buflen > 1 && *ptr == tag) {
3801 ptr++;
3802 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3803 &encoded_len);
3804 if (tmplen < 0) {
3805 ret = -1;
3806 } else if (tmplen > buflen - (ptr - *buf)) {
3807 ret = -1;
3808 } else
3809 ret = 0;
3811 *outlen = tmplen;
3812 *buf = ptr;
3813 return (ret);
3816 static int
3817 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3819 unsigned char *buf = *buf_in;
3820 unsigned char *endptr = buf + cur_size;
3821 unsigned int seqsize;
3822 int ret = 0;
3823 unsigned int bytes;
3826 * Verify this is a NegotiationToken type token
3827 * - check for a0(context specific identifier)
3828 * - get length and verify that enoughd ata exists
3830 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3831 return (G_BAD_TOK_HEADER);
3833 cur_size = seqsize; /* should indicate bytes remaining */
3836 * Verify the next piece, it should identify this as
3837 * a strucure of type NegTokenInit.
3839 if (*buf++ == SEQUENCE) {
3840 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3841 return (G_BAD_TOK_HEADER);
3843 * Make sure we have the entire buffer as described
3845 if (buf + seqsize > endptr)
3846 return (G_BAD_TOK_HEADER);
3847 } else {
3848 return (G_BAD_TOK_HEADER);
3851 cur_size = seqsize; /* should indicate bytes remaining */
3854 * Verify that the first blob is a sequence of mechTypes
3856 if (*buf++ == CONTEXT) {
3857 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3858 return (G_BAD_TOK_HEADER);
3860 * Make sure we have the entire buffer as described
3862 if (buf + bytes > endptr)
3863 return (G_BAD_TOK_HEADER);
3864 } else {
3865 return (G_BAD_TOK_HEADER);
3869 * At this point, *buf should be at the beginning of the
3870 * DER encoded list of mech types that are to be negotiated.
3872 *buf_in = buf;
3874 return (ret);
3878 /* verify token header. */
3879 static int
3880 g_verify_token_header(gss_OID_const mech,
3881 unsigned int *body_size,
3882 unsigned char **buf_in,
3883 int tok_type,
3884 unsigned int toksize)
3886 unsigned char *buf = *buf_in;
3887 int seqsize;
3888 gss_OID_desc toid;
3889 int ret = 0;
3890 unsigned int bytes;
3892 if (toksize-- < 1)
3893 return (G_BAD_TOK_HEADER);
3895 if (*buf++ != HEADER_ID)
3896 return (G_BAD_TOK_HEADER);
3898 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3899 return (G_BAD_TOK_HEADER);
3901 if ((seqsize + bytes) != toksize)
3902 return (G_BAD_TOK_HEADER);
3904 if (toksize-- < 1)
3905 return (G_BAD_TOK_HEADER);
3908 if (*buf++ != MECH_OID)
3909 return (G_BAD_TOK_HEADER);
3911 if (toksize-- < 1)
3912 return (G_BAD_TOK_HEADER);
3914 toid.length = *buf++;
3916 if (toksize < toid.length)
3917 return (G_BAD_TOK_HEADER);
3918 else
3919 toksize -= toid.length;
3921 toid.elements = buf;
3922 buf += toid.length;
3924 if (!g_OID_equal(&toid, mech))
3925 ret = G_WRONG_MECH;
3928 * G_WRONG_MECH is not returned immediately because it's more important
3929 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3931 if (toksize < 2)
3932 return (G_BAD_TOK_HEADER);
3933 else
3934 toksize -= 2;
3936 if (!ret) {
3937 *buf_in = buf;
3938 *body_size = toksize;
3941 return (ret);
3945 * Return non-zero if the oid is one of the kerberos mech oids,
3946 * otherwise return zero.
3948 * N.B. There are 3 oids that represent the kerberos mech:
3949 * RFC-specified GSS_MECH_KRB5_OID,
3950 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3951 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3954 static int
3955 is_kerb_mech(gss_OID oid)
3957 int answer = 0;
3958 OM_uint32 minor;
3959 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3961 (void) gss_test_oid_set_member(&minor,
3962 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3964 return (answer);