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