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