Print this page
6817447 libgss and various mechs are hiding both the real minor_status and the error token
6405422 Solaris acceptors fail in AD-KDC environments when using non-"host" services (e.g. "cifs")
6824434 Unable to accept context establishment initiated by Windows 2000 clients
6787343 kclient's site lookups fail in certain network environments
6692646 kclient should output errors to stderr
6525327 kinit failed when arcfour-hmac-md5-exp was used for the principal's key
6745582 SUNWkdcu missing package dependencies after kclientv2 integration
| 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 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
|
↓ open down ↓ |
10 lines elided |
↑ open up ↑ |
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 +
21 22 /*
22 - * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 + * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 24 * Use is subject to license terms.
24 25 *
25 26 * A module that implements the spnego security mechanism.
26 27 * It is used to negotiate the security mechanism between
27 28 * peers using the GSS-API.
28 29 *
29 30 */
30 31
31 -#pragma ident "%Z%%M% %I% %E% SMI"
32 -
33 32 #include <stdio.h>
34 33 #include <stdlib.h>
35 34 #include <errno.h>
36 35 #include "gssapiP_spnego.h"
37 36 #include <mechglueP.h>
38 37 #include <gssapi_err_generic.h>
39 38 #include <rpc/types.h>
40 39 #include <libintl.h>
41 40
42 41 /* der routines defined in libgss */
43 42 extern unsigned int der_length_size(OM_uint32);
44 43 extern int get_der_length(uchar_t **, OM_uint32, OM_uint32*);
45 44 extern int put_der_length(OM_uint32, uchar_t **, OM_uint32);
46 45
47 46
48 47 /* private routines for spnego_mechanism */
49 48 static spnego_token_t make_spnego_token(char *);
50 49 static gss_buffer_desc make_err_msg(char *);
51 50 static int g_token_size(gss_OID, OM_uint32);
52 51 static int g_make_token_header(gss_OID, int, uchar_t **, int);
53 52 static int g_verify_token_header(gss_OID, int *, uchar_t **, int, int);
54 53 static int g_verify_neg_token_init(uchar_t **, int);
55 54 static OM_uint32 get_negResult(unsigned char **, int);
56 55 static gss_OID get_mech_oid(OM_uint32 *, uchar_t **, size_t);
57 56 static gss_buffer_t get_input_token(unsigned char **, int);
58 57 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, int);
59 58 static OM_uint32 get_req_flags(uchar_t **, int *, OM_uint32 *);
60 59 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t,
61 60 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *);
62 61 static void release_spnego_ctx(spnego_gss_ctx_id_t *);
63 62 static void check_spnego_options(spnego_gss_ctx_id_t);
64 63 static spnego_gss_ctx_id_t create_spnego_ctx(void);
65 64 static int put_mech_set(uchar_t **, gss_OID_set, int);
66 65 static int put_input_token(uchar_t **, gss_buffer_t, int);
67 66 static int put_mech_oid(uchar_t **, gss_OID_desc *, int);
68 67 static int put_negResult(uchar_t **, OM_uint32, int);
69 68
70 69 static gss_OID
71 70 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set,
72 71 OM_uint32 *, bool_t *);
73 72 static int
74 73 g_get_tag_and_length(unsigned char **, uchar_t, int, int *);
75 74
76 75 static int
77 76 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, gss_OID_set,
78 77 gss_buffer_t, send_token_flag,
79 78 gss_buffer_t);
80 79 static int
81 80 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t,
82 81 gss_buffer_t, send_token_flag, int,
83 82 gss_buffer_t);
84 83
85 84 /*
86 85 * The Mech OID for SPNEGO:
87 86 * { iso(1) org(3) dod(6) internet(1) security(5)
88 87 * mechanism(5) spnego(2) }
89 88 */
90 89 static struct gss_config spnego_mechanism =
91 90 {{SPNEGO_OID_LENGTH, SPNEGO_OID},
92 91 NULL,
93 92 spnego_gss_acquire_cred,
94 93 spnego_gss_release_cred,
95 94 spnego_gss_init_sec_context,
96 95 spnego_gss_accept_sec_context,
97 96 /* EXPORT DELETE START */ /* CRYPT DELETE START */
98 97 spnego_gss_unseal, /* gss_unseal */
99 98 /* EXPORT DELETE END */ /* CRYPT DELETE END */
100 99 NULL, /* gss_process_context_token */
101 100 spnego_gss_delete_sec_context, /* gss_delete_sec_context */
102 101 spnego_gss_context_time, /* gss_context_time */
103 102 spnego_gss_display_status,
104 103 NULL, /* gss_indicate_mechs */
105 104 NULL, /* gss_compare_name */
106 105 spnego_gss_display_name,
107 106 spnego_gss_import_name,
108 107 spnego_gss_release_name,
109 108 spnego_gss_inquire_cred, /* gss_inquire_cred */
110 109 NULL, /* gss_add_cred */
111 110 /* EXPORT DELETE START */ /* CRYPT DELETE START */
112 111 spnego_gss_seal, /* gss_seal */
113 112 /* EXPORT DELETE END */ /* CRYPT DELETE END */
114 113 spnego_gss_export_sec_context, /* gss_export_sec_context */
115 114 spnego_gss_import_sec_context, /* gss_import_sec_context */
116 115 NULL, /* gss_inquire_cred_by_mech */
117 116 spnego_gss_inquire_names_for_mech,
118 117 spnego_gss_inquire_context, /* gss_inquire_context */
119 118 NULL, /* gss_internal_release_oid */
120 119 spnego_gss_wrap_size_limit, /* gss_wrap_size_limit */
121 120 NULL, /* gss_pname_to_uid */
122 121 NULL, /* __gss_userok */
123 122 NULL, /* gss_export_name */
124 123 /* EXPORT DELETE START */
125 124 /* CRYPT DELETE START */
126 125 #if 0
127 126 /* CRYPT DELETE END */
128 127 spnego_gss_seal,
129 128 spnego_gss_unseal,
130 129 /* CRYPT DELETE START */
131 130 #endif
132 131 /* CRYPT DELETE END */
133 132 /* EXPORT DELETE END */
134 133 spnego_gss_sign, /* gss_sign */
135 134 spnego_gss_verify, /* gss_verify */
136 135 NULL, /* gss_store_cred */
137 136 };
138 137
139 138 gss_mechanism
140 139 gss_mech_initialize(const gss_OID oid)
141 140 {
142 141 dsyslog("Entering gss_mech_initialize\n");
143 142
144 143 if (oid == NULL ||
145 144 !g_OID_equal(oid, &spnego_mechanism.mech_type)) {
146 145 dsyslog("invalid spnego mechanism oid.\n");
147 146 return (NULL);
148 147 }
149 148
150 149 dsyslog("Leaving gss_mech_initialize\n");
151 150 return (&spnego_mechanism);
152 151 }
153 152
154 153 /*ARGSUSED*/
155 154 OM_uint32
156 155 spnego_gss_acquire_cred(void *ctx,
157 156 OM_uint32 *minor_status,
158 157 gss_name_t desired_name,
159 158 OM_uint32 time_req,
160 159 gss_OID_set desired_mechs,
161 160 gss_cred_usage_t cred_usage,
162 161 gss_cred_id_t *output_cred_handle,
163 162 gss_OID_set *actual_mechs,
164 163 OM_uint32 *time_rec)
165 164 {
166 165 OM_uint32 status;
167 166 gss_OID_set amechs;
168 167 dsyslog("Entering spnego_gss_acquire_cred\n");
169 168
170 169 if (actual_mechs)
171 170 *actual_mechs = NULL;
172 171
173 172 if (time_rec)
174 173 *time_rec = 0;
175 174
176 175 /*
177 176 * If the user did not specify a list of mechs,
178 177 * use get_available_mechs to collect a list of
179 178 * mechs for which creds are available.
180 179 */
181 180 if (desired_mechs == GSS_C_NULL_OID_SET) {
182 181 status = get_available_mechs(minor_status,
183 182 desired_name, cred_usage,
184 183 output_cred_handle, &amechs);
185 184 } else {
186 185 /*
187 186 * The caller gave a specific list of mechanisms,
188 187 * so just get whatever creds are available.
189 188 * gss_acquire_creds will return the subset of mechs for
190 189 * which the given 'output_cred_handle' is valid.
191 190 */
192 191 status = gss_acquire_cred(minor_status,
193 192 desired_name, time_req, desired_mechs, cred_usage,
194 193 output_cred_handle, &amechs, time_rec);
195 194 }
196 195
197 196 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) {
198 197 (void) gss_copy_oid_set(minor_status, amechs, actual_mechs);
199 198 }
200 199 (void) gss_release_oid_set(minor_status, &amechs);
201 200
202 201 dsyslog("Leaving spnego_gss_acquire_cred\n");
203 202 return (status);
204 203 }
205 204
206 205 /*ARGSUSED*/
207 206 OM_uint32
208 207 spnego_gss_release_cred(void *ctx,
209 208 OM_uint32 *minor_status,
210 209 gss_cred_id_t *cred_handle)
211 210 {
212 211 OM_uint32 status;
213 212
214 213 dsyslog("Entering spnego_gss_release_cred\n");
215 214
216 215 if (minor_status == NULL || cred_handle == NULL)
217 216 return (GSS_S_CALL_INACCESSIBLE_WRITE);
218 217
219 218 *minor_status = 0;
220 219
221 220 if (*cred_handle == GSS_C_NO_CREDENTIAL)
222 221 return (GSS_S_COMPLETE);
223 222
224 223 status = gss_release_cred(minor_status, cred_handle);
225 224
226 225 dsyslog("Leaving spnego_gss_release_cred\n");
227 226 return (status);
228 227 }
229 228
230 229 static void
231 230 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx)
232 231 {
233 232 spnego_ctx->optionStr = __gss_get_modOptions(
234 233 (const gss_OID)&spnego_oids[0]);
235 234 if (spnego_ctx->optionStr != NULL &&
236 235 strstr(spnego_ctx->optionStr, "msinterop")) {
237 236 spnego_ctx->MS_Interop = 1;
238 237 } else {
239 238 spnego_ctx->MS_Interop = 0;
240 239 }
241 240 }
242 241
243 242 static spnego_gss_ctx_id_t
244 243 create_spnego_ctx(void)
245 244 {
246 245 spnego_gss_ctx_id_t spnego_ctx = NULL;
247 246 spnego_ctx = (spnego_gss_ctx_id_t)
248 247 malloc(sizeof (spnego_gss_ctx_id_rec));
249 248
250 249 if (spnego_ctx == NULL) {
251 250 return (NULL);
252 251 }
253 252
254 253 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
255 254 spnego_ctx->internal_mech = NULL;
256 255 spnego_ctx->optionStr = NULL;
257 256 spnego_ctx->optimistic = 0;
258 257 spnego_ctx->MS_Interop = 0;
259 258 spnego_ctx->DER_mechTypes.length = NULL;
260 259 spnego_ctx->DER_mechTypes.value = GSS_C_NO_BUFFER;
261 260
262 261 check_spnego_options(spnego_ctx);
263 262
264 263 return (spnego_ctx);
265 264 }
266 265
267 266 /*ARGSUSED*/
268 267 OM_uint32
269 268 spnego_gss_init_sec_context(void *ct,
270 269 OM_uint32 *minor_status,
271 270 gss_cred_id_t claimant_cred_handle,
272 271 gss_ctx_id_t *context_handle,
273 272 gss_name_t target_name,
274 273 gss_OID mech_type,
275 274 OM_uint32 req_flags,
276 275 OM_uint32 time_req,
277 276 gss_channel_bindings_t input_chan_bindings,
278 277 gss_buffer_t input_token,
279 278 gss_OID *actual_mech,
280 279 gss_buffer_t output_token,
281 280 OM_uint32 *ret_flags,
282 281 OM_uint32 *time_rec)
283 282 {
284 283 OM_uint32 ret = 0;
285 284 OM_uint32 status = 0;
286 285 OM_uint32 mstat;
287 286 OM_uint32 local_ret_flags = 0;
288 287
289 288 /*
290 289 * send_token is used to indicate in later steps
291 290 * what type of token, if any should be sent or processed.
292 291 * NO_TOKEN_SEND = no token should be sent
293 292 * INIT_TOKEN_SEND = initial token will be sent
294 293 * CONT_TOKEN_SEND = continuing tokens to be sent
295 294 * CHECK_MIC = no token to be sent, but have a MIC to check.
296 295 */
297 296 send_token_flag send_token = NO_TOKEN_SEND;
298 297
299 298 gss_OID_set mechSet;
300 299 spnego_gss_ctx_id_t spnego_ctx = NULL;
301 300 gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
302 301 gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
303 302 gss_buffer_t mechListMIC = NULL;
304 303 gss_cred_id_t *credlistptr = NULL, credlist;
305 304 gss_qop_t *qop_state = NULL;
306 305 unsigned char *ptr;
307 306 int len;
308 307
309 308 dsyslog("Entering init_sec_context\n");
310 309
311 310 if (context_handle == NULL)
312 311 return (GSS_S_NO_CONTEXT);
313 312
314 313 *minor_status = 0;
315 314 output_token->length = 0;
316 315 output_token->value = NULL;
317 316
318 317 if (actual_mech)
319 318 *actual_mech = NULL;
320 319
321 320 if (*context_handle == GSS_C_NO_CONTEXT) {
322 321
323 322 /* determine negotiation mech set */
324 323 if (claimant_cred_handle == GSS_C_NO_CREDENTIAL) {
325 324 credlistptr = &credlist;
326 325
327 326 mstat = get_available_mechs(minor_status,
328 327 GSS_C_NO_NAME, GSS_C_INITIATE,
329 328 credlistptr, &mechSet);
330 329 } else {
331 330 /*
332 331 * Use the list of mechs included in the
333 332 * cred that we were given.
334 333 */
335 334 mstat = gss_inquire_cred(minor_status,
336 335 claimant_cred_handle, NULL, NULL,
337 336 NULL, &mechSet);
338 337 }
339 338 if (mstat != GSS_S_COMPLETE)
340 339 return (mstat);
341 340
342 341 if ((spnego_ctx = create_spnego_ctx()) == NULL) {
343 342 ret = GSS_S_FAILURE;
344 343 goto cleanup;
345 344 }
346 345
347 346 /*
348 347 * need to pull the first mech from mechSet to do first
349 348 * init ctx
350 349 */
351 350 status = generic_gss_copy_oid(minor_status,
352 351 mechSet->elements, &spnego_ctx->internal_mech);
353 352
354 353 if (status != GSS_S_COMPLETE) {
355 354 ret = GSS_S_FAILURE;
356 355 goto cleanup;
357 356 }
358 357
359 358 if (input_token != NULL && input_token->value != NULL) {
360 359 ret = GSS_S_DEFECTIVE_TOKEN;
361 360 goto cleanup;
362 361 }
363 362
364 363 /*
365 364 * The actual context is not yet determined,
366 365 * set the output context_handle to refer to
367 366 * the spnego context itself.
368 367 */
369 368 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
370 369 *context_handle = (gss_ctx_id_t)spnego_ctx;
371 370 send_token = INIT_TOKEN_SEND;
372 371 ret = GSS_S_CONTINUE_NEEDED;
373 372 } else {
374 373 mechSet = NULL;
375 374 spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
376 375
377 376 if (input_token == NULL || input_token->value == NULL) {
378 377 ret = GSS_S_DEFECTIVE_TOKEN;
379 378 goto cleanup;
380 379 }
381 380 ptr = (unsigned char *) input_token->value;
382 381
383 382 switch (get_negResult(&ptr, input_token->length)) {
384 383 case ACCEPT_DEFECTIVE_TOKEN:
385 384 *minor_status = 1;
386 385 ret = GSS_S_DEFECTIVE_TOKEN;
387 386 break;
388 387 case ACCEPT_INCOMPLETE: {
389 388 /* pull out mech from token */
390 389 gss_OID internal_mech =
391 390 get_mech_oid(minor_status, &ptr,
392 391 input_token->length -
393 392 (ptr - (uchar_t *)input_token->value));
394 393
395 394 /*
396 395 * check if first mech in neg set, if it isn't,
397 396 * release and copy chosen mech to context,
398 397 * delete internal context from prior mech
399 398 */
400 399 if (internal_mech != NULL &&
401 400 ((internal_mech->length !=
402 401 spnego_ctx->internal_mech->length) ||
403 402 /* CSTYLED */
404 403 memcmp(spnego_ctx->internal_mech->elements,
405 404 internal_mech->elements,
406 405 spnego_ctx->internal_mech->length))) {
407 406
408 407 (void) gss_delete_sec_context(&mstat,
409 408 &spnego_ctx->ctx_handle, NULL);
410 409
411 410 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT;
412 411 (void) generic_gss_release_oid(
413 412 &mstat, &spnego_ctx->internal_mech);
414 413
415 414 status = generic_gss_copy_oid(
416 415 minor_status, internal_mech,
417 416 &spnego_ctx->internal_mech);
418 417
419 418 if (status != GSS_S_COMPLETE)
420 419 ret = GSS_S_DEFECTIVE_TOKEN;
421 420 else
422 421 ret = GSS_S_COMPLETE;
423 422
424 423 (void) generic_gss_release_oid(&mstat,
425 424 &internal_mech);
426 425 } else if (internal_mech == NULL) {
427 426 ret = GSS_S_DEFECTIVE_TOKEN;
428 427 send_token = NO_TOKEN_SEND;
429 428 } else {
430 429 ret = GSS_S_COMPLETE;
431 430 }
432 431 if (ret == GSS_S_COMPLETE) {
433 432 /*
434 433 * Check for a token, it may contain
435 434 * an error message.
436 435 */
437 436 if (*ptr == (CONTEXT | 0x02)) {
438 437 if (g_get_tag_and_length(&ptr,
439 438 (CONTEXT | 0x02),
440 439 input_token->length - (ptr -
441 440 (uchar_t *)input_token->value),
442 441 &len) < 0) {
443 442 ret = GSS_S_DEFECTIVE_TOKEN;
444 443 } else {
445 444 i_input_token = get_input_token(
446 445 &ptr, len);
447 446 if (i_input_token != NULL) {
448 447 /*CSTYLED*/
449 448 ret = GSS_S_CONTINUE_NEEDED;
450 449 send_token =
451 450 CONT_TOKEN_SEND;
452 451 } else {
453 452 /*CSTYLED*/
454 453 ret = GSS_S_DEFECTIVE_TOKEN;
455 454 send_token =
456 455 NO_TOKEN_SEND;
457 456 }
458 457 }
459 458 }
460 459 }
461 460 break;
462 461 }
463 462 case ACCEPT_COMPLETE:
464 463 /* pull out mech from token */
465 464 if (spnego_ctx->internal_mech != NULL)
466 465 (void) generic_gss_release_oid(&mstat,
467 466 &spnego_ctx->internal_mech);
468 467
469 468 spnego_ctx->internal_mech =
470 469 get_mech_oid(minor_status, &ptr,
471 470 input_token->length -
472 471 (ptr - (uchar_t *)input_token->value));
473 472
474 473 if (spnego_ctx->internal_mech == NULL) {
475 474 /* CSTYLED */
476 475 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR;
477 476 ret = GSS_S_FAILURE;
478 477 }
479 478
480 479 if (ret != GSS_S_FAILURE && *ptr == (CONTEXT | 0x02)) {
481 480 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
482 481 input_token->length - (ptr -
483 482 (uchar_t *)input_token->value), &len) < 0) {
484 483 ret = GSS_S_DEFECTIVE_TOKEN;
485 484 } else {
486 485 i_input_token = get_input_token(&ptr,
487 486 len);
488 487 if (i_input_token != NULL) {
489 488 ret = GSS_S_COMPLETE;
490 489 send_token = CHECK_MIC;
491 490 } else {
492 491 ret = GSS_S_DEFECTIVE_TOKEN;
493 492 send_token = NO_TOKEN_SEND;
494 493 }
495 494 }
496 495 } else if (ret == GSS_S_CONTINUE_NEEDED ||
497 496 ret == GSS_S_COMPLETE) {
498 497 send_token = CHECK_MIC;
499 498 }
500 499
501 500 /*
502 501 * If we sent "optimistic" initial token,
503 502 * but the acceptor did not send a response token,
504 503 * this is an error.
505 504 */
506 505 if (ret == GSS_S_COMPLETE &&
507 506 i_input_token == GSS_C_NO_BUFFER &&
508 507 spnego_ctx->last_status == GSS_S_CONTINUE_NEEDED &&
509 508 spnego_ctx->optimistic) {
510 509 /* CSTYLED */
511 510 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR;
512 511 ret = GSS_S_DEFECTIVE_TOKEN;
513 512 send_token = NO_TOKEN_SEND;
514 513 }
515 514
516 515 if (send_token != NO_TOKEN_SEND) {
517 516 if (i_input_token == NULL)
518 517 ret = GSS_S_COMPLETE;
519 518 else
520 519 ret = GSS_S_CONTINUE_NEEDED;
521 520 send_token = CHECK_MIC;
522 521 }
523 522 break;
524 523
525 524 case REJECT:
526 525 ret = GSS_S_BAD_MECH;
527 526 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED;
528 527 send_token = NO_TOKEN_SEND;
529 528 break;
530 529
531 530 default:
532 531 ret = GSS_S_FAILURE;
533 532 send_token = NO_TOKEN_SEND;
534 533 break;
535 534 }
536 535 }
537 536
538 537
539 538 if (send_token == NO_TOKEN_SEND) {
540 539 output_token->length = 0;
541 540 output_token->value = NULL;
542 541 goto cleanup;
543 542 }
544 543
545 544 i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
546 545
547 546 if (i_output_token == NULL) {
548 547 ret = status = GSS_S_FAILURE;
549 548 goto cleanup;
550 549 }
551 550
552 551 i_output_token->length = 0;
553 552 i_output_token->value = NULL;
554 553
555 554 if (ret == GSS_S_CONTINUE_NEEDED) {
556 555 gss_OID inner_mech_type = GSS_C_NO_OID;
557 556
558 557 status = gss_init_sec_context(minor_status,
559 558 claimant_cred_handle,
560 559 &spnego_ctx->ctx_handle,
561 560 target_name,
562 561 spnego_ctx->internal_mech,
563 562 req_flags,
564 563 time_req,
565 564 NULL,
566 565 i_input_token,
567 566 &inner_mech_type,
568 567 i_output_token,
569 568 &local_ret_flags,
570 569 time_rec);
571 570
572 571 if (ret_flags)
573 572 *ret_flags = local_ret_flags;
574 573
575 574 spnego_ctx->last_status = status;
576 575
577 576 if (i_input_token != GSS_C_NO_BUFFER) {
578 577 (void) gss_release_buffer(&mstat, i_input_token);
579 578 free(i_input_token);
580 579 }
581 580
582 581 if ((status != GSS_S_COMPLETE) &&
583 582 (status != GSS_S_CONTINUE_NEEDED)) {
584 583 ret = status;
585 584 }
586 585
587 586 /* create mic/check mic */
588 587 if ((i_output_token->length == 0) &&
589 588 (status == GSS_S_COMPLETE) &&
590 589 (local_ret_flags & GSS_C_INTEG_FLAG)) {
591 590 if (*ptr == (CONTEXT | 0x03)) {
592 591 if (g_get_tag_and_length(&ptr,
593 592 (CONTEXT | 0x03), input_token->length -
594 593 (ptr - (uchar_t *)input_token->value),
595 594 &len) < 0) {
596 595 ret = GSS_S_DEFECTIVE_TOKEN;
597 596 } else {
598 597 ret = GSS_S_COMPLETE;
599 598 mechListMIC = get_input_token(&ptr,
600 599 len);
601 600 if (mechListMIC == NULL)
602 601 ret = GSS_S_DEFECTIVE_TOKEN;
603 602 else if (!spnego_ctx->MS_Interop &&
604 603 spnego_ctx->DER_mechTypes.length >
605 604 0) {
606 605 status = gss_verify_mic(
607 606 minor_status,
608 607 spnego_ctx->ctx_handle,
609 608 &spnego_ctx->DER_mechTypes,
610 609 mechListMIC, qop_state);
611 610 }
612 611 }
613 612 } else if (!spnego_ctx->MS_Interop) {
614 613 /*
615 614 * If no MIC was sent and we are in
616 615 * "standard" mode (i.e. NOT MS_Interop),
617 616 * the MIC must be present.
618 617 */
619 618 ret = GSS_S_DEFECTIVE_TOKEN;
620 619 } else {
621 620 /* In "MS_Interop" mode, MIC is ignored. */
622 621 ret = GSS_S_COMPLETE;
623 622 }
624 623 }
625 624 }
626 625
627 626 if ((status == GSS_S_COMPLETE) &&
628 627 (ret == GSS_S_COMPLETE)) {
629 628 if (actual_mech) {
630 629 (void) generic_gss_release_oid(&mstat, actual_mech);
631 630 ret = generic_gss_copy_oid(&mstat,
632 631 spnego_ctx->internal_mech, actual_mech);
633 632 if (ret != GSS_S_COMPLETE)
634 633 goto cleanup;
635 634 }
636 635
637 636 } else if (ret == GSS_S_CONTINUE_NEEDED) {
638 637 if (make_spnego_tokenInit_msg(spnego_ctx,
639 638 mechSet, i_output_token, send_token,
640 639 output_token) < 0) {
641 640 ret = GSS_S_DEFECTIVE_TOKEN;
642 641 }
643 642 }
644 643
645 644 cleanup:
646 645 if (status != GSS_S_COMPLETE)
647 646 ret = status;
648 647 if (ret != GSS_S_COMPLETE &&
649 648 ret != GSS_S_CONTINUE_NEEDED) {
650 649 if (spnego_ctx != NULL &&
651 650 spnego_ctx->ctx_handle != NULL)
652 651 gss_delete_sec_context(&mstat, &spnego_ctx->ctx_handle,
653 652 GSS_C_NO_BUFFER);
654 653
655 654 if (spnego_ctx != NULL)
656 655 release_spnego_ctx(&spnego_ctx);
657 656
658 657 *context_handle = GSS_C_NO_CONTEXT;
659 658
660 659 if (output_token)
661 660 (void) gss_release_buffer(&mstat, output_token);
662 661 }
663 662
664 663 if (i_output_token != GSS_C_NO_BUFFER) {
665 664 (void) gss_release_buffer(&mstat, i_output_token);
666 665 free(i_output_token);
667 666 }
668 667
669 668 if (mechListMIC != GSS_C_NO_BUFFER) {
670 669 (void) gss_release_buffer(&mstat, mechListMIC);
671 670 free(mechListMIC);
672 671 }
673 672
674 673 if (mechSet != NULL)
675 674 (void) gss_release_oid_set(&mstat, &mechSet);
676 675
677 676 if (credlistptr != NULL)
678 677 (void) gss_release_cred(&mstat, credlistptr);
679 678
680 679 return (ret);
681 680 } /* init_sec_context */
682 681
683 682 /*ARGSUSED*/
684 683 OM_uint32
685 684 spnego_gss_accept_sec_context(void *ct,
686 685 OM_uint32 *minor_status,
687 686 gss_ctx_id_t *context_handle,
688 687 gss_cred_id_t verifier_cred_handle,
689 688 gss_buffer_t input_token,
690 689 gss_channel_bindings_t input_chan_bindings,
691 690 gss_name_t *src_name,
692 691 gss_OID *mech_type,
693 692 gss_buffer_t output_token,
694 693 OM_uint32 *ret_flags,
695 694 OM_uint32 *time_rec,
696 695 gss_cred_id_t *delegated_cred_handle)
697 696 {
698 697 spnego_gss_ctx_id_t spnego_ctx = NULL;
699 698 gss_OID mech_wanted = NULL;
700 699 gss_OID_set mechSet = GSS_C_NO_OID_SET;
701 700 gss_OID_set supported_mechSet = GSS_C_NO_OID_SET;
702 701 gss_buffer_t i_output_token = GSS_C_NO_BUFFER;
703 702 gss_buffer_t i_input_token = GSS_C_NO_BUFFER;
704 703 gss_buffer_t mechListMIC = GSS_C_NO_BUFFER;
705 704 gss_cred_id_t acquired_cred = NULL;
706 705 gss_name_t internal_name = GSS_C_NO_NAME;
707 706 OM_uint32 status = GSS_S_COMPLETE;
708 707 OM_uint32 ret = GSS_S_COMPLETE;
709 708 unsigned char *ptr;
710 709 unsigned char *bufstart;
711 710 int bodysize;
712 711 int err, len;
713 712 OM_uint32 negResult;
714 713 OM_uint32 minor_stat;
715 714 OM_uint32 mstat;
716 715 OM_uint32 req_flags;
717 716 OM_uint32 mechsetlen;
718 717 gss_qop_t qop_state;
719 718 send_token_flag return_token = NO_TOKEN_SEND;
720 719 bool_t firstMech;
721 720 bool_t Need_Cred = FALSE;
722 721 OM_uint32 local_ret_flags = 0;
723 722 uchar_t *buf, *tmp;
724 723
725 724 dsyslog("Entering accept_sec_context\n");
726 725
727 726 if (context_handle == NULL)
728 727 return (GSS_S_NO_CONTEXT);
729 728
730 729 if (src_name)
731 730 *src_name = (gss_name_t)NULL;
732 731
733 732 output_token->length = 0;
734 733 output_token->value = NULL;
735 734 *minor_status = 0;
736 735
737 736 if (mech_type)
738 737 *mech_type = GSS_C_NULL_OID;
739 738
740 739 /* return a bogus cred handle */
741 740 if (delegated_cred_handle)
742 741 *delegated_cred_handle = GSS_C_NO_CREDENTIAL;
743 742
744 743 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
745 744 Need_Cred = TRUE;
746 745 }
747 746
748 747 /* Check for defective input token. */
749 748 ptr = bufstart = (unsigned char *) input_token->value;
750 749 if (err = g_verify_token_header((gss_OID)gss_mech_spnego, &bodysize,
751 750 &ptr, 0, input_token->length)) {
752 751 *minor_status = err;
753 752 ret = GSS_S_DEFECTIVE_TOKEN;
754 753 negResult = REJECT;
755 754 return_token = ERROR_TOKEN_SEND;
756 755 goto senderror;
757 756 }
758 757
759 758 /*
760 759 * set up of context, determine mech to be used, save mechset
761 760 * for use later in integrety check.
762 761 */
763 762 if (*context_handle == GSS_C_NO_CONTEXT) {
764 763 if ((spnego_ctx = create_spnego_ctx()) == NULL)
765 764 return (GSS_S_FAILURE);
766 765
767 766 /*
768 767 * Until the accept operation is complete, the
769 768 * context_handle returned should refer to
770 769 * the spnego context.
771 770 */
772 771 *context_handle = (gss_ctx_id_t)spnego_ctx;
773 772 minor_stat = get_available_mechs(minor_status,
774 773 GSS_C_NO_NAME, GSS_C_ACCEPT,
775 774 NULL, &supported_mechSet);
776 775
777 776 if (minor_stat != GSS_S_COMPLETE) {
778 777 release_spnego_ctx(&spnego_ctx);
779 778 *context_handle = GSS_C_NO_CONTEXT;
780 779 return (minor_stat);
781 780 }
782 781
783 782 if (Need_Cred) {
784 783 minor_stat = gss_acquire_cred(minor_status,
785 784 GSS_C_NO_NAME, NULL, supported_mechSet,
786 785 GSS_C_ACCEPT, &acquired_cred, NULL,
787 786 NULL);
788 787
789 788 if (minor_stat != GSS_S_COMPLETE) {
790 789 (void) gss_release_oid_set(minor_status,
791 790 &supported_mechSet);
792 791 release_spnego_ctx(&spnego_ctx);
793 792 *context_handle = GSS_C_NO_CONTEXT;
794 793 return (minor_stat);
795 794 } else {
796 795 verifier_cred_handle = acquired_cred;
797 796 }
798 797 }
799 798
800 799 if (err = g_verify_neg_token_init(&ptr, input_token->length)) {
801 800 *minor_status = err;
802 801 ret = GSS_S_DEFECTIVE_TOKEN;
803 802 negResult = REJECT;
804 803 return_token = ERROR_TOKEN_SEND;
805 804 goto senderror;
806 805 }
807 806
808 807 /*
809 808 * Allocate space to hold the mechTypes
810 809 * because we need it later.
811 810 */
812 811 mechsetlen = input_token->length - (ptr - bufstart);
813 812 buf = (uchar_t *)malloc(mechsetlen);
814 813 if (buf == NULL) {
815 814 ret = GSS_S_FAILURE;
816 815 goto cleanup;
817 816 }
818 817 (void) memcpy(buf, ptr, mechsetlen);
819 818 ptr = bufstart = buf;
820 819
821 820 /*
822 821 * Get pointers to the DER encoded MechSet so we
823 822 * can properly check and calculate a MIC later.
824 823 */
825 824 spnego_ctx->DER_mechTypes.value = ptr;
826 825 mechSet = get_mech_set(minor_status, &ptr, mechsetlen);
827 826 if (mechSet == NULL) {
828 827 ret = GSS_S_DEFECTIVE_TOKEN;
829 828 negResult = REJECT;
830 829 return_token = ERROR_TOKEN_SEND;
831 830 goto senderror;
832 831 }
833 832 spnego_ctx->DER_mechTypes.length = ptr - bufstart;
834 833 mechsetlen -= (ptr - bufstart);
835 834
836 835 /*
837 836 * Select the best match between the list of mechs
838 837 * that the initiator requested and the list that
839 838 * the acceptor will support.
840 839 */
841 840 mech_wanted = negotiate_mech_type(minor_status,
842 841 supported_mechSet, mechSet, &negResult,
843 842 &firstMech);
844 843
845 844 (void) gss_release_oid_set(&minor_stat, &supported_mechSet);
846 845 (void) gss_release_oid_set(&minor_stat, &mechSet);
847 846 supported_mechSet = NULL;
848 847 mechSet = NULL;
849 848
850 849 if (get_req_flags(&ptr, (int *)&mechsetlen, &req_flags) ==
851 850 ACCEPT_DEFECTIVE_TOKEN) {
852 851 negResult = REJECT;
853 852 }
854 853
855 854 tmp = ptr;
856 855 if (negResult == ACCEPT_COMPLETE) {
857 856 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02),
858 857 mechsetlen, &len) < 0) {
859 858 negResult = REJECT;
860 859 } else {
861 860 i_input_token = get_input_token(&ptr, len);
862 861 if (i_input_token == NULL) {
863 862 negResult = REJECT;
864 863 }
865 864 }
866 865 return_token = INIT_TOKEN_SEND;
867 866 }
868 867 if (negResult == REJECT) {
869 868 ret = GSS_S_DEFECTIVE_TOKEN;
870 869 return_token = ERROR_TOKEN_SEND;
871 870 } else {
872 871 ret = GSS_S_CONTINUE_NEEDED;
873 872 return_token = INIT_TOKEN_SEND;
874 873 }
875 874
876 875 mechsetlen -= ptr - tmp;
877 876 /*
878 877 * Check to see if there is a MechListMIC field
879 878 */
880 879 if (negResult == ACCEPT_COMPLETE && mechsetlen > 0) {
881 880 tmp = ptr;
882 881 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03),
883 882 mechsetlen, &len) >= 0) {
884 883 mechListMIC = get_input_token(&ptr, len);
885 884 if (mechListMIC == GSS_C_NO_BUFFER) {
886 885 negResult = REJECT;
887 886 return_token = ERROR_TOKEN_SEND;
888 887 ret = GSS_S_DEFECTIVE_TOKEN;
889 888 }
890 889 mechsetlen -= (ptr - tmp);
891 890 }
892 891 }
893 892 } else {
894 893 /*
895 894 * get internal input token and context for continued
896 895 * calls of spnego_gss_init_sec_context.
897 896 */
898 897 i_input_token = get_input_token(&ptr,
899 898 input_token->length - (ptr -
900 899 (uchar_t *)input_token->value));
901 900 if (i_input_token == NULL) {
902 901 negResult = REJECT;
903 902 return_token = ERROR_TOKEN_SEND;
904 903 ret = GSS_S_DEFECTIVE_TOKEN;
905 904 } else {
906 905 spnego_ctx = (spnego_gss_ctx_id_t)(*context_handle);
907 906 return_token = CONT_TOKEN_SEND;
908 907 }
909 908 }
910 909
911 910 /*
912 911 * If we still don't have a cred, we have an error.
913 912 */
914 913 if (verifier_cred_handle == GSS_C_NO_CREDENTIAL) {
915 914 ret = GSS_S_FAILURE;
916 915 goto cleanup;
917 916 }
918 917
919 918 /* If we have an error already, bail out */
920 919 if (ret != GSS_S_COMPLETE && ret != GSS_S_CONTINUE_NEEDED)
921 920 goto senderror;
922 921
923 922 if (i_input_token != GSS_C_NO_BUFFER) {
924 923 i_output_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
925 924
926 925 if (i_output_token == NULL) {
927 926 ret = GSS_S_FAILURE;
928 927 goto cleanup;
929 928 }
930 929
931 930 i_output_token->length = 0;
932 931 i_output_token->value = NULL;
933 932
934 933 status = gss_accept_sec_context(&minor_stat,
935 934 &spnego_ctx->ctx_handle, verifier_cred_handle,
936 935 i_input_token, GSS_C_NO_CHANNEL_BINDINGS,
937 936 &internal_name, mech_type, i_output_token,
938 937 &local_ret_flags, time_rec, delegated_cred_handle);
939 938
940 939 if ((status != GSS_S_COMPLETE) &&
941 940 (status != GSS_S_CONTINUE_NEEDED)) {
942 941 *minor_status = minor_stat;
943 942 (void) gss_release_buffer(&mstat, i_input_token);
944 943
945 944 if (i_input_token != GSS_C_NO_BUFFER) {
946 945 free(i_input_token);
947 946 i_input_token = GSS_C_NO_BUFFER;
948 947 }
949 948
950 949 ret = status;
951 950
952 951 /*
953 952 * Reject the request with an error token.
954 953 */
955 954 negResult = REJECT;
956 955 return_token = ERROR_TOKEN_SEND;
957 956
958 957 goto senderror;
959 958 }
960 959
961 960 if (ret_flags)
962 961 *ret_flags = local_ret_flags;
963 962
964 963 if (i_input_token != GSS_C_NO_BUFFER) {
965 964 (void) gss_release_buffer(&mstat, i_input_token);
966 965 free(i_input_token);
967 966 i_input_token = GSS_C_NO_BUFFER;
968 967 }
969 968
970 969 /* If we got a MIC, verify it if possible */
971 970 if ((status == GSS_S_COMPLETE) &&
972 971 (local_ret_flags & GSS_C_INTEG_FLAG) &&
973 972 mechListMIC != GSS_C_NO_BUFFER &&
974 973 !spnego_ctx->MS_Interop) {
975 974
976 975 ret = gss_verify_mic(minor_status,
977 976 spnego_ctx->ctx_handle,
978 977 &spnego_ctx->DER_mechTypes,
979 978 mechListMIC, &qop_state);
980 979
981 980 (void) gss_release_buffer(&mstat, mechListMIC);
982 981 free(mechListMIC);
983 982 mechListMIC = GSS_C_NO_BUFFER;
984 983
985 984 if (ret != GSS_S_COMPLETE) {
986 985 negResult = REJECT;
987 986 return_token = ERROR_TOKEN_SEND;
988 987 goto senderror;
989 988 }
990 989 }
991 990
992 991 /*
993 992 * If the MIC was verified OK, create a new MIC
994 993 * for the response message.
995 994 */
996 995 if (status == GSS_S_COMPLETE &&
997 996 (local_ret_flags & GSS_C_INTEG_FLAG) &&
998 997 !spnego_ctx->MS_Interop) {
999 998 mechListMIC = (gss_buffer_t)
1000 999 malloc(sizeof (gss_buffer_desc));
1001 1000
1002 1001 if (mechListMIC == NULL ||
1003 1002 spnego_ctx->DER_mechTypes.length == 0) {
1004 1003 ret = GSS_S_FAILURE;
1005 1004 goto cleanup;
1006 1005 }
1007 1006
1008 1007 ret = gss_get_mic(minor_status,
1009 1008 spnego_ctx->ctx_handle,
1010 1009 GSS_C_QOP_DEFAULT,
1011 1010 &spnego_ctx->DER_mechTypes,
1012 1011 mechListMIC);
1013 1012
1014 1013 if (ret != GSS_S_COMPLETE) {
1015 1014 negResult = REJECT;
1016 1015 return_token = ERROR_TOKEN_SEND;
1017 1016 goto senderror;
1018 1017 }
1019 1018 }
1020 1019 ret = status;
1021 1020
1022 1021 if (status == GSS_S_COMPLETE) {
1023 1022 if (internal_name != NULL && src_name != NULL)
1024 1023 *src_name = internal_name;
1025 1024 }
1026 1025
1027 1026
1028 1027 if (status == GSS_S_CONTINUE_NEEDED) {
1029 1028 if (return_token == INIT_TOKEN_SEND)
1030 1029 negResult = ACCEPT_INCOMPLETE;
1031 1030 }
1032 1031 }
1033 1032
1034 1033 senderror:
1035 1034 if ((return_token == INIT_TOKEN_SEND) ||
1036 1035 (return_token == CONT_TOKEN_SEND) ||
1037 1036 (return_token == ERROR_TOKEN_SEND)) {
1038 1037 int MS_Interop = 0;
1039 1038
1040 1039 if (spnego_ctx)
1041 1040 MS_Interop = spnego_ctx->MS_Interop;
1042 1041
1043 1042 /*
1044 1043 * create response for the initiator.
1045 1044 */
1046 1045 err = make_spnego_tokenTarg_msg(negResult,
1047 1046 mech_wanted, i_output_token,
1048 1047 mechListMIC, return_token,
1049 1048 MS_Interop, output_token);
1050 1049
1051 1050 (void) gss_release_buffer(&mstat, mechListMIC);
1052 1051 free(mechListMIC);
1053 1052
1054 1053 /*
1055 1054 * If we could not make the response token,
1056 1055 * we will have to fail without sending a response.
1057 1056 */
1058 1057 if (err) {
1059 1058 (void) gss_release_buffer(&mstat, output_token);
1060 1059 }
1061 1060 } else {
1062 1061 (void) gss_release_buffer(&mstat, output_token);
1063 1062 }
1064 1063
1065 1064 cleanup:
1066 1065 if (ret != GSS_S_COMPLETE &&
1067 1066 ret != GSS_S_CONTINUE_NEEDED) {
1068 1067 if (spnego_ctx != NULL) {
1069 1068 (void) gss_delete_sec_context(&mstat,
1070 1069 &spnego_ctx->ctx_handle, NULL);
1071 1070
|
↓ open down ↓ |
1029 lines elided |
↑ open up ↑ |
1072 1071 spnego_ctx->ctx_handle = NULL;
1073 1072
1074 1073 release_spnego_ctx(&spnego_ctx);
1075 1074 }
1076 1075 *context_handle = GSS_C_NO_CONTEXT;
1077 1076 }
1078 1077 if (mech_wanted != NULL) {
1079 1078 generic_gss_release_oid(&mstat, &mech_wanted);
1080 1079 }
1081 1080
1082 - (void) gss_release_cred(minor_status, &acquired_cred);
1083 - (void) gss_release_oid_set(minor_status, &supported_mechSet);
1081 + (void) gss_release_cred(&mstat, &acquired_cred);
1082 + (void) gss_release_oid_set(&mstat, &supported_mechSet);
1084 1083
1085 1084 (void) gss_release_buffer(&mstat, i_output_token);
1086 1085 free(i_output_token);
1087 1086
1088 1087 return (ret);
1089 1088 }
1090 1089
1091 1090 /*ARGSUSED*/
1092 1091 OM_uint32
1093 1092 spnego_gss_display_status(void *ctx,
1094 1093 OM_uint32 *minor_status,
1095 1094 OM_uint32 status_value,
1096 1095 int status_type,
1097 1096 gss_OID mech_type,
1098 1097 OM_uint32 *message_context,
1099 1098 gss_buffer_t status_string)
1100 1099 {
1101 1100 OM_uint32 ret = GSS_S_COMPLETE;
1102 1101 dsyslog("Entering display_status\n");
1103 1102
1104 1103 *message_context = 0;
1105 1104 switch (status_value) {
1106 1105 case ERR_SPNEGO_NO_MECHS_AVAILABLE:
1107 1106 *status_string = make_err_msg(gettext(
1108 1107 "SPNEGO cannot find mechanisms to negotiate"));
1109 1108 break;
1110 1109 case ERR_SPNEGO_NO_CREDS_ACQUIRED:
1111 1110 *status_string = make_err_msg(gettext(
1112 1111 "SPNEGO failed to acquire creds"));
1113 1112 break;
1114 1113 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR:
1115 1114 *status_string = make_err_msg(gettext(
1116 1115 "SPNEGO acceptor did not select a mechanism"));
1117 1116 break;
1118 1117 case ERR_SPNEGO_NEGOTIATION_FAILED:
1119 1118 *status_string = make_err_msg(gettext(
1120 1119 "SPNEGO failed to negotiate a mechanism"));
1121 1120 break;
1122 1121 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR:
1123 1122 *status_string = make_err_msg(gettext(
1124 1123 "SPNEGO acceptor did not return a valid token"));
1125 1124 break;
1126 1125 case ERR_SPNEGO_BAD_INPUT_PARAMETER:
1127 1126 *status_string = make_err_msg(gettext(
1128 1127 "SPNEGO function received an incorrect input "
1129 1128 "parameter"));
1130 1129 break;
1131 1130 default:
1132 1131 status_string->length = 0;
1133 1132 status_string->value = "";
1134 1133 ret = GSS_S_BAD_STATUS;
1135 1134 break;
1136 1135 }
1137 1136
1138 1137 dsyslog("Leaving display_status\n");
1139 1138 return (ret);
1140 1139 }
1141 1140
1142 1141 /*ARGSUSED*/
1143 1142 OM_uint32
1144 1143 spnego_gss_import_name(void *ctx,
1145 1144 OM_uint32 *minor_status,
1146 1145 gss_buffer_t input_name_buffer,
1147 1146 gss_OID input_name_type,
1148 1147 gss_name_t *output_name)
1149 1148 {
1150 1149 OM_uint32 status;
1151 1150
1152 1151 dsyslog("Entering import_name\n");
1153 1152
1154 1153 status = gss_import_name(minor_status, input_name_buffer,
1155 1154 input_name_type, output_name);
1156 1155
1157 1156 dsyslog("Leaving import_name\n");
1158 1157 return (status);
1159 1158 }
1160 1159
1161 1160 /*ARGSUSED*/
1162 1161 OM_uint32
1163 1162 spnego_gss_release_name(void *ctx,
1164 1163 OM_uint32 *minor_status,
1165 1164 gss_name_t *input_name)
1166 1165 {
1167 1166 OM_uint32 status;
1168 1167
1169 1168 dsyslog("Entering release_name\n");
1170 1169
1171 1170 status = gss_release_name(minor_status, input_name);
1172 1171
1173 1172 dsyslog("Leaving release_name\n");
1174 1173 return (status);
1175 1174 }
1176 1175
1177 1176 /*ARGSUSED*/
1178 1177 OM_uint32
1179 1178 spnego_gss_display_name(void *ctx,
1180 1179 OM_uint32 *minor_status,
1181 1180 gss_name_t input_name,
1182 1181 gss_buffer_t output_name_buffer,
1183 1182 gss_OID *output_name_type)
1184 1183 {
1185 1184 OM_uint32 status = GSS_S_COMPLETE;
1186 1185 dsyslog("Entering display_name\n");
1187 1186
1188 1187 status = gss_display_name(minor_status, input_name,
1189 1188 output_name_buffer, output_name_type);
1190 1189
1191 1190 dsyslog("Leaving display_name\n");
1192 1191 return (status);
1193 1192 }
1194 1193
1195 1194 /*ARGSUSED*/
1196 1195 OM_uint32
1197 1196 spnego_gss_inquire_cred(void *ctx,
1198 1197 OM_uint32 *minor_status,
1199 1198 const gss_cred_id_t cred_handle,
1200 1199 gss_name_t *name,
1201 1200 OM_uint32 *lifetime,
1202 1201 gss_cred_usage_t *cred_usage,
1203 1202 gss_OID_set *mechanisms)
1204 1203 {
1205 1204 OM_uint32 stat = GSS_S_COMPLETE;
1206 1205 gss_cred_id_t *credlistptr = NULL, credlist = NULL;
1207 1206 OM_uint32 init_lt, accept_lt;
1208 1207 int i;
1209 1208
1210 1209 if (cred_handle == GSS_C_NO_CREDENTIAL) {
1211 1210 OM_uint32 tstat;
1212 1211 credlistptr = &credlist;
1213 1212
1214 1213 /*
1215 1214 * Get a list of all non-SPNEGO
1216 1215 * mechanisms that are available and
1217 1216 * acquire a default cred.
1218 1217 */
1219 1218 stat = get_available_mechs(minor_status,
1220 1219 NULL, GSS_C_BOTH, credlistptr, mechanisms);
1221 1220
1222 1221 /*
1223 1222 * inquire about the default cred from the
1224 1223 * first non-SPNEGO mechanism that was found.
1225 1224 */
1226 1225 if (stat == GSS_S_COMPLETE && mechanisms &&
1227 1226 (*mechanisms)->count > 0) {
1228 1227 i = 0;
1229 1228 do {
1230 1229 stat = gss_inquire_cred_by_mech(
1231 1230 minor_status, credlist,
1232 1231 &((*mechanisms)->elements[i]),
1233 1232 name, &init_lt, &accept_lt,
1234 1233 cred_usage);
1235 1234
1236 1235 /*
1237 1236 * Set the lifetime to the correct value.
1238 1237 */
1239 1238 if (stat == GSS_S_COMPLETE) {
1240 1239 if (*cred_usage == GSS_C_INITIATE)
1241 1240 *lifetime = init_lt;
1242 1241 else
1243 1242 *lifetime = accept_lt;
1244 1243 }
1245 1244 if (credlist != GSS_C_NO_CREDENTIAL)
1246 1245 (void) gss_release_cred(&tstat,
1247 1246 &credlist);
1248 1247 } while (stat != GSS_S_COMPLETE &&
1249 1248 (i < (*mechanisms)->count));
1250 1249 }
1251 1250 } else {
1252 1251 /*
1253 1252 * This should not happen, it cannot be processed.
1254 1253 */
1255 1254 stat = GSS_S_FAILURE;
1256 1255 if (minor_status != NULL)
1257 1256 *minor_status = ERR_SPNEGO_BAD_INPUT_PARAMETER;
1258 1257 }
1259 1258 return (stat);
1260 1259 }
1261 1260
1262 1261 /*ARGSUSED*/
1263 1262 OM_uint32
1264 1263 spnego_gss_inquire_names_for_mech(void *ctx,
1265 1264 OM_uint32 *minor_status,
1266 1265 gss_OID mechanism,
1267 1266 gss_OID_set *name_types)
1268 1267 {
1269 1268 OM_uint32 major, minor;
1270 1269
1271 1270 dsyslog("Entering inquire_names_for_mech\n");
1272 1271 /*
1273 1272 * We only know how to handle our own mechanism.
1274 1273 */
1275 1274 if ((mechanism != GSS_C_NULL_OID) &&
1276 1275 !g_OID_equal(gss_mech_spnego, mechanism)) {
1277 1276 *minor_status = 0;
1278 1277 return (GSS_S_FAILURE);
1279 1278 }
1280 1279
1281 1280 major = gss_create_empty_oid_set(minor_status, name_types);
1282 1281 if (major == GSS_S_COMPLETE) {
1283 1282 /* Now add our members. */
1284 1283 if (((major = gss_add_oid_set_member(minor_status,
1285 1284 (gss_OID) GSS_C_NT_USER_NAME,
1286 1285 name_types)) == GSS_S_COMPLETE) &&
1287 1286 ((major = gss_add_oid_set_member(minor_status,
1288 1287 (gss_OID) GSS_C_NT_MACHINE_UID_NAME,
1289 1288 name_types)) == GSS_S_COMPLETE) &&
1290 1289 ((major = gss_add_oid_set_member(minor_status,
1291 1290 (gss_OID) GSS_C_NT_STRING_UID_NAME,
1292 1291 name_types)) == GSS_S_COMPLETE)) {
1293 1292 major = gss_add_oid_set_member(minor_status,
1294 1293 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
1295 1294 name_types);
1296 1295 }
1297 1296
1298 1297 if (major != GSS_S_COMPLETE)
1299 1298 (void) gss_release_oid_set(&minor, name_types);
1300 1299 }
1301 1300
1302 1301 dsyslog("Leaving inquire_names_for_mech\n");
1303 1302 return (major);
1304 1303 }
1305 1304
1306 1305 OM_uint32
1307 1306 spnego_gss_unseal(void *context,
1308 1307 OM_uint32 *minor_status,
1309 1308 gss_ctx_id_t context_handle,
1310 1309 gss_buffer_t input_message_buffer,
1311 1310 gss_buffer_t output_message_buffer,
1312 1311 int *conf_state,
1313 1312 int *qop_state)
1314 1313 {
1315 1314 OM_uint32 ret;
1316 1315 spnego_gss_ctx_id_t ctx = (spnego_gss_ctx_id_t)context_handle;
1317 1316
1318 1317 if (context_handle == NULL)
1319 1318 return (GSS_S_NO_CONTEXT);
1320 1319
1321 1320 ret = gss_unseal(minor_status,
1322 1321 ctx->ctx_handle, input_message_buffer,
1323 1322 output_message_buffer, conf_state, qop_state);
1324 1323
1325 1324 return (ret);
1326 1325 }
1327 1326
1328 1327 OM_uint32
1329 1328 spnego_gss_seal(void *context,
1330 1329 OM_uint32 *minor_status,
1331 1330 gss_ctx_id_t context_handle,
1332 1331 int conf_req_flag,
1333 1332 int qop_req,
1334 1333 gss_buffer_t input_message_buffer,
1335 1334 int *conf_state,
1336 1335 gss_buffer_t output_message_buffer)
1337 1336 {
1338 1337 OM_uint32 ret;
1339 1338 spnego_gss_ctx_id_t ctx =
1340 1339 (spnego_gss_ctx_id_t)context_handle;
1341 1340
1342 1341 if (context_handle == NULL)
1343 1342 return (GSS_S_NO_CONTEXT);
1344 1343
1345 1344 ret = gss_seal(minor_status,
1346 1345 ctx->ctx_handle, conf_req_flag,
1347 1346 qop_req, input_message_buffer,
1348 1347 conf_state, output_message_buffer);
1349 1348
1350 1349 return (ret);
1351 1350 }
1352 1351
1353 1352 OM_uint32
1354 1353 spnego_gss_process_context_token(void *context,
1355 1354 OM_uint32 *minor_status,
1356 1355 const gss_ctx_id_t context_handle,
1357 1356 const gss_buffer_t token_buffer)
1358 1357 {
1359 1358 OM_uint32 ret;
1360 1359 spnego_gss_ctx_id_t ctx =
1361 1360 (spnego_gss_ctx_id_t)context_handle;
1362 1361
1363 1362 if (context_handle == NULL)
1364 1363 return (GSS_S_NO_CONTEXT);
1365 1364
1366 1365 ret = gss_process_context_token(minor_status,
1367 1366 ctx->ctx_handle, token_buffer);
1368 1367
1369 1368 return (ret);
1370 1369 }
1371 1370
1372 1371 OM_uint32
1373 1372 spnego_gss_delete_sec_context(void *context,
1374 1373 OM_uint32 *minor_status,
1375 1374 gss_ctx_id_t *context_handle,
1376 1375 gss_buffer_t output_token)
1377 1376 {
1378 1377 OM_uint32 ret = GSS_S_COMPLETE;
1379 1378 spnego_gss_ctx_id_t *ctx =
1380 1379 (spnego_gss_ctx_id_t *)context_handle;
1381 1380
1382 1381 if (context_handle == NULL || *ctx == NULL)
1383 1382 return (GSS_S_NO_CONTEXT);
1384 1383
1385 1384 /*
1386 1385 * If this is still a SPNEGO mech, release it locally.
1387 1386 */
1388 1387 if ((*ctx)->ctx_handle == GSS_C_NO_CONTEXT) {
1389 1388 (void) release_spnego_ctx(ctx);
1390 1389 } else {
1391 1390 ret = gss_delete_sec_context(minor_status,
1392 1391 &(*ctx)->ctx_handle, output_token);
1393 1392 }
1394 1393
1395 1394 return (ret);
1396 1395 }
1397 1396
1398 1397 OM_uint32
1399 1398 spnego_gss_context_time(void *context,
1400 1399 OM_uint32 *minor_status,
1401 1400 const gss_ctx_id_t context_handle,
1402 1401 OM_uint32 *time_rec)
1403 1402 {
1404 1403 OM_uint32 ret;
1405 1404 spnego_gss_ctx_id_t ctx =
1406 1405 (spnego_gss_ctx_id_t)context_handle;
1407 1406
1408 1407 if (context_handle == NULL)
1409 1408 return (GSS_S_NO_CONTEXT);
1410 1409
1411 1410 ret = gss_context_time(minor_status,
1412 1411 ctx->ctx_handle, time_rec);
1413 1412
1414 1413 return (ret);
1415 1414 }
1416 1415
1417 1416 OM_uint32
1418 1417 spnego_gss_export_sec_context(void *context,
1419 1418 OM_uint32 *minor_status,
1420 1419 gss_ctx_id_t *context_handle,
1421 1420 gss_buffer_t interprocess_token)
1422 1421 {
1423 1422 OM_uint32 ret;
1424 1423 spnego_gss_ctx_id_t *ctx =
1425 1424 (spnego_gss_ctx_id_t *)context_handle;
1426 1425
1427 1426 if (context_handle == NULL || *ctx == NULL)
1428 1427 return (GSS_S_NO_CONTEXT);
1429 1428
1430 1429 ret = gss_export_sec_context(minor_status,
1431 1430 &(*ctx)->ctx_handle, interprocess_token);
1432 1431 return (ret);
1433 1432 }
1434 1433
1435 1434 OM_uint32
1436 1435 spnego_gss_import_sec_context(void *context,
1437 1436 OM_uint32 *minor_status,
1438 1437 const gss_buffer_t interprocess_token,
1439 1438 gss_ctx_id_t *context_handle)
1440 1439 {
1441 1440 OM_uint32 ret;
1442 1441 spnego_gss_ctx_id_t ctx;
1443 1442
1444 1443 if (context_handle == NULL)
1445 1444 return (GSS_S_NO_CONTEXT);
1446 1445
1447 1446 if ((ctx = create_spnego_ctx()) == NULL) {
1448 1447 *minor_status = ENOMEM;
1449 1448 return (GSS_S_FAILURE);
1450 1449 }
1451 1450
1452 1451 ret = gss_import_sec_context(minor_status,
1453 1452 interprocess_token, &(ctx->ctx_handle));
1454 1453 if (GSS_ERROR(ret)) {
1455 1454 (void) release_spnego_ctx(&ctx);
1456 1455 return (ret);
1457 1456 }
1458 1457
1459 1458 *context_handle = (gss_ctx_id_t)ctx;
1460 1459
1461 1460 return (ret);
1462 1461 }
1463 1462
1464 1463 OM_uint32
1465 1464 spnego_gss_inquire_context(void *context,
1466 1465 OM_uint32 *minor_status,
1467 1466 const gss_ctx_id_t context_handle,
1468 1467 gss_name_t *src_name,
1469 1468 gss_name_t *targ_name,
1470 1469 OM_uint32 *lifetime_rec,
1471 1470 gss_OID *mech_type,
1472 1471 OM_uint32 *ctx_flags,
1473 1472 int *locally_initiated,
1474 1473 int *open)
1475 1474 {
1476 1475 OM_uint32 ret = GSS_S_COMPLETE;
1477 1476 spnego_gss_ctx_id_t ctx =
1478 1477 (spnego_gss_ctx_id_t)context_handle;
1479 1478
1480 1479 if (context_handle == NULL)
1481 1480 return (GSS_S_NO_CONTEXT);
1482 1481
1483 1482 ret = gss_inquire_context(minor_status,
1484 1483 ctx->ctx_handle, src_name,
1485 1484 targ_name, lifetime_rec,
1486 1485 mech_type, ctx_flags,
1487 1486 locally_initiated, open);
1488 1487
1489 1488 return (ret);
1490 1489 }
1491 1490
1492 1491 OM_uint32
1493 1492 spnego_gss_wrap_size_limit(void *context,
1494 1493 OM_uint32 *minor_status,
1495 1494 const gss_ctx_id_t context_handle,
1496 1495 int conf_req_flag,
1497 1496 gss_qop_t qop_req,
1498 1497 OM_uint32 req_output_size,
1499 1498 OM_uint32 *max_input_size)
1500 1499 {
1501 1500 OM_uint32 ret;
1502 1501 spnego_gss_ctx_id_t ctx =
1503 1502 (spnego_gss_ctx_id_t)context_handle;
1504 1503
1505 1504 if (context_handle == NULL)
1506 1505 return (GSS_S_NO_CONTEXT);
1507 1506
1508 1507 ret = gss_wrap_size_limit(minor_status,
1509 1508 ctx->ctx_handle, conf_req_flag,
1510 1509 qop_req, req_output_size,
1511 1510 max_input_size);
1512 1511 return (ret);
1513 1512 }
1514 1513
1515 1514 OM_uint32
1516 1515 spnego_gss_sign(void *context,
1517 1516 OM_uint32 *minor_status,
1518 1517 const gss_ctx_id_t context_handle,
1519 1518 int qop_req,
1520 1519 const gss_buffer_t message_buffer,
1521 1520 gss_buffer_t message_token)
1522 1521 {
1523 1522 OM_uint32 ret;
1524 1523 spnego_gss_ctx_id_t ctx =
1525 1524 (spnego_gss_ctx_id_t)context_handle;
1526 1525
1527 1526 if (context_handle == NULL)
1528 1527 return (GSS_S_NO_CONTEXT);
1529 1528
1530 1529 ret = gss_sign(minor_status,
1531 1530 ctx->ctx_handle,
1532 1531 qop_req,
1533 1532 message_buffer,
1534 1533 message_token);
1535 1534
1536 1535 return (ret);
1537 1536 }
1538 1537
1539 1538 OM_uint32
1540 1539 spnego_gss_verify(void *context,
1541 1540 OM_uint32 *minor_status,
1542 1541 const gss_ctx_id_t context_handle,
1543 1542 const gss_buffer_t msg_buffer,
1544 1543 const gss_buffer_t token_buffer,
1545 1544 int *qop_state)
1546 1545 {
1547 1546 OM_uint32 ret;
1548 1547 spnego_gss_ctx_id_t ctx =
1549 1548 (spnego_gss_ctx_id_t)context_handle;
1550 1549
1551 1550 if (context_handle == NULL)
1552 1551 return (GSS_S_NO_CONTEXT);
1553 1552
1554 1553 ret = gss_verify_mic(minor_status,
1555 1554 ctx->ctx_handle,
1556 1555 msg_buffer,
1557 1556 token_buffer,
1558 1557 (uint32_t *)qop_state);
1559 1558 return (ret);
1560 1559 }
1561 1560
1562 1561 /*
1563 1562 * We will release everything but the ctx_handle so that it
1564 1563 * can be passed back to init/accept context. This routine should
1565 1564 * not be called until after the ctx_handle memory is assigned to
1566 1565 * the supplied context handle from init/accept context.
1567 1566 */
1568 1567 static void
1569 1568 release_spnego_ctx(spnego_gss_ctx_id_t *ctx)
1570 1569 {
1571 1570 spnego_gss_ctx_id_t context;
1572 1571 OM_uint32 minor_stat;
1573 1572
1574 1573 if (ctx != NULL)
1575 1574 context = *ctx;
1576 1575 else
1577 1576 return;
1578 1577
1579 1578 if (context != NULL) {
1580 1579 (void) gss_release_buffer(&minor_stat,
1581 1580 &context->DER_mechTypes);
1582 1581
1583 1582 (void) generic_gss_release_oid(&minor_stat,
1584 1583 &context->internal_mech);
1585 1584
1586 1585 if (context->optionStr != NULL) {
1587 1586 free(context->optionStr);
1588 1587 context->optionStr = NULL;
1589 1588 }
1590 1589 if (context->ctx_handle != GSS_C_NO_CONTEXT)
1591 1590 gss_delete_sec_context(&minor_stat,
1592 1591 &context->ctx_handle, GSS_C_NO_BUFFER);
1593 1592
1594 1593 free(context);
1595 1594 *ctx = NULL;
1596 1595 }
1597 1596 }
1598 1597
1599 1598 /*
1600 1599 * Can't use gss_indicate_mechs by itself to get available mechs for
1601 1600 * SPNEGO because it will also return the SPNEGO mech and we do not
1602 1601 * want to consider SPNEGO as an available security mech for
1603 1602 * negotiation. For this reason, get_available_mechs will return
1604 1603 * all available mechs except SPNEGO.
1605 1604 *
1606 1605 * If a ptr to a creds list is given, this function will attempt
1607 1606 * to acquire creds for the creds given and trim the list of
1608 1607 * returned mechanisms to only those for which creds are valid.
1609 1608 *
1610 1609 */
1611 1610 static OM_uint32
1612 1611 get_available_mechs(OM_uint32 *minor_status,
1613 1612 gss_name_t name, gss_cred_usage_t usage,
1614 1613 gss_cred_id_t *creds, gss_OID_set *rmechs)
1615 1614 {
1616 1615 int i;
1617 1616 int found = 0;
1618 1617 OM_uint32 stat = GSS_S_COMPLETE;
1619 1618 gss_OID_set mechs, goodmechs;
1620 1619
1621 1620 stat = gss_indicate_mechs(minor_status, &mechs);
1622 1621
1623 1622 if (stat != GSS_S_COMPLETE) {
1624 1623 return (stat);
1625 1624 }
1626 1625
1627 1626 stat = gss_create_empty_oid_set(minor_status, rmechs);
1628 1627
1629 1628 if (stat != GSS_S_COMPLETE) {
1630 1629 (void) gss_release_oid_set(minor_status, &mechs);
1631 1630 return (stat);
1632 1631 }
1633 1632
1634 1633 for (i = 0; i < mechs->count && stat == GSS_S_COMPLETE; i++) {
1635 1634 if ((mechs->elements[i].length
1636 1635 != spnego_mechanism.mech_type.length) ||
1637 1636 memcmp(mechs->elements[i].elements,
1638 1637 spnego_mechanism.mech_type.elements,
1639 1638 spnego_mechanism.mech_type.length)) {
1640 1639
1641 1640 stat = gss_add_oid_set_member(minor_status,
1642 1641 &mechs->elements[i], rmechs);
1643 1642 if (stat == GSS_S_COMPLETE)
1644 1643 found++;
1645 1644 }
1646 1645 }
1647 1646
1648 1647 /*
1649 1648 * If the caller wanted a list of creds returned,
1650 1649 * trim the list of mechanisms down to only those
1651 1650 * for which the creds are valid.
1652 1651 */
1653 1652 if (found > 0 && stat == GSS_S_COMPLETE && creds != NULL) {
1654 1653 stat = gss_acquire_cred(minor_status,
1655 1654 name, NULL, *rmechs, usage, creds,
1656 1655 &goodmechs, NULL);
1657 1656
1658 1657 /*
1659 1658 * Drop the old list in favor of the new
1660 1659 * "trimmed" list.
1661 1660 */
1662 1661 (void) gss_release_oid_set(minor_status, rmechs);
1663 1662 if (stat == GSS_S_COMPLETE) {
1664 1663 (void) gss_copy_oid_set(minor_status,
1665 1664 goodmechs, rmechs);
1666 1665 (void) gss_release_oid_set(minor_status, &goodmechs);
1667 1666 }
1668 1667 }
1669 1668
1670 1669 (void) gss_release_oid_set(minor_status, &mechs);
1671 1670 if (found == 0 || stat != GSS_S_COMPLETE) {
1672 1671 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE;
1673 1672 if (stat == GSS_S_COMPLETE)
1674 1673 stat = GSS_S_FAILURE;
1675 1674 }
1676 1675
1677 1676 return (stat);
1678 1677 }
1679 1678
1680 1679 /* following are token creation and reading routines */
1681 1680
1682 1681 /*
1683 1682 * If buff_in is not pointing to a MECH_OID, then return NULL and do not
1684 1683 * advance the buffer, otherwise, decode the mech_oid from the buffer and
1685 1684 * place in gss_OID.
1686 1685 */
1687 1686 static gss_OID
1688 1687 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length)
1689 1688 {
1690 1689 OM_uint32 status;
1691 1690 gss_OID_desc toid;
1692 1691 gss_OID mech_out = NULL;
1693 1692 uchar_t *start, *end;
1694 1693
1695 1694 if (length < 1 || **buff_in != MECH_OID)
1696 1695 return (NULL);
1697 1696
1698 1697 start = *buff_in;
1699 1698 end = start + length;
1700 1699
1701 1700 (*buff_in)++;
1702 1701 toid.length = *(*buff_in)++;
1703 1702
1704 1703 if ((*buff_in + toid.length) > end)
1705 1704 return (NULL);
1706 1705
1707 1706 toid.elements = *buff_in;
1708 1707 *buff_in += toid.length;
1709 1708
1710 1709 status = generic_gss_copy_oid(minor_status, &toid, &mech_out);
1711 1710
1712 1711 if (status != GSS_S_COMPLETE)
1713 1712 mech_out = NULL;
1714 1713
1715 1714 return (mech_out);
1716 1715 }
1717 1716
1718 1717 /*
1719 1718 * der encode the given mechanism oid into buf_out, advancing the
1720 1719 * buffer pointer.
1721 1720 */
1722 1721
1723 1722 static int
1724 1723 put_mech_oid(unsigned char **buf_out, gss_OID_desc *mech, int buflen)
1725 1724 {
1726 1725 if (buflen < mech->length + 2)
1727 1726 return (-1);
1728 1727 *(*buf_out)++ = MECH_OID;
1729 1728 *(*buf_out)++ = (unsigned char) mech->length;
1730 1729 memcpy((void *)(*buf_out), mech->elements, mech->length);
1731 1730 *buf_out += mech->length;
1732 1731 return (0);
1733 1732 }
1734 1733
1735 1734 /*
1736 1735 * verify that buff_in points to an octet string, if it does not,
1737 1736 * return NULL and don't advance the pointer. If it is an octet string
1738 1737 * decode buff_in into a gss_buffer_t and return it, advancing the
1739 1738 * buffer pointer.
1740 1739 */
1741 1740 static gss_buffer_t
1742 1741 get_input_token(unsigned char **buff_in, int buff_length)
1743 1742 {
1744 1743 gss_buffer_t input_token;
1745 1744 unsigned int bytes;
1746 1745
1747 1746 if (**buff_in != OCTET_STRING)
1748 1747 return (NULL);
1749 1748
1750 1749 (*buff_in)++;
1751 1750 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc));
1752 1751
1753 1752 if (input_token == NULL)
1754 1753 return (NULL);
1755 1754
1756 1755 input_token->length = get_der_length(buff_in, buff_length, &bytes);
1757 1756 if ((int)input_token->length == -1) {
1758 1757 free(input_token);
1759 1758 return (NULL);
1760 1759 }
1761 1760 input_token->value = malloc(input_token->length);
1762 1761
1763 1762 if (input_token->value == NULL) {
1764 1763 free(input_token);
1765 1764 return (NULL);
1766 1765 }
1767 1766
1768 1767 (void) memcpy(input_token->value, *buff_in, input_token->length);
1769 1768 *buff_in += input_token->length;
1770 1769 return (input_token);
1771 1770 }
1772 1771
1773 1772 /*
1774 1773 * verify that the input token length is not 0. If it is, just return.
1775 1774 * If the token length is greater than 0, der encode as an octet string
1776 1775 * and place in buf_out, advancing buf_out.
1777 1776 */
1778 1777
1779 1778 static int
1780 1779 put_input_token(unsigned char **buf_out, gss_buffer_t input_token,
1781 1780 int buflen)
1782 1781 {
1783 1782 int ret;
1784 1783
1785 1784 /* if token length is 0, we do not want to send */
1786 1785 if (input_token->length == 0)
1787 1786 return (0);
1788 1787
1789 1788 if (input_token->length > buflen)
1790 1789 return (-1);
1791 1790
1792 1791 *(*buf_out)++ = OCTET_STRING;
1793 1792 if ((ret = put_der_length(input_token->length, buf_out,
1794 1793 input_token->length)))
1795 1794 return (ret);
1796 1795 TWRITE_STR(*buf_out, input_token->value, ((int)input_token->length));
1797 1796 return (0);
1798 1797 }
1799 1798
1800 1799 /*
1801 1800 * verify that buff_in points to a sequence of der encoding. The mech
1802 1801 * set is the only sequence of encoded object in the token, so if it is
1803 1802 * a sequence of encoding, decode the mechset into a gss_OID_set and
1804 1803 * return it, advancing the buffer pointer.
1805 1804 */
1806 1805 static gss_OID_set
1807 1806 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, int buff_length)
1808 1807 {
1809 1808 gss_OID_set returned_mechSet;
1810 1809 OM_uint32 major_status;
1811 1810 OM_uint32 length;
1812 1811 OM_uint32 bytes;
1813 1812 OM_uint32 set_length;
1814 1813 uchar_t *start;
1815 1814 int i;
1816 1815
1817 1816 if (**buff_in != SEQUENCE_OF)
1818 1817 return (NULL);
1819 1818
1820 1819 start = *buff_in;
1821 1820 (*buff_in)++;
1822 1821
1823 1822 length = get_der_length(buff_in, buff_length, &bytes);
1824 1823
1825 1824 major_status = gss_create_empty_oid_set(minor_status,
1826 1825 &returned_mechSet);
1827 1826 if (major_status != GSS_S_COMPLETE)
1828 1827 return (NULL);
1829 1828
1830 1829 for (set_length = 0, i = 0; set_length < length; i++) {
1831 1830 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in,
1832 1831 buff_length - (*buff_in - start));
1833 1832 if (temp != NULL) {
1834 1833 major_status = gss_add_oid_set_member(minor_status,
1835 1834 temp, &returned_mechSet);
1836 1835 if (major_status == GSS_S_COMPLETE) {
1837 1836 set_length +=
1838 1837 returned_mechSet->elements[i].length +2;
1839 1838 generic_gss_release_oid(minor_status, &temp);
1840 1839 }
1841 1840 }
1842 1841 }
1843 1842
1844 1843 return (returned_mechSet);
1845 1844 }
1846 1845
1847 1846 /*
1848 1847 * der encode the passed mechSet and place it into buf_out,
1849 1848 * advancing the buffer pointer.
1850 1849 */
1851 1850 static int
1852 1851 put_mech_set(uchar_t **buf_out, gss_OID_set mechSet, int buflen)
1853 1852 {
1854 1853 int i, ret;
1855 1854 OM_uint32 length = 0;
1856 1855 uchar_t *start;
1857 1856
1858 1857 if (buf_out == NULL || *buf_out == NULL)
1859 1858 return (-1);
1860 1859
1861 1860 start = *buf_out;
1862 1861
1863 1862 *(*buf_out)++ = SEQUENCE_OF;
1864 1863
1865 1864 for (i = 0; i < mechSet->count; i++) {
1866 1865 /*
1867 1866 * Mech OID ASN.1 size = 2 + length.
1868 1867 * 1 = 0x06, 1 for length of OID
1869 1868 * typically, less than 128, so only 1 byte needed.
1870 1869 */
1871 1870 length += 1 + der_length_size(mechSet->elements[i].length) +
1872 1871 mechSet->elements[i].length;
1873 1872 }
1874 1873 if (length > (buflen-1))
1875 1874 return (-1);
1876 1875
1877 1876 if (put_der_length(length, buf_out, buflen-1) < 0)
1878 1877 return (-1);
1879 1878
1880 1879 for (i = 0; i < mechSet->count; i++) {
1881 1880 if ((ret = put_mech_oid(buf_out, &mechSet->elements[i],
1882 1881 buflen - (int)(*buf_out - start))))
1883 1882 return (ret);
1884 1883 }
1885 1884 return (0);
1886 1885 }
1887 1886
1888 1887 /*
1889 1888 * Verify that buff_in is pointing to a BIT_STRING with the correct
1890 1889 * length and padding for the req_flags. If it is, decode req_flags
1891 1890 * and return them, otherwise, return NULL.
1892 1891 */
1893 1892 static OM_uint32
1894 1893 get_req_flags(unsigned char **buff_in, int *bodysize, OM_uint32 *req_flags)
1895 1894 {
1896 1895 int len;
1897 1896 uchar_t *start = *buff_in;
1898 1897
1899 1898 /* It is OK if no ReqFlags data is sent. */
1900 1899 if (**buff_in != (CONTEXT | 0x01))
1901 1900 return (0);
1902 1901
1903 1902 /* If they are sent, make sure the fields are correct. */
1904 1903 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1905 1904 *bodysize, &len) < 0)
1906 1905 return (ACCEPT_DEFECTIVE_TOKEN);
1907 1906
1908 1907 /* We don't care what the flags are. */
1909 1908 (*buff_in) += len;
1910 1909
1911 1910 /* Don't return any flags, this field is useless */
1912 1911 *req_flags = 0;
1913 1912
1914 1913 *bodysize -= *buff_in - start;
1915 1914 return (0);
1916 1915 }
1917 1916
1918 1917 /*
1919 1918 * get the negotiation results, decoding the ENUMERATED type result
1920 1919 * from the buffer, advancing the buffer pointer.
1921 1920 */
1922 1921 static OM_uint32
1923 1922 get_negResult(unsigned char **buff_in, int bodysize)
1924 1923 {
1925 1924 unsigned char *iptr = *buff_in;
1926 1925 int len;
1927 1926 unsigned int bytes;
1928 1927 OM_uint32 result;
1929 1928 /*
1930 1929 * Verify that the data is ASN.1 encoded correctly
1931 1930 */
1932 1931 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1933 1932 bodysize, &len) < 0)
1934 1933 return (ACCEPT_DEFECTIVE_TOKEN);
1935 1934
1936 1935 if (*(*buff_in)++ == SEQUENCE) {
1937 1936 if ((len = get_der_length(buff_in,
1938 1937 bodysize - (*buff_in - iptr), &bytes)) < 0)
1939 1938 return (ACCEPT_DEFECTIVE_TOKEN);
1940 1939 } else {
1941 1940 return (ACCEPT_INCOMPLETE);
1942 1941 }
1943 1942
1944 1943 /*
1945 1944 * if we find an octet string, we need to return
1946 1945 * incomplete so that we process the token correctly.
1947 1946 * Anything else unexpected, we reject.
1948 1947 */
1949 1948 if (*(*buff_in)++ == CONTEXT) {
1950 1949 if ((len = get_der_length(buff_in, bodysize -
1951 1950 (*buff_in - iptr), &bytes)) < 0)
1952 1951 return (ACCEPT_DEFECTIVE_TOKEN);
1953 1952 } else {
1954 1953 return (ACCEPT_INCOMPLETE);
1955 1954 }
1956 1955
1957 1956 if (*(*buff_in) == OCTET_STRING)
1958 1957 return (ACCEPT_INCOMPLETE);
1959 1958
1960 1959 if (*(*buff_in)++ != ENUMERATED)
1961 1960 return (ACCEPT_DEFECTIVE_TOKEN);
1962 1961
1963 1962 if (*(*buff_in)++ != ENUMERATION_LENGTH)
1964 1963 return (ACCEPT_DEFECTIVE_TOKEN);
1965 1964
1966 1965 /*
1967 1966 * Save the result byte to return later.
1968 1967 * This is the result
1969 1968 */
1970 1969 result = (OM_uint32)*(*buff_in)++;
1971 1970
1972 1971 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01),
1973 1972 bodysize - (*buff_in - iptr), &len) < 0)
1974 1973 result = ACCEPT_DEFECTIVE_TOKEN;
1975 1974
1976 1975 return (result);
1977 1976 }
1978 1977
1979 1978 /*
1980 1979 * der encode the passed negResults as an ENUMERATED type and
1981 1980 * place it in buf_out, advancing the buffer.
1982 1981 */
1983 1982
1984 1983 static int
1985 1984 put_negResult(uchar_t **buf_out, OM_uint32 negResult, int buflen)
1986 1985 {
1987 1986 if (buflen < 3)
1988 1987 return (-1);
1989 1988 *(*buf_out)++ = ENUMERATED;
1990 1989 *(*buf_out)++ = ENUMERATION_LENGTH;
1991 1990 *(*buf_out)++ = (unsigned char) negResult;
1992 1991 return (0);
1993 1992 }
1994 1993
1995 1994 /*
1996 1995 * This routine compares the recieved mechset to the mechset that
1997 1996 * this server can support. It looks sequentially through the mechset
1998 1997 * and the first one that matches what the server can support is
1999 1998 * chosen as the negotiated mechanism. If one is found, negResult
2000 1999 * is set to ACCEPT_COMPLETE, otherwise we return NULL and negResult
2001 2000 * is set to REJECT. Also, for purposes of determining latter behavior,
2002 2001 * the flag, firstMech is used to indicate if the chosen mechanism is the
2003 2002 * first of the mechset or not.
2004 2003 */
2005 2004 static gss_OID
2006 2005 negotiate_mech_type(OM_uint32 *minor_status,
2007 2006 gss_OID_set supported_mechSet,
2008 2007 gss_OID_set mechset,
2009 2008 OM_uint32 *negResult,
2010 2009 bool_t *firstMech)
2011 2010 {
2012 2011 gss_OID returned_mech;
2013 2012 OM_uint32 status;
2014 2013 int present;
2015 2014 int i;
2016 2015
2017 2016 for (i = 0; i < mechset->count; i++) {
2018 2017 gss_test_oid_set_member(minor_status, &mechset->elements[i],
2019 2018 supported_mechSet, &present);
2020 2019 if (present == TRUE) {
2021 2020 *negResult = ACCEPT_COMPLETE;
2022 2021
2023 2022 if (i == 0)
2024 2023 *firstMech = TRUE;
2025 2024 else
2026 2025 *firstMech = FALSE;
2027 2026
2028 2027 status = generic_gss_copy_oid(minor_status,
2029 2028 &mechset->elements[i], &returned_mech);
2030 2029
2031 2030 if (status != GSS_S_COMPLETE) {
2032 2031 *negResult = REJECT;
2033 2032 return (NULL);
2034 2033 }
2035 2034
2036 2035 return (returned_mech);
2037 2036 }
2038 2037 }
2039 2038
2040 2039 *negResult = REJECT;
2041 2040 return (NULL);
2042 2041 }
2043 2042
2044 2043 /*
2045 2044 * the next two routines make a token buffer suitable for
2046 2045 * spnego_gss_display_status. These currently take the string
2047 2046 * in name and place it in the token. Eventually, if
2048 2047 * spnego_gss_display_status returns valid error messages,
2049 2048 * these routines will be changes to return the error string.
2050 2049 */
2051 2050 static spnego_token_t
2052 2051 make_spnego_token(char *name)
2053 2052 {
2054 2053 spnego_token_t token;
2055 2054
2056 2055 token = (spnego_token_t)malloc(strlen(name)+1);
2057 2056
2058 2057 if (token == NULL)
2059 2058 return (NULL);
2060 2059 strcpy(token, name);
2061 2060 return (token);
2062 2061 }
2063 2062
2064 2063 static gss_buffer_desc
2065 2064 make_err_msg(char *name)
2066 2065 {
2067 2066 gss_buffer_desc buffer;
2068 2067
2069 2068 if (name == NULL) {
2070 2069 buffer.length = 0;
2071 2070 buffer.value = NULL;
2072 2071 } else {
2073 2072 buffer.length = strlen(name)+1;
2074 2073 buffer.value = make_spnego_token(name);
2075 2074 }
2076 2075
2077 2076 return (buffer);
2078 2077 }
2079 2078
2080 2079 /*
2081 2080 * Create the client side spnego token passed back to gss_init_sec_context
2082 2081 * and eventually up to the application program and over to the server.
2083 2082 *
2084 2083 * Use DER rules, definite length method per RFC 2478
2085 2084 */
2086 2085 static int
2087 2086 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx,
2088 2087 gss_OID_set mechSet,
2089 2088 gss_buffer_t data, send_token_flag sendtoken,
2090 2089 gss_buffer_t outbuf)
2091 2090 {
2092 2091 OM_uint32 status, minor_stat;
2093 2092 int tlen, dataLen = 0, ret = 0;
2094 2093 int MechSetLen = 0;
2095 2094 int negTokenInitSize = 0;
2096 2095 int i;
2097 2096 unsigned char *t;
2098 2097 unsigned char *ptr;
2099 2098 unsigned char *MechListPtr = NULL;
2100 2099 gss_buffer_desc MICbuff;
2101 2100
2102 2101 if (outbuf == GSS_C_NO_BUFFER)
2103 2102 return (-1);
2104 2103
2105 2104 outbuf->length = 0;
2106 2105 outbuf->value = NULL;
2107 2106
2108 2107 /* calculate the data length */
2109 2108
2110 2109 /* no token generated if sendtoken is not init or cont */
2111 2110 if ((sendtoken < INIT_TOKEN_SEND) ||
2112 2111 (sendtoken > CONT_TOKEN_SEND)) {
2113 2112 return (-1);
2114 2113 }
2115 2114
2116 2115 /*
2117 2116 * if this is the init token, we will send the mechset
2118 2117 * so include it's length.
2119 2118 */
2120 2119 if (sendtoken == INIT_TOKEN_SEND) {
2121 2120 /*
2122 2121 * Count bytes for the mechSet data
2123 2122 * Encoded in final output as:
2124 2123 * 0xa0 [DER LEN] 0x30 [DER LEN] [DATA]
2125 2124 */
2126 2125 for (i = 0; i < mechSet->count; i++)
2127 2126 MechSetLen += 1 +
2128 2127 der_length_size(mechSet->elements[i].length) +
2129 2128 mechSet->elements[i].length;
2130 2129
2131 2130 MechSetLen += 1 + der_length_size(MechSetLen);
2132 2131 dataLen += 1 + der_length_size(MechSetLen) + MechSetLen;
2133 2132
2134 2133 MechListPtr = (uchar_t *)malloc(dataLen);
2135 2134 ptr = (uchar_t *)MechListPtr;
2136 2135
2137 2136 if (MechListPtr != NULL) {
2138 2137 if ((ret = put_mech_set(&ptr, mechSet, dataLen))) {
2139 2138 free(MechListPtr);
2140 2139 goto errout;
2141 2140 }
2142 2141 } else {
2143 2142 ret = -1;
2144 2143 goto errout;
2145 2144 }
2146 2145
2147 2146 /*
2148 2147 * The MIC is done over the DER encoded mechSet.
2149 2148 */
2150 2149 spnego_ctx->DER_mechTypes.value = MechListPtr;
2151 2150 spnego_ctx->DER_mechTypes.length = ptr - MechListPtr;
2152 2151
2153 2152 /*
2154 2153 * Only send the MIC if we are *NOT* interoperating
2155 2154 * with Microsoft.
2156 2155 */
2157 2156 if (!spnego_ctx->MS_Interop) {
2158 2157 /*
2159 2158 * MechListMIC = DER(MIC(DER(MechSet)))
2160 2159 * Calculate it here, stick it in the buffer later.
2161 2160 */
2162 2161 MICbuff.length = 0;
2163 2162 MICbuff.value = NULL;
2164 2163 status = gss_get_mic(&minor_stat,
2165 2164 spnego_ctx->ctx_handle, GSS_C_QOP_DEFAULT,
2166 2165 &spnego_ctx->DER_mechTypes, &MICbuff);
2167 2166 /*
2168 2167 * If the MIC operation succeeded, use it,
2169 2168 * but don't fail if it did not succeed.
2170 2169 * MIC is optional and is not supported by all
2171 2170 * mechanisms all the time.
2172 2171 */
2173 2172 if (status == GSS_S_COMPLETE) {
2174 2173 /*
2175 2174 * Encoded in final output as:
2176 2175 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA]
2177 2176 * --s-- -------tlen------------
2178 2177 */
2179 2178 tlen = 1 + der_length_size(MICbuff.length) +
2180 2179 MICbuff.length;
2181 2180
2182 2181 dataLen += 1 + der_length_size(tlen) + tlen;
2183 2182 }
2184 2183 }
2185 2184 }
2186 2185
2187 2186 /*
2188 2187 * If a token from gss_init_sec_context exists,
2189 2188 * add the length of the token + the ASN.1 overhead
2190 2189 */
2191 2190 if (data != NULL) {
2192 2191 /*
2193 2192 * Encoded in final output as:
2194 2193 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA]
2195 2194 * -----s--------|--------s2----------
2196 2195 */
2197 2196 tlen = 1 + der_length_size(data->length) + data->length;
2198 2197
2199 2198 dataLen += 1 + der_length_size(tlen) + tlen;
2200 2199 }
2201 2200
2202 2201 /*
2203 2202 * Add size of DER encoding
2204 2203 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ]
2205 2204 * 0x30 [DER_LEN] [data]
2206 2205 *
2207 2206 */
2208 2207 dataLen += 1 + der_length_size(dataLen);
2209 2208
2210 2209 /*
2211 2210 * negTokenInitSize indicates the bytes needed to
2212 2211 * hold the ASN.1 encoding of the entire NegTokenInit
2213 2212 * SEQUENCE.
2214 2213 * 0xa0 [DER_LEN] + data
2215 2214 *
2216 2215 */
2217 2216 negTokenInitSize = dataLen;
2218 2217
2219 2218 tlen = g_token_size((gss_OID)gss_mech_spnego,
2220 2219 negTokenInitSize + 1 +
2221 2220 der_length_size(negTokenInitSize));
2222 2221
2223 2222 t = (unsigned char *) malloc(tlen);
2224 2223
2225 2224 if (t == NULL) {
2226 2225 return (-1);
2227 2226 }
2228 2227
2229 2228 ptr = t;
2230 2229
2231 2230 /* create the message */
2232 2231 if ((ret = g_make_token_header((gss_OID)gss_mech_spnego,
2233 2232 1 + negTokenInitSize + der_length_size(negTokenInitSize),
2234 2233 &ptr, tlen)))
2235 2234 goto errout;
2236 2235
2237 2236 if (sendtoken == INIT_TOKEN_SEND) {
2238 2237 *ptr++ = CONTEXT; /* NegotiationToken identifier */
2239 2238 if ((ret = put_der_length(negTokenInitSize, &ptr, tlen)))
2240 2239 goto errout;
2241 2240
2242 2241 *ptr++ = SEQUENCE;
2243 2242 if ((ret = put_der_length(negTokenInitSize - 4, &ptr,
2244 2243 tlen - (int)(ptr-t))))
2245 2244 goto errout;
2246 2245
2247 2246 *ptr++ = CONTEXT; /* MechTypeList identifier */
2248 2247 if ((ret = put_der_length(spnego_ctx->DER_mechTypes.length,
2249 2248 &ptr, tlen - (int)(ptr-t))))
2250 2249 goto errout;
2251 2250
2252 2251 /* We already encoded the MechSetList */
2253 2252 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value,
2254 2253 spnego_ctx->DER_mechTypes.length);
2255 2254
2256 2255 ptr += spnego_ctx->DER_mechTypes.length;
2257 2256
2258 2257 }
2259 2258
2260 2259 if (data != NULL) {
2261 2260 *ptr++ = CONTEXT | 0x02;
2262 2261 if ((ret = put_der_length(data->length + 4,
2263 2262 &ptr, tlen - (int)(ptr - t))))
2264 2263 goto errout;
2265 2264
2266 2265 if ((ret = put_input_token(&ptr, data,
2267 2266 tlen - (int)(ptr - t))))
2268 2267 goto errout;
2269 2268
2270 2269 /*
2271 2270 * We are in "optimistic" mode if we send a token
2272 2271 * with out initial message.
2273 2272 */
2274 2273 spnego_ctx->optimistic = (sendtoken == INIT_TOKEN_SEND);
2275 2274 }
2276 2275
2277 2276 if (!spnego_ctx->MS_Interop && MICbuff.length > 0) {
2278 2277 /* We already calculated the MechListMIC above */
2279 2278 int len = 1 + der_length_size(MICbuff.length) + MICbuff.length;
2280 2279 *ptr++ = CONTEXT | 0x03;
2281 2280 if ((ret = put_der_length(len, &ptr, tlen - (int)(ptr - t))))
2282 2281 goto errout;
2283 2282
2284 2283 if ((ret = put_input_token(&ptr, &MICbuff,
2285 2284 tlen - (int)(ptr - t))))
2286 2285 goto errout;
2287 2286
2288 2287 (void) gss_release_buffer(&minor_stat, &MICbuff);
2289 2288 }
2290 2289
2291 2290 errout:
2292 2291 if (ret != 0) {
2293 2292 if (t)
2294 2293 free(t);
2295 2294 t = NULL;
2296 2295 tlen = 0;
2297 2296 }
2298 2297 outbuf->length = tlen;
2299 2298 outbuf->value = (void *) t;
2300 2299
2301 2300 return (ret);
2302 2301 }
2303 2302
2304 2303 /*
2305 2304 * create the server side spnego token passed back to
2306 2305 * gss_accept_sec_context and eventually up to the application program
2307 2306 * and over to the client.
2308 2307 */
2309 2308 static int
2310 2309 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted,
2311 2310 gss_buffer_t data, gss_buffer_t mechListMIC,
2312 2311 send_token_flag sendtoken, int MS_Flag,
2313 2312 gss_buffer_t outbuf)
2314 2313 {
2315 2314 int tlen;
2316 2315 int ret;
2317 2316 int NegTokenTargSize;
2318 2317 int negresultTokenSize;
2319 2318 int NegTokenSize;
2320 2319 int rspTokenSize;
2321 2320 int micTokenSize;
2322 2321 int dataLen = 0;
2323 2322 unsigned char *t;
2324 2323 unsigned char *ptr;
2325 2324
2326 2325 if (outbuf == GSS_C_NO_BUFFER)
2327 2326 return (GSS_S_DEFECTIVE_TOKEN);
2328 2327
2329 2328 outbuf->length = 0;
2330 2329 outbuf->value = NULL;
2331 2330
2332 2331 /*
2333 2332 * ASN.1 encoding of the negResult
2334 2333 * ENUMERATED type is 3 bytes
2335 2334 * ENUMERATED TAG, Length, Value,
2336 2335 * Plus 2 bytes for the CONTEXT id and length.
2337 2336 */
2338 2337 negresultTokenSize = 5;
2339 2338
2340 2339 /*
2341 2340 * calculate data length
2342 2341 *
2343 2342 * If this is the initial token, include length of
2344 2343 * mech_type and the negotiation result fields.
2345 2344 */
2346 2345 if (sendtoken == INIT_TOKEN_SEND) {
2347 2346
2348 2347 if (mech_wanted != NULL) {
2349 2348 int mechlistTokenSize;
2350 2349 /*
2351 2350 * 1 byte for the CONTEXT ID(0xa0),
2352 2351 * 1 byte for the OID ID(0x06)
2353 2352 * 1 byte for OID Length field
2354 2353 * Plus the rest... (OID Length, OID value)
2355 2354 */
2356 2355 mechlistTokenSize = 3 + mech_wanted->length +
2357 2356 der_length_size(mech_wanted->length);
2358 2357
2359 2358 dataLen = negresultTokenSize + mechlistTokenSize;
2360 2359 }
2361 2360 } else {
2362 2361 /*
2363 2362 * If this is a response from a server, count
2364 2363 * the space needed for the negResult field.
2365 2364 * LENGTH(2) + ENUM(2) + result
2366 2365 */
2367 2366 dataLen = negresultTokenSize;
2368 2367 }
2369 2368 if (data != NULL && data->length > 0) {
2370 2369 /* Length of the inner token */
2371 2370 rspTokenSize = 1 + der_length_size(data->length) +
2372 2371 data->length;
2373 2372
2374 2373 dataLen += rspTokenSize;
2375 2374
2376 2375 /* Length of the outer token */
2377 2376 dataLen += 1 + der_length_size(rspTokenSize);
2378 2377 }
2379 2378 if (mechListMIC != NULL) {
2380 2379
2381 2380 /* Length of the inner token */
2382 2381 micTokenSize = 1 + der_length_size(mechListMIC->length) +
2383 2382 mechListMIC->length;
2384 2383
2385 2384 dataLen += micTokenSize;
2386 2385
2387 2386 /* Length of the outer token */
2388 2387 dataLen += 1 + der_length_size(micTokenSize);
2389 2388 } else if (data != NULL && data->length > 0 && MS_Flag) {
2390 2389 dataLen += rspTokenSize;
2391 2390 dataLen += 1 + der_length_size(rspTokenSize);
2392 2391 }
2393 2392
2394 2393 /*
2395 2394 * Add size of DER encoded:
2396 2395 * NegTokenTarg [ SEQUENCE ] of
2397 2396 * NegResult[0] ENUMERATED {
2398 2397 * accept_completed(0),
2399 2398 * accept_incomplete(1),
2400 2399 * reject(2) }
2401 2400 * supportedMech [1] MechType OPTIONAL,
2402 2401 * responseToken [2] OCTET STRING OPTIONAL,
2403 2402 * mechListMIC [3] OCTET STRING OPTIONAL
2404 2403 *
2405 2404 * size = data->length + MechListMic + SupportedMech len +
2406 2405 * Result Length + ASN.1 overhead
2407 2406 */
2408 2407 NegTokenTargSize = dataLen;
2409 2408 dataLen += 1 + der_length_size(NegTokenTargSize);
2410 2409
2411 2410 /*
2412 2411 * NegotiationToken [ CHOICE ]{
2413 2412 * negTokenInit [0] NegTokenInit,
2414 2413 * negTokenTarg [1] NegTokenTarg }
2415 2414 */
2416 2415 NegTokenSize = dataLen;
2417 2416 dataLen += 1 + der_length_size(NegTokenSize);
2418 2417
2419 2418 tlen = dataLen;
2420 2419 t = (unsigned char *) malloc(tlen);
2421 2420
2422 2421 if (t == NULL) {
2423 2422 ret = GSS_S_DEFECTIVE_TOKEN;
2424 2423 goto errout;
2425 2424 }
2426 2425
2427 2426 ptr = t;
2428 2427
2429 2428 if (sendtoken == INIT_TOKEN_SEND ||
2430 2429 sendtoken == ERROR_TOKEN_SEND) {
2431 2430 /*
2432 2431 * Indicate that we are sending CHOICE 1
2433 2432 * (NegTokenTarg)
2434 2433 */
2435 2434 *ptr++ = CONTEXT | 0x01;
2436 2435 if ((ret = put_der_length(NegTokenSize, &ptr, dataLen))) {
2437 2436 ret = GSS_S_DEFECTIVE_TOKEN;
2438 2437 goto errout;
2439 2438 }
2440 2439
2441 2440 *ptr++ = SEQUENCE;
2442 2441 if ((ret = put_der_length(NegTokenTargSize, &ptr,
2443 2442 tlen - (int)(ptr-t)))) {
2444 2443 ret = GSS_S_DEFECTIVE_TOKEN;
2445 2444 goto errout;
2446 2445 }
2447 2446
2448 2447 /*
2449 2448 * First field of the NegTokenTarg SEQUENCE
2450 2449 * is the ENUMERATED NegResult.
2451 2450 */
2452 2451 *ptr++ = CONTEXT;
2453 2452 if ((ret = put_der_length(3, &ptr,
2454 2453 tlen - (int)(ptr-t)))) {
2455 2454 ret = GSS_S_DEFECTIVE_TOKEN;
2456 2455 goto errout;
2457 2456 }
2458 2457 if ((ret = put_negResult(&ptr, status,
2459 2458 tlen - (int)(ptr - t)))) {
2460 2459 ret = GSS_S_DEFECTIVE_TOKEN;
2461 2460 goto errout;
2462 2461 }
2463 2462
2464 2463 if (sendtoken != ERROR_TOKEN_SEND && mech_wanted != NULL) {
2465 2464 /*
2466 2465 * Next, is the Supported MechType
2467 2466 */
2468 2467 *ptr++ = CONTEXT | 0x01;
2469 2468 if ((ret = put_der_length(mech_wanted->length + 2,
2470 2469 &ptr, tlen - (int)(ptr - t)))) {
2471 2470 ret = GSS_S_DEFECTIVE_TOKEN;
2472 2471 goto errout;
2473 2472 }
2474 2473 if ((ret = put_mech_oid(&ptr, mech_wanted,
2475 2474 tlen - (int)(ptr - t)))) {
2476 2475 ret = GSS_S_DEFECTIVE_TOKEN;
2477 2476 goto errout;
2478 2477 }
2479 2478 }
2480 2479 }
2481 2480
2482 2481 if (data != NULL && data->length > 0) {
2483 2482 *ptr++ = CONTEXT | 0x02;
2484 2483 if ((ret = put_der_length(rspTokenSize, &ptr,
2485 2484 tlen - (int)(ptr - t)))) {
2486 2485 ret = GSS_S_DEFECTIVE_TOKEN;
2487 2486 goto errout;
2488 2487 }
2489 2488 if ((ret = put_input_token(&ptr, data,
2490 2489 tlen - (int)(ptr - t)))) {
2491 2490 ret = GSS_S_DEFECTIVE_TOKEN;
2492 2491 goto errout;
2493 2492 }
2494 2493 }
2495 2494 if (mechListMIC != NULL) {
2496 2495 *ptr++ = CONTEXT | 0x03;
2497 2496 if ((ret = put_der_length(micTokenSize, &ptr,
2498 2497 tlen - (int)(ptr - t)))) {
2499 2498 ret = GSS_S_DEFECTIVE_TOKEN;
2500 2499 goto errout;
2501 2500 }
2502 2501 if ((ret = put_input_token(&ptr, mechListMIC,
2503 2502 tlen - (int)(ptr - t)))) {
2504 2503 ret = GSS_S_DEFECTIVE_TOKEN;
2505 2504 goto errout;
2506 2505 }
2507 2506 } else if (data != NULL && data->length > 0 && MS_Flag) {
2508 2507 *ptr++ = CONTEXT | 0x03;
2509 2508 if ((ret = put_der_length(rspTokenSize, &ptr,
2510 2509 tlen - (int)(ptr - t)))) {
2511 2510 ret = GSS_S_DEFECTIVE_TOKEN;
2512 2511 goto errout;
2513 2512 }
2514 2513 if ((ret = put_input_token(&ptr, data,
2515 2514 tlen - (int)(ptr - t)))) {
2516 2515 ret = GSS_S_DEFECTIVE_TOKEN;
2517 2516 }
2518 2517 }
2519 2518 errout:
2520 2519 if (ret != 0) {
2521 2520 if (t)
2522 2521 free(t);
2523 2522 } else {
2524 2523 outbuf->length = ptr - t;
2525 2524 outbuf->value = (void *) t;
2526 2525 }
2527 2526
2528 2527 return (ret);
2529 2528 }
2530 2529
2531 2530 /* determine size of token */
2532 2531 static int
2533 2532 g_token_size(gss_OID mech, unsigned int body_size)
2534 2533 {
2535 2534 int hdrsize;
2536 2535
2537 2536 /*
2538 2537 * Initialize the header size to the
2539 2538 * MECH_OID byte + the bytes needed to indicate the
2540 2539 * length of the OID + the OID itself.
2541 2540 *
2542 2541 * 0x06 [MECHLENFIELD] MECHDATA
2543 2542 */
2544 2543 hdrsize = 1 + der_length_size(mech->length) + mech->length;
2545 2544
2546 2545 /*
2547 2546 * Now add the bytes needed for the initial header
2548 2547 * token bytes:
2549 2548 * 0x60 + [DER_LEN] + HDRSIZE
2550 2549 */
2551 2550 hdrsize += 1 + der_length_size(body_size + hdrsize);
2552 2551
2553 2552 return (hdrsize + body_size);
2554 2553 }
2555 2554
2556 2555 /*
2557 2556 * generate token header.
2558 2557 *
2559 2558 * Use DER Definite Length method per RFC2478
2560 2559 * Use of indefinite length encoding will not be compatible
2561 2560 * with Microsoft or others that actually follow the spec.
2562 2561 */
2563 2562 static int
2564 2563 g_make_token_header(gss_OID mech,
2565 2564 int body_size,
2566 2565 unsigned char **buf,
2567 2566 int totallen)
2568 2567 {
2569 2568 int hdrsize, ret = 0;
2570 2569 unsigned char *p = *buf;
2571 2570
2572 2571 hdrsize = 1 + der_length_size(mech->length) + mech->length;
2573 2572
2574 2573 *(*buf)++ = HEADER_ID;
2575 2574 if ((ret = put_der_length(hdrsize + body_size, buf, totallen)))
2576 2575 return (ret);
2577 2576
2578 2577 *(*buf)++ = MECH_OID;
2579 2578 if ((ret = put_der_length(mech->length, buf,
2580 2579 totallen - (int)(p - *buf))))
2581 2580 return (ret);
2582 2581 TWRITE_STR(*buf, mech->elements, ((int)mech->length));
2583 2582 return (0);
2584 2583 }
2585 2584
2586 2585 static int
2587 2586 g_get_tag_and_length(unsigned char **buf, uchar_t tag, int buflen, int *outlen)
2588 2587 {
2589 2588 unsigned char *ptr = *buf;
2590 2589 int ret = -1; /* pessimists, assume failure ! */
2591 2590 OM_uint32 encoded_len;
2592 2591
2593 2592 if (buflen > 0 && *ptr == tag) {
2594 2593 ptr++;
2595 2594 *outlen = get_der_length(&ptr, buflen, &encoded_len);
2596 2595 if (*outlen < 0)
2597 2596 ret = *outlen;
2598 2597 if ((ptr + *outlen) > (*buf + buflen))
2599 2598 ret = -1;
2600 2599 else
2601 2600 ret = 0;
2602 2601 }
2603 2602
2604 2603 *buf = ptr;
2605 2604 return (ret);
2606 2605 }
2607 2606
2608 2607 static int
2609 2608 g_verify_neg_token_init(unsigned char **buf_in, int cur_size)
2610 2609 {
2611 2610 unsigned char *buf = *buf_in;
2612 2611 unsigned char *endptr = buf + cur_size;
2613 2612 int seqsize;
2614 2613 int ret = 0;
2615 2614 unsigned int bytes;
2616 2615
2617 2616 /*
2618 2617 * Verify this is a NegotiationToken type token
2619 2618 * - check for a0(context specific identifier)
2620 2619 * - get length and verify that enoughd ata exists
2621 2620 */
2622 2621 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0)
2623 2622 return (G_BAD_TOK_HEADER);
2624 2623
2625 2624 cur_size = seqsize; /* should indicate bytes remaining */
2626 2625
2627 2626 /*
2628 2627 * Verify the next piece, it should identify this as
2629 2628 * a strucure of type NegTokenInit.
2630 2629 */
2631 2630 if (*buf++ == SEQUENCE) {
2632 2631 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2633 2632 return (G_BAD_TOK_HEADER);
2634 2633 /*
2635 2634 * Make sure we have the entire buffer as described
2636 2635 */
2637 2636 if (buf + seqsize > endptr)
2638 2637 return (G_BAD_TOK_HEADER);
2639 2638 } else {
2640 2639 return (G_BAD_TOK_HEADER);
2641 2640 }
2642 2641
2643 2642 cur_size = seqsize; /* should indicate bytes remaining */
2644 2643
2645 2644 /*
2646 2645 * Verify that the first blob is a sequence of mechTypes
2647 2646 */
2648 2647 if (*buf++ == CONTEXT) {
2649 2648 if ((seqsize = get_der_length(&buf, cur_size, &bytes)) < 0)
2650 2649 return (G_BAD_TOK_HEADER);
2651 2650 /*
2652 2651 * Make sure we have the entire buffer as described
2653 2652 */
2654 2653 if (buf + bytes > endptr)
2655 2654 return (G_BAD_TOK_HEADER);
2656 2655 } else {
2657 2656 return (G_BAD_TOK_HEADER);
2658 2657 }
2659 2658
2660 2659 /*
2661 2660 * At this point, *buf should be at the beginning of the
2662 2661 * DER encoded list of mech types that are to be negotiated.
2663 2662 */
2664 2663 *buf_in = buf;
2665 2664
2666 2665 return (ret);
2667 2666
2668 2667 }
2669 2668
2670 2669 /* verify token header. */
2671 2670 static int
2672 2671 g_verify_token_header(gss_OID mech,
2673 2672 int *body_size,
2674 2673 unsigned char **buf_in,
2675 2674 int tok_type,
2676 2675 int toksize)
2677 2676 {
2678 2677 unsigned char *buf = *buf_in;
2679 2678 int seqsize;
2680 2679 gss_OID_desc toid;
2681 2680 int ret = 0;
2682 2681 unsigned int bytes;
2683 2682
2684 2683 if ((toksize -= 1) < 0)
2685 2684 return (G_BAD_TOK_HEADER);
2686 2685
2687 2686 if (*buf++ != HEADER_ID)
2688 2687 return (G_BAD_TOK_HEADER);
2689 2688
2690 2689 if ((seqsize = get_der_length(&buf, toksize, &bytes)) < 0)
2691 2690 return (G_BAD_TOK_HEADER);
2692 2691
2693 2692 if ((seqsize + bytes) != toksize)
2694 2693 return (G_BAD_TOK_HEADER);
2695 2694
2696 2695 if ((toksize -= 1) < 0)
2697 2696 return (G_BAD_TOK_HEADER);
2698 2697
2699 2698
2700 2699 if (*buf++ != MECH_OID)
2701 2700 return (G_BAD_TOK_HEADER);
2702 2701
2703 2702 if ((toksize -= 1) < 0)
2704 2703 return (G_BAD_TOK_HEADER);
2705 2704
2706 2705 toid.length = *buf++;
2707 2706
2708 2707 if ((toksize -= toid.length) < 0)
2709 2708 return (G_BAD_TOK_HEADER);
2710 2709
2711 2710 toid.elements = buf;
2712 2711 buf += toid.length;
2713 2712
2714 2713 if (!g_OID_equal(&toid, mech))
2715 2714 ret = G_WRONG_MECH;
2716 2715
2717 2716 /*
2718 2717 * G_WRONG_MECH is not returned immediately because it's more important
2719 2718 * to return G_BAD_TOK_HEADER if the token header is in fact bad
2720 2719 */
2721 2720 if ((toksize -= 2) < 0)
2722 2721 return (G_BAD_TOK_HEADER);
2723 2722
2724 2723 if (!ret) {
2725 2724 *buf_in = buf;
2726 2725 *body_size = toksize;
2727 2726 }
2728 2727
2729 2728 return (ret);
2730 2729 }
|
↓ open down ↓ |
1637 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX