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.
7 *
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.
12 *
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.
26 *
27 */
28
29 /*
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.
33 *
34 */
35
36 /*
37 * Copyright (c) 2006-2008, Novell, Inc.
38 * All rights reserved.
39 *
40 * Redistribution and use in source and binary forms, with or without
41 * modification, are permitted provided that the following conditions are met:
42 *
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.
50 *
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.
62 */
63 /* #pragma ident "@(#)spnego_mech.c 1.7 04/09/28 SMI" */
64
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>
78
79 /*
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.
84 */
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
92
93
94 #undef g_token_size
95 #undef g_verify_token_header
96 #undef g_make_token_header
97
98 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED)
99 typedef const gss_OID_desc *gss_OID_const;
100
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);
105
106
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);
130
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 *);
137
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 *);
158
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 *);
175
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 *);
181
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);
192
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 *);
200
201 static int
202 is_kerb_mech(gss_OID oid);
203
204 /* SPNEGO oid structure */
205 static const gss_OID_desc spnego_oids[] = {
206 {SPNEGO_OID_LENGTH, SPNEGO_OID},
207 };
208
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},
212 };
213 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0;
214
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 *);
220
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 */
227
228 /*
229 * The Mech OID for SPNEGO:
230 * { iso(1) org(3) dod(6) internet(1) security(5)
231 * mechanism(5) spnego(2) }
232 */
233 static struct gss_config spnego_mechanism =
234 {
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 /* EXPORT DELETE START */ /* CRYPT DELETE START */
246 NULL, /* unseal */
247 /* EXPORT DELETE END */ /* CRYPT DELETE END */
248 NULL, /* gss_process_context_token */
249 glue_spnego_gss_delete_sec_context, /* gss_delete_sec_context */
250 glue_spnego_gss_context_time,
251 glue_spnego_gss_display_status,
252 NULL, /* gss_indicate_mechs */
253 glue_spnego_gss_compare_name,
254 glue_spnego_gss_display_name,
255 glue_spnego_gss_import_name, /* glue */
256 glue_spnego_gss_release_name,
257 NULL, /* gss_inquire_cred */
258 NULL, /* gss_add_cred */
259 /* EXPORT DELETE START */ /* CRYPT DELETE START */
260 NULL, /* seal */
261 /* EXPORT DELETE END */ /* CRYPT DELETE END */
262 #ifndef LEAN_CLIENT
263 glue_spnego_gss_export_sec_context, /* gss_export_sec_context */
264 glue_spnego_gss_import_sec_context, /* gss_import_sec_context */
265 #else
266 NULL, /* gss_export_sec_context */
267 NULL, /* gss_import_sec_context */
268 #endif /* LEAN_CLIENT */
269 NULL, /* gss_inquire_cred_by_mech */
270 glue_spnego_gss_inquire_names_for_mech,
271 glue_spnego_gss_inquire_context,
272 NULL, /* gss_internal_release_oid */
273 glue_spnego_gss_wrap_size_limit,
274 NULL, /* pname */
275 NULL, /* userok */
276 NULL, /* gss_export_name */
277 /* EXPORT DELETE START */
278 /* CRYPT DELETE START */
279 #if 0
280 /* CRYPT DELETE END */
281 NULL, /* seal */
282 NULL, /* unseal */
283 /* CRYPT DELETE START */
284 #endif
285 /* CRYPT DELETE END */
286 /* EXPORT DELETE END */
287 NULL, /* sign */
288 NULL, /* verify */
289 NULL, /* gss_store_cred */
290 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */
291 };
292
293 #ifdef _GSS_STATIC_LINK
294 #include "mglueP.h"
295
296 static
gss_spnegomechglue_init(void)297 int gss_spnegomechglue_init(void)
298 {
299 struct gss_mech_config mech_spnego;
300
301 memset(&mech_spnego, 0, sizeof(mech_spnego));
302 mech_spnego.mech = &spnego_mechanism;
303 mech_spnego.mechNameStr = "spnego";
304 mech_spnego.mech_type = GSS_C_NO_OID;
305
306 return gssint_register_mechinfo(&mech_spnego);
307 }
308 #else
309 /* Entry point for libgss */
310 gss_mechanism KRB5_CALLCONV
gss_mech_initialize(void)311 gss_mech_initialize(void)
312 {
313 int err;
314
315 err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
316 spnego_gss_delete_error_info);
317 if (err) {
318 syslog(LOG_NOTICE,
319 "SPNEGO gss_mech_initialize: error message TSD key register fail");
320 return (NULL);
321 }
322
323 return (&spnego_mechanism);
324 }
325
326 #if 0 /* SUNW17PACresync */
327 MAKE_INIT_FUNCTION(gss_krb5int_lib_init);
328 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini);
329 int gss_krb5int_lib_init(void)
330 #endif
331
332 #endif /* _GSS_STATIC_LINK */
333
334 static
gss_spnegoint_lib_init(void)335 int gss_spnegoint_lib_init(void)
336 {
337 #ifdef _GSS_STATIC_LINK
338 return gss_spnegomechglue_init();
339 #else
340 int err;
341
342 err = k5_key_register(K5_KEY_GSS_SPNEGO_ERROR_MESSAGE,
343 spnego_gss_delete_error_info);
344 if (err) {
345 syslog(LOG_NOTICE,
346 "SPNEGO gss_mech_initialize: error message TSD key register fail: err=%d",
347 err);
348 return err;
349 }
350
351 return 0;
352 #endif
353 }
354
gss_spnegoint_lib_fini(void)355 static void gss_spnegoint_lib_fini(void)
356 {
357 }
358
359 /*ARGSUSED*/
360 OM_uint32
glue_spnego_gss_acquire_cred(void * context,OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)361 glue_spnego_gss_acquire_cred(
362 void *context,
363 OM_uint32 *minor_status,
364 gss_name_t desired_name,
365 OM_uint32 time_req,
366 gss_OID_set desired_mechs,
367 gss_cred_usage_t cred_usage,
368 gss_cred_id_t *output_cred_handle,
369 gss_OID_set *actual_mechs,
370 OM_uint32 *time_rec)
371 {
372 return(spnego_gss_acquire_cred(minor_status,
373 desired_name,
374 time_req,
375 desired_mechs,
376 cred_usage,
377 output_cred_handle,
378 actual_mechs,
379 time_rec));
380 }
381
382 /*ARGSUSED*/
383 OM_uint32
spnego_gss_acquire_cred(OM_uint32 * minor_status,gss_name_t desired_name,OM_uint32 time_req,gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)384 spnego_gss_acquire_cred(OM_uint32 *minor_status,
385 gss_name_t desired_name,
386 OM_uint32 time_req,
387 gss_OID_set desired_mechs,
388 gss_cred_usage_t cred_usage,
389 gss_cred_id_t *output_cred_handle,
390 gss_OID_set *actual_mechs,
391 OM_uint32 *time_rec)
392 {
393 OM_uint32 status;
394 gss_OID_set amechs;
395 dsyslog("Entering spnego_gss_acquire_cred\n");
396
397 if (actual_mechs)
398 *actual_mechs = NULL;
399
400 if (time_rec)
401 *time_rec = 0;
402
403 /*
404 * If the user did not specify a list of mechs,
405 * use get_available_mechs to collect a list of
406 * mechs for which creds are available.
407 */
408 if (desired_mechs == GSS_C_NULL_OID_SET) {
409 status = get_available_mechs(minor_status,
410 desired_name, cred_usage,
411 output_cred_handle, &amechs);
412 } else {
413 /*
414 * The caller gave a specific list of mechanisms,
415 * so just get whatever creds are available.
416 * gss_acquire_creds will return the subset of mechs for
417 * which the given 'output_cred_handle' is valid.
418 */
419 status = gss_acquire_cred(minor_status,
420 desired_name, time_req,
421 desired_mechs, cred_usage,
422 output_cred_handle, &amechs,
423 time_rec);
424 }
425
426 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
427 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs);
428 }
429 (void) gss_release_oid_set(minor_status, &amechs);
430
431 dsyslog("Leaving spnego_gss_acquire_cred\n");
432 return (status);
433 }
434
435 /*ARGSUSED*/
436 OM_uint32
glue_spnego_gss_release_cred(void * context,OM_uint32 * minor_status,gss_cred_id_t * cred_handle)437 glue_spnego_gss_release_cred(void *context,
438 OM_uint32 *minor_status,
439 gss_cred_id_t *cred_handle)
440 {
441 return( spnego_gss_release_cred(minor_status, cred_handle));
442 }
443
444 /*ARGSUSED*/
445 OM_uint32
spnego_gss_release_cred(OM_uint32 * minor_status,gss_cred_id_t * cred_handle)446 spnego_gss_release_cred(OM_uint32 *minor_status,
447 gss_cred_id_t *cred_handle)
448 {
449 OM_uint32 status;
450
451 dsyslog("Entering spnego_gss_release_cred\n");
452
453 if (minor_status == NULL || cred_handle == NULL)
454 return (GSS_S_CALL_INACCESSIBLE_WRITE);
455
456 *minor_status = 0;
457
458 if (*cred_handle == GSS_C_NO_CREDENTIAL)
459 return (GSS_S_COMPLETE);
460
461 status = gss_release_cred(minor_status, cred_handle);
462
463 dsyslog("Leaving spnego_gss_release_cred\n");
464 return (status);
465 }
466
467 static void
check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)468 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
469 {
470 spnego_ctx->optionStr = gssint_get_modOptions(
471 (const gss_OID)&spnego_oids[0]);
472 }
473
474 static spnego_gss_ctx_id_t
create_spnego_ctx(void)475 create_spnego_ctx(void)
476 {
477 spnego_gss_ctx_id_t spnego_ctx = NULL;
478 spnego_ctx = (spnego_gss_ctx_id_t)
479 malloc(sizeof (spnego_gss_ctx_id_rec));
480
481 if (spnego_ctx == NULL) {
482 return (NULL);
483 }
484
485 spnego_ctx->magic_num = SPNEGO_MAGIC_ID;
486 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
487 spnego_ctx->internal_mech = NULL;
488 spnego_ctx->optionStr = NULL;
489 spnego_ctx->DER_mechTypes.length = 0;
490 spnego_ctx->DER_mechTypes.value = NULL;
491 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL;
492 spnego_ctx->mic_reqd = 0;
493 spnego_ctx->mic_sent = 0;
494 spnego_ctx->mic_rcvd = 0;
495 spnego_ctx->mech_complete = 0;
496 spnego_ctx->nego_done = 0;
497 spnego_ctx->internal_name = GSS_C_NO_NAME;
498 spnego_ctx->actual_mech = GSS_C_NO_OID;
499 spnego_ctx->err.msg = NULL;
500 spnego_ctx->err.scratch_buf[0] = 0;
501 check_spnego_options(spnego_ctx);
502
503 return (spnego_ctx);
504 }
505
506 /*
507 * Both initiator and acceptor call here to verify and/or create
508 * mechListMIC, and to consistency-check the MIC state.
509 */
510 static OM_uint32
handle_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,int send_mechtok,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)511 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
512 int send_mechtok, spnego_gss_ctx_id_t sc,
513 gss_buffer_t *mic_out,
514 OM_uint32 *negState, send_token_flag *tokflag)
515 {
516 OM_uint32 ret;
517
518 ret = GSS_S_FAILURE;
519 *mic_out = GSS_C_NO_BUFFER;
520 if (mic_in != GSS_C_NO_BUFFER) {
521 if (sc->mic_rcvd) {
522 /* Reject MIC if we've already received a MIC. */
523 *negState = REJECT;
524 *tokflag = ERROR_TOKEN_SEND;
525 return GSS_S_DEFECTIVE_TOKEN;
526 }
527 } else if (sc->mic_reqd && !send_mechtok) {
528 /*
529 * If the peer sends the final mechanism token, it
530 * must send the MIC with that token if the
531 * negotiation requires MICs.
532 */
533 *negState = REJECT;
534 *tokflag = ERROR_TOKEN_SEND;
535 return GSS_S_DEFECTIVE_TOKEN;
536 }
537 ret = process_mic(minor_status, mic_in, sc, mic_out,
538 negState, tokflag);
539 if (ret != GSS_S_COMPLETE) {
540 return ret;
541 }
542 if (sc->mic_reqd) {
543 assert(sc->mic_sent || sc->mic_rcvd);
544 }
545 if (sc->mic_sent && sc->mic_rcvd) {
546 ret = GSS_S_COMPLETE;
547 *negState = ACCEPT_COMPLETE;
548 if (*mic_out == GSS_C_NO_BUFFER) {
549 /*
550 * We sent a MIC on the previous pass; we
551 * shouldn't be sending a mechanism token.
552 */
553 assert(!send_mechtok);
554 *tokflag = NO_TOKEN_SEND;
555 } else {
556 *tokflag = CONT_TOKEN_SEND;
557 }
558 } else if (sc->mic_reqd) {
559 *negState = ACCEPT_INCOMPLETE;
560 ret = GSS_S_CONTINUE_NEEDED;
561 } else if (*negState == ACCEPT_COMPLETE) {
562 ret = GSS_S_COMPLETE;
563 } else {
564 ret = GSS_S_CONTINUE_NEEDED;
565 }
566 return ret;
567 }
568
569 /*
570 * Perform the actual verification and/or generation of mechListMIC.
571 */
572 static OM_uint32
process_mic(OM_uint32 * minor_status,gss_buffer_t mic_in,spnego_gss_ctx_id_t sc,gss_buffer_t * mic_out,OM_uint32 * negState,send_token_flag * tokflag)573 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in,
574 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out,
575 OM_uint32 *negState, send_token_flag *tokflag)
576 {
577 OM_uint32 ret, tmpmin;
578 gss_qop_t qop_state;
579 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER;
580
581 ret = GSS_S_FAILURE;
582 if (mic_in != GSS_C_NO_BUFFER) {
583 ret = gss_verify_mic(minor_status, sc->ctx_handle,
584 &sc->DER_mechTypes,
585 mic_in, &qop_state);
586 if (ret != GSS_S_COMPLETE) {
587 *negState = REJECT;
588 *tokflag = ERROR_TOKEN_SEND;
589 return ret;
590 }
591 /* If we got a MIC, we must send a MIC. */
592 sc->mic_reqd = 1;
593 sc->mic_rcvd = 1;
594 }
595 if (sc->mic_reqd && !sc->mic_sent) {
596 ret = gss_get_mic(minor_status, sc->ctx_handle,
597 GSS_C_QOP_DEFAULT,
598 &sc->DER_mechTypes,
599 &tmpmic);
600 if (ret != GSS_S_COMPLETE) {
601 gss_release_buffer(&tmpmin, &tmpmic);
602 *tokflag = NO_TOKEN_SEND;
603 return ret;
604 }
605 *mic_out = malloc(sizeof(gss_buffer_desc));
606 if (*mic_out == GSS_C_NO_BUFFER) {
607 gss_release_buffer(&tmpmin, &tmpmic);
608 *tokflag = NO_TOKEN_SEND;
609 return GSS_S_FAILURE;
610 }
611 **mic_out = tmpmic;
612 sc->mic_sent = 1;
613 }
614 return GSS_S_COMPLETE;
615 }
616
617 /*
618 * Initial call to spnego_gss_init_sec_context().
619 */
620 static OM_uint32
init_ctx_new(OM_uint32 * minor_status,gss_cred_id_t cred,gss_ctx_id_t * ctx,gss_OID_set * mechSet,send_token_flag * tokflag)621 init_ctx_new(OM_uint32 *minor_status,
622 gss_cred_id_t cred,
623 gss_ctx_id_t *ctx,
624 gss_OID_set *mechSet,
625 send_token_flag *tokflag)
626 {
627 OM_uint32 ret, tmpmin;
628 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL;
629 spnego_gss_ctx_id_t sc = NULL;
630
631 /* determine negotiation mech set */
632 if (cred == GSS_C_NO_CREDENTIAL) {
633 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
634 GSS_C_INITIATE, &creds, mechSet);
635 gss_release_cred(&tmpmin, &creds);
636 } else {
637 /*
638 * Use the list of mechs included in the cred that we
639 * were given.
640 */
641 ret = gss_inquire_cred(minor_status, cred,
642 NULL, NULL, NULL, mechSet);
643 }
644 if (ret != GSS_S_COMPLETE)
645 return ret;
646
647 sc = create_spnego_ctx();
648 if (sc == NULL)
649 return GSS_S_FAILURE;
650
651 /*
652 * need to pull the first mech from mechSet to do first
653 * gss_init_sec_context()
654 */
655 ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements,
656 &sc->internal_mech);
657 if (ret != GSS_S_COMPLETE) {
658 map_errcode(minor_status);
659 goto cleanup;
660 }
661
662 if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) {
663 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
664 ret = GSS_S_FAILURE;
665 goto cleanup;
666 }
667 /*
668 * The actual context is not yet determined, set the output
669 * context handle to refer to the spnego context itself.
670 */
671 sc->ctx_handle = GSS_C_NO_CONTEXT;
672 *ctx = (gss_ctx_id_t)sc;
673 *tokflag = INIT_TOKEN_SEND;
674 ret = GSS_S_CONTINUE_NEEDED;
675
676 cleanup:
677 gss_release_oid_set(&tmpmin, mechSet);
678 return ret;
679 }
680
681 /*
682 * Called by second and later calls to spnego_gss_init_sec_context()
683 * to decode reply and update state.
684 */
685 static OM_uint32
init_ctx_cont(OM_uint32 * minor_status,gss_ctx_id_t * ctx,gss_buffer_t buf,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * tokflag)686 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf,
687 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
688 OM_uint32 *negState, send_token_flag *tokflag)
689 {
690 OM_uint32 ret, tmpmin, acc_negState;
691 unsigned char *ptr;
692 spnego_gss_ctx_id_t sc;
693 gss_OID supportedMech = GSS_C_NO_OID;
694
695 sc = (spnego_gss_ctx_id_t)*ctx;
696 *negState = REJECT;
697 *tokflag = ERROR_TOKEN_SEND;
698
699 ptr = buf->value;
700 ret = get_negTokenResp(minor_status, ptr, buf->length,
701 &acc_negState, &supportedMech,
702 responseToken, mechListMIC);
703 if (ret != GSS_S_COMPLETE)
704 goto cleanup;
705 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN &&
706 supportedMech == GSS_C_NO_OID &&
707 *responseToken == GSS_C_NO_BUFFER &&
708 *mechListMIC == GSS_C_NO_BUFFER) {
709 /* Reject "empty" token. */
710 ret = GSS_S_DEFECTIVE_TOKEN;
711 }
712 if (acc_negState == REJECT) {
713 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
714 /* Solaris SPNEGO */
715 spnego_set_error_message(sc, *minor_status,
716 dgettext(TEXT_DOMAIN,
717 "SPNEGO failed to negotiate a mechanism: server rejected request"));
718 map_errcode(minor_status);
719 *tokflag = NO_TOKEN_SEND;
720 ret = GSS_S_FAILURE;
721 goto cleanup;
722 }
723 /*
724 * nego_done is false for the first call to init_ctx_cont()
725 */
726 if (!sc->nego_done) {
727 ret = init_ctx_nego(minor_status, sc,
728 acc_negState,
729 supportedMech, responseToken,
730 mechListMIC,
731 negState, tokflag);
732 } else if (!sc->mech_complete &&
733 *responseToken == GSS_C_NO_BUFFER) {
734 /*
735 * mech not finished and mech token missing
736 */
737 ret = GSS_S_DEFECTIVE_TOKEN;
738 } else if (sc->mic_reqd &&
739 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
740 *negState = ACCEPT_INCOMPLETE;
741 *tokflag = CONT_TOKEN_SEND;
742 ret = GSS_S_CONTINUE_NEEDED;
743 } else {
744 *negState = ACCEPT_COMPLETE;
745 *tokflag = NO_TOKEN_SEND;
746 ret = GSS_S_COMPLETE;
747 }
748 cleanup:
749 if (supportedMech != GSS_C_NO_OID)
750 generic_gss_release_oid(&tmpmin, &supportedMech);
751 return ret;
752 }
753
754 /*
755 * Consistency checking and mechanism negotiation handling for second
756 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to
757 * update internal state if acceptor has counter-proposed.
758 */
759 static OM_uint32
init_ctx_nego(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * tokflag)760 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
761 OM_uint32 acc_negState, gss_OID supportedMech,
762 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
763 OM_uint32 *negState, send_token_flag *tokflag)
764 {
765 OM_uint32 ret;
766
767 *negState = REJECT;
768 *tokflag = ERROR_TOKEN_SEND;
769 ret = GSS_S_DEFECTIVE_TOKEN;
770 /*
771 * Both supportedMech and negState must be present in first
772 * acceptor token.
773 */
774 if (supportedMech == GSS_C_NO_OID) {
775 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
776 map_errcode(minor_status);
777 return GSS_S_DEFECTIVE_TOKEN;
778 }
779 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) {
780 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
781 /* Solaris SPNEGO */
782 spnego_set_error_message(sc, *minor_status,
783 dgettext(TEXT_DOMAIN,
784 "SPNEGO failed to negotiate a mechanism: defective token"));
785 map_errcode(minor_status);
786 return GSS_S_DEFECTIVE_TOKEN;
787 }
788
789 /*
790 * If the mechanism we sent is not the mechanism returned from
791 * the server, we need to handle the server's counter
792 * proposal. There is a bug in SAMBA servers that always send
793 * the old Kerberos mech OID, even though we sent the new one.
794 * So we will treat all the Kerberos mech OIDS as the same.
795 */
796 if (!(is_kerb_mech(supportedMech) &&
797 is_kerb_mech(sc->internal_mech)) &&
798 !g_OID_equal(supportedMech, sc->internal_mech)) {
799 ret = init_ctx_reselect(minor_status, sc,
800 acc_negState, supportedMech,
801 responseToken, mechListMIC,
802 negState, tokflag);
803
804 } else if (*responseToken == GSS_C_NO_BUFFER) {
805 if (sc->mech_complete) {
806 /*
807 * Mech completed on first call to its
808 * init_sec_context(). Acceptor sends no mech
809 * token.
810 */
811 *negState = ACCEPT_COMPLETE;
812 *tokflag = NO_TOKEN_SEND;
813 ret = GSS_S_COMPLETE;
814 } else {
815 /*
816 * Reject missing mech token when optimistic
817 * mech selected.
818 */
819 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
820 map_errcode(minor_status);
821 ret = GSS_S_DEFECTIVE_TOKEN;
822 }
823 } else if (sc->mech_complete) {
824 /* Reject spurious mech token. */
825 ret = GSS_S_DEFECTIVE_TOKEN;
826 } else {
827 *negState = ACCEPT_INCOMPLETE;
828 *tokflag = CONT_TOKEN_SEND;
829 ret = GSS_S_CONTINUE_NEEDED;
830 }
831 sc->nego_done = 1;
832 return ret;
833 }
834
835 /*
836 * Handle acceptor's counter-proposal of an alternative mechanism.
837 */
838 static OM_uint32
init_ctx_reselect(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,OM_uint32 acc_negState,gss_OID supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * tokflag)839 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
840 OM_uint32 acc_negState, gss_OID supportedMech,
841 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC,
842 OM_uint32 *negState, send_token_flag *tokflag)
843 {
844 OM_uint32 ret, tmpmin;
845
846 generic_gss_release_oid(&tmpmin, &sc->internal_mech);
847 gss_delete_sec_context(&tmpmin, &sc->ctx_handle,
848 GSS_C_NO_BUFFER);
849
850 ret = generic_gss_copy_oid(minor_status, supportedMech,
851 &sc->internal_mech);
852 if (ret != GSS_S_COMPLETE) {
853 map_errcode(minor_status);
854 sc->internal_mech = GSS_C_NO_OID;
855 *tokflag = NO_TOKEN_SEND;
856 return ret;
857 }
858 if (*responseToken != GSS_C_NO_BUFFER) {
859 /* Reject spurious mech token. */
860 return GSS_S_DEFECTIVE_TOKEN;
861 }
862 /*
863 * Windows 2003 and earlier don't correctly send a
864 * negState of request-mic when counter-proposing a
865 * mechanism. They probably don't handle mechListMICs
866 * properly either.
867 */
868 if (acc_negState != REQUEST_MIC)
869 return GSS_S_DEFECTIVE_TOKEN;
870
871 sc->mech_complete = 0;
872 sc->mic_reqd = 1;
873 *negState = REQUEST_MIC;
874 *tokflag = CONT_TOKEN_SEND;
875 return GSS_S_CONTINUE_NEEDED;
876 }
877
878 /*
879 * Wrap call to mechanism gss_init_sec_context() and update state
880 * accordingly.
881 */
882 static OM_uint32
init_ctx_call_init(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_cred_id_t claimant_cred_handle,gss_name_t target_name,OM_uint32 req_flags,OM_uint32 time_req,gss_buffer_t mechtok_in,gss_OID * actual_mech,gss_buffer_t mechtok_out,OM_uint32 * ret_flags,OM_uint32 * time_rec,OM_uint32 * negState,send_token_flag * send_token)883 init_ctx_call_init(OM_uint32 *minor_status,
884 spnego_gss_ctx_id_t sc,
885 gss_cred_id_t claimant_cred_handle,
886 gss_name_t target_name,
887 OM_uint32 req_flags,
888 OM_uint32 time_req,
889 gss_buffer_t mechtok_in,
890 gss_OID *actual_mech,
891 gss_buffer_t mechtok_out,
892 OM_uint32 *ret_flags,
893 OM_uint32 *time_rec,
894 OM_uint32 *negState,
895 send_token_flag *send_token)
896 {
897 OM_uint32 ret;
898
899 ret = gss_init_sec_context(minor_status,
900 claimant_cred_handle,
901 &sc->ctx_handle,
902 target_name,
903 sc->internal_mech,
904 (req_flags | GSS_C_INTEG_FLAG),
905 time_req,
906 GSS_C_NO_CHANNEL_BINDINGS,
907 mechtok_in,
908 &sc->actual_mech,
909 mechtok_out,
910 &sc->ctx_flags,
911 time_rec);
912 if (ret == GSS_S_COMPLETE) {
913 sc->mech_complete = 1;
914 if (ret_flags != NULL)
915 *ret_flags = sc->ctx_flags;
916 /*
917 * If this isn't the first time we've been called,
918 * we're done unless a MIC needs to be
919 * generated/handled.
920 */
921 if (*send_token == CONT_TOKEN_SEND &&
922 mechtok_out->length == 0 &&
923 (!sc->mic_reqd ||
924 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) {
925
926 *negState = ACCEPT_COMPLETE;
927 ret = GSS_S_COMPLETE;
928 if (mechtok_out->length == 0) {
929 *send_token = NO_TOKEN_SEND;
930 }
931 } else {
932 *negState = ACCEPT_INCOMPLETE;
933 ret = GSS_S_CONTINUE_NEEDED;
934 }
935 } else if (ret != GSS_S_CONTINUE_NEEDED) {
936 if (*send_token == INIT_TOKEN_SEND) {
937 /* Don't output token on error if first call. */
938 *send_token = NO_TOKEN_SEND;
939 } else {
940 *send_token = ERROR_TOKEN_SEND;
941 }
942 *negState = REJECT;
943 }
944 return ret;
945 }
946
947 /*ARGSUSED*/
948 OM_uint32
glue_spnego_gss_init_sec_context(void * context,OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t input_chan_bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)949 glue_spnego_gss_init_sec_context(
950 void *context,
951 OM_uint32 *minor_status,
952 gss_cred_id_t claimant_cred_handle,
953 gss_ctx_id_t *context_handle,
954 gss_name_t target_name,
955 gss_OID mech_type,
956 OM_uint32 req_flags,
957 OM_uint32 time_req,
958 gss_channel_bindings_t input_chan_bindings,
959 gss_buffer_t input_token,
960 gss_OID *actual_mech,
961 gss_buffer_t output_token,
962 OM_uint32 *ret_flags,
963 OM_uint32 *time_rec)
964 {
965 return(spnego_gss_init_sec_context(
966 minor_status,
967 claimant_cred_handle,
968 context_handle,
969 target_name,
970 mech_type,
971 req_flags,
972 time_req,
973 input_chan_bindings,
974 input_token,
975 actual_mech,
976 output_token,
977 ret_flags,
978 time_rec));
979 }
980
981 /*ARGSUSED*/
982 OM_uint32
spnego_gss_init_sec_context(OM_uint32 * minor_status,gss_cred_id_t claimant_cred_handle,gss_ctx_id_t * context_handle,gss_name_t target_name,gss_OID mech_type,OM_uint32 req_flags,OM_uint32 time_req,gss_channel_bindings_t input_chan_bindings,gss_buffer_t input_token,gss_OID * actual_mech,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec)983 spnego_gss_init_sec_context(
984 OM_uint32 *minor_status,
985 gss_cred_id_t claimant_cred_handle,
986 gss_ctx_id_t *context_handle,
987 gss_name_t target_name,
988 gss_OID mech_type,
989 OM_uint32 req_flags,
990 OM_uint32 time_req,
991 gss_channel_bindings_t input_chan_bindings,
992 gss_buffer_t input_token,
993 gss_OID *actual_mech,
994 gss_buffer_t output_token,
995 OM_uint32 *ret_flags,
996 OM_uint32 *time_rec)
997 {
998 /*
999 * send_token is used to indicate in later steps
1000 * what type of token, if any should be sent or processed.
1001 * NO_TOKEN_SEND = no token should be sent
1002 * INIT_TOKEN_SEND = initial token will be sent
1003 * CONT_TOKEN_SEND = continuing tokens to be sent
1004 * CHECK_MIC = no token to be sent, but have a MIC to check.
1005 */
1006 send_token_flag send_token = NO_TOKEN_SEND;
1007 OM_uint32 tmpmin, ret, negState;
1008 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out;
1009 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1010 gss_OID_set mechSet = GSS_C_NO_OID_SET;
1011 spnego_gss_ctx_id_t spnego_ctx = NULL;
1012
1013 dsyslog("Entering init_sec_context\n");
1014
1015 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER;
1016 negState = REJECT;
1017
1018 if (minor_status != NULL)
1019 *minor_status = 0;
1020 if (output_token != GSS_C_NO_BUFFER) {
1021 output_token->length = 0;
1022 output_token->value = NULL;
1023 }
1024 if (minor_status == NULL ||
1025 output_token == GSS_C_NO_BUFFER ||
1026 context_handle == NULL)
1027 return GSS_S_CALL_INACCESSIBLE_WRITE;
1028
1029 if (actual_mech != NULL)
1030 *actual_mech = GSS_C_NO_OID;
1031
1032 if (*context_handle == GSS_C_NO_CONTEXT) {
1033 ret = init_ctx_new(minor_status, claimant_cred_handle,
1034 context_handle, &mechSet, &send_token);
1035 if (ret != GSS_S_CONTINUE_NEEDED) {
1036 goto cleanup;
1037 }
1038 } else {
1039 ret = init_ctx_cont(minor_status, context_handle,
1040 input_token, &mechtok_in,
1041 &mechListMIC_in, &negState, &send_token);
1042 if (HARD_ERROR(ret)) {
1043 goto cleanup;
1044 }
1045 }
1046 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle;
1047
1048 /* Solaris SPNEGO */
1049 if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1050 spnego_gss_save_error_info(*minor_status, spnego_ctx);
1051
1052 if (!spnego_ctx->mech_complete) {
1053 ret = init_ctx_call_init(
1054 minor_status, spnego_ctx,
1055 claimant_cred_handle,
1056 target_name, req_flags,
1057 time_req, mechtok_in,
1058 actual_mech, &mechtok_out,
1059 ret_flags, time_rec,
1060 &negState, &send_token);
1061 }
1062 /* create mic/check mic */
1063 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete &&
1064 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) {
1065
1066 ret = handle_mic(minor_status,
1067 mechListMIC_in,
1068 (mechtok_out.length != 0),
1069 spnego_ctx, &mechListMIC_out,
1070 &negState, &send_token);
1071 }
1072 cleanup:
1073 if (send_token == INIT_TOKEN_SEND) {
1074 if (make_spnego_tokenInit_msg(spnego_ctx,
1075 0,
1076 mechListMIC_out,
1077 req_flags,
1078 &mechtok_out, send_token,
1079 output_token) < 0) {
1080 ret = GSS_S_FAILURE;
1081 }
1082 } else if (send_token != NO_TOKEN_SEND) {
1083 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID,
1084 &mechtok_out, mechListMIC_out,
1085 send_token,
1086 output_token) < 0) {
1087 ret = GSS_S_FAILURE;
1088 }
1089 }
1090 gss_release_buffer(&tmpmin, &mechtok_out);
1091 if (ret == GSS_S_COMPLETE) {
1092 /*
1093 * Now, switch the output context to refer to the
1094 * negotiated mechanism's context.
1095 */
1096 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle;
1097 if (actual_mech != NULL)
1098 *actual_mech = spnego_ctx->actual_mech;
1099 if (ret_flags != NULL)
1100 *ret_flags = spnego_ctx->ctx_flags;
1101 release_spnego_ctx(&spnego_ctx);
1102 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1103 if (spnego_ctx != NULL) {
1104 gss_delete_sec_context(&tmpmin,
1105 &spnego_ctx->ctx_handle,
1106 GSS_C_NO_BUFFER);
1107 release_spnego_ctx(&spnego_ctx);
1108 }
1109 *context_handle = GSS_C_NO_CONTEXT;
1110 }
1111 if (mechtok_in != GSS_C_NO_BUFFER) {
1112 gss_release_buffer(&tmpmin, mechtok_in);
1113 free(mechtok_in);
1114 }
1115 if (mechListMIC_in != GSS_C_NO_BUFFER) {
1116 gss_release_buffer(&tmpmin, mechListMIC_in);
1117 free(mechListMIC_in);
1118 }
1119 if (mechListMIC_out != GSS_C_NO_BUFFER) {
1120 gss_release_buffer(&tmpmin, mechListMIC_out);
1121 free(mechListMIC_out);
1122 }
1123 if (mechSet != GSS_C_NO_OID_SET) {
1124 gss_release_oid_set(&tmpmin, &mechSet);
1125 }
1126 return ret;
1127 } /* init_sec_context */
1128
1129 /* We don't want to import KRB5 headers here */
1130 static const gss_OID_desc gss_mech_krb5_oid =
1131 { 9, "\052\206\110\206\367\022\001\002\002" };
1132 static const gss_OID_desc gss_mech_krb5_wrong_oid =
1133 { 9, "\052\206\110\202\367\022\001\002\002" };
1134
1135 /*
1136 * verify that the input token length is not 0. If it is, just return.
1137 * If the token length is greater than 0, der encode as a sequence
1138 * and place in buf_out, advancing buf_out.
1139 */
1140
1141 static int
put_neg_hints(unsigned char ** buf_out,gss_buffer_t input_token,unsigned int buflen)1142 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token,
1143 unsigned int buflen)
1144 {
1145 int ret;
1146
1147 /* if token length is 0, we do not want to send */
1148 if (input_token->length == 0)
1149 return (0);
1150
1151 if (input_token->length > buflen)
1152 return (-1);
1153
1154 *(*buf_out)++ = SEQUENCE;
1155 if ((ret = gssint_put_der_length(input_token->length, buf_out,
1156 input_token->length)))
1157 return (ret);
1158 TWRITE_STR(*buf_out, input_token->value, input_token->length);
1159 return (0);
1160 }
1161
1162 /*
1163 * NegHints ::= SEQUENCE {
1164 * hintName [0] GeneralString OPTIONAL,
1165 * hintAddress [1] OCTET STRING OPTIONAL
1166 * }
1167 */
1168
1169 #define HOST_PREFIX "host@"
1170 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1)
1171
1172 static int
make_NegHints(OM_uint32 * minor_status,gss_cred_id_t cred,gss_buffer_t * outbuf)1173 make_NegHints(OM_uint32 *minor_status,
1174 gss_cred_id_t cred, gss_buffer_t *outbuf)
1175 {
1176 gss_buffer_desc hintNameBuf;
1177 gss_name_t hintName = GSS_C_NO_NAME;
1178 gss_name_t hintKerberosName;
1179 gss_OID hintNameType;
1180 OM_uint32 major_status;
1181 OM_uint32 minor;
1182 unsigned int tlen = 0;
1183 unsigned int hintNameSize = 0;
1184 unsigned int negHintsSize = 0;
1185 unsigned char *ptr;
1186 unsigned char *t;
1187
1188 *outbuf = GSS_C_NO_BUFFER;
1189
1190 if (cred != GSS_C_NO_CREDENTIAL) {
1191 major_status = gss_inquire_cred(minor_status,
1192 cred,
1193 &hintName,
1194 NULL,
1195 NULL,
1196 NULL);
1197 if (major_status != GSS_S_COMPLETE)
1198 return (major_status);
1199 }
1200
1201 if (hintName == GSS_C_NO_NAME) {
1202 krb5_error_code code;
1203 krb5int_access kaccess;
1204 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX;
1205
1206 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION);
1207 if (code != 0) {
1208 *minor_status = code;
1209 return (GSS_S_FAILURE);
1210 }
1211
1212 /* this breaks mutual authentication but Samba relies on it */
1213 code = (*kaccess.clean_hostname)(NULL, NULL,
1214 &hostname[HOST_PREFIX_LEN],
1215 MAXHOSTNAMELEN);
1216 if (code != 0) {
1217 *minor_status = code;
1218 return (GSS_S_FAILURE);
1219 }
1220
1221 hintNameBuf.value = hostname;
1222 hintNameBuf.length = strlen(hostname);
1223
1224 major_status = gss_import_name(minor_status,
1225 &hintNameBuf,
1226 GSS_C_NT_HOSTBASED_SERVICE,
1227 &hintName);
1228 if (major_status != GSS_S_COMPLETE) {
1229 return (major_status);
1230 }
1231 }
1232
1233 hintNameBuf.value = NULL;
1234 hintNameBuf.length = 0;
1235
1236 major_status = gss_canonicalize_name(minor_status,
1237 hintName,
1238 (gss_OID)&gss_mech_krb5_oid,
1239 &hintKerberosName);
1240 if (major_status != GSS_S_COMPLETE) {
1241 gss_release_name(&minor, &hintName);
1242 return (major_status);
1243 }
1244 gss_release_name(&minor, &hintName);
1245
1246 major_status = gss_display_name(minor_status,
1247 hintKerberosName,
1248 &hintNameBuf,
1249 &hintNameType);
1250 if (major_status != GSS_S_COMPLETE) {
1251 gss_release_name(&minor, &hintName);
1252 return (major_status);
1253 }
1254 gss_release_name(&minor, &hintKerberosName);
1255
1256 /*
1257 * Now encode the name hint into a NegHints ASN.1 type
1258 */
1259 major_status = GSS_S_FAILURE;
1260
1261 /* Length of DER encoded GeneralString */
1262 tlen = 1 + gssint_der_length_size(hintNameBuf.length) +
1263 hintNameBuf.length;
1264 hintNameSize = tlen;
1265
1266 /* Length of DER encoded hintName */
1267 tlen += 1 + gssint_der_length_size(hintNameSize);
1268 negHintsSize = tlen;
1269
1270 t = (unsigned char *)malloc(tlen);
1271 if (t == NULL) {
1272 *minor_status = ENOMEM;
1273 goto errout;
1274 }
1275
1276 ptr = t;
1277
1278 *ptr++ = CONTEXT | 0x00; /* hintName identifier */
1279 if (gssint_put_der_length(hintNameSize,
1280 &ptr, tlen - (int)(ptr-t)))
1281 goto errout;
1282
1283 *ptr++ = GENERAL_STRING;
1284 if (gssint_put_der_length(hintNameBuf.length,
1285 &ptr, tlen - (int)(ptr-t)))
1286 goto errout;
1287
1288 memcpy(ptr, hintNameBuf.value, hintNameBuf.length);
1289 ptr += hintNameBuf.length;
1290
1291 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc));
1292 if (*outbuf == NULL) {
1293 *minor_status = ENOMEM;
1294 goto errout;
1295 }
1296 (*outbuf)->value = (void *)t;
1297 (*outbuf)->length = ptr - t;
1298
1299 t = NULL; /* don't free */
1300
1301 *minor_status = 0;
1302 major_status = GSS_S_COMPLETE;
1303
1304 errout:
1305 if (t != NULL) {
1306 free(t);
1307 }
1308
1309 gss_release_buffer(&minor, &hintNameBuf);
1310 return (major_status);
1311 }
1312
1313 static OM_uint32
acc_ctx_hints(OM_uint32 * minor_status,gss_ctx_id_t * ctx,gss_cred_id_t cred,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1314 acc_ctx_hints(OM_uint32 *minor_status,
1315 gss_ctx_id_t *ctx,
1316 gss_cred_id_t cred,
1317 gss_buffer_t *mechListMIC,
1318 OM_uint32 *negState,
1319 send_token_flag *return_token)
1320 {
1321 OM_uint32 tmpmin, ret;
1322 gss_OID_set supported_mechSet;
1323 spnego_gss_ctx_id_t sc = NULL;
1324
1325 *mechListMIC = GSS_C_NO_BUFFER;
1326 supported_mechSet = GSS_C_NO_OID_SET;
1327 *return_token = ERROR_TOKEN_SEND;
1328 *negState = REJECT;
1329 *minor_status = 0;
1330
1331 *ctx = GSS_C_NO_CONTEXT;
1332 ret = GSS_S_DEFECTIVE_TOKEN;
1333
1334 if (cred != GSS_C_NO_CREDENTIAL) {
1335 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1336 NULL, &supported_mechSet);
1337 if (ret != GSS_S_COMPLETE) {
1338 *return_token = NO_TOKEN_SEND;
1339 goto cleanup;
1340 }
1341 } else {
1342 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1343 GSS_C_ACCEPT, NULL,
1344 &supported_mechSet);
1345 if (ret != GSS_S_COMPLETE) {
1346 *return_token = NO_TOKEN_SEND;
1347 goto cleanup;
1348 }
1349 }
1350
1351 ret = make_NegHints(minor_status, cred, mechListMIC);
1352 if (ret != GSS_S_COMPLETE) {
1353 *return_token = NO_TOKEN_SEND;
1354 goto cleanup;
1355 }
1356
1357 /*
1358 * Select the best match between the list of mechs
1359 * that the initiator requested and the list that
1360 * the acceptor will support.
1361 */
1362 sc = create_spnego_ctx();
1363 if (sc == NULL) {
1364 ret = GSS_S_FAILURE;
1365 *return_token = NO_TOKEN_SEND;
1366 goto cleanup;
1367 }
1368 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) {
1369 ret = GSS_S_FAILURE;
1370 *return_token = NO_TOKEN_SEND;
1371 goto cleanup;
1372 }
1373 sc->internal_mech = GSS_C_NO_OID;
1374
1375 *negState = ACCEPT_INCOMPLETE;
1376 *return_token = INIT_TOKEN_SEND;
1377 sc->firstpass = 1;
1378 *ctx = (gss_ctx_id_t)sc;
1379 ret = GSS_S_COMPLETE;
1380
1381 cleanup:
1382 gss_release_oid_set(&tmpmin, &supported_mechSet);
1383 return ret;
1384 }
1385
1386 /*
1387 * Solaris SPNEGO
1388 * mechoidset2str()
1389 * Input an OID set of mechs and output a string like so:
1390 * '{ x y z } (mechname0), { a b c } (mechname1) ...'.
1391 * On error return NULL.
1392 * Caller needs to free returned string.
1393 */
1394 static const char *mech_no_map = "Can't map OID to mechname via /etc/gss/mech";
1395 static const char *oid_no_map = "Can't map OID to string";
1396 static char *
mechoidset2str(gss_OID_set mechset)1397 mechoidset2str(gss_OID_set mechset)
1398 {
1399 int i, l;
1400 char buf[256] = {0};
1401 char *s = NULL;
1402
1403 if (!mechset)
1404 return NULL;
1405
1406 for (i = 0; i < mechset->count; i++) {
1407 OM_uint32 maj, min;
1408 gss_buffer_desc oidstr;
1409 gss_buffer_t oidstrp = &oidstr;
1410 gss_OID mech_oid = &mechset->elements[i];
1411 /* No need to free mech_name. */
1412 const char *mech_name = __gss_oid_to_mech(mech_oid);
1413
1414 if (i > 0)
1415 if (strlcat(buf, ", ", sizeof (buf)) >= sizeof (buf)) {
1416 if (oidstrp->value)
1417 gss_release_buffer(&min, oidstrp);
1418 break;
1419 }
1420
1421 /* Add '{ x y x ... }'. */
1422 maj = gss_oid_to_str(&min, mech_oid, oidstrp);
1423 if (strlcat(buf, maj ? oid_no_map : oidstrp->value,
1424 sizeof (buf)) >= sizeof (buf)) {
1425 if (oidstrp->value)
1426 gss_release_buffer(&min, oidstrp);
1427 break;
1428 }
1429 if (oidstrp->value)
1430 gss_release_buffer(&min, oidstrp);
1431
1432 /* Add '(mech name)'. */
1433 if (strlcat(buf, " (", sizeof (buf)) >= sizeof (buf))
1434 break;
1435 if (strlcat(buf, mech_name ? mech_name : mech_no_map,
1436 sizeof (buf)) >= sizeof (buf))
1437 break;
1438 if (strlcat(buf, ") ", sizeof (buf)) >= sizeof (buf))
1439 break;
1440 }
1441
1442 /* Even if we have buf overflow, let's output what we got so far. */
1443 if (mechset->count) {
1444 l = strlen(buf);
1445 if (l > 0) {
1446 s = malloc(l + 1);
1447 if (!s)
1448 return NULL;
1449 (void) strlcpy(s, buf, l);
1450 }
1451 }
1452
1453 return s ? s : NULL;
1454 }
1455
1456 /*
1457 * Set negState to REJECT if the token is defective, else
1458 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's
1459 * preferred mechanism is supported.
1460 */
1461 static OM_uint32
acc_ctx_new(OM_uint32 * minor_status,gss_buffer_t buf,gss_ctx_id_t * ctx,gss_cred_id_t cred,gss_buffer_t * mechToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1462 acc_ctx_new(OM_uint32 *minor_status,
1463 gss_buffer_t buf,
1464 gss_ctx_id_t *ctx,
1465 gss_cred_id_t cred,
1466 gss_buffer_t *mechToken,
1467 gss_buffer_t *mechListMIC,
1468 OM_uint32 *negState,
1469 send_token_flag *return_token)
1470 {
1471 OM_uint32 tmpmin, ret, req_flags;
1472 gss_OID_set supported_mechSet, mechTypes;
1473 gss_buffer_desc der_mechTypes;
1474 gss_OID mech_wanted;
1475 spnego_gss_ctx_id_t sc = NULL;
1476
1477 ret = GSS_S_DEFECTIVE_TOKEN;
1478 der_mechTypes.length = 0;
1479 der_mechTypes.value = NULL;
1480 *mechToken = *mechListMIC = GSS_C_NO_BUFFER;
1481 supported_mechSet = mechTypes = GSS_C_NO_OID_SET;
1482 *return_token = ERROR_TOKEN_SEND;
1483 *negState = REJECT;
1484 *minor_status = 0;
1485
1486 ret = get_negTokenInit(minor_status, buf, &der_mechTypes,
1487 &mechTypes, &req_flags,
1488 mechToken, mechListMIC);
1489 if (ret != GSS_S_COMPLETE) {
1490 goto cleanup;
1491 }
1492 if (cred != GSS_C_NO_CREDENTIAL) {
1493 ret = gss_inquire_cred(minor_status, cred, NULL, NULL,
1494 NULL, &supported_mechSet);
1495 if (ret != GSS_S_COMPLETE) {
1496 *return_token = NO_TOKEN_SEND;
1497 goto cleanup;
1498 }
1499 } else {
1500 ret = get_available_mechs(minor_status, GSS_C_NO_NAME,
1501 GSS_C_ACCEPT, NULL,
1502 &supported_mechSet);
1503 if (ret != GSS_S_COMPLETE) {
1504 *return_token = NO_TOKEN_SEND;
1505 goto cleanup;
1506 }
1507 }
1508 /*
1509 * Select the best match between the list of mechs
1510 * that the initiator requested and the list that
1511 * the acceptor will support.
1512 */
1513 mech_wanted = negotiate_mech_type(minor_status,
1514 supported_mechSet,
1515 mechTypes,
1516 negState);
1517 if (*negState == REJECT) {
1518 /* Solaris SPNEGO: Spruce-up error msg */
1519 char *mechTypesStr = mechoidset2str(mechTypes);
1520 spnego_gss_ctx_id_t tmpsc = create_spnego_ctx();
1521 if (tmpsc && *minor_status == ERR_SPNEGO_NEGOTIATION_FAILED) {
1522 spnego_set_error_message(tmpsc, *minor_status,
1523 dgettext(TEXT_DOMAIN,
1524 "SPNEGO failed to negotiate a mechanism: client requested mech set '%s'"),
1525 mechTypesStr ? mechTypesStr : "<null>");
1526 }
1527 if (mechTypesStr)
1528 free(mechTypesStr);
1529
1530 /*
1531 * We save error here cuz the tmp ctx goes away (very) soon.
1532 * So callers of acc_ctx_new() should NOT call it again.
1533 */
1534 spnego_gss_save_error_info(*minor_status, tmpsc);
1535 if (tmpsc)
1536 release_spnego_ctx(&tmpsc);
1537 ret = GSS_S_BAD_MECH;
1538 goto cleanup;
1539 }
1540
1541 sc = (spnego_gss_ctx_id_t)*ctx;
1542 if (sc != NULL) {
1543 gss_release_buffer(&tmpmin, &sc->DER_mechTypes);
1544 assert(mech_wanted != GSS_C_NO_OID);
1545 } else
1546 sc = create_spnego_ctx();
1547 if (sc == NULL) {
1548 ret = GSS_S_FAILURE;
1549 *return_token = NO_TOKEN_SEND;
1550 generic_gss_release_oid(&tmpmin, &mech_wanted);
1551 goto cleanup;
1552 }
1553 sc->internal_mech = mech_wanted;
1554 sc->DER_mechTypes = der_mechTypes;
1555 der_mechTypes.length = 0;
1556 der_mechTypes.value = NULL;
1557
1558 if (*negState == REQUEST_MIC)
1559 sc->mic_reqd = 1;
1560
1561 *return_token = INIT_TOKEN_SEND;
1562 sc->firstpass = 1;
1563 *ctx = (gss_ctx_id_t)sc;
1564 ret = GSS_S_COMPLETE;
1565 cleanup:
1566 gss_release_oid_set(&tmpmin, &mechTypes);
1567 gss_release_oid_set(&tmpmin, &supported_mechSet);
1568 if (der_mechTypes.length != 0)
1569 gss_release_buffer(&tmpmin, &der_mechTypes);
1570 return ret;
1571 }
1572
1573 static OM_uint32
acc_ctx_cont(OM_uint32 * minstat,gss_buffer_t buf,gss_ctx_id_t * ctx,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC,OM_uint32 * negState,send_token_flag * return_token)1574 acc_ctx_cont(OM_uint32 *minstat,
1575 gss_buffer_t buf,
1576 gss_ctx_id_t *ctx,
1577 gss_buffer_t *responseToken,
1578 gss_buffer_t *mechListMIC,
1579 OM_uint32 *negState,
1580 send_token_flag *return_token)
1581 {
1582 OM_uint32 ret, tmpmin;
1583 gss_OID supportedMech;
1584 spnego_gss_ctx_id_t sc;
1585 unsigned int len;
1586 unsigned char *ptr, *bufstart;
1587
1588 sc = (spnego_gss_ctx_id_t)*ctx;
1589 ret = GSS_S_DEFECTIVE_TOKEN;
1590 *negState = REJECT;
1591 *minstat = 0;
1592 supportedMech = GSS_C_NO_OID;
1593 *return_token = ERROR_TOKEN_SEND;
1594 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
1595
1596 ptr = bufstart = buf->value;
1597 #define REMAIN (buf->length - (ptr - bufstart))
1598 if (REMAIN > INT_MAX)
1599 return GSS_S_DEFECTIVE_TOKEN;
1600
1601 /*
1602 * Attempt to work with old Sun SPNEGO.
1603 */
1604 if (*ptr == HEADER_ID) {
1605 ret = g_verify_token_header(gss_mech_spnego,
1606 &len, &ptr, 0, REMAIN);
1607 if (ret) {
1608 *minstat = ret;
1609 return GSS_S_DEFECTIVE_TOKEN;
1610 }
1611 }
1612 if (*ptr != (CONTEXT | 0x01)) {
1613 return GSS_S_DEFECTIVE_TOKEN;
1614 }
1615 ret = get_negTokenResp(minstat, ptr, REMAIN,
1616 negState, &supportedMech,
1617 responseToken, mechListMIC);
1618 if (ret != GSS_S_COMPLETE)
1619 goto cleanup;
1620
1621 if (*responseToken == GSS_C_NO_BUFFER &&
1622 *mechListMIC == GSS_C_NO_BUFFER) {
1623
1624 ret = GSS_S_DEFECTIVE_TOKEN;
1625 goto cleanup;
1626 }
1627 if (supportedMech != GSS_C_NO_OID) {
1628 ret = GSS_S_DEFECTIVE_TOKEN;
1629 goto cleanup;
1630 }
1631 sc->firstpass = 0;
1632 *negState = ACCEPT_INCOMPLETE;
1633 *return_token = CONT_TOKEN_SEND;
1634 cleanup:
1635 if (supportedMech != GSS_C_NO_OID) {
1636 generic_gss_release_oid(&tmpmin, &supportedMech);
1637 }
1638 return ret;
1639 #undef REMAIN
1640 }
1641
1642 /*
1643 * Verify that mech OID is either exactly the same as the negotiated
1644 * mech OID, or is a mech OID supported by the negotiated mech. MS
1645 * implementations can list a most preferred mech using an incorrect
1646 * krb5 OID while emitting a krb5 initiator mech token having the
1647 * correct krb5 mech OID.
1648 */
1649 static OM_uint32
acc_ctx_vfy_oid(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_OID mechoid,OM_uint32 * negState,send_token_flag * tokflag)1650 acc_ctx_vfy_oid(OM_uint32 *minor_status,
1651 spnego_gss_ctx_id_t sc, gss_OID mechoid,
1652 OM_uint32 *negState, send_token_flag *tokflag)
1653 {
1654 OM_uint32 ret, tmpmin;
1655 gss_mechanism mech = NULL;
1656 gss_OID_set mech_set = GSS_C_NO_OID_SET;
1657 int present = 0;
1658
1659 if (g_OID_equal(sc->internal_mech, mechoid))
1660 return GSS_S_COMPLETE;
1661
1662 /*
1663 * SUNW17PACresync
1664 * If both mechs are kerb, we are done.
1665 */
1666 if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) {
1667 return GSS_S_COMPLETE;
1668 }
1669
1670 mech = gssint_get_mechanism(mechoid);
1671 if (mech == NULL || mech->gss_indicate_mechs == NULL) {
1672 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1673 {
1674 /*
1675 * Solaris SPNEGO
1676 * Spruce-up error msg.
1677 */
1678 OM_uint32 maj, maj_sc, min;
1679 gss_buffer_desc oidstr, oidstr_sc;
1680 /* No need to free mnamestr. */
1681 const char *mnamestr = __gss_oid_to_mech(
1682 sc->internal_mech);
1683 maj_sc = gss_oid_to_str(&min,
1684 sc->internal_mech,
1685 &oidstr_sc);
1686 maj = gss_oid_to_str(&min, mechoid, &oidstr);
1687 spnego_set_error_message(sc, *minor_status,
1688 dgettext(TEXT_DOMAIN,
1689 "SPNEGO failed to negotiate a mechanism: unsupported mech OID ('%s') in the token. Negotiated mech OID is '%s' (%s)"),
1690 maj ? oid_no_map: oidstr.value,
1691 maj_sc ? oid_no_map: oidstr_sc.value,
1692 mnamestr ? mnamestr : mech_no_map);
1693 if (!maj)
1694 (void) gss_release_buffer(&min, &oidstr);
1695 if (!maj_sc)
1696 (void) gss_release_buffer(&min, &oidstr_sc);
1697 }
1698 map_errcode(minor_status);
1699 *negState = REJECT;
1700 *tokflag = ERROR_TOKEN_SEND;
1701 return GSS_S_BAD_MECH;
1702 }
1703 ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set);
1704 if (ret != GSS_S_COMPLETE) {
1705 *tokflag = NO_TOKEN_SEND;
1706 map_error(minor_status, mech);
1707 goto cleanup;
1708 }
1709 ret = gss_test_oid_set_member(minor_status, sc->internal_mech,
1710 mech_set, &present);
1711 if (ret != GSS_S_COMPLETE)
1712 goto cleanup;
1713 if (!present) {
1714 {
1715 /*
1716 * Solaris SPNEGO
1717 * Spruce-up error msg.
1718 */
1719 OM_uint32 maj, min;
1720 gss_buffer_desc oidstr;
1721 char *mech_set_str = mechoidset2str(mech_set);
1722 /* No need to free mnamestr. */
1723 const char *mnamestr =
1724 __gss_oid_to_mech(sc->internal_mech);
1725 maj = gss_oid_to_str(&min, sc->internal_mech, &oidstr);
1726 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
1727 spnego_set_error_message(sc, *minor_status,
1728 dgettext(TEXT_DOMAIN,
1729 "SPNEGO failed to negotiate a mechanism: negotiated mech OID '%s' (%s) not found in mechset ('%s') of token mech"),
1730 maj ? oid_no_map: oidstr.value,
1731 mnamestr ? mnamestr : mech_no_map,
1732 mech_set_str ? mech_set_str : "<null>");
1733 if (!maj)
1734 (void) gss_release_buffer(&min, &oidstr);
1735 if (mech_set_str)
1736 free(mech_set_str);
1737 }
1738 map_errcode(minor_status);
1739 *negState = REJECT;
1740 *tokflag = ERROR_TOKEN_SEND;
1741 ret = GSS_S_BAD_MECH;
1742 }
1743 cleanup:
1744 gss_release_oid_set(&tmpmin, &mech_set);
1745 return ret;
1746 }
1747 #ifndef LEAN_CLIENT
1748 /*
1749 * Wrap call to gss_accept_sec_context() and update state
1750 * accordingly.
1751 */
1752 static OM_uint32
acc_ctx_call_acc(OM_uint32 * minor_status,spnego_gss_ctx_id_t sc,gss_cred_id_t cred,gss_buffer_t mechtok_in,gss_OID * mech_type,gss_buffer_t mechtok_out,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle,OM_uint32 * negState,send_token_flag * tokflag)1753 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc,
1754 gss_cred_id_t cred, gss_buffer_t mechtok_in,
1755 gss_OID *mech_type, gss_buffer_t mechtok_out,
1756 OM_uint32 *ret_flags, OM_uint32 *time_rec,
1757 gss_cred_id_t *delegated_cred_handle,
1758 OM_uint32 *negState, send_token_flag *tokflag)
1759 {
1760 OM_uint32 ret;
1761 gss_OID_desc mechoid;
1762
1763 if (sc->ctx_handle == GSS_C_NO_CONTEXT) {
1764 /*
1765 * mechoid is an alias; don't free it.
1766 */
1767 ret = gssint_get_mech_type(&mechoid, mechtok_in);
1768 if (ret != GSS_S_COMPLETE) {
1769 *tokflag = NO_TOKEN_SEND;
1770 return ret;
1771 }
1772 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid,
1773 negState, tokflag);
1774 if (ret != GSS_S_COMPLETE)
1775 return ret;
1776 }
1777
1778 ret = gss_accept_sec_context(minor_status,
1779 &sc->ctx_handle,
1780 cred,
1781 mechtok_in,
1782 GSS_C_NO_CHANNEL_BINDINGS,
1783 &sc->internal_name,
1784 mech_type,
1785 mechtok_out,
1786 &sc->ctx_flags,
1787 time_rec,
1788 delegated_cred_handle);
1789
1790 if (ret == GSS_S_COMPLETE) {
1791 #ifdef MS_BUG_TEST
1792 /*
1793 * Force MIC to be not required even if we previously
1794 * requested a MIC.
1795 */
1796 char *envstr = getenv("MS_FORCE_NO_MIC");
1797
1798 if (envstr != NULL && strcmp(envstr, "1") == 0 &&
1799 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) &&
1800 sc->mic_reqd) {
1801
1802 sc->mic_reqd = 0;
1803 }
1804 #endif
1805 sc->mech_complete = 1;
1806 if (ret_flags != NULL)
1807 *ret_flags = sc->ctx_flags;
1808
1809 if (!sc->mic_reqd) {
1810 *negState = ACCEPT_COMPLETE;
1811 ret = GSS_S_COMPLETE;
1812 } else {
1813 ret = GSS_S_CONTINUE_NEEDED;
1814 }
1815 } else if (ret != GSS_S_CONTINUE_NEEDED) {
1816 *negState = REJECT;
1817 *tokflag = ERROR_TOKEN_SEND;
1818 }
1819 return ret;
1820 }
1821
1822 /*ARGSUSED*/
1823 OM_uint32
glue_spnego_gss_accept_sec_context(void * context,OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)1824 glue_spnego_gss_accept_sec_context(
1825 void *context,
1826 OM_uint32 *minor_status,
1827 gss_ctx_id_t *context_handle,
1828 gss_cred_id_t verifier_cred_handle,
1829 gss_buffer_t input_token,
1830 gss_channel_bindings_t input_chan_bindings,
1831 gss_name_t *src_name,
1832 gss_OID *mech_type,
1833 gss_buffer_t output_token,
1834 OM_uint32 *ret_flags,
1835 OM_uint32 *time_rec,
1836 gss_cred_id_t *delegated_cred_handle)
1837 {
1838 return(spnego_gss_accept_sec_context(
1839 minor_status,
1840 context_handle,
1841 verifier_cred_handle,
1842 input_token,
1843 input_chan_bindings,
1844 src_name,
1845 mech_type,
1846 output_token,
1847 ret_flags,
1848 time_rec,
1849 delegated_cred_handle));
1850 }
1851
1852 /*ARGSUSED*/
1853 OM_uint32
spnego_gss_accept_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_cred_id_t verifier_cred_handle,gss_buffer_t input_token,gss_channel_bindings_t input_chan_bindings,gss_name_t * src_name,gss_OID * mech_type,gss_buffer_t output_token,OM_uint32 * ret_flags,OM_uint32 * time_rec,gss_cred_id_t * delegated_cred_handle)1854 spnego_gss_accept_sec_context(
1855 OM_uint32 *minor_status,
1856 gss_ctx_id_t *context_handle,
1857 gss_cred_id_t verifier_cred_handle,
1858 gss_buffer_t input_token,
1859 gss_channel_bindings_t input_chan_bindings,
1860 gss_name_t *src_name,
1861 gss_OID *mech_type,
1862 gss_buffer_t output_token,
1863 OM_uint32 *ret_flags,
1864 OM_uint32 *time_rec,
1865 gss_cred_id_t *delegated_cred_handle)
1866 {
1867 OM_uint32 ret, tmpmin, negState;
1868 send_token_flag return_token;
1869 gss_buffer_t mechtok_in, mic_in, mic_out;
1870 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER;
1871 spnego_gss_ctx_id_t sc = NULL;
1872 OM_uint32 mechstat = GSS_S_FAILURE;
1873 int sendTokenInit = 0, tmpret;
1874
1875 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER;
1876
1877 if (minor_status != NULL)
1878 *minor_status = 0;
1879 if (output_token != GSS_C_NO_BUFFER) {
1880 output_token->length = 0;
1881 output_token->value = NULL;
1882 }
1883
1884
1885 if (minor_status == NULL ||
1886 output_token == GSS_C_NO_BUFFER ||
1887 context_handle == NULL) {
1888 return GSS_S_CALL_INACCESSIBLE_WRITE;
1889 }
1890
1891 if (input_token == GSS_C_NO_BUFFER) {
1892 return GSS_S_CALL_INACCESSIBLE_READ;
1893 }
1894
1895 sc = (spnego_gss_ctx_id_t)*context_handle;
1896 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) {
1897 if (src_name != NULL)
1898 *src_name = GSS_C_NO_NAME;
1899 if (mech_type != NULL)
1900 *mech_type = GSS_C_NO_OID;
1901 if (time_rec != NULL)
1902 *time_rec = 0;
1903 if (ret_flags != NULL)
1904 *ret_flags = 0;
1905 if (delegated_cred_handle != NULL)
1906 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
1907 if (input_token->length == 0) {
1908 ret = acc_ctx_hints(minor_status,
1909 context_handle,
1910 verifier_cred_handle,
1911 &mic_out,
1912 &negState,
1913 &return_token);
1914 if (ret != GSS_S_COMPLETE)
1915 goto cleanup;
1916 sendTokenInit = 1;
1917 ret = GSS_S_CONTINUE_NEEDED;
1918 } else {
1919 /* Can set negState to REQUEST_MIC */
1920 ret = acc_ctx_new(minor_status, input_token,
1921 context_handle, verifier_cred_handle,
1922 &mechtok_in, &mic_in,
1923 &negState, &return_token);
1924 if (ret != GSS_S_COMPLETE)
1925 goto cleanup;
1926 ret = GSS_S_CONTINUE_NEEDED;
1927 }
1928 } else {
1929 /* Can set negState to ACCEPT_INCOMPLETE */
1930 ret = acc_ctx_cont(minor_status, input_token,
1931 context_handle, &mechtok_in,
1932 &mic_in, &negState, &return_token);
1933 if (ret != GSS_S_COMPLETE)
1934 goto cleanup;
1935 ret = GSS_S_CONTINUE_NEEDED;
1936 }
1937
1938 sc = (spnego_gss_ctx_id_t)*context_handle;
1939 /*
1940 * Handle mechtok_in and mic_in only if they are
1941 * present in input_token. If neither is present, whether
1942 * this is an error depends on whether this is the first
1943 * round-trip. RET is set to a default value according to
1944 * whether it is the first round-trip.
1945 */
1946 mechstat = GSS_S_FAILURE;
1947 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) {
1948 ret = acc_ctx_call_acc(minor_status, sc,
1949 verifier_cred_handle, mechtok_in,
1950 mech_type, &mechtok_out,
1951 ret_flags, time_rec,
1952 delegated_cred_handle,
1953 &negState, &return_token);
1954 } else if (negState == REQUEST_MIC) {
1955 mechstat = GSS_S_CONTINUE_NEEDED;
1956 }
1957
1958 /* Solaris SPNEGO */
1959 if (*minor_status == ERR_SPNEGO_NEGOTIATION_FAILED)
1960 spnego_gss_save_error_info(*minor_status, sc);
1961
1962 if (!HARD_ERROR(ret) && sc->mech_complete &&
1963 (sc->ctx_flags & GSS_C_INTEG_FLAG)) {
1964
1965 ret = handle_mic(minor_status, mic_in,
1966 (mechtok_out.length != 0),
1967 sc, &mic_out,
1968 &negState, &return_token);
1969 }
1970
1971 cleanup:
1972 if (return_token == INIT_TOKEN_SEND && sendTokenInit) {
1973 assert(sc != NULL);
1974 tmpret = make_spnego_tokenInit_msg(sc, 1, mic_out, 0,
1975 GSS_C_NO_BUFFER,
1976 return_token, output_token);
1977 if (tmpret < 0)
1978 ret = GSS_S_FAILURE;
1979 } else if (return_token != NO_TOKEN_SEND &&
1980 return_token != CHECK_MIC) {
1981 tmpret = make_spnego_tokenTarg_msg(negState,
1982 sc ? sc->internal_mech :
1983 GSS_C_NO_OID,
1984 &mechtok_out, mic_out,
1985 return_token,
1986 output_token);
1987 if (tmpret < 0)
1988 ret = GSS_S_FAILURE;
1989 }
1990 if (ret == GSS_S_COMPLETE) {
1991 *context_handle = (gss_ctx_id_t)sc->ctx_handle;
1992 if (sc->internal_name != GSS_C_NO_NAME &&
1993 src_name != NULL) {
1994 *src_name = sc->internal_name;
1995 }
1996 release_spnego_ctx(&sc);
1997 }
1998 gss_release_buffer(&tmpmin, &mechtok_out);
1999 if (mechtok_in != GSS_C_NO_BUFFER) {
2000 gss_release_buffer(&tmpmin, mechtok_in);
2001 free(mechtok_in);
2002 }
2003 if (mic_in != GSS_C_NO_BUFFER) {
2004 gss_release_buffer(&tmpmin, mic_in);
2005 free(mic_in);
2006 }
2007 if (mic_out != GSS_C_NO_BUFFER) {
2008 gss_release_buffer(&tmpmin, mic_out);
2009 free(mic_out);
2010 }
2011 return ret;
2012 }
2013 #endif /* LEAN_CLIENT */
2014
2015 /*ARGSUSED*/
2016 OM_uint32
glue_spnego_gss_display_status(void * context,OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)2017 glue_spnego_gss_display_status(
2018 void *context,
2019 OM_uint32 *minor_status,
2020 OM_uint32 status_value,
2021 int status_type,
2022 gss_OID mech_type,
2023 OM_uint32 *message_context,
2024 gss_buffer_t status_string)
2025 {
2026 return (spnego_gss_display_status(minor_status,
2027 status_value,
2028 status_type,
2029 mech_type,
2030 message_context,
2031 status_string));
2032 }
2033
2034 /*ARGSUSED*/
2035 OM_uint32
spnego_gss_display_status(OM_uint32 * minor_status,OM_uint32 status_value,int status_type,gss_OID mech_type,OM_uint32 * message_context,gss_buffer_t status_string)2036 spnego_gss_display_status(
2037 OM_uint32 *minor_status,
2038 OM_uint32 status_value,
2039 int status_type,
2040 gss_OID mech_type,
2041 OM_uint32 *message_context,
2042 gss_buffer_t status_string)
2043 {
2044 dsyslog("Entering display_status\n");
2045
2046 *message_context = 0;
2047 switch (status_value) {
2048 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
2049 /* CSTYLED */
2050 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate");
2051 break;
2052 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
2053 /* CSTYLED */
2054 *status_string = make_err_msg("SPNEGO failed to acquire creds");
2055 break;
2056 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
2057 /* CSTYLED */
2058 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism");
2059 break;
2060 case ERR_SPNEGO_NEGOTIATION_FAILED:
2061 /* CSTYLED */
2062 return(spnego_gss_display_status2(minor_status,
2063 status_value,
2064 status_type,
2065 mech_type,
2066 message_context,
2067 status_string));
2068 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
2069 /* CSTYLED */
2070 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token");
2071 break;
2072 default:
2073 /*
2074 * Solaris SPNEGO
2075 * If mech_spnego calls mech_krb5 (via libgss) and an
2076 * error occurs there, give it a shot.
2077 */
2078 /* CSTYLED */
2079 return(krb5_gss_display_status2(minor_status,
2080 status_value,
2081 status_type,
2082 (gss_OID)&gss_mech_krb5_oid,
2083 message_context,
2084 status_string));
2085
2086 }
2087
2088 dsyslog("Leaving display_status\n");
2089 return (GSS_S_COMPLETE);
2090 }
2091
2092 /*ARGSUSED*/
2093 OM_uint32
glue_spnego_gss_import_name(void * context,OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)2094 glue_spnego_gss_import_name(
2095 void *context,
2096 OM_uint32 *minor_status,
2097 gss_buffer_t input_name_buffer,
2098 gss_OID input_name_type,
2099 gss_name_t *output_name)
2100 {
2101 return(spnego_gss_import_name(minor_status,
2102 input_name_buffer,
2103 input_name_type,
2104 output_name));
2105 }
2106
2107 /*ARGSUSED*/
2108 OM_uint32
spnego_gss_import_name(OM_uint32 * minor_status,gss_buffer_t input_name_buffer,gss_OID input_name_type,gss_name_t * output_name)2109 spnego_gss_import_name(
2110 OM_uint32 *minor_status,
2111 gss_buffer_t input_name_buffer,
2112 gss_OID input_name_type,
2113 gss_name_t *output_name)
2114 {
2115 OM_uint32 status;
2116
2117 dsyslog("Entering import_name\n");
2118
2119 status = gss_import_name(minor_status, input_name_buffer,
2120 input_name_type, output_name);
2121
2122 dsyslog("Leaving import_name\n");
2123 return (status);
2124 }
2125
2126 /*ARGSUSED*/
2127 OM_uint32
glue_spnego_gss_release_name(void * context,OM_uint32 * minor_status,gss_name_t * input_name)2128 glue_spnego_gss_release_name(
2129 void *context,
2130 OM_uint32 *minor_status,
2131 gss_name_t *input_name)
2132 {
2133 return(spnego_gss_release_name(minor_status, input_name));
2134 }
2135
2136 /*ARGSUSED*/
2137 OM_uint32
spnego_gss_release_name(OM_uint32 * minor_status,gss_name_t * input_name)2138 spnego_gss_release_name(
2139 OM_uint32 *minor_status,
2140 gss_name_t *input_name)
2141 {
2142 OM_uint32 status;
2143
2144 dsyslog("Entering release_name\n");
2145
2146 status = gss_release_name(minor_status, input_name);
2147
2148 dsyslog("Leaving release_name\n");
2149 return (status);
2150 }
2151
2152 /*ARGSUSED*/
2153 OM_uint32
glue_spnego_gss_compare_name(void * context,OM_uint32 * minor_status,const gss_name_t name1,const gss_name_t name2,int * name_equal)2154 glue_spnego_gss_compare_name(
2155 void *context,
2156 OM_uint32 *minor_status,
2157 const gss_name_t name1,
2158 const gss_name_t name2,
2159 int *name_equal)
2160 {
2161 return(spnego_gss_compare_name(minor_status,
2162 name1,
2163 name2,
2164 name_equal));
2165 }
2166 /*ARGSUSED*/
2167 OM_uint32
spnego_gss_compare_name(OM_uint32 * minor_status,const gss_name_t name1,const gss_name_t name2,int * name_equal)2168 spnego_gss_compare_name(
2169 OM_uint32 *minor_status,
2170 const gss_name_t name1,
2171 const gss_name_t name2,
2172 int *name_equal)
2173 {
2174 OM_uint32 status = GSS_S_COMPLETE;
2175 dsyslog("Entering compare_name\n");
2176
2177 status = gss_compare_name(minor_status, name1, name2, name_equal);
2178
2179 dsyslog("Leaving compare_name\n");
2180 return (status);
2181 }
2182
2183 /*ARGSUSED*/
2184 OM_uint32
glue_spnego_gss_display_name(void * context,OM_uint32 * minor_status,gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)2185 glue_spnego_gss_display_name(
2186 void *context,
2187 OM_uint32 *minor_status,
2188 gss_name_t input_name,
2189 gss_buffer_t output_name_buffer,
2190 gss_OID *output_name_type)
2191 {
2192 return(spnego_gss_display_name(
2193 minor_status,
2194 input_name,
2195 output_name_buffer,
2196 output_name_type));
2197 }
2198
2199 /*ARGSUSED*/
2200 OM_uint32
spnego_gss_display_name(OM_uint32 * minor_status,gss_name_t input_name,gss_buffer_t output_name_buffer,gss_OID * output_name_type)2201 spnego_gss_display_name(
2202 OM_uint32 *minor_status,
2203 gss_name_t input_name,
2204 gss_buffer_t output_name_buffer,
2205 gss_OID *output_name_type)
2206 {
2207 OM_uint32 status = GSS_S_COMPLETE;
2208 dsyslog("Entering display_name\n");
2209
2210 status = gss_display_name(minor_status, input_name,
2211 output_name_buffer, output_name_type);
2212
2213 dsyslog("Leaving display_name\n");
2214 return (status);
2215 }
2216
2217
2218 /*ARGSUSED*/
2219 OM_uint32
glue_spnego_gss_inquire_names_for_mech(void * context,OM_uint32 * minor_status,gss_OID mechanism,gss_OID_set * name_types)2220 glue_spnego_gss_inquire_names_for_mech(
2221 void *context,
2222 OM_uint32 *minor_status,
2223 gss_OID mechanism,
2224 gss_OID_set *name_types)
2225 {
2226 return(spnego_gss_inquire_names_for_mech(minor_status,
2227 mechanism,
2228 name_types));
2229 }
2230 /*ARGSUSED*/
2231 OM_uint32
spnego_gss_inquire_names_for_mech(OM_uint32 * minor_status,gss_OID mechanism,gss_OID_set * name_types)2232 spnego_gss_inquire_names_for_mech(
2233 OM_uint32 *minor_status,
2234 gss_OID mechanism,
2235 gss_OID_set *name_types)
2236 {
2237 OM_uint32 major, minor;
2238
2239 dsyslog("Entering inquire_names_for_mech\n");
2240 /*
2241 * We only know how to handle our own mechanism.
2242 */
2243 if ((mechanism != GSS_C_NULL_OID) &&
2244 !g_OID_equal(gss_mech_spnego, mechanism)) {
2245 *minor_status = 0;
2246 return (GSS_S_FAILURE);
2247 }
2248
2249 major = gss_create_empty_oid_set(minor_status, name_types);
2250 if (major == GSS_S_COMPLETE) {
2251 /* Now add our members. */
2252 if (((major = gss_add_oid_set_member(minor_status,
2253 (gss_OID) GSS_C_NT_USER_NAME,
2254 name_types)) == GSS_S_COMPLETE) &&
2255 ((major = gss_add_oid_set_member(minor_status,
2256 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
2257 name_types)) == GSS_S_COMPLETE) &&
2258 ((major = gss_add_oid_set_member(minor_status,
2259 (gss_OID) GSS_C_NT_STRING_UID_NAME,
2260 name_types)) == GSS_S_COMPLETE)) {
2261 major = gss_add_oid_set_member(minor_status,
2262 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
2263 name_types);
2264 }
2265
2266 if (major != GSS_S_COMPLETE)
2267 (void) gss_release_oid_set(&minor, name_types);
2268 }
2269
2270 dsyslog("Leaving inquire_names_for_mech\n");
2271 return (major);
2272 }
2273
2274 OM_uint32
spnego_gss_unwrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,gss_buffer_t input_message_buffer,gss_buffer_t output_message_buffer,int * conf_state,gss_qop_t * qop_state)2275 spnego_gss_unwrap(
2276 OM_uint32 *minor_status,
2277 gss_ctx_id_t context_handle,
2278 gss_buffer_t input_message_buffer,
2279 gss_buffer_t output_message_buffer,
2280 int *conf_state,
2281 gss_qop_t *qop_state)
2282 {
2283 OM_uint32 ret;
2284 ret = gss_unwrap(minor_status,
2285 context_handle,
2286 input_message_buffer,
2287 output_message_buffer,
2288 conf_state,
2289 qop_state);
2290
2291 return (ret);
2292 }
2293
2294 OM_uint32
spnego_gss_wrap(OM_uint32 * minor_status,gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,gss_buffer_t input_message_buffer,int * conf_state,gss_buffer_t output_message_buffer)2295 spnego_gss_wrap(
2296 OM_uint32 *minor_status,
2297 gss_ctx_id_t context_handle,
2298 int conf_req_flag,
2299 gss_qop_t qop_req,
2300 gss_buffer_t input_message_buffer,
2301 int *conf_state,
2302 gss_buffer_t output_message_buffer)
2303 {
2304 OM_uint32 ret;
2305 ret = gss_wrap(minor_status,
2306 context_handle,
2307 conf_req_flag,
2308 qop_req,
2309 input_message_buffer,
2310 conf_state,
2311 output_message_buffer);
2312
2313 return (ret);
2314 }
2315
2316 OM_uint32
spnego_gss_process_context_token(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t token_buffer)2317 spnego_gss_process_context_token(
2318 OM_uint32 *minor_status,
2319 const gss_ctx_id_t context_handle,
2320 const gss_buffer_t token_buffer)
2321 {
2322 OM_uint32 ret;
2323 ret = gss_process_context_token(minor_status,
2324 context_handle,
2325 token_buffer);
2326
2327 return (ret);
2328 }
2329
2330 OM_uint32
glue_spnego_gss_delete_sec_context(void * context,OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)2331 glue_spnego_gss_delete_sec_context(
2332 void *context,
2333 OM_uint32 *minor_status,
2334 gss_ctx_id_t *context_handle,
2335 gss_buffer_t output_token)
2336 {
2337 return(spnego_gss_delete_sec_context(minor_status,
2338 context_handle, output_token));
2339 }
2340
2341 OM_uint32
spnego_gss_delete_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t output_token)2342 spnego_gss_delete_sec_context(
2343 OM_uint32 *minor_status,
2344 gss_ctx_id_t *context_handle,
2345 gss_buffer_t output_token)
2346 {
2347 OM_uint32 ret = GSS_S_COMPLETE;
2348 spnego_gss_ctx_id_t *ctx =
2349 (spnego_gss_ctx_id_t *)context_handle;
2350
2351 if (context_handle == NULL)
2352 return (GSS_S_FAILURE);
2353
2354 /*
2355 * If this is still an SPNEGO mech, release it locally.
2356 */
2357 if (*ctx != NULL &&
2358 (*ctx)->magic_num == SPNEGO_MAGIC_ID) {
2359 (void) release_spnego_ctx(ctx);
2360 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */
2361 if (output_token) {
2362 output_token->length = 0;
2363 output_token->value = NULL;
2364 }
2365 } else {
2366 ret = gss_delete_sec_context(minor_status,
2367 context_handle,
2368 output_token);
2369 }
2370
2371 return (ret);
2372 }
2373
2374 OM_uint32
glue_spnego_gss_context_time(void * context,OM_uint32 * minor_status,const gss_ctx_id_t context_handle,OM_uint32 * time_rec)2375 glue_spnego_gss_context_time(
2376 void *context,
2377 OM_uint32 *minor_status,
2378 const gss_ctx_id_t context_handle,
2379 OM_uint32 *time_rec)
2380 {
2381 return(spnego_gss_context_time(minor_status,
2382 context_handle,
2383 time_rec));
2384 }
2385
2386 OM_uint32
spnego_gss_context_time(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,OM_uint32 * time_rec)2387 spnego_gss_context_time(
2388 OM_uint32 *minor_status,
2389 const gss_ctx_id_t context_handle,
2390 OM_uint32 *time_rec)
2391 {
2392 OM_uint32 ret;
2393 ret = gss_context_time(minor_status,
2394 context_handle,
2395 time_rec);
2396 return (ret);
2397 }
2398
2399 #ifndef LEAN_CLIENT
2400 OM_uint32
glue_spnego_gss_export_sec_context(void * context,OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)2401 glue_spnego_gss_export_sec_context(
2402 void *context,
2403 OM_uint32 *minor_status,
2404 gss_ctx_id_t *context_handle,
2405 gss_buffer_t interprocess_token)
2406 {
2407 return(spnego_gss_export_sec_context(minor_status,
2408 context_handle,
2409 interprocess_token));
2410 }
2411 OM_uint32
spnego_gss_export_sec_context(OM_uint32 * minor_status,gss_ctx_id_t * context_handle,gss_buffer_t interprocess_token)2412 spnego_gss_export_sec_context(
2413 OM_uint32 *minor_status,
2414 gss_ctx_id_t *context_handle,
2415 gss_buffer_t interprocess_token)
2416 {
2417 OM_uint32 ret;
2418 ret = gss_export_sec_context(minor_status,
2419 context_handle,
2420 interprocess_token);
2421 return (ret);
2422 }
2423
2424 OM_uint32
glue_spnego_gss_import_sec_context(void * context,OM_uint32 * minor_status,const gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)2425 glue_spnego_gss_import_sec_context(
2426 void *context,
2427 OM_uint32 *minor_status,
2428 const gss_buffer_t interprocess_token,
2429 gss_ctx_id_t *context_handle)
2430 {
2431 return(spnego_gss_import_sec_context(minor_status,
2432 interprocess_token,
2433 context_handle));
2434 }
2435 OM_uint32
spnego_gss_import_sec_context(OM_uint32 * minor_status,const gss_buffer_t interprocess_token,gss_ctx_id_t * context_handle)2436 spnego_gss_import_sec_context(
2437 OM_uint32 *minor_status,
2438 const gss_buffer_t interprocess_token,
2439 gss_ctx_id_t *context_handle)
2440 {
2441 OM_uint32 ret;
2442 ret = gss_import_sec_context(minor_status,
2443 interprocess_token,
2444 context_handle);
2445 return (ret);
2446 }
2447 #endif /* LEAN_CLIENT */
2448
2449 OM_uint32
glue_spnego_gss_inquire_context(void * context,OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * opened)2450 glue_spnego_gss_inquire_context(
2451 void *context,
2452 OM_uint32 *minor_status,
2453 const gss_ctx_id_t context_handle,
2454 gss_name_t *src_name,
2455 gss_name_t *targ_name,
2456 OM_uint32 *lifetime_rec,
2457 gss_OID *mech_type,
2458 OM_uint32 *ctx_flags,
2459 int *locally_initiated,
2460 int *opened)
2461 {
2462 return(spnego_gss_inquire_context(
2463 minor_status,
2464 context_handle,
2465 src_name,
2466 targ_name,
2467 lifetime_rec,
2468 mech_type,
2469 ctx_flags,
2470 locally_initiated,
2471 opened));
2472 }
2473
2474 OM_uint32
spnego_gss_inquire_context(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,gss_name_t * src_name,gss_name_t * targ_name,OM_uint32 * lifetime_rec,gss_OID * mech_type,OM_uint32 * ctx_flags,int * locally_initiated,int * opened)2475 spnego_gss_inquire_context(
2476 OM_uint32 *minor_status,
2477 const gss_ctx_id_t context_handle,
2478 gss_name_t *src_name,
2479 gss_name_t *targ_name,
2480 OM_uint32 *lifetime_rec,
2481 gss_OID *mech_type,
2482 OM_uint32 *ctx_flags,
2483 int *locally_initiated,
2484 int *opened)
2485 {
2486 OM_uint32 ret = GSS_S_COMPLETE;
2487
2488 ret = gss_inquire_context(minor_status,
2489 context_handle,
2490 src_name,
2491 targ_name,
2492 lifetime_rec,
2493 mech_type,
2494 ctx_flags,
2495 locally_initiated,
2496 opened);
2497
2498 return (ret);
2499 }
2500
2501 OM_uint32
glue_spnego_gss_wrap_size_limit(void * context,OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)2502 glue_spnego_gss_wrap_size_limit(
2503 void *context,
2504 OM_uint32 *minor_status,
2505 const gss_ctx_id_t context_handle,
2506 int conf_req_flag,
2507 gss_qop_t qop_req,
2508 OM_uint32 req_output_size,
2509 OM_uint32 *max_input_size)
2510 {
2511 return(spnego_gss_wrap_size_limit(minor_status,
2512 context_handle,
2513 conf_req_flag,
2514 qop_req,
2515 req_output_size,
2516 max_input_size));
2517 }
2518
2519 OM_uint32
spnego_gss_wrap_size_limit(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,int conf_req_flag,gss_qop_t qop_req,OM_uint32 req_output_size,OM_uint32 * max_input_size)2520 spnego_gss_wrap_size_limit(
2521 OM_uint32 *minor_status,
2522 const gss_ctx_id_t context_handle,
2523 int conf_req_flag,
2524 gss_qop_t qop_req,
2525 OM_uint32 req_output_size,
2526 OM_uint32 *max_input_size)
2527 {
2528 OM_uint32 ret;
2529 ret = gss_wrap_size_limit(minor_status,
2530 context_handle,
2531 conf_req_flag,
2532 qop_req,
2533 req_output_size,
2534 max_input_size);
2535 return (ret);
2536 }
2537
2538 #if 0 /* SUNW17PACresync */
2539 OM_uint32
2540 spnego_gss_get_mic(
2541 OM_uint32 *minor_status,
2542 const gss_ctx_id_t context_handle,
2543 gss_qop_t qop_req,
2544 const gss_buffer_t message_buffer,
2545 gss_buffer_t message_token)
2546 {
2547 OM_uint32 ret;
2548 ret = gss_get_mic(minor_status,
2549 context_handle,
2550 qop_req,
2551 message_buffer,
2552 message_token);
2553 return (ret);
2554 }
2555 #endif
2556
2557 OM_uint32
spnego_gss_verify_mic(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_buffer_t msg_buffer,const gss_buffer_t token_buffer,gss_qop_t * qop_state)2558 spnego_gss_verify_mic(
2559 OM_uint32 *minor_status,
2560 const gss_ctx_id_t context_handle,
2561 const gss_buffer_t msg_buffer,
2562 const gss_buffer_t token_buffer,
2563 gss_qop_t *qop_state)
2564 {
2565 OM_uint32 ret;
2566 ret = gss_verify_mic(minor_status,
2567 context_handle,
2568 msg_buffer,
2569 token_buffer,
2570 qop_state);
2571 return (ret);
2572 }
2573
2574 OM_uint32
spnego_gss_inquire_sec_context_by_oid(OM_uint32 * minor_status,const gss_ctx_id_t context_handle,const gss_OID desired_object,gss_buffer_set_t * data_set)2575 spnego_gss_inquire_sec_context_by_oid(
2576 OM_uint32 *minor_status,
2577 const gss_ctx_id_t context_handle,
2578 const gss_OID desired_object,
2579 gss_buffer_set_t *data_set)
2580 {
2581 OM_uint32 ret;
2582 ret = gss_inquire_sec_context_by_oid(minor_status,
2583 context_handle,
2584 desired_object,
2585 data_set);
2586 return (ret);
2587 }
2588
2589 /*
2590 * SUNW17PACresync
2591 * These GSS funcs not needed yet, so disable them.
2592 * Revisit for full 1.7 resync.
2593 */
2594 #if 0
2595 OM_uint32
2596 spnego_gss_set_sec_context_option(
2597 OM_uint32 *minor_status,
2598 gss_ctx_id_t *context_handle,
2599 const gss_OID desired_object,
2600 const gss_buffer_t value)
2601 {
2602 OM_uint32 ret;
2603 ret = gss_set_sec_context_option(minor_status,
2604 context_handle,
2605 desired_object,
2606 value);
2607 return (ret);
2608 }
2609
2610 OM_uint32
2611 spnego_gss_wrap_aead(OM_uint32 *minor_status,
2612 gss_ctx_id_t context_handle,
2613 int conf_req_flag,
2614 gss_qop_t qop_req,
2615 gss_buffer_t input_assoc_buffer,
2616 gss_buffer_t input_payload_buffer,
2617 int *conf_state,
2618 gss_buffer_t output_message_buffer)
2619 {
2620 OM_uint32 ret;
2621 ret = gss_wrap_aead(minor_status,
2622 context_handle,
2623 conf_req_flag,
2624 qop_req,
2625 input_assoc_buffer,
2626 input_payload_buffer,
2627 conf_state,
2628 output_message_buffer);
2629
2630 return (ret);
2631 }
2632
2633 OM_uint32
2634 spnego_gss_unwrap_aead(OM_uint32 *minor_status,
2635 gss_ctx_id_t context_handle,
2636 gss_buffer_t input_message_buffer,
2637 gss_buffer_t input_assoc_buffer,
2638 gss_buffer_t output_payload_buffer,
2639 int *conf_state,
2640 gss_qop_t *qop_state)
2641 {
2642 OM_uint32 ret;
2643 ret = gss_unwrap_aead(minor_status,
2644 context_handle,
2645 input_message_buffer,
2646 input_assoc_buffer,
2647 output_payload_buffer,
2648 conf_state,
2649 qop_state);
2650 return (ret);
2651 }
2652
2653 OM_uint32
2654 spnego_gss_wrap_iov(OM_uint32 *minor_status,
2655 gss_ctx_id_t context_handle,
2656 int conf_req_flag,
2657 gss_qop_t qop_req,
2658 int *conf_state,
2659 gss_iov_buffer_desc *iov,
2660 int iov_count)
2661 {
2662 OM_uint32 ret;
2663 ret = gss_wrap_iov(minor_status,
2664 context_handle,
2665 conf_req_flag,
2666 qop_req,
2667 conf_state,
2668 iov,
2669 iov_count);
2670 return (ret);
2671 }
2672
2673 OM_uint32
2674 spnego_gss_unwrap_iov(OM_uint32 *minor_status,
2675 gss_ctx_id_t context_handle,
2676 int *conf_state,
2677 gss_qop_t *qop_state,
2678 gss_iov_buffer_desc *iov,
2679 int iov_count)
2680 {
2681 OM_uint32 ret;
2682 ret = gss_unwrap_iov(minor_status,
2683 context_handle,
2684 conf_state,
2685 qop_state,
2686 iov,
2687 iov_count);
2688 return (ret);
2689 }
2690
2691 OM_uint32
2692 spnego_gss_wrap_iov_length(OM_uint32 *minor_status,
2693 gss_ctx_id_t context_handle,
2694 int conf_req_flag,
2695 gss_qop_t qop_req,
2696 int *conf_state,
2697 gss_iov_buffer_desc *iov,
2698 int iov_count)
2699 {
2700 OM_uint32 ret;
2701 ret = gss_wrap_iov_length(minor_status,
2702 context_handle,
2703 conf_req_flag,
2704 qop_req,
2705 conf_state,
2706 iov,
2707 iov_count);
2708 return (ret);
2709 }
2710
2711
2712 OM_uint32
2713 spnego_gss_complete_auth_token(
2714 OM_uint32 *minor_status,
2715 const gss_ctx_id_t context_handle,
2716 gss_buffer_t input_message_buffer)
2717 {
2718 OM_uint32 ret;
2719 ret = gss_complete_auth_token(minor_status,
2720 context_handle,
2721 input_message_buffer);
2722 return (ret);
2723 }
2724 #endif /* 0 */
2725
2726 /*
2727 * We will release everything but the ctx_handle so that it
2728 * can be passed back to init/accept context. This routine should
2729 * not be called until after the ctx_handle memory is assigned to
2730 * the supplied context handle from init/accept context.
2731 */
2732 static void
release_spnego_ctx(spnego_gss_ctx_id_t * ctx)2733 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
2734 {
2735 spnego_gss_ctx_id_t context;
2736 OM_uint32 minor_stat;
2737 context = *ctx;
2738
2739 if (context != NULL) {
2740 (void) gss_release_buffer(&minor_stat,
2741 &context->DER_mechTypes);
2742
2743 (void) generic_gss_release_oid(&minor_stat,
2744 &context->internal_mech);
2745
2746 if (context->optionStr != NULL) {
2747 free(context->optionStr);
2748 context->optionStr = NULL;
2749 }
2750 free(context);
2751 *ctx = NULL;
2752 }
2753 }
2754
2755 /*
2756 * Can't use gss_indicate_mechs by itself to get available mechs for
2757 * SPNEGO because it will also return the SPNEGO mech and we do not
2758 * want to consider SPNEGO as an available security mech for
2759 * negotiation. For this reason, get_available_mechs will return
2760 * all available mechs except SPNEGO.
2761 *
2762 * If a ptr to a creds list is given, this function will attempt
2763 * to acquire creds for the creds given and trim the list of
2764 * returned mechanisms to only those for which creds are valid.
2765 *
2766 */
2767 static OM_uint32
get_available_mechs(OM_uint32 * minor_status,gss_name_t name,gss_cred_usage_t usage,gss_cred_id_t * creds,gss_OID_set * rmechs)2768 get_available_mechs(OM_uint32 *minor_status,
2769 gss_name_t name, gss_cred_usage_t usage,
2770 gss_cred_id_t *creds, gss_OID_set *rmechs)
2771 {
2772 unsigned int i;
2773 int found = 0;
2774 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin;
2775 gss_OID_set mechs, goodmechs;
2776
2777 major_status = gss_indicate_mechs(minor_status, &mechs);
2778
2779 if (major_status != GSS_S_COMPLETE) {
2780 return (major_status);
2781 }
2782
2783 major_status = gss_create_empty_oid_set(minor_status, rmechs);
2784
2785 if (major_status != GSS_S_COMPLETE) {
2786 (void) gss_release_oid_set(minor_status, &mechs);
2787 return (major_status);
2788 }
2789
2790 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) {
2791 if ((mechs->elements[i].length
2792 != spnego_mechanism.mech_type.length) ||
2793 memcmp(mechs->elements[i].elements,
2794 spnego_mechanism.mech_type.elements,
2795 spnego_mechanism.mech_type.length)) {
2796 /*
2797 * Solaris SPNEGO Kerberos: gss_indicate_mechs is stupid as
2798 * it never inferences any of the related OIDs of the
2799 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG.
2800 * We add KRB5_WRONG here so that old MS clients can
2801 * negotiate this mechanism, which allows extensions
2802 * in Kerberos (clock skew adjustment, refresh ccache).
2803 */
2804 if (is_kerb_mech(&mechs->elements[i])) {
2805 extern gss_OID_desc * const gss_mech_krb5_wrong;
2806
2807 major_status =
2808 gss_add_oid_set_member(minor_status,
2809 gss_mech_krb5_wrong, rmechs);
2810 }
2811
2812 major_status = gss_add_oid_set_member(minor_status,
2813 &mechs->elements[i],
2814 rmechs);
2815 if (major_status == GSS_S_COMPLETE)
2816 found++;
2817 }
2818 }
2819
2820 /*
2821 * If the caller wanted a list of creds returned,
2822 * trim the list of mechanisms down to only those
2823 * for which the creds are valid.
2824 */
2825 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) {
2826 major_status = gss_acquire_cred(minor_status,
2827 name, GSS_C_INDEFINITE,
2828 *rmechs, usage, creds,
2829 &goodmechs, NULL);
2830
2831 /*
2832 * Drop the old list in favor of the new
2833 * "trimmed" list.
2834 */
2835 (void) gss_release_oid_set(&tmpmin, rmechs);
2836 if (major_status == GSS_S_COMPLETE) {
2837 (void) gssint_copy_oid_set(&tmpmin,
2838 goodmechs, rmechs);
2839 (void) gss_release_oid_set(&tmpmin, &goodmechs);
2840 }
2841 }
2842
2843 (void) gss_release_oid_set(&tmpmin, &mechs);
2844 if (found == 0 || major_status != GSS_S_COMPLETE) {
2845 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
2846 map_errcode(minor_status);
2847 if (major_status == GSS_S_COMPLETE)
2848 major_status = GSS_S_FAILURE;
2849 }
2850
2851 return (major_status);
2852 }
2853
2854 /* following are token creation and reading routines */
2855
2856 /*
2857 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
2858 * advance the buffer, otherwise, decode the mech_oid from the buffer and
2859 * place in gss_OID.
2860 */
2861 static gss_OID
get_mech_oid(OM_uint32 * minor_status,unsigned char ** buff_in,size_t length)2862 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
2863 {
2864 OM_uint32 status;
2865 gss_OID_desc toid;
2866 gss_OID mech_out = NULL;
2867 unsigned char *start, *end;
2868
2869 if (length < 1 || **buff_in != MECH_OID)
2870 return (NULL);
2871
2872 start = *buff_in;
2873 end = start + length;
2874
2875 (*buff_in)++;
2876 toid.length = *(*buff_in)++;
2877
2878 if ((*buff_in + toid.length) > end)
2879 return (NULL);
2880
2881 toid.elements = *buff_in;
2882 *buff_in += toid.length;
2883
2884 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
2885
2886 if (status != GSS_S_COMPLETE) {
2887 map_errcode(minor_status);
2888 mech_out = NULL;
2889 }
2890
2891 return (mech_out);
2892 }
2893
2894 /*
2895 * der encode the given mechanism oid into buf_out, advancing the
2896 * buffer pointer.
2897 */
2898
2899 static int
put_mech_oid(unsigned char ** buf_out,gss_OID_const mech,unsigned int buflen)2900 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen)
2901 {
2902 if (buflen < mech->length + 2)
2903 return (-1);
2904 *(*buf_out)++ = MECH_OID;
2905 *(*buf_out)++ = (unsigned char) mech->length;
2906 memcpy((void *)(*buf_out), mech->elements, mech->length);
2907 *buf_out += mech->length;
2908 return (0);
2909 }
2910
2911 /*
2912 * verify that buff_in points to an octet string, if it does not,
2913 * return NULL and don't advance the pointer. If it is an octet string
2914 * decode buff_in into a gss_buffer_t and return it, advancing the
2915 * buffer pointer.
2916 */
2917 static gss_buffer_t
get_input_token(unsigned char ** buff_in,unsigned int buff_length)2918 get_input_token(unsigned char **buff_in, unsigned int buff_length)
2919 {
2920 gss_buffer_t input_token;
2921 unsigned int bytes;
2922
2923 if (**buff_in != OCTET_STRING)
2924 return (NULL);
2925
2926 (*buff_in)++;
2927 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
2928
2929 if (input_token == NULL)
2930 return (NULL);
2931
2932 input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes);
2933 if ((int)input_token->length == -1) {
2934 free(input_token);
2935 return (NULL);
2936 }
2937 input_token->value = malloc(input_token->length);
2938
2939 if (input_token->value == NULL) {
2940 free(input_token);
2941 return (NULL);
2942 }
2943
2944 (void) memcpy(input_token->value, *buff_in, input_token->length);
2945 *buff_in += input_token->length;
2946 return (input_token);
2947 }
2948
2949 /*
2950 * verify that the input token length is not 0. If it is, just return.
2951 * If the token length is greater than 0, der encode as an octet string
2952 * and place in buf_out, advancing buf_out.
2953 */
2954
2955 static int
put_input_token(unsigned char ** buf_out,gss_buffer_t input_token,unsigned int buflen)2956 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
2957 unsigned int buflen)
2958 {
2959 int ret;
2960
2961 /* if token length is 0, we do not want to send */
2962 if (input_token->length == 0)
2963 return (0);
2964
2965 if (input_token->length > buflen)
2966 return (-1);
2967
2968 *(*buf_out)++ = OCTET_STRING;
2969 if ((ret = gssint_put_der_length(input_token->length, buf_out,
2970 input_token->length)))
2971 return (ret);
2972 TWRITE_STR(*buf_out, input_token->value, input_token->length);
2973 return (0);
2974 }
2975
2976 /*
2977 * verify that buff_in points to a sequence of der encoding. The mech
2978 * set is the only sequence of encoded object in the token, so if it is
2979 * a sequence of encoding, decode the mechset into a gss_OID_set and
2980 * return it, advancing the buffer pointer.
2981 */
2982 static gss_OID_set
get_mech_set(OM_uint32 * minor_status,unsigned char ** buff_in,unsigned int buff_length)2983 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in,
2984 unsigned int buff_length)
2985 {
2986 gss_OID_set returned_mechSet;
2987 OM_uint32 major_status;
2988 int length; /* SUNW17PACresync */
2989 OM_uint32 bytes;
2990 OM_uint32 set_length;
2991 unsigned char *start;
2992 int i;
2993
2994 if (**buff_in != SEQUENCE_OF)
2995 return (NULL);
2996
2997 start = *buff_in;
2998 (*buff_in)++;
2999
3000 length = gssint_get_der_length(buff_in, buff_length, &bytes);
3001 if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */
3002 return (NULL);
3003
3004 major_status = gss_create_empty_oid_set(minor_status,
3005 &returned_mechSet);
3006 if (major_status != GSS_S_COMPLETE)
3007 return (NULL);
3008
3009 for (set_length = 0, i = 0; set_length < length; i++) {
3010 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
3011 buff_length - (*buff_in - start));
3012 if (temp != NULL) {
3013 major_status = gss_add_oid_set_member(minor_status,
3014 temp, &returned_mechSet);
3015 if (major_status == GSS_S_COMPLETE) {
3016 set_length += returned_mechSet->elements[i].length +2;
3017 if (generic_gss_release_oid(minor_status, &temp))
3018 map_errcode(minor_status);
3019 }
3020 }
3021 }
3022
3023 return (returned_mechSet);
3024 }
3025
3026 /*
3027 * Encode mechSet into buf.
3028 */
3029 static int
put_mech_set(gss_OID_set mechSet,gss_buffer_t buf)3030 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf)
3031 {
3032 unsigned char *ptr;
3033 unsigned int i;
3034 unsigned int tlen, ilen;
3035
3036 tlen = ilen = 0;
3037 for (i = 0; i < mechSet->count; i++) {
3038 /*
3039 * 0x06 [DER LEN] [OID]
3040 */
3041 ilen += 1 +
3042 gssint_der_length_size(mechSet->elements[i].length) +
3043 mechSet->elements[i].length;
3044 }
3045 /*
3046 * 0x30 [DER LEN]
3047 */
3048 tlen = 1 + gssint_der_length_size(ilen) + ilen;
3049 ptr = malloc(tlen);
3050 if (ptr == NULL)
3051 return -1;
3052
3053 buf->value = ptr;
3054 buf->length = tlen;
3055 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr))
3056
3057 *ptr++ = SEQUENCE_OF;
3058 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0)
3059 return -1;
3060 for (i = 0; i < mechSet->count; i++) {
3061 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) {
3062 return -1;
3063 }
3064 }
3065 return 0;
3066 #undef REMAIN
3067 }
3068
3069 /*
3070 * Verify that buff_in is pointing to a BIT_STRING with the correct
3071 * length and padding for the req_flags. If it is, decode req_flags
3072 * and return them, otherwise, return NULL.
3073 */
3074 static OM_uint32
get_req_flags(unsigned char ** buff_in,OM_uint32 bodysize,OM_uint32 * req_flags)3075 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize,
3076 OM_uint32 *req_flags)
3077 {
3078 unsigned int len;
3079
3080 if (**buff_in != (CONTEXT | 0x01))
3081 return (0);
3082
3083 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
3084 bodysize, &len) < 0)
3085 return GSS_S_DEFECTIVE_TOKEN;
3086
3087 if (*(*buff_in)++ != BIT_STRING)
3088 return GSS_S_DEFECTIVE_TOKEN;
3089
3090 if (*(*buff_in)++ != BIT_STRING_LENGTH)
3091 return GSS_S_DEFECTIVE_TOKEN;
3092
3093 if (*(*buff_in)++ != BIT_STRING_PADDING)
3094 return GSS_S_DEFECTIVE_TOKEN;
3095
3096 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1);
3097 return (0);
3098 }
3099
3100 static OM_uint32
get_negTokenInit(OM_uint32 * minor_status,gss_buffer_t buf,gss_buffer_t der_mechSet,gss_OID_set * mechSet,OM_uint32 * req_flags,gss_buffer_t * mechtok,gss_buffer_t * mechListMIC)3101 get_negTokenInit(OM_uint32 *minor_status,
3102 gss_buffer_t buf,
3103 gss_buffer_t der_mechSet,
3104 gss_OID_set *mechSet,
3105 OM_uint32 *req_flags,
3106 gss_buffer_t *mechtok,
3107 gss_buffer_t *mechListMIC)
3108 {
3109 OM_uint32 err;
3110 unsigned char *ptr, *bufstart;
3111 unsigned int len;
3112 gss_buffer_desc tmpbuf;
3113
3114 *minor_status = 0;
3115 der_mechSet->length = 0;
3116 der_mechSet->value = NULL;
3117 *mechSet = GSS_C_NO_OID_SET;
3118 *req_flags = 0;
3119 *mechtok = *mechListMIC = GSS_C_NO_BUFFER;
3120
3121 ptr = bufstart = buf->value;
3122 if ((buf->length - (ptr - bufstart)) > INT_MAX)
3123 return GSS_S_FAILURE;
3124 #define REMAIN (buf->length - (ptr - bufstart))
3125
3126 err = g_verify_token_header(gss_mech_spnego,
3127 &len, &ptr, 0, REMAIN);
3128 if (err) {
3129 *minor_status = err;
3130 map_errcode(minor_status);
3131 return GSS_S_FAILURE;
3132 }
3133 *minor_status = g_verify_neg_token_init(&ptr, REMAIN);
3134 if (*minor_status) {
3135 map_errcode(minor_status);
3136 return GSS_S_FAILURE;
3137 }
3138
3139 /* alias into input_token */
3140 tmpbuf.value = ptr;
3141 tmpbuf.length = REMAIN;
3142 *mechSet = get_mech_set(minor_status, &ptr, REMAIN);
3143 if (*mechSet == NULL)
3144 return GSS_S_FAILURE;
3145
3146 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value;
3147 der_mechSet->value = malloc(tmpbuf.length);
3148 if (der_mechSet->value == NULL)
3149 return GSS_S_FAILURE;
3150 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length);
3151 der_mechSet->length = tmpbuf.length;
3152
3153 err = get_req_flags(&ptr, REMAIN, req_flags);
3154 if (err != GSS_S_COMPLETE) {
3155 return err;
3156 }
3157 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
3158 REMAIN, &len) >= 0) {
3159 *mechtok = get_input_token(&ptr, len);
3160 if (*mechtok == GSS_C_NO_BUFFER) {
3161 return GSS_S_FAILURE;
3162 }
3163 }
3164 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
3165 REMAIN, &len) >= 0) {
3166 *mechListMIC = get_input_token(&ptr, len);
3167 if (*mechListMIC == GSS_C_NO_BUFFER) {
3168 return GSS_S_FAILURE;
3169 }
3170 }
3171 return GSS_S_COMPLETE;
3172 #undef REMAIN
3173 }
3174
3175 static OM_uint32
get_negTokenResp(OM_uint32 * minor_status,unsigned char * buf,unsigned int buflen,OM_uint32 * negState,gss_OID * supportedMech,gss_buffer_t * responseToken,gss_buffer_t * mechListMIC)3176 get_negTokenResp(OM_uint32 *minor_status,
3177 unsigned char *buf, unsigned int buflen,
3178 OM_uint32 *negState,
3179 gss_OID *supportedMech,
3180 gss_buffer_t *responseToken,
3181 gss_buffer_t *mechListMIC)
3182 {
3183 unsigned char *ptr, *bufstart;
3184 unsigned int len;
3185 int tmplen;
3186 unsigned int tag, bytes;
3187
3188 *negState = ACCEPT_DEFECTIVE_TOKEN;
3189 *supportedMech = GSS_C_NO_OID;
3190 *responseToken = *mechListMIC = GSS_C_NO_BUFFER;
3191 ptr = bufstart = buf;
3192 #define REMAIN (buflen - (ptr - bufstart))
3193
3194 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0)
3195 return GSS_S_DEFECTIVE_TOKEN;
3196 if (*ptr++ == SEQUENCE) {
3197 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3198 if (tmplen < 0)
3199 return GSS_S_DEFECTIVE_TOKEN;
3200 }
3201 if (REMAIN < 1)
3202 tag = 0;
3203 else
3204 tag = *ptr++;
3205
3206 if (tag == CONTEXT) {
3207 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3208 if (tmplen < 0)
3209 return GSS_S_DEFECTIVE_TOKEN;
3210
3211 if (g_get_tag_and_length(&ptr, ENUMERATED,
3212 REMAIN, &len) < 0)
3213 return GSS_S_DEFECTIVE_TOKEN;
3214
3215 if (len != ENUMERATION_LENGTH)
3216 return GSS_S_DEFECTIVE_TOKEN;
3217
3218 if (REMAIN < 1)
3219 return GSS_S_DEFECTIVE_TOKEN;
3220 *negState = *ptr++;
3221
3222 if (REMAIN < 1)
3223 tag = 0;
3224 else
3225 tag = *ptr++;
3226 }
3227 if (tag == (CONTEXT | 0x01)) {
3228 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3229 if (tmplen < 0)
3230 return GSS_S_DEFECTIVE_TOKEN;
3231
3232 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN);
3233 if (*supportedMech == GSS_C_NO_OID)
3234 return GSS_S_DEFECTIVE_TOKEN;
3235
3236 if (REMAIN < 1)
3237 tag = 0;
3238 else
3239 tag = *ptr++;
3240 }
3241 if (tag == (CONTEXT | 0x02)) {
3242 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3243 if (tmplen < 0)
3244 return GSS_S_DEFECTIVE_TOKEN;
3245
3246 *responseToken = get_input_token(&ptr, REMAIN);
3247 if (*responseToken == GSS_C_NO_BUFFER)
3248 return GSS_S_DEFECTIVE_TOKEN;
3249
3250 if (REMAIN < 1)
3251 tag = 0;
3252 else
3253 tag = *ptr++;
3254 }
3255 if (tag == (CONTEXT | 0x03)) {
3256 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes);
3257 if (tmplen < 0)
3258 return GSS_S_DEFECTIVE_TOKEN;
3259
3260 *mechListMIC = get_input_token(&ptr, REMAIN);
3261 if (*mechListMIC == GSS_C_NO_BUFFER)
3262 return GSS_S_DEFECTIVE_TOKEN;
3263 }
3264 return GSS_S_COMPLETE;
3265 #undef REMAIN
3266 }
3267
3268 /*
3269 * der encode the passed negResults as an ENUMERATED type and
3270 * place it in buf_out, advancing the buffer.
3271 */
3272
3273 static int
put_negResult(unsigned char ** buf_out,OM_uint32 negResult,unsigned int buflen)3274 put_negResult(unsigned char **buf_out, OM_uint32 negResult,
3275 unsigned int buflen)
3276 {
3277 if (buflen < 3)
3278 return (-1);
3279 *(*buf_out)++ = ENUMERATED;
3280 *(*buf_out)++ = ENUMERATION_LENGTH;
3281 *(*buf_out)++ = (unsigned char) negResult;
3282 return (0);
3283 }
3284
3285 /*
3286 * This routine compares the recieved mechset to the mechset that
3287 * this server can support. It looks sequentially through the mechset
3288 * and the first one that matches what the server can support is
3289 * chosen as the negotiated mechanism. If one is found, negResult
3290 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if
3291 * it's not the first mech, otherwise we return NULL and negResult
3292 * is set to REJECT.
3293 *
3294 * NOTE: There is currently no way to specify a preference order of
3295 * mechanisms supported by the acceptor.
3296 */
3297 static gss_OID
negotiate_mech_type(OM_uint32 * minor_status,gss_OID_set supported_mechSet,gss_OID_set mechset,OM_uint32 * negResult)3298 negotiate_mech_type(OM_uint32 *minor_status,
3299 gss_OID_set supported_mechSet,
3300 gss_OID_set mechset,
3301 OM_uint32 *negResult)
3302 {
3303 gss_OID returned_mech;
3304 OM_uint32 status;
3305 int present;
3306 unsigned int i;
3307
3308 for (i = 0; i < mechset->count; i++) {
3309 gss_OID mech_oid = &mechset->elements[i];
3310
3311 /*
3312 * Solaris SPNEGO Kerberos: MIT compares against MS' wrong OID, but
3313 * we actually want to select it if the client supports, as this
3314 * will enable features on MS clients that allow credential
3315 * refresh on rekeying and caching system times from servers.
3316 */
3317 #if 0
3318 /* Accept wrong mechanism OID from MS clients */
3319 if (mech_oid->length == gss_mech_krb5_wrong_oid.length &&
3320 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0)
3321 mech_oid = (gss_OID)&gss_mech_krb5_oid;
3322 #endif
3323
3324 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present);
3325 if (!present)
3326 continue;
3327
3328 if (i == 0)
3329 *negResult = ACCEPT_INCOMPLETE;
3330 else
3331 *negResult = REQUEST_MIC;
3332
3333 status = generic_gss_copy_oid(minor_status,
3334 &mechset->elements[i],
3335 &returned_mech);
3336 if (status != GSS_S_COMPLETE) {
3337 *negResult = REJECT;
3338 map_errcode(minor_status);
3339 return (NULL);
3340 }
3341 return (returned_mech);
3342 }
3343 /* Solaris SPNEGO */
3344 *minor_status= ERR_SPNEGO_NEGOTIATION_FAILED;
3345
3346 *negResult = REJECT;
3347 return (NULL);
3348 }
3349
3350 /*
3351 * the next two routines make a token buffer suitable for
3352 * spnego_gss_display_status. These currently take the string
3353 * in name and place it in the token. Eventually, if
3354 * spnego_gss_display_status returns valid error messages,
3355 * these routines will be changes to return the error string.
3356 */
3357 static spnego_token_t
make_spnego_token(char * name)3358 make_spnego_token(char *name)
3359 {
3360 return (spnego_token_t)strdup(name);
3361 }
3362
3363 static gss_buffer_desc
make_err_msg(char * name)3364 make_err_msg(char *name)
3365 {
3366 gss_buffer_desc buffer;
3367
3368 if (name == NULL) {
3369 buffer.length = 0;
3370 buffer.value = NULL;
3371 } else {
3372 buffer.length = strlen(name)+1;
3373 buffer.value = make_spnego_token(name);
3374 }
3375
3376 return (buffer);
3377 }
3378
3379 /*
3380 * Create the client side spnego token passed back to gss_init_sec_context
3381 * and eventually up to the application program and over to the server.
3382 *
3383 * Use DER rules, definite length method per RFC 2478
3384 */
3385 static int
make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,int negHintsCompat,gss_buffer_t mechListMIC,OM_uint32 req_flags,gss_buffer_t data,send_token_flag sendtoken,gss_buffer_t outbuf)3386 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
3387 int negHintsCompat,
3388 gss_buffer_t mechListMIC, OM_uint32 req_flags,
3389 gss_buffer_t data, send_token_flag sendtoken,
3390 gss_buffer_t outbuf)
3391 {
3392 int ret = 0;
3393 unsigned int tlen, dataLen = 0;
3394 unsigned int negTokenInitSize = 0;
3395 unsigned int negTokenInitSeqSize = 0;
3396 unsigned int negTokenInitContSize = 0;
3397 unsigned int rspTokenSize = 0;
3398 unsigned int mechListTokenSize = 0;
3399 unsigned int micTokenSize = 0;
3400 unsigned char *t;
3401 unsigned char *ptr;
3402
3403 if (outbuf == GSS_C_NO_BUFFER)
3404 return (-1);
3405
3406 outbuf->length = 0;
3407 outbuf->value = NULL;
3408
3409 /* calculate the data length */
3410
3411 /*
3412 * 0xa0 [DER LEN] [mechTypes]
3413 */
3414 mechListTokenSize = 1 +
3415 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) +
3416 spnego_ctx->DER_mechTypes.length;
3417 dataLen += mechListTokenSize;
3418
3419 /*
3420 * If a token from gss_init_sec_context exists,
3421 * add the length of the token + the ASN.1 overhead
3422 */
3423 if (data != NULL) {
3424 /*
3425 * Encoded in final output as:
3426 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
3427 * -----s--------|--------s2----------
3428 */
3429 rspTokenSize = 1 +
3430 gssint_der_length_size(data->length) +
3431 data->length;
3432 dataLen += 1 + gssint_der_length_size(rspTokenSize) +
3433 rspTokenSize;
3434 }
3435
3436 if (mechListMIC) {
3437 /*
3438 * Encoded in final output as:
3439 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
3440 * --s-- -----tlen------------
3441 */
3442 micTokenSize = 1 +
3443 gssint_der_length_size(mechListMIC->length) +
3444 mechListMIC->length;
3445 dataLen += 1 +
3446 gssint_der_length_size(micTokenSize) +
3447 micTokenSize;
3448 }
3449
3450 /*
3451 * Add size of DER encoding
3452 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
3453 * 0x30 [DER_LEN] [data]
3454 *
3455 */
3456 negTokenInitContSize = dataLen;
3457 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen;
3458 dataLen = negTokenInitSeqSize;
3459
3460 /*
3461 * negTokenInitSize indicates the bytes needed to
3462 * hold the ASN.1 encoding of the entire NegTokenInit
3463 * SEQUENCE.
3464 * 0xa0 [DER_LEN] + data
3465 *
3466 */
3467 negTokenInitSize = 1 +
3468 gssint_der_length_size(negTokenInitSeqSize) +
3469 negTokenInitSeqSize;
3470
3471 tlen = g_token_size(gss_mech_spnego, negTokenInitSize);
3472
3473 t = (unsigned char *) malloc(tlen);
3474
3475 if (t == NULL) {
3476 return (-1);
3477 }
3478
3479 ptr = t;
3480
3481 /* create the message */
3482 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize,
3483 &ptr, tlen)))
3484 goto errout;
3485
3486 *ptr++ = CONTEXT; /* NegotiationToken identifier */
3487 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen)))
3488 goto errout;
3489
3490 *ptr++ = SEQUENCE;
3491 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr,
3492 tlen - (int)(ptr-t))))
3493 goto errout;
3494
3495 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */
3496 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length,
3497 &ptr, tlen - (int)(ptr-t))))
3498 goto errout;
3499
3500 /* We already encoded the MechSetList */
3501 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
3502 spnego_ctx->DER_mechTypes.length);
3503
3504 ptr += spnego_ctx->DER_mechTypes.length;
3505
3506 if (data != NULL) {
3507 *ptr++ = CONTEXT | 0x02;
3508 if ((ret = gssint_put_der_length(rspTokenSize,
3509 &ptr, tlen - (int)(ptr - t))))
3510 goto errout;
3511
3512 if ((ret = put_input_token(&ptr, data,
3513 tlen - (int)(ptr - t))))
3514 goto errout;
3515 }
3516
3517 if (mechListMIC != GSS_C_NO_BUFFER) {
3518 *ptr++ = CONTEXT | 0x03;
3519 if ((ret = gssint_put_der_length(micTokenSize,
3520 &ptr, tlen - (int)(ptr - t))))
3521 goto errout;
3522
3523 if (negHintsCompat) {
3524 ret = put_neg_hints(&ptr, mechListMIC,
3525 tlen - (int)(ptr - t));
3526 if (ret)
3527 goto errout;
3528 } else if ((ret = put_input_token(&ptr, mechListMIC,
3529 tlen - (int)(ptr - t))))
3530 goto errout;
3531 }
3532
3533 errout:
3534 if (ret != 0) {
3535 if (t)
3536 free(t);
3537 t = NULL;
3538 tlen = 0;
3539 }
3540 outbuf->length = tlen;
3541 outbuf->value = (void *) t;
3542
3543 return (ret);
3544 }
3545
3546 /*
3547 * create the server side spnego token passed back to
3548 * gss_accept_sec_context and eventually up to the application program
3549 * and over to the client.
3550 */
3551 static int
make_spnego_tokenTarg_msg(OM_uint32 status,gss_OID mech_wanted,gss_buffer_t data,gss_buffer_t mechListMIC,send_token_flag sendtoken,gss_buffer_t outbuf)3552 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
3553 gss_buffer_t data, gss_buffer_t mechListMIC,
3554 send_token_flag sendtoken,
3555 gss_buffer_t outbuf)
3556 {
3557 unsigned int tlen = 0;
3558 unsigned int ret = 0;
3559 unsigned int NegTokenTargSize = 0;
3560 unsigned int NegTokenSize = 0;
3561 unsigned int rspTokenSize = 0;
3562 unsigned int micTokenSize = 0;
3563 unsigned int dataLen = 0;
3564 unsigned char *t;
3565 unsigned char *ptr;
3566
3567 if (outbuf == GSS_C_NO_BUFFER)
3568 return (GSS_S_DEFECTIVE_TOKEN);
3569
3570 outbuf->length = 0;
3571 outbuf->value = NULL;
3572
3573 /*
3574 * ASN.1 encoding of the negResult
3575 * ENUMERATED type is 3 bytes
3576 * ENUMERATED TAG, Length, Value,
3577 * Plus 2 bytes for the CONTEXT id and length.
3578 */
3579 dataLen = 5;
3580
3581 /*
3582 * calculate data length
3583 *
3584 * If this is the initial token, include length of
3585 * mech_type and the negotiation result fields.
3586 */
3587 if (sendtoken == INIT_TOKEN_SEND) {
3588 int mechlistTokenSize;
3589 /*
3590 * 1 byte for the CONTEXT ID(0xa0),
3591 * 1 byte for the OID ID(0x06)
3592 * 1 byte for OID Length field
3593 * Plus the rest... (OID Length, OID value)
3594 */
3595 mechlistTokenSize = 3 + mech_wanted->length +
3596 gssint_der_length_size(mech_wanted->length);
3597
3598 dataLen += mechlistTokenSize;
3599 }
3600 if (data != NULL && data->length > 0) {
3601 /* Length of the inner token */
3602 rspTokenSize = 1 + gssint_der_length_size(data->length) +
3603 data->length;
3604
3605 dataLen += rspTokenSize;
3606
3607 /* Length of the outer token */
3608 dataLen += 1 + gssint_der_length_size(rspTokenSize);
3609 }
3610 if (mechListMIC != NULL) {
3611
3612 /* Length of the inner token */
3613 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) +
3614 mechListMIC->length;
3615
3616 dataLen += micTokenSize;
3617
3618 /* Length of the outer token */
3619 dataLen += 1 + gssint_der_length_size(micTokenSize);
3620 }
3621 /*
3622 * Add size of DER encoded:
3623 * NegTokenTarg [ SEQUENCE ] of
3624 * NegResult[0] ENUMERATED {
3625 * accept_completed(0),
3626 * accept_incomplete(1),
3627 * reject(2) }
3628 * supportedMech [1] MechType OPTIONAL,
3629 * responseToken [2] OCTET STRING OPTIONAL,
3630 * mechListMIC [3] OCTET STRING OPTIONAL
3631 *
3632 * size = data->length + MechListMic + SupportedMech len +
3633 * Result Length + ASN.1 overhead
3634 */
3635 NegTokenTargSize = dataLen;
3636 dataLen += 1 + gssint_der_length_size(NegTokenTargSize);
3637
3638 /*
3639 * NegotiationToken [ CHOICE ]{
3640 * negTokenInit [0] NegTokenInit,
3641 * negTokenTarg [1] NegTokenTarg }
3642 */
3643 NegTokenSize = dataLen;
3644 dataLen += 1 + gssint_der_length_size(NegTokenSize);
3645
3646 tlen = dataLen;
3647 t = (unsigned char *) malloc(tlen);
3648
3649 if (t == NULL) {
3650 ret = GSS_S_DEFECTIVE_TOKEN;
3651 goto errout;
3652 }
3653
3654 ptr = t;
3655
3656 /*
3657 * Indicate that we are sending CHOICE 1
3658 * (NegTokenTarg)
3659 */
3660 *ptr++ = CONTEXT | 0x01;
3661 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) {
3662 ret = GSS_S_DEFECTIVE_TOKEN;
3663 goto errout;
3664 }
3665 *ptr++ = SEQUENCE;
3666 if (gssint_put_der_length(NegTokenTargSize, &ptr,
3667 tlen - (int)(ptr-t)) < 0) {
3668 ret = GSS_S_DEFECTIVE_TOKEN;
3669 goto errout;
3670 }
3671
3672 /*
3673 * First field of the NegTokenTarg SEQUENCE
3674 * is the ENUMERATED NegResult.
3675 */
3676 *ptr++ = CONTEXT;
3677 if (gssint_put_der_length(3, &ptr,
3678 tlen - (int)(ptr-t)) < 0) {
3679 ret = GSS_S_DEFECTIVE_TOKEN;
3680 goto errout;
3681 }
3682 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) {
3683 ret = GSS_S_DEFECTIVE_TOKEN;
3684 goto errout;
3685 }
3686 if (sendtoken == INIT_TOKEN_SEND) {
3687 /*
3688 * Next, is the Supported MechType
3689 */
3690 *ptr++ = CONTEXT | 0x01;
3691 if (gssint_put_der_length(mech_wanted->length + 2,
3692 &ptr,
3693 tlen - (int)(ptr - t)) < 0) {
3694 ret = GSS_S_DEFECTIVE_TOKEN;
3695 goto errout;
3696 }
3697 if (put_mech_oid(&ptr, mech_wanted,
3698 tlen - (int)(ptr - t)) < 0) {
3699 ret = GSS_S_DEFECTIVE_TOKEN;
3700 goto errout;
3701 }
3702 }
3703 if (data != NULL && data->length > 0) {
3704 *ptr++ = CONTEXT | 0x02;
3705 if (gssint_put_der_length(rspTokenSize, &ptr,
3706 tlen - (int)(ptr - t)) < 0) {
3707 ret = GSS_S_DEFECTIVE_TOKEN;
3708 goto errout;
3709 }
3710 if (put_input_token(&ptr, data,
3711 tlen - (int)(ptr - t)) < 0) {
3712 ret = GSS_S_DEFECTIVE_TOKEN;
3713 goto errout;
3714 }
3715 }
3716 if (mechListMIC != NULL) {
3717 *ptr++ = CONTEXT | 0x03;
3718 if (gssint_put_der_length(micTokenSize, &ptr,
3719 tlen - (int)(ptr - t)) < 0) {
3720 ret = GSS_S_DEFECTIVE_TOKEN;
3721 goto errout;
3722 }
3723 if (put_input_token(&ptr, mechListMIC,
3724 tlen - (int)(ptr - t)) < 0) {
3725 ret = GSS_S_DEFECTIVE_TOKEN;
3726 goto errout;
3727 }
3728 }
3729 ret = GSS_S_COMPLETE;
3730 errout:
3731 if (ret != GSS_S_COMPLETE) {
3732 if (t)
3733 free(t);
3734 } else {
3735 outbuf->length = ptr - t;
3736 outbuf->value = (void *) t;
3737 }
3738
3739 return (ret);
3740 }
3741
3742 /* determine size of token */
3743 static int
g_token_size(gss_OID_const mech,unsigned int body_size)3744 g_token_size(gss_OID_const mech, unsigned int body_size)
3745 {
3746 int hdrsize;
3747
3748 /*
3749 * Initialize the header size to the
3750 * MECH_OID byte + the bytes needed to indicate the
3751 * length of the OID + the OID itself.
3752 *
3753 * 0x06 [MECHLENFIELD] MECHDATA
3754 */
3755 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3756
3757 /*
3758 * Now add the bytes needed for the initial header
3759 * token bytes:
3760 * 0x60 + [DER_LEN] + HDRSIZE
3761 */
3762 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize);
3763
3764 return (hdrsize + body_size);
3765 }
3766
3767 /*
3768 * generate token header.
3769 *
3770 * Use DER Definite Length method per RFC2478
3771 * Use of indefinite length encoding will not be compatible
3772 * with Microsoft or others that actually follow the spec.
3773 */
3774 static int
g_make_token_header(gss_OID_const mech,unsigned int body_size,unsigned char ** buf,unsigned int totallen)3775 g_make_token_header(gss_OID_const mech,
3776 unsigned int body_size,
3777 unsigned char **buf,
3778 unsigned int totallen)
3779 {
3780 int ret = 0;
3781 unsigned int hdrsize;
3782 unsigned char *p = *buf;
3783
3784 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length;
3785
3786 *(*buf)++ = HEADER_ID;
3787 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen)))
3788 return (ret);
3789
3790 *(*buf)++ = MECH_OID;
3791 if ((ret = gssint_put_der_length(mech->length, buf,
3792 totallen - (int)(p - *buf))))
3793 return (ret);
3794 TWRITE_STR(*buf, mech->elements, mech->length);
3795 return (0);
3796 }
3797
3798 /*
3799 * NOTE: This checks that the length returned by
3800 * gssint_get_der_length() is not greater than the number of octets
3801 * remaining, even though gssint_get_der_length() already checks, in
3802 * theory.
3803 */
3804 static int
g_get_tag_and_length(unsigned char ** buf,int tag,unsigned int buflen,unsigned int * outlen)3805 g_get_tag_and_length(unsigned char **buf, int tag,
3806 unsigned int buflen, unsigned int *outlen)
3807 {
3808 unsigned char *ptr = *buf;
3809 int ret = -1; /* pessimists, assume failure ! */
3810 unsigned int encoded_len;
3811 unsigned int tmplen = 0;
3812
3813 *outlen = 0;
3814 if (buflen > 1 && *ptr == tag) {
3815 ptr++;
3816 tmplen = gssint_get_der_length(&ptr, buflen - 1,
3817 &encoded_len);
3818 if (tmplen < 0) {
3819 ret = -1;
3820 } else if (tmplen > buflen - (ptr - *buf)) {
3821 ret = -1;
3822 } else
3823 ret = 0;
3824 }
3825 *outlen = tmplen;
3826 *buf = ptr;
3827 return (ret);
3828 }
3829
3830 static int
g_verify_neg_token_init(unsigned char ** buf_in,unsigned int cur_size)3831 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size)
3832 {
3833 unsigned char *buf = *buf_in;
3834 unsigned char *endptr = buf + cur_size;
3835 unsigned int seqsize;
3836 int ret = 0;
3837 unsigned int bytes;
3838
3839 /*
3840 * Verify this is a NegotiationToken type token
3841 * - check for a0(context specific identifier)
3842 * - get length and verify that enoughd ata exists
3843 */
3844 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
3845 return (G_BAD_TOK_HEADER);
3846
3847 cur_size = seqsize; /* should indicate bytes remaining */
3848
3849 /*
3850 * Verify the next piece, it should identify this as
3851 * a strucure of type NegTokenInit.
3852 */
3853 if (*buf++ == SEQUENCE) {
3854 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3855 return (G_BAD_TOK_HEADER);
3856 /*
3857 * Make sure we have the entire buffer as described
3858 */
3859 if (buf + seqsize > endptr)
3860 return (G_BAD_TOK_HEADER);
3861 } else {
3862 return (G_BAD_TOK_HEADER);
3863 }
3864
3865 cur_size = seqsize; /* should indicate bytes remaining */
3866
3867 /*
3868 * Verify that the first blob is a sequence of mechTypes
3869 */
3870 if (*buf++ == CONTEXT) {
3871 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0)
3872 return (G_BAD_TOK_HEADER);
3873 /*
3874 * Make sure we have the entire buffer as described
3875 */
3876 if (buf + bytes > endptr)
3877 return (G_BAD_TOK_HEADER);
3878 } else {
3879 return (G_BAD_TOK_HEADER);
3880 }
3881
3882 /*
3883 * At this point, *buf should be at the beginning of the
3884 * DER encoded list of mech types that are to be negotiated.
3885 */
3886 *buf_in = buf;
3887
3888 return (ret);
3889
3890 }
3891
3892 /* verify token header. */
3893 static int
g_verify_token_header(gss_OID_const mech,unsigned int * body_size,unsigned char ** buf_in,int tok_type,unsigned int toksize)3894 g_verify_token_header(gss_OID_const mech,
3895 unsigned int *body_size,
3896 unsigned char **buf_in,
3897 int tok_type,
3898 unsigned int toksize)
3899 {
3900 unsigned char *buf = *buf_in;
3901 int seqsize;
3902 gss_OID_desc toid;
3903 int ret = 0;
3904 unsigned int bytes;
3905
3906 if (toksize-- < 1)
3907 return (G_BAD_TOK_HEADER);
3908
3909 if (*buf++ != HEADER_ID)
3910 return (G_BAD_TOK_HEADER);
3911
3912 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0)
3913 return (G_BAD_TOK_HEADER);
3914
3915 if ((seqsize + bytes) != toksize)
3916 return (G_BAD_TOK_HEADER);
3917
3918 if (toksize-- < 1)
3919 return (G_BAD_TOK_HEADER);
3920
3921
3922 if (*buf++ != MECH_OID)
3923 return (G_BAD_TOK_HEADER);
3924
3925 if (toksize-- < 1)
3926 return (G_BAD_TOK_HEADER);
3927
3928 toid.length = *buf++;
3929
3930 if (toksize < toid.length)
3931 return (G_BAD_TOK_HEADER);
3932 else
3933 toksize -= toid.length;
3934
3935 toid.elements = buf;
3936 buf += toid.length;
3937
3938 if (!g_OID_equal(&toid, mech))
3939 ret = G_WRONG_MECH;
3940
3941 /*
3942 * G_WRONG_MECH is not returned immediately because it's more important
3943 * to return G_BAD_TOK_HEADER if the token header is in fact bad
3944 */
3945 if (toksize < 2)
3946 return (G_BAD_TOK_HEADER);
3947 else
3948 toksize -= 2;
3949
3950 if (!ret) {
3951 *buf_in = buf;
3952 *body_size = toksize;
3953 }
3954
3955 return (ret);
3956 }
3957
3958 /*
3959 * Return non-zero if the oid is one of the kerberos mech oids,
3960 * otherwise return zero.
3961 *
3962 * N.B. There are 3 oids that represent the kerberos mech:
3963 * RFC-specified GSS_MECH_KRB5_OID,
3964 * Old pre-RFC GSS_MECH_KRB5_OLD_OID,
3965 * Incorrect MS GSS_MECH_KRB5_WRONG_OID
3966 */
3967
3968 static int
is_kerb_mech(gss_OID oid)3969 is_kerb_mech(gss_OID oid)
3970 {
3971 int answer = 0;
3972 OM_uint32 minor;
3973 extern const gss_OID_set_desc * const gss_mech_set_krb5_both;
3974
3975 (void) gss_test_oid_set_member(&minor,
3976 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer);
3977
3978 return (answer);
3979 }
3980