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