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 2009 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 *ctx = GSS_C_NO_CONTEXT; 1363 1364 ret = GSS_S_DEFECTIVE_TOKEN; 1365 der_mechTypes.length = 0; 1366 der_mechTypes.value = NULL; 1367 *mechToken = *mechListMIC = GSS_C_NO_BUFFER; 1368 supported_mechSet = mechTypes = GSS_C_NO_OID_SET; 1369 *return_token = ERROR_TOKEN_SEND; 1370 *negState = REJECT; 1371 *minor_status = 0; 1372 1373 ret = get_negTokenInit(minor_status, buf, &der_mechTypes, 1374 &mechTypes, &req_flags, 1375 mechToken, mechListMIC); 1376 if (ret != GSS_S_COMPLETE) { 1377 goto cleanup; 1378 } 1379 if (cred != GSS_C_NO_CREDENTIAL) { 1380 ret = gss_inquire_cred(minor_status, cred, NULL, NULL, 1381 NULL, &supported_mechSet); 1382 if (ret != GSS_S_COMPLETE) { 1383 *return_token = NO_TOKEN_SEND; 1384 goto cleanup; 1385 } 1386 } else { 1387 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, 1388 GSS_C_ACCEPT, NULL, 1389 &supported_mechSet); 1390 if (ret != GSS_S_COMPLETE) { 1391 *return_token = NO_TOKEN_SEND; 1392 goto cleanup; 1393 } 1394 } 1395 /* 1396 * Select the best match between the list of mechs 1397 * that the initiator requested and the list that 1398 * the acceptor will support. 1399 */ 1400 mech_wanted = negotiate_mech_type(minor_status, 1401 supported_mechSet, 1402 mechTypes, 1403 negState); 1404 if (*negState == REJECT) { 1405 ret = GSS_S_BAD_MECH; 1406 /* 1407 * Solaris Kerberos: If we can't negotiate a mechanism then 1408 * there is no context to associate with an error token. 1409 */ 1410 *return_token = NO_TOKEN_SEND; 1411 goto cleanup; 1412 } 1413 sc = (spnego_gss_ctx_id_t)*ctx; 1414 if (sc != NULL) { 1415 gss_release_buffer(&tmpmin, &sc->DER_mechTypes); 1416 assert(mech_wanted != GSS_C_NO_OID); 1417 } else 1418 sc = create_spnego_ctx(); 1419 if (sc == NULL) { 1420 ret = GSS_S_FAILURE; 1421 *return_token = NO_TOKEN_SEND; 1422 generic_gss_release_oid(&tmpmin, &mech_wanted); 1423 goto cleanup; 1424 } 1425 sc->internal_mech = mech_wanted; 1426 sc->DER_mechTypes = der_mechTypes; 1427 der_mechTypes.length = 0; 1428 der_mechTypes.value = NULL; 1429 1430 if (*negState == REQUEST_MIC) 1431 sc->mic_reqd = 1; 1432 1433 *return_token = INIT_TOKEN_SEND; 1434 sc->firstpass = 1; 1435 *ctx = (gss_ctx_id_t)sc; 1436 ret = GSS_S_COMPLETE; 1437 cleanup: 1438 gss_release_oid_set(&tmpmin, &mechTypes); 1439 gss_release_oid_set(&tmpmin, &supported_mechSet); 1440 if (der_mechTypes.length != 0) 1441 gss_release_buffer(&tmpmin, &der_mechTypes); 1442 return ret; 1443 } 1444 1445 static OM_uint32 1446 acc_ctx_cont(OM_uint32 *minstat, 1447 gss_buffer_t buf, 1448 gss_ctx_id_t *ctx, 1449 gss_buffer_t *responseToken, 1450 gss_buffer_t *mechListMIC, 1451 OM_uint32 *negState, 1452 send_token_flag *return_token) 1453 { 1454 OM_uint32 ret, tmpmin; 1455 gss_OID supportedMech; 1456 spnego_gss_ctx_id_t sc; 1457 unsigned int len; 1458 unsigned char *ptr, *bufstart; 1459 1460 sc = (spnego_gss_ctx_id_t)*ctx; 1461 ret = GSS_S_DEFECTIVE_TOKEN; 1462 *negState = REJECT; 1463 *minstat = 0; 1464 supportedMech = GSS_C_NO_OID; 1465 *return_token = ERROR_TOKEN_SEND; 1466 *responseToken = *mechListMIC = GSS_C_NO_BUFFER; 1467 1468 ptr = bufstart = buf->value; 1469 #define REMAIN (buf->length - (ptr - bufstart)) 1470 if (REMAIN > INT_MAX) 1471 return GSS_S_DEFECTIVE_TOKEN; 1472 1473 /* 1474 * Attempt to work with old Sun SPNEGO. 1475 */ 1476 if (*ptr == HEADER_ID) { 1477 ret = g_verify_token_header(gss_mech_spnego, 1478 &len, &ptr, 0, REMAIN); 1479 if (ret) { 1480 *minstat = ret; 1481 return GSS_S_DEFECTIVE_TOKEN; 1482 } 1483 } 1484 if (*ptr != (CONTEXT | 0x01)) { 1485 return GSS_S_DEFECTIVE_TOKEN; 1486 } 1487 ret = get_negTokenResp(minstat, ptr, REMAIN, 1488 negState, &supportedMech, 1489 responseToken, mechListMIC); 1490 if (ret != GSS_S_COMPLETE) 1491 goto cleanup; 1492 1493 if (*responseToken == GSS_C_NO_BUFFER && 1494 *mechListMIC == GSS_C_NO_BUFFER) { 1495 1496 ret = GSS_S_DEFECTIVE_TOKEN; 1497 goto cleanup; 1498 } 1499 if (supportedMech != GSS_C_NO_OID) { 1500 ret = GSS_S_DEFECTIVE_TOKEN; 1501 goto cleanup; 1502 } 1503 sc->firstpass = 0; 1504 *negState = ACCEPT_INCOMPLETE; 1505 *return_token = CONT_TOKEN_SEND; 1506 cleanup: 1507 if (supportedMech != GSS_C_NO_OID) { 1508 generic_gss_release_oid(&tmpmin, &supportedMech); 1509 } 1510 return ret; 1511 #undef REMAIN 1512 } 1513 1514 /* 1515 * Verify that mech OID is either exactly the same as the negotiated 1516 * mech OID, or is a mech OID supported by the negotiated mech. MS 1517 * implementations can list a most preferred mech using an incorrect 1518 * krb5 OID while emitting a krb5 initiator mech token having the 1519 * correct krb5 mech OID. 1520 */ 1521 static OM_uint32 1522 acc_ctx_vfy_oid(OM_uint32 *minor_status, 1523 spnego_gss_ctx_id_t sc, gss_OID mechoid, 1524 OM_uint32 *negState, send_token_flag *tokflag) 1525 { 1526 OM_uint32 ret, tmpmin; 1527 gss_mechanism mech = NULL; 1528 gss_OID_set mech_set = GSS_C_NO_OID_SET; 1529 int present = 0; 1530 1531 if (g_OID_equal(sc->internal_mech, mechoid)) 1532 return GSS_S_COMPLETE; 1533 1534 /* 1535 * SUNW17PACresync 1536 * If both mechs are kerb, we are done. 1537 */ 1538 if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) { 1539 return GSS_S_COMPLETE; 1540 } 1541 1542 mech = gssint_get_mechanism(mechoid); 1543 if (mech == NULL || mech->gss_indicate_mechs == NULL) { 1544 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 1545 map_errcode(minor_status); 1546 *negState = REJECT; 1547 *tokflag = ERROR_TOKEN_SEND; 1548 return GSS_S_BAD_MECH; 1549 } 1550 ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set); 1551 if (ret != GSS_S_COMPLETE) { 1552 *tokflag = NO_TOKEN_SEND; 1553 map_error(minor_status, mech); 1554 goto cleanup; 1555 } 1556 ret = gss_test_oid_set_member(minor_status, sc->internal_mech, 1557 mech_set, &present); 1558 if (ret != GSS_S_COMPLETE) 1559 goto cleanup; 1560 if (!present) { 1561 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 1562 map_errcode(minor_status); 1563 *negState = REJECT; 1564 *tokflag = ERROR_TOKEN_SEND; 1565 ret = GSS_S_BAD_MECH; 1566 } 1567 cleanup: 1568 gss_release_oid_set(&tmpmin, &mech_set); 1569 return ret; 1570 } 1571 #ifndef LEAN_CLIENT 1572 /* 1573 * Wrap call to gss_accept_sec_context() and update state 1574 * accordingly. 1575 */ 1576 static OM_uint32 1577 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, 1578 gss_cred_id_t cred, gss_buffer_t mechtok_in, 1579 gss_OID *mech_type, gss_buffer_t mechtok_out, 1580 OM_uint32 *ret_flags, OM_uint32 *time_rec, 1581 gss_cred_id_t *delegated_cred_handle, 1582 OM_uint32 *negState, send_token_flag *tokflag) 1583 { 1584 OM_uint32 ret; 1585 gss_OID_desc mechoid; 1586 1587 if (sc->ctx_handle == GSS_C_NO_CONTEXT) { 1588 /* 1589 * mechoid is an alias; don't free it. 1590 */ 1591 ret = gssint_get_mech_type(&mechoid, mechtok_in); 1592 if (ret != GSS_S_COMPLETE) { 1593 *tokflag = NO_TOKEN_SEND; 1594 return ret; 1595 } 1596 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid, 1597 negState, tokflag); 1598 if (ret != GSS_S_COMPLETE) 1599 return ret; 1600 } 1601 1602 ret = gss_accept_sec_context(minor_status, 1603 &sc->ctx_handle, 1604 cred, 1605 mechtok_in, 1606 GSS_C_NO_CHANNEL_BINDINGS, 1607 &sc->internal_name, 1608 mech_type, 1609 mechtok_out, 1610 &sc->ctx_flags, 1611 time_rec, 1612 delegated_cred_handle); 1613 1614 if (ret == GSS_S_COMPLETE) { 1615 #ifdef MS_BUG_TEST 1616 /* 1617 * Force MIC to be not required even if we previously 1618 * requested a MIC. 1619 */ 1620 char *envstr = getenv("MS_FORCE_NO_MIC"); 1621 1622 if (envstr != NULL && strcmp(envstr, "1") == 0 && 1623 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) && 1624 sc->mic_reqd) { 1625 1626 sc->mic_reqd = 0; 1627 } 1628 #endif 1629 sc->mech_complete = 1; 1630 if (ret_flags != NULL) 1631 *ret_flags = sc->ctx_flags; 1632 1633 if (!sc->mic_reqd) { 1634 *negState = ACCEPT_COMPLETE; 1635 ret = GSS_S_COMPLETE; 1636 } else { 1637 ret = GSS_S_CONTINUE_NEEDED; 1638 } 1639 } else if (ret != GSS_S_CONTINUE_NEEDED) { 1640 *negState = REJECT; 1641 *tokflag = ERROR_TOKEN_SEND; 1642 } 1643 return ret; 1644 } 1645 1646 /*ARGSUSED*/ 1647 OM_uint32 1648 glue_spnego_gss_accept_sec_context( 1649 void *context, 1650 OM_uint32 *minor_status, 1651 gss_ctx_id_t *context_handle, 1652 gss_cred_id_t verifier_cred_handle, 1653 gss_buffer_t input_token, 1654 gss_channel_bindings_t input_chan_bindings, 1655 gss_name_t *src_name, 1656 gss_OID *mech_type, 1657 gss_buffer_t output_token, 1658 OM_uint32 *ret_flags, 1659 OM_uint32 *time_rec, 1660 gss_cred_id_t *delegated_cred_handle) 1661 { 1662 return(spnego_gss_accept_sec_context( 1663 minor_status, 1664 context_handle, 1665 verifier_cred_handle, 1666 input_token, 1667 input_chan_bindings, 1668 src_name, 1669 mech_type, 1670 output_token, 1671 ret_flags, 1672 time_rec, 1673 delegated_cred_handle)); 1674 } 1675 1676 /*ARGSUSED*/ 1677 OM_uint32 1678 spnego_gss_accept_sec_context( 1679 OM_uint32 *minor_status, 1680 gss_ctx_id_t *context_handle, 1681 gss_cred_id_t verifier_cred_handle, 1682 gss_buffer_t input_token, 1683 gss_channel_bindings_t input_chan_bindings, 1684 gss_name_t *src_name, 1685 gss_OID *mech_type, 1686 gss_buffer_t output_token, 1687 OM_uint32 *ret_flags, 1688 OM_uint32 *time_rec, 1689 gss_cred_id_t *delegated_cred_handle) 1690 { 1691 OM_uint32 ret, tmpmin, negState; 1692 send_token_flag return_token; 1693 gss_buffer_t mechtok_in, mic_in, mic_out; 1694 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER; 1695 spnego_gss_ctx_id_t sc = NULL; 1696 OM_uint32 mechstat = GSS_S_FAILURE; 1697 int sendTokenInit = 0; 1698 1699 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER; 1700 1701 if (minor_status != NULL) 1702 *minor_status = 0; 1703 if (output_token != GSS_C_NO_BUFFER) { 1704 output_token->length = 0; 1705 output_token->value = NULL; 1706 } 1707 1708 1709 if (minor_status == NULL || 1710 output_token == GSS_C_NO_BUFFER || 1711 context_handle == NULL) { 1712 return GSS_S_CALL_INACCESSIBLE_WRITE; 1713 } 1714 1715 if (input_token == GSS_C_NO_BUFFER) { 1716 return GSS_S_CALL_INACCESSIBLE_READ; 1717 } 1718 1719 sc = (spnego_gss_ctx_id_t)*context_handle; 1720 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) { 1721 if (src_name != NULL) 1722 *src_name = GSS_C_NO_NAME; 1723 if (mech_type != NULL) 1724 *mech_type = GSS_C_NO_OID; 1725 if (time_rec != NULL) 1726 *time_rec = 0; 1727 if (ret_flags != NULL) 1728 *ret_flags = 0; 1729 if (delegated_cred_handle != NULL) 1730 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 1731 if (input_token->length == 0) { 1732 sendTokenInit = 1; 1733 ret = acc_ctx_hints(minor_status, 1734 context_handle, 1735 verifier_cred_handle, 1736 &mic_out, 1737 &negState, 1738 &return_token); 1739 if (ret != GSS_S_COMPLETE) 1740 goto cleanup; 1741 ret = GSS_S_CONTINUE_NEEDED; 1742 } else { 1743 /* Can set negState to REQUEST_MIC */ 1744 ret = acc_ctx_new(minor_status, input_token, 1745 context_handle, verifier_cred_handle, 1746 &mechtok_in, &mic_in, 1747 &negState, &return_token); 1748 if (ret != GSS_S_COMPLETE) 1749 goto cleanup; 1750 ret = GSS_S_CONTINUE_NEEDED; 1751 } 1752 } else { 1753 /* Can set negState to ACCEPT_INCOMPLETE */ 1754 ret = acc_ctx_cont(minor_status, input_token, 1755 context_handle, &mechtok_in, 1756 &mic_in, &negState, &return_token); 1757 if (ret != GSS_S_COMPLETE) 1758 goto cleanup; 1759 ret = GSS_S_CONTINUE_NEEDED; 1760 } 1761 1762 sc = (spnego_gss_ctx_id_t)*context_handle; 1763 /* 1764 * Handle mechtok_in and mic_in only if they are 1765 * present in input_token. If neither is present, whether 1766 * this is an error depends on whether this is the first 1767 * round-trip. RET is set to a default value according to 1768 * whether it is the first round-trip. 1769 */ 1770 mechstat = GSS_S_FAILURE; 1771 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) { 1772 ret = acc_ctx_call_acc(minor_status, sc, 1773 verifier_cred_handle, mechtok_in, 1774 mech_type, &mechtok_out, 1775 ret_flags, time_rec, 1776 delegated_cred_handle, 1777 &negState, &return_token); 1778 } else if (negState == REQUEST_MIC) { 1779 mechstat = GSS_S_CONTINUE_NEEDED; 1780 } 1781 1782 if (!HARD_ERROR(ret) && sc->mech_complete && 1783 (sc->ctx_flags & GSS_C_INTEG_FLAG)) { 1784 1785 ret = handle_mic(minor_status, mic_in, 1786 (mechtok_out.length != 0), 1787 sc, &mic_out, 1788 &negState, &return_token); 1789 } 1790 1791 cleanup: 1792 if (return_token != NO_TOKEN_SEND && return_token != CHECK_MIC) { 1793 /* For acceptor-sends-first send a tokenInit */ 1794 int tmpret; 1795 1796 assert(sc != NULL); 1797 1798 if (sendTokenInit) { 1799 tmpret = make_spnego_tokenInit_msg(sc, 1800 1, 1801 mic_out, 1802 0, 1803 GSS_C_NO_BUFFER, 1804 return_token, 1805 output_token); 1806 } else { 1807 tmpret = make_spnego_tokenTarg_msg(negState, sc->internal_mech, 1808 &mechtok_out, mic_out, 1809 return_token, 1810 output_token); 1811 } 1812 if (tmpret < 0) 1813 ret = GSS_S_FAILURE; 1814 } 1815 if (ret == GSS_S_COMPLETE) { 1816 *context_handle = (gss_ctx_id_t)sc->ctx_handle; 1817 if (sc->internal_name != GSS_C_NO_NAME && 1818 src_name != NULL) { 1819 *src_name = sc->internal_name; 1820 } 1821 release_spnego_ctx(&sc); 1822 } 1823 gss_release_buffer(&tmpmin, &mechtok_out); 1824 if (mechtok_in != GSS_C_NO_BUFFER) { 1825 gss_release_buffer(&tmpmin, mechtok_in); 1826 free(mechtok_in); 1827 } 1828 if (mic_in != GSS_C_NO_BUFFER) { 1829 gss_release_buffer(&tmpmin, mic_in); 1830 free(mic_in); 1831 } 1832 if (mic_out != GSS_C_NO_BUFFER) { 1833 gss_release_buffer(&tmpmin, mic_out); 1834 free(mic_out); 1835 } 1836 return ret; 1837 } 1838 #endif /* LEAN_CLIENT */ 1839 1840 /*ARGSUSED*/ 1841 OM_uint32 1842 glue_spnego_gss_display_status( 1843 void *context, 1844 OM_uint32 *minor_status, 1845 OM_uint32 status_value, 1846 int status_type, 1847 gss_OID mech_type, 1848 OM_uint32 *message_context, 1849 gss_buffer_t status_string) 1850 { 1851 return (spnego_gss_display_status(minor_status, 1852 status_value, 1853 status_type, 1854 mech_type, 1855 message_context, 1856 status_string)); 1857 } 1858 1859 /*ARGSUSED*/ 1860 OM_uint32 1861 spnego_gss_display_status( 1862 OM_uint32 *minor_status, 1863 OM_uint32 status_value, 1864 int status_type, 1865 gss_OID mech_type, 1866 OM_uint32 *message_context, 1867 gss_buffer_t status_string) 1868 { 1869 dsyslog("Entering display_status\n"); 1870 1871 *message_context = 0; 1872 switch (status_value) { 1873 case ERR_SPNEGO_NO_MECHS_AVAILABLE: 1874 /* CSTYLED */ 1875 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate"); 1876 break; 1877 case ERR_SPNEGO_NO_CREDS_ACQUIRED: 1878 /* CSTYLED */ 1879 *status_string = make_err_msg("SPNEGO failed to acquire creds"); 1880 break; 1881 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR: 1882 /* CSTYLED */ 1883 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism"); 1884 break; 1885 case ERR_SPNEGO_NEGOTIATION_FAILED: 1886 /* CSTYLED */ 1887 *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism"); 1888 break; 1889 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR: 1890 /* CSTYLED */ 1891 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token"); 1892 break; 1893 default: 1894 status_string->length = 0; 1895 status_string->value = ""; 1896 break; 1897 } 1898 1899 dsyslog("Leaving display_status\n"); 1900 return (GSS_S_COMPLETE); 1901 } 1902 1903 /*ARGSUSED*/ 1904 OM_uint32 1905 glue_spnego_gss_import_name( 1906 void *context, 1907 OM_uint32 *minor_status, 1908 gss_buffer_t input_name_buffer, 1909 gss_OID input_name_type, 1910 gss_name_t *output_name) 1911 { 1912 return(spnego_gss_import_name(minor_status, 1913 input_name_buffer, 1914 input_name_type, 1915 output_name)); 1916 } 1917 1918 /*ARGSUSED*/ 1919 OM_uint32 1920 spnego_gss_import_name( 1921 OM_uint32 *minor_status, 1922 gss_buffer_t input_name_buffer, 1923 gss_OID input_name_type, 1924 gss_name_t *output_name) 1925 { 1926 OM_uint32 status; 1927 1928 dsyslog("Entering import_name\n"); 1929 1930 status = gss_import_name(minor_status, input_name_buffer, 1931 input_name_type, output_name); 1932 1933 dsyslog("Leaving import_name\n"); 1934 return (status); 1935 } 1936 1937 /*ARGSUSED*/ 1938 OM_uint32 1939 glue_spnego_gss_release_name( 1940 void *context, 1941 OM_uint32 *minor_status, 1942 gss_name_t *input_name) 1943 { 1944 return(spnego_gss_release_name(minor_status, input_name)); 1945 } 1946 1947 /*ARGSUSED*/ 1948 OM_uint32 1949 spnego_gss_release_name( 1950 OM_uint32 *minor_status, 1951 gss_name_t *input_name) 1952 { 1953 OM_uint32 status; 1954 1955 dsyslog("Entering release_name\n"); 1956 1957 status = gss_release_name(minor_status, input_name); 1958 1959 dsyslog("Leaving release_name\n"); 1960 return (status); 1961 } 1962 1963 /*ARGSUSED*/ 1964 OM_uint32 1965 glue_spnego_gss_compare_name( 1966 void *context, 1967 OM_uint32 *minor_status, 1968 const gss_name_t name1, 1969 const gss_name_t name2, 1970 int *name_equal) 1971 { 1972 return(spnego_gss_compare_name(minor_status, 1973 name1, 1974 name2, 1975 name_equal)); 1976 } 1977 /*ARGSUSED*/ 1978 OM_uint32 1979 spnego_gss_compare_name( 1980 OM_uint32 *minor_status, 1981 const gss_name_t name1, 1982 const gss_name_t name2, 1983 int *name_equal) 1984 { 1985 OM_uint32 status = GSS_S_COMPLETE; 1986 dsyslog("Entering compare_name\n"); 1987 1988 status = gss_compare_name(minor_status, name1, name2, name_equal); 1989 1990 dsyslog("Leaving compare_name\n"); 1991 return (status); 1992 } 1993 1994 /*ARGSUSED*/ 1995 OM_uint32 1996 glue_spnego_gss_display_name( 1997 void *context, 1998 OM_uint32 *minor_status, 1999 gss_name_t input_name, 2000 gss_buffer_t output_name_buffer, 2001 gss_OID *output_name_type) 2002 { 2003 return(spnego_gss_display_name( 2004 minor_status, 2005 input_name, 2006 output_name_buffer, 2007 output_name_type)); 2008 } 2009 2010 /*ARGSUSED*/ 2011 OM_uint32 2012 spnego_gss_display_name( 2013 OM_uint32 *minor_status, 2014 gss_name_t input_name, 2015 gss_buffer_t output_name_buffer, 2016 gss_OID *output_name_type) 2017 { 2018 OM_uint32 status = GSS_S_COMPLETE; 2019 dsyslog("Entering display_name\n"); 2020 2021 status = gss_display_name(minor_status, input_name, 2022 output_name_buffer, output_name_type); 2023 2024 dsyslog("Leaving display_name\n"); 2025 return (status); 2026 } 2027 2028 2029 /*ARGSUSED*/ 2030 OM_uint32 2031 glue_spnego_gss_inquire_names_for_mech( 2032 void *context, 2033 OM_uint32 *minor_status, 2034 gss_OID mechanism, 2035 gss_OID_set *name_types) 2036 { 2037 return(spnego_gss_inquire_names_for_mech(minor_status, 2038 mechanism, 2039 name_types)); 2040 } 2041 /*ARGSUSED*/ 2042 OM_uint32 2043 spnego_gss_inquire_names_for_mech( 2044 OM_uint32 *minor_status, 2045 gss_OID mechanism, 2046 gss_OID_set *name_types) 2047 { 2048 OM_uint32 major, minor; 2049 2050 dsyslog("Entering inquire_names_for_mech\n"); 2051 /* 2052 * We only know how to handle our own mechanism. 2053 */ 2054 if ((mechanism != GSS_C_NULL_OID) && 2055 !g_OID_equal(gss_mech_spnego, mechanism)) { 2056 *minor_status = 0; 2057 return (GSS_S_FAILURE); 2058 } 2059 2060 major = gss_create_empty_oid_set(minor_status, name_types); 2061 if (major == GSS_S_COMPLETE) { 2062 /* Now add our members. */ 2063 if (((major = gss_add_oid_set_member(minor_status, 2064 (gss_OID) GSS_C_NT_USER_NAME, 2065 name_types)) == GSS_S_COMPLETE) && 2066 ((major = gss_add_oid_set_member(minor_status, 2067 (gss_OID) GSS_C_NT_MACHINE_UID_NAME, 2068 name_types)) == GSS_S_COMPLETE) && 2069 ((major = gss_add_oid_set_member(minor_status, 2070 (gss_OID) GSS_C_NT_STRING_UID_NAME, 2071 name_types)) == GSS_S_COMPLETE)) { 2072 major = gss_add_oid_set_member(minor_status, 2073 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, 2074 name_types); 2075 } 2076 2077 if (major != GSS_S_COMPLETE) 2078 (void) gss_release_oid_set(&minor, name_types); 2079 } 2080 2081 dsyslog("Leaving inquire_names_for_mech\n"); 2082 return (major); 2083 } 2084 2085 OM_uint32 2086 spnego_gss_unwrap( 2087 OM_uint32 *minor_status, 2088 gss_ctx_id_t context_handle, 2089 gss_buffer_t input_message_buffer, 2090 gss_buffer_t output_message_buffer, 2091 int *conf_state, 2092 gss_qop_t *qop_state) 2093 { 2094 OM_uint32 ret; 2095 ret = gss_unwrap(minor_status, 2096 context_handle, 2097 input_message_buffer, 2098 output_message_buffer, 2099 conf_state, 2100 qop_state); 2101 2102 return (ret); 2103 } 2104 2105 OM_uint32 2106 spnego_gss_wrap( 2107 OM_uint32 *minor_status, 2108 gss_ctx_id_t context_handle, 2109 int conf_req_flag, 2110 gss_qop_t qop_req, 2111 gss_buffer_t input_message_buffer, 2112 int *conf_state, 2113 gss_buffer_t output_message_buffer) 2114 { 2115 OM_uint32 ret; 2116 ret = gss_wrap(minor_status, 2117 context_handle, 2118 conf_req_flag, 2119 qop_req, 2120 input_message_buffer, 2121 conf_state, 2122 output_message_buffer); 2123 2124 return (ret); 2125 } 2126 2127 OM_uint32 2128 spnego_gss_process_context_token( 2129 OM_uint32 *minor_status, 2130 const gss_ctx_id_t context_handle, 2131 const gss_buffer_t token_buffer) 2132 { 2133 OM_uint32 ret; 2134 ret = gss_process_context_token(minor_status, 2135 context_handle, 2136 token_buffer); 2137 2138 return (ret); 2139 } 2140 2141 OM_uint32 2142 glue_spnego_gss_delete_sec_context( 2143 void *context, 2144 OM_uint32 *minor_status, 2145 gss_ctx_id_t *context_handle, 2146 gss_buffer_t output_token) 2147 { 2148 return(spnego_gss_delete_sec_context(minor_status, 2149 context_handle, output_token)); 2150 } 2151 2152 OM_uint32 2153 spnego_gss_delete_sec_context( 2154 OM_uint32 *minor_status, 2155 gss_ctx_id_t *context_handle, 2156 gss_buffer_t output_token) 2157 { 2158 OM_uint32 ret = GSS_S_COMPLETE; 2159 spnego_gss_ctx_id_t *ctx = 2160 (spnego_gss_ctx_id_t *)context_handle; 2161 2162 if (context_handle == NULL) 2163 return (GSS_S_FAILURE); 2164 2165 /* 2166 * If this is still an SPNEGO mech, release it locally. 2167 */ 2168 if (*ctx != NULL && 2169 (*ctx)->magic_num == SPNEGO_MAGIC_ID) { 2170 (void) release_spnego_ctx(ctx); 2171 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */ 2172 if (output_token) { 2173 output_token->length = 0; 2174 output_token->value = NULL; 2175 } 2176 } else { 2177 ret = gss_delete_sec_context(minor_status, 2178 context_handle, 2179 output_token); 2180 } 2181 2182 return (ret); 2183 } 2184 2185 OM_uint32 2186 glue_spnego_gss_context_time( 2187 void *context, 2188 OM_uint32 *minor_status, 2189 const gss_ctx_id_t context_handle, 2190 OM_uint32 *time_rec) 2191 { 2192 return(spnego_gss_context_time(minor_status, 2193 context_handle, 2194 time_rec)); 2195 } 2196 2197 OM_uint32 2198 spnego_gss_context_time( 2199 OM_uint32 *minor_status, 2200 const gss_ctx_id_t context_handle, 2201 OM_uint32 *time_rec) 2202 { 2203 OM_uint32 ret; 2204 ret = gss_context_time(minor_status, 2205 context_handle, 2206 time_rec); 2207 return (ret); 2208 } 2209 2210 #ifndef LEAN_CLIENT 2211 OM_uint32 2212 glue_spnego_gss_export_sec_context( 2213 void *context, 2214 OM_uint32 *minor_status, 2215 gss_ctx_id_t *context_handle, 2216 gss_buffer_t interprocess_token) 2217 { 2218 return(spnego_gss_export_sec_context(minor_status, 2219 context_handle, 2220 interprocess_token)); 2221 } 2222 OM_uint32 2223 spnego_gss_export_sec_context( 2224 OM_uint32 *minor_status, 2225 gss_ctx_id_t *context_handle, 2226 gss_buffer_t interprocess_token) 2227 { 2228 OM_uint32 ret; 2229 ret = gss_export_sec_context(minor_status, 2230 context_handle, 2231 interprocess_token); 2232 return (ret); 2233 } 2234 2235 OM_uint32 2236 glue_spnego_gss_import_sec_context( 2237 void *context, 2238 OM_uint32 *minor_status, 2239 const gss_buffer_t interprocess_token, 2240 gss_ctx_id_t *context_handle) 2241 { 2242 return(spnego_gss_import_sec_context(minor_status, 2243 interprocess_token, 2244 context_handle)); 2245 } 2246 OM_uint32 2247 spnego_gss_import_sec_context( 2248 OM_uint32 *minor_status, 2249 const gss_buffer_t interprocess_token, 2250 gss_ctx_id_t *context_handle) 2251 { 2252 OM_uint32 ret; 2253 ret = gss_import_sec_context(minor_status, 2254 interprocess_token, 2255 context_handle); 2256 return (ret); 2257 } 2258 #endif /* LEAN_CLIENT */ 2259 2260 OM_uint32 2261 glue_spnego_gss_inquire_context( 2262 void *context, 2263 OM_uint32 *minor_status, 2264 const gss_ctx_id_t context_handle, 2265 gss_name_t *src_name, 2266 gss_name_t *targ_name, 2267 OM_uint32 *lifetime_rec, 2268 gss_OID *mech_type, 2269 OM_uint32 *ctx_flags, 2270 int *locally_initiated, 2271 int *opened) 2272 { 2273 return(spnego_gss_inquire_context( 2274 minor_status, 2275 context_handle, 2276 src_name, 2277 targ_name, 2278 lifetime_rec, 2279 mech_type, 2280 ctx_flags, 2281 locally_initiated, 2282 opened)); 2283 } 2284 2285 OM_uint32 2286 spnego_gss_inquire_context( 2287 OM_uint32 *minor_status, 2288 const gss_ctx_id_t context_handle, 2289 gss_name_t *src_name, 2290 gss_name_t *targ_name, 2291 OM_uint32 *lifetime_rec, 2292 gss_OID *mech_type, 2293 OM_uint32 *ctx_flags, 2294 int *locally_initiated, 2295 int *opened) 2296 { 2297 OM_uint32 ret = GSS_S_COMPLETE; 2298 2299 ret = gss_inquire_context(minor_status, 2300 context_handle, 2301 src_name, 2302 targ_name, 2303 lifetime_rec, 2304 mech_type, 2305 ctx_flags, 2306 locally_initiated, 2307 opened); 2308 2309 return (ret); 2310 } 2311 2312 OM_uint32 2313 glue_spnego_gss_wrap_size_limit( 2314 void *context, 2315 OM_uint32 *minor_status, 2316 const gss_ctx_id_t context_handle, 2317 int conf_req_flag, 2318 gss_qop_t qop_req, 2319 OM_uint32 req_output_size, 2320 OM_uint32 *max_input_size) 2321 { 2322 return(spnego_gss_wrap_size_limit(minor_status, 2323 context_handle, 2324 conf_req_flag, 2325 qop_req, 2326 req_output_size, 2327 max_input_size)); 2328 } 2329 2330 OM_uint32 2331 spnego_gss_wrap_size_limit( 2332 OM_uint32 *minor_status, 2333 const gss_ctx_id_t context_handle, 2334 int conf_req_flag, 2335 gss_qop_t qop_req, 2336 OM_uint32 req_output_size, 2337 OM_uint32 *max_input_size) 2338 { 2339 OM_uint32 ret; 2340 ret = gss_wrap_size_limit(minor_status, 2341 context_handle, 2342 conf_req_flag, 2343 qop_req, 2344 req_output_size, 2345 max_input_size); 2346 return (ret); 2347 } 2348 2349 #if 0 /* SUNW17PACresync */ 2350 OM_uint32 2351 spnego_gss_get_mic( 2352 OM_uint32 *minor_status, 2353 const gss_ctx_id_t context_handle, 2354 gss_qop_t qop_req, 2355 const gss_buffer_t message_buffer, 2356 gss_buffer_t message_token) 2357 { 2358 OM_uint32 ret; 2359 ret = gss_get_mic(minor_status, 2360 context_handle, 2361 qop_req, 2362 message_buffer, 2363 message_token); 2364 return (ret); 2365 } 2366 #endif 2367 2368 OM_uint32 2369 spnego_gss_verify_mic( 2370 OM_uint32 *minor_status, 2371 const gss_ctx_id_t context_handle, 2372 const gss_buffer_t msg_buffer, 2373 const gss_buffer_t token_buffer, 2374 gss_qop_t *qop_state) 2375 { 2376 OM_uint32 ret; 2377 ret = gss_verify_mic(minor_status, 2378 context_handle, 2379 msg_buffer, 2380 token_buffer, 2381 qop_state); 2382 return (ret); 2383 } 2384 2385 OM_uint32 2386 spnego_gss_inquire_sec_context_by_oid( 2387 OM_uint32 *minor_status, 2388 const gss_ctx_id_t context_handle, 2389 const gss_OID desired_object, 2390 gss_buffer_set_t *data_set) 2391 { 2392 OM_uint32 ret; 2393 ret = gss_inquire_sec_context_by_oid(minor_status, 2394 context_handle, 2395 desired_object, 2396 data_set); 2397 return (ret); 2398 } 2399 2400 /* 2401 * SUNW17PACresync 2402 * These GSS funcs not needed yet, so disable them. 2403 * Revisit for full 1.7 resync. 2404 */ 2405 #if 0 2406 OM_uint32 2407 spnego_gss_set_sec_context_option( 2408 OM_uint32 *minor_status, 2409 gss_ctx_id_t *context_handle, 2410 const gss_OID desired_object, 2411 const gss_buffer_t value) 2412 { 2413 OM_uint32 ret; 2414 ret = gss_set_sec_context_option(minor_status, 2415 context_handle, 2416 desired_object, 2417 value); 2418 return (ret); 2419 } 2420 2421 OM_uint32 2422 spnego_gss_wrap_aead(OM_uint32 *minor_status, 2423 gss_ctx_id_t context_handle, 2424 int conf_req_flag, 2425 gss_qop_t qop_req, 2426 gss_buffer_t input_assoc_buffer, 2427 gss_buffer_t input_payload_buffer, 2428 int *conf_state, 2429 gss_buffer_t output_message_buffer) 2430 { 2431 OM_uint32 ret; 2432 ret = gss_wrap_aead(minor_status, 2433 context_handle, 2434 conf_req_flag, 2435 qop_req, 2436 input_assoc_buffer, 2437 input_payload_buffer, 2438 conf_state, 2439 output_message_buffer); 2440 2441 return (ret); 2442 } 2443 2444 OM_uint32 2445 spnego_gss_unwrap_aead(OM_uint32 *minor_status, 2446 gss_ctx_id_t context_handle, 2447 gss_buffer_t input_message_buffer, 2448 gss_buffer_t input_assoc_buffer, 2449 gss_buffer_t output_payload_buffer, 2450 int *conf_state, 2451 gss_qop_t *qop_state) 2452 { 2453 OM_uint32 ret; 2454 ret = gss_unwrap_aead(minor_status, 2455 context_handle, 2456 input_message_buffer, 2457 input_assoc_buffer, 2458 output_payload_buffer, 2459 conf_state, 2460 qop_state); 2461 return (ret); 2462 } 2463 2464 OM_uint32 2465 spnego_gss_wrap_iov(OM_uint32 *minor_status, 2466 gss_ctx_id_t context_handle, 2467 int conf_req_flag, 2468 gss_qop_t qop_req, 2469 int *conf_state, 2470 gss_iov_buffer_desc *iov, 2471 int iov_count) 2472 { 2473 OM_uint32 ret; 2474 ret = gss_wrap_iov(minor_status, 2475 context_handle, 2476 conf_req_flag, 2477 qop_req, 2478 conf_state, 2479 iov, 2480 iov_count); 2481 return (ret); 2482 } 2483 2484 OM_uint32 2485 spnego_gss_unwrap_iov(OM_uint32 *minor_status, 2486 gss_ctx_id_t context_handle, 2487 int *conf_state, 2488 gss_qop_t *qop_state, 2489 gss_iov_buffer_desc *iov, 2490 int iov_count) 2491 { 2492 OM_uint32 ret; 2493 ret = gss_unwrap_iov(minor_status, 2494 context_handle, 2495 conf_state, 2496 qop_state, 2497 iov, 2498 iov_count); 2499 return (ret); 2500 } 2501 2502 OM_uint32 2503 spnego_gss_wrap_iov_length(OM_uint32 *minor_status, 2504 gss_ctx_id_t context_handle, 2505 int conf_req_flag, 2506 gss_qop_t qop_req, 2507 int *conf_state, 2508 gss_iov_buffer_desc *iov, 2509 int iov_count) 2510 { 2511 OM_uint32 ret; 2512 ret = gss_wrap_iov_length(minor_status, 2513 context_handle, 2514 conf_req_flag, 2515 qop_req, 2516 conf_state, 2517 iov, 2518 iov_count); 2519 return (ret); 2520 } 2521 2522 2523 OM_uint32 2524 spnego_gss_complete_auth_token( 2525 OM_uint32 *minor_status, 2526 const gss_ctx_id_t context_handle, 2527 gss_buffer_t input_message_buffer) 2528 { 2529 OM_uint32 ret; 2530 ret = gss_complete_auth_token(minor_status, 2531 context_handle, 2532 input_message_buffer); 2533 return (ret); 2534 } 2535 #endif /* 0 */ 2536 2537 /* 2538 * We will release everything but the ctx_handle so that it 2539 * can be passed back to init/accept context. This routine should 2540 * not be called until after the ctx_handle memory is assigned to 2541 * the supplied context handle from init/accept context. 2542 */ 2543 static void 2544 release_spnego_ctx(spnego_gss_ctx_id_t *ctx) 2545 { 2546 spnego_gss_ctx_id_t context; 2547 OM_uint32 minor_stat; 2548 context = *ctx; 2549 2550 if (context != NULL) { 2551 (void) gss_release_buffer(&minor_stat, 2552 &context->DER_mechTypes); 2553 2554 (void) generic_gss_release_oid(&minor_stat, 2555 &context->internal_mech); 2556 2557 if (context->optionStr != NULL) { 2558 free(context->optionStr); 2559 context->optionStr = NULL; 2560 } 2561 free(context); 2562 *ctx = NULL; 2563 } 2564 } 2565 2566 /* 2567 * Can't use gss_indicate_mechs by itself to get available mechs for 2568 * SPNEGO because it will also return the SPNEGO mech and we do not 2569 * want to consider SPNEGO as an available security mech for 2570 * negotiation. For this reason, get_available_mechs will return 2571 * all available mechs except SPNEGO. 2572 * 2573 * If a ptr to a creds list is given, this function will attempt 2574 * to acquire creds for the creds given and trim the list of 2575 * returned mechanisms to only those for which creds are valid. 2576 * 2577 */ 2578 static OM_uint32 2579 get_available_mechs(OM_uint32 *minor_status, 2580 gss_name_t name, gss_cred_usage_t usage, 2581 gss_cred_id_t *creds, gss_OID_set *rmechs) 2582 { 2583 unsigned int i; 2584 int found = 0; 2585 OM_uint32 major_status = GSS_S_COMPLETE, tmpmin; 2586 gss_OID_set mechs, goodmechs; 2587 2588 major_status = gss_indicate_mechs(minor_status, &mechs); 2589 2590 if (major_status != GSS_S_COMPLETE) { 2591 return (major_status); 2592 } 2593 2594 major_status = gss_create_empty_oid_set(minor_status, rmechs); 2595 2596 if (major_status != GSS_S_COMPLETE) { 2597 (void) gss_release_oid_set(minor_status, &mechs); 2598 return (major_status); 2599 } 2600 2601 for (i = 0; i < mechs->count && major_status == GSS_S_COMPLETE; i++) { 2602 if ((mechs->elements[i].length 2603 != spnego_mechanism.mech_type.length) || 2604 memcmp(mechs->elements[i].elements, 2605 spnego_mechanism.mech_type.elements, 2606 spnego_mechanism.mech_type.length)) { 2607 /* 2608 * Solaris Kerberos: gss_indicate_mechs is stupid as 2609 * it never inferences any of the related OIDs of the 2610 * mechanisms configured, e.g. KRB5_OLD, KRB5_WRONG. 2611 * We add KRB5_WRONG here so that old MS clients can 2612 * negotiate this mechanism, which allows extensions 2613 * in Kerberos (clock skew adjustment, refresh ccache). 2614 */ 2615 if (is_kerb_mech(&mechs->elements[i])) { 2616 extern gss_OID_desc * const gss_mech_krb5_wrong; 2617 2618 major_status = 2619 gss_add_oid_set_member(minor_status, 2620 gss_mech_krb5_wrong, rmechs); 2621 } 2622 2623 major_status = gss_add_oid_set_member(minor_status, 2624 &mechs->elements[i], 2625 rmechs); 2626 if (major_status == GSS_S_COMPLETE) 2627 found++; 2628 } 2629 } 2630 2631 /* 2632 * If the caller wanted a list of creds returned, 2633 * trim the list of mechanisms down to only those 2634 * for which the creds are valid. 2635 */ 2636 if (found > 0 && major_status == GSS_S_COMPLETE && creds != NULL) { 2637 major_status = gss_acquire_cred(minor_status, 2638 name, GSS_C_INDEFINITE, 2639 *rmechs, usage, creds, 2640 &goodmechs, NULL); 2641 2642 /* 2643 * Drop the old list in favor of the new 2644 * "trimmed" list. 2645 */ 2646 (void) gss_release_oid_set(&tmpmin, rmechs); 2647 if (major_status == GSS_S_COMPLETE) { 2648 (void) gssint_copy_oid_set(&tmpmin, 2649 goodmechs, rmechs); 2650 (void) gss_release_oid_set(&tmpmin, &goodmechs); 2651 } 2652 } 2653 2654 (void) gss_release_oid_set(&tmpmin, &mechs); 2655 if (found == 0 || major_status != GSS_S_COMPLETE) { 2656 *minor_status = ERR_SPNEGO_NO_MECHS_AVAILABLE; 2657 map_errcode(minor_status); 2658 if (major_status == GSS_S_COMPLETE) 2659 major_status = GSS_S_FAILURE; 2660 } 2661 2662 return (major_status); 2663 } 2664 2665 /* following are token creation and reading routines */ 2666 2667 /* 2668 * If buff_in is not pointing to a MECH_OID, then return NULL and do not 2669 * advance the buffer, otherwise, decode the mech_oid from the buffer and 2670 * place in gss_OID. 2671 */ 2672 static gss_OID 2673 get_mech_oid(OM_uint32 *minor_status, unsigned char **buff_in, size_t length) 2674 { 2675 OM_uint32 status; 2676 gss_OID_desc toid; 2677 gss_OID mech_out = NULL; 2678 unsigned char *start, *end; 2679 2680 if (length < 1 || **buff_in != MECH_OID) 2681 return (NULL); 2682 2683 start = *buff_in; 2684 end = start + length; 2685 2686 (*buff_in)++; 2687 toid.length = *(*buff_in)++; 2688 2689 if ((*buff_in + toid.length) > end) 2690 return (NULL); 2691 2692 toid.elements = *buff_in; 2693 *buff_in += toid.length; 2694 2695 status = generic_gss_copy_oid(minor_status, &toid, &mech_out); 2696 2697 if (status != GSS_S_COMPLETE) { 2698 map_errcode(minor_status); 2699 mech_out = NULL; 2700 } 2701 2702 return (mech_out); 2703 } 2704 2705 /* 2706 * der encode the given mechanism oid into buf_out, advancing the 2707 * buffer pointer. 2708 */ 2709 2710 static int 2711 put_mech_oid(unsigned char **buf_out, gss_OID_const mech, unsigned int buflen) 2712 { 2713 if (buflen < mech->length + 2) 2714 return (-1); 2715 *(*buf_out)++ = MECH_OID; 2716 *(*buf_out)++ = (unsigned char) mech->length; 2717 memcpy((void *)(*buf_out), mech->elements, mech->length); 2718 *buf_out += mech->length; 2719 return (0); 2720 } 2721 2722 /* 2723 * verify that buff_in points to an octet string, if it does not, 2724 * return NULL and don't advance the pointer. If it is an octet string 2725 * decode buff_in into a gss_buffer_t and return it, advancing the 2726 * buffer pointer. 2727 */ 2728 static gss_buffer_t 2729 get_input_token(unsigned char **buff_in, unsigned int buff_length) 2730 { 2731 gss_buffer_t input_token; 2732 unsigned int bytes; 2733 2734 if (**buff_in != OCTET_STRING) 2735 return (NULL); 2736 2737 (*buff_in)++; 2738 input_token = (gss_buffer_t)malloc(sizeof (gss_buffer_desc)); 2739 2740 if (input_token == NULL) 2741 return (NULL); 2742 2743 input_token->length = gssint_get_der_length(buff_in, buff_length, &bytes); 2744 if ((int)input_token->length == -1) { 2745 free(input_token); 2746 return (NULL); 2747 } 2748 input_token->value = malloc(input_token->length); 2749 2750 if (input_token->value == NULL) { 2751 free(input_token); 2752 return (NULL); 2753 } 2754 2755 (void) memcpy(input_token->value, *buff_in, input_token->length); 2756 *buff_in += input_token->length; 2757 return (input_token); 2758 } 2759 2760 /* 2761 * verify that the input token length is not 0. If it is, just return. 2762 * If the token length is greater than 0, der encode as an octet string 2763 * and place in buf_out, advancing buf_out. 2764 */ 2765 2766 static int 2767 put_input_token(unsigned char **buf_out, gss_buffer_t input_token, 2768 unsigned int buflen) 2769 { 2770 int ret; 2771 2772 /* if token length is 0, we do not want to send */ 2773 if (input_token->length == 0) 2774 return (0); 2775 2776 if (input_token->length > buflen) 2777 return (-1); 2778 2779 *(*buf_out)++ = OCTET_STRING; 2780 if ((ret = gssint_put_der_length(input_token->length, buf_out, 2781 input_token->length))) 2782 return (ret); 2783 TWRITE_STR(*buf_out, input_token->value, input_token->length); 2784 return (0); 2785 } 2786 2787 /* 2788 * verify that buff_in points to a sequence of der encoding. The mech 2789 * set is the only sequence of encoded object in the token, so if it is 2790 * a sequence of encoding, decode the mechset into a gss_OID_set and 2791 * return it, advancing the buffer pointer. 2792 */ 2793 static gss_OID_set 2794 get_mech_set(OM_uint32 *minor_status, unsigned char **buff_in, 2795 unsigned int buff_length) 2796 { 2797 gss_OID_set returned_mechSet; 2798 OM_uint32 major_status; 2799 int length; /* SUNW17PACresync */ 2800 OM_uint32 bytes; 2801 OM_uint32 set_length; 2802 unsigned char *start; 2803 int i; 2804 2805 if (**buff_in != SEQUENCE_OF) 2806 return (NULL); 2807 2808 start = *buff_in; 2809 (*buff_in)++; 2810 2811 length = gssint_get_der_length(buff_in, buff_length, &bytes); 2812 if (length < 0) /* SUNW17PACresync - MIT17 lacks this check */ 2813 return (NULL); 2814 2815 major_status = gss_create_empty_oid_set(minor_status, 2816 &returned_mechSet); 2817 if (major_status != GSS_S_COMPLETE) 2818 return (NULL); 2819 2820 for (set_length = 0, i = 0; set_length < length; i++) { 2821 gss_OID_desc *temp = get_mech_oid(minor_status, buff_in, 2822 buff_length - (*buff_in - start)); 2823 if (temp != NULL) { 2824 major_status = gss_add_oid_set_member(minor_status, 2825 temp, &returned_mechSet); 2826 if (major_status == GSS_S_COMPLETE) { 2827 set_length += returned_mechSet->elements[i].length +2; 2828 if (generic_gss_release_oid(minor_status, &temp)) 2829 map_errcode(minor_status); 2830 } 2831 } 2832 } 2833 2834 return (returned_mechSet); 2835 } 2836 2837 /* 2838 * Encode mechSet into buf. 2839 */ 2840 static int 2841 put_mech_set(gss_OID_set mechSet, gss_buffer_t buf) 2842 { 2843 unsigned char *ptr; 2844 unsigned int i; 2845 unsigned int tlen, ilen; 2846 2847 tlen = ilen = 0; 2848 for (i = 0; i < mechSet->count; i++) { 2849 /* 2850 * 0x06 [DER LEN] [OID] 2851 */ 2852 ilen += 1 + 2853 gssint_der_length_size(mechSet->elements[i].length) + 2854 mechSet->elements[i].length; 2855 } 2856 /* 2857 * 0x30 [DER LEN] 2858 */ 2859 tlen = 1 + gssint_der_length_size(ilen) + ilen; 2860 ptr = malloc(tlen); 2861 if (ptr == NULL) 2862 return -1; 2863 2864 buf->value = ptr; 2865 buf->length = tlen; 2866 #define REMAIN (buf->length - ((unsigned char *)buf->value - ptr)) 2867 2868 *ptr++ = SEQUENCE_OF; 2869 if (gssint_put_der_length(ilen, &ptr, REMAIN) < 0) 2870 return -1; 2871 for (i = 0; i < mechSet->count; i++) { 2872 if (put_mech_oid(&ptr, &mechSet->elements[i], REMAIN) < 0) { 2873 return -1; 2874 } 2875 } 2876 return 0; 2877 #undef REMAIN 2878 } 2879 2880 /* 2881 * Verify that buff_in is pointing to a BIT_STRING with the correct 2882 * length and padding for the req_flags. If it is, decode req_flags 2883 * and return them, otherwise, return NULL. 2884 */ 2885 static OM_uint32 2886 get_req_flags(unsigned char **buff_in, OM_uint32 bodysize, 2887 OM_uint32 *req_flags) 2888 { 2889 unsigned int len; 2890 2891 if (**buff_in != (CONTEXT | 0x01)) 2892 return (0); 2893 2894 if (g_get_tag_and_length(buff_in, (CONTEXT | 0x01), 2895 bodysize, &len) < 0) 2896 return GSS_S_DEFECTIVE_TOKEN; 2897 2898 if (*(*buff_in)++ != BIT_STRING) 2899 return GSS_S_DEFECTIVE_TOKEN; 2900 2901 if (*(*buff_in)++ != BIT_STRING_LENGTH) 2902 return GSS_S_DEFECTIVE_TOKEN; 2903 2904 if (*(*buff_in)++ != BIT_STRING_PADDING) 2905 return GSS_S_DEFECTIVE_TOKEN; 2906 2907 *req_flags = (OM_uint32) (*(*buff_in)++ >> 1); 2908 return (0); 2909 } 2910 2911 static OM_uint32 2912 get_negTokenInit(OM_uint32 *minor_status, 2913 gss_buffer_t buf, 2914 gss_buffer_t der_mechSet, 2915 gss_OID_set *mechSet, 2916 OM_uint32 *req_flags, 2917 gss_buffer_t *mechtok, 2918 gss_buffer_t *mechListMIC) 2919 { 2920 OM_uint32 err; 2921 unsigned char *ptr, *bufstart; 2922 unsigned int len; 2923 gss_buffer_desc tmpbuf; 2924 2925 *minor_status = 0; 2926 der_mechSet->length = 0; 2927 der_mechSet->value = NULL; 2928 *mechSet = GSS_C_NO_OID_SET; 2929 *req_flags = 0; 2930 *mechtok = *mechListMIC = GSS_C_NO_BUFFER; 2931 2932 ptr = bufstart = buf->value; 2933 if ((buf->length - (ptr - bufstart)) > INT_MAX) 2934 return GSS_S_FAILURE; 2935 #define REMAIN (buf->length - (ptr - bufstart)) 2936 2937 err = g_verify_token_header(gss_mech_spnego, 2938 &len, &ptr, 0, REMAIN); 2939 if (err) { 2940 *minor_status = err; 2941 map_errcode(minor_status); 2942 return GSS_S_FAILURE; 2943 } 2944 *minor_status = g_verify_neg_token_init(&ptr, REMAIN); 2945 if (*minor_status) { 2946 map_errcode(minor_status); 2947 return GSS_S_FAILURE; 2948 } 2949 2950 /* alias into input_token */ 2951 tmpbuf.value = ptr; 2952 tmpbuf.length = REMAIN; 2953 *mechSet = get_mech_set(minor_status, &ptr, REMAIN); 2954 if (*mechSet == NULL) 2955 return GSS_S_FAILURE; 2956 2957 tmpbuf.length = ptr - (unsigned char *)tmpbuf.value; 2958 der_mechSet->value = malloc(tmpbuf.length); 2959 if (der_mechSet->value == NULL) 2960 return GSS_S_FAILURE; 2961 memcpy(der_mechSet->value, tmpbuf.value, tmpbuf.length); 2962 der_mechSet->length = tmpbuf.length; 2963 2964 err = get_req_flags(&ptr, REMAIN, req_flags); 2965 if (err != GSS_S_COMPLETE) { 2966 return err; 2967 } 2968 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x02), 2969 REMAIN, &len) >= 0) { 2970 *mechtok = get_input_token(&ptr, len); 2971 if (*mechtok == GSS_C_NO_BUFFER) { 2972 return GSS_S_FAILURE; 2973 } 2974 } 2975 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x03), 2976 REMAIN, &len) >= 0) { 2977 *mechListMIC = get_input_token(&ptr, len); 2978 if (*mechListMIC == GSS_C_NO_BUFFER) { 2979 return GSS_S_FAILURE; 2980 } 2981 } 2982 return GSS_S_COMPLETE; 2983 #undef REMAIN 2984 } 2985 2986 static OM_uint32 2987 get_negTokenResp(OM_uint32 *minor_status, 2988 unsigned char *buf, unsigned int buflen, 2989 OM_uint32 *negState, 2990 gss_OID *supportedMech, 2991 gss_buffer_t *responseToken, 2992 gss_buffer_t *mechListMIC) 2993 { 2994 unsigned char *ptr, *bufstart; 2995 unsigned int len; 2996 int tmplen; 2997 unsigned int tag, bytes; 2998 2999 *negState = ACCEPT_DEFECTIVE_TOKEN; 3000 *supportedMech = GSS_C_NO_OID; 3001 *responseToken = *mechListMIC = GSS_C_NO_BUFFER; 3002 ptr = bufstart = buf; 3003 #define REMAIN (buflen - (ptr - bufstart)) 3004 3005 if (g_get_tag_and_length(&ptr, (CONTEXT | 0x01), REMAIN, &len) < 0) 3006 return GSS_S_DEFECTIVE_TOKEN; 3007 if (*ptr++ == SEQUENCE) { 3008 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); 3009 if (tmplen < 0) 3010 return GSS_S_DEFECTIVE_TOKEN; 3011 } 3012 if (REMAIN < 1) 3013 tag = 0; 3014 else 3015 tag = *ptr++; 3016 3017 if (tag == CONTEXT) { 3018 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); 3019 if (tmplen < 0) 3020 return GSS_S_DEFECTIVE_TOKEN; 3021 3022 if (g_get_tag_and_length(&ptr, ENUMERATED, 3023 REMAIN, &len) < 0) 3024 return GSS_S_DEFECTIVE_TOKEN; 3025 3026 if (len != ENUMERATION_LENGTH) 3027 return GSS_S_DEFECTIVE_TOKEN; 3028 3029 if (REMAIN < 1) 3030 return GSS_S_DEFECTIVE_TOKEN; 3031 *negState = *ptr++; 3032 3033 if (REMAIN < 1) 3034 tag = 0; 3035 else 3036 tag = *ptr++; 3037 } 3038 if (tag == (CONTEXT | 0x01)) { 3039 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); 3040 if (tmplen < 0) 3041 return GSS_S_DEFECTIVE_TOKEN; 3042 3043 *supportedMech = get_mech_oid(minor_status, &ptr, REMAIN); 3044 if (*supportedMech == GSS_C_NO_OID) 3045 return GSS_S_DEFECTIVE_TOKEN; 3046 3047 if (REMAIN < 1) 3048 tag = 0; 3049 else 3050 tag = *ptr++; 3051 } 3052 if (tag == (CONTEXT | 0x02)) { 3053 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); 3054 if (tmplen < 0) 3055 return GSS_S_DEFECTIVE_TOKEN; 3056 3057 *responseToken = get_input_token(&ptr, REMAIN); 3058 if (*responseToken == GSS_C_NO_BUFFER) 3059 return GSS_S_DEFECTIVE_TOKEN; 3060 3061 if (REMAIN < 1) 3062 tag = 0; 3063 else 3064 tag = *ptr++; 3065 } 3066 if (tag == (CONTEXT | 0x03)) { 3067 tmplen = gssint_get_der_length(&ptr, REMAIN, &bytes); 3068 if (tmplen < 0) 3069 return GSS_S_DEFECTIVE_TOKEN; 3070 3071 *mechListMIC = get_input_token(&ptr, REMAIN); 3072 if (*mechListMIC == GSS_C_NO_BUFFER) 3073 return GSS_S_DEFECTIVE_TOKEN; 3074 } 3075 return GSS_S_COMPLETE; 3076 #undef REMAIN 3077 } 3078 3079 /* 3080 * der encode the passed negResults as an ENUMERATED type and 3081 * place it in buf_out, advancing the buffer. 3082 */ 3083 3084 static int 3085 put_negResult(unsigned char **buf_out, OM_uint32 negResult, 3086 unsigned int buflen) 3087 { 3088 if (buflen < 3) 3089 return (-1); 3090 *(*buf_out)++ = ENUMERATED; 3091 *(*buf_out)++ = ENUMERATION_LENGTH; 3092 *(*buf_out)++ = (unsigned char) negResult; 3093 return (0); 3094 } 3095 3096 /* 3097 * This routine compares the recieved mechset to the mechset that 3098 * this server can support. It looks sequentially through the mechset 3099 * and the first one that matches what the server can support is 3100 * chosen as the negotiated mechanism. If one is found, negResult 3101 * is set to ACCEPT_INCOMPLETE if it's the first mech, REQUEST_MIC if 3102 * it's not the first mech, otherwise we return NULL and negResult 3103 * is set to REJECT. 3104 * 3105 * NOTE: There is currently no way to specify a preference order of 3106 * mechanisms supported by the acceptor. 3107 */ 3108 static gss_OID 3109 negotiate_mech_type(OM_uint32 *minor_status, 3110 gss_OID_set supported_mechSet, 3111 gss_OID_set mechset, 3112 OM_uint32 *negResult) 3113 { 3114 gss_OID returned_mech; 3115 OM_uint32 status; 3116 int present; 3117 unsigned int i; 3118 3119 for (i = 0; i < mechset->count; i++) { 3120 gss_OID mech_oid = &mechset->elements[i]; 3121 3122 /* 3123 * Solaris Kerberos: MIT compares against MS' wrong OID, but 3124 * we actually want to select it if the client supports, as this 3125 * will enable features on MS clients that allow credential 3126 * refresh on rekeying and caching system times from servers. 3127 */ 3128 #if 0 3129 /* Accept wrong mechanism OID from MS clients */ 3130 if (mech_oid->length == gss_mech_krb5_wrong_oid.length && 3131 memcmp(mech_oid->elements, gss_mech_krb5_wrong_oid.elements, mech_oid->length) == 0) 3132 mech_oid = (gss_OID)&gss_mech_krb5_oid; 3133 #endif 3134 3135 gss_test_oid_set_member(minor_status, mech_oid, supported_mechSet, &present); 3136 if (!present) 3137 continue; 3138 3139 if (i == 0) 3140 *negResult = ACCEPT_INCOMPLETE; 3141 else 3142 *negResult = REQUEST_MIC; 3143 3144 status = generic_gss_copy_oid(minor_status, 3145 &mechset->elements[i], 3146 &returned_mech); 3147 if (status != GSS_S_COMPLETE) { 3148 *negResult = REJECT; 3149 map_errcode(minor_status); 3150 return (NULL); 3151 } 3152 return (returned_mech); 3153 } 3154 *negResult = REJECT; 3155 return (NULL); 3156 } 3157 3158 /* 3159 * the next two routines make a token buffer suitable for 3160 * spnego_gss_display_status. These currently take the string 3161 * in name and place it in the token. Eventually, if 3162 * spnego_gss_display_status returns valid error messages, 3163 * these routines will be changes to return the error string. 3164 */ 3165 static spnego_token_t 3166 make_spnego_token(char *name) 3167 { 3168 return (spnego_token_t)strdup(name); 3169 } 3170 3171 static gss_buffer_desc 3172 make_err_msg(char *name) 3173 { 3174 gss_buffer_desc buffer; 3175 3176 if (name == NULL) { 3177 buffer.length = 0; 3178 buffer.value = NULL; 3179 } else { 3180 buffer.length = strlen(name)+1; 3181 buffer.value = make_spnego_token(name); 3182 } 3183 3184 return (buffer); 3185 } 3186 3187 /* 3188 * Create the client side spnego token passed back to gss_init_sec_context 3189 * and eventually up to the application program and over to the server. 3190 * 3191 * Use DER rules, definite length method per RFC 2478 3192 */ 3193 static int 3194 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t spnego_ctx, 3195 int negHintsCompat, 3196 gss_buffer_t mechListMIC, OM_uint32 req_flags, 3197 gss_buffer_t data, send_token_flag sendtoken, 3198 gss_buffer_t outbuf) 3199 { 3200 int ret = 0; 3201 unsigned int tlen, dataLen = 0; 3202 unsigned int negTokenInitSize = 0; 3203 unsigned int negTokenInitSeqSize = 0; 3204 unsigned int negTokenInitContSize = 0; 3205 unsigned int rspTokenSize = 0; 3206 unsigned int mechListTokenSize = 0; 3207 unsigned int micTokenSize = 0; 3208 unsigned char *t; 3209 unsigned char *ptr; 3210 3211 if (outbuf == GSS_C_NO_BUFFER) 3212 return (-1); 3213 3214 outbuf->length = 0; 3215 outbuf->value = NULL; 3216 3217 /* calculate the data length */ 3218 3219 /* 3220 * 0xa0 [DER LEN] [mechTypes] 3221 */ 3222 mechListTokenSize = 1 + 3223 gssint_der_length_size(spnego_ctx->DER_mechTypes.length) + 3224 spnego_ctx->DER_mechTypes.length; 3225 dataLen += mechListTokenSize; 3226 3227 /* 3228 * If a token from gss_init_sec_context exists, 3229 * add the length of the token + the ASN.1 overhead 3230 */ 3231 if (data != NULL) { 3232 /* 3233 * Encoded in final output as: 3234 * 0xa2 [DER LEN] 0x04 [DER LEN] [DATA] 3235 * -----s--------|--------s2---------- 3236 */ 3237 rspTokenSize = 1 + 3238 gssint_der_length_size(data->length) + 3239 data->length; 3240 dataLen += 1 + gssint_der_length_size(rspTokenSize) + 3241 rspTokenSize; 3242 } 3243 3244 if (mechListMIC) { 3245 /* 3246 * Encoded in final output as: 3247 * 0xa3 [DER LEN] 0x04 [DER LEN] [DATA] 3248 * --s-- -----tlen------------ 3249 */ 3250 micTokenSize = 1 + 3251 gssint_der_length_size(mechListMIC->length) + 3252 mechListMIC->length; 3253 dataLen += 1 + 3254 gssint_der_length_size(micTokenSize) + 3255 micTokenSize; 3256 } 3257 3258 /* 3259 * Add size of DER encoding 3260 * [ SEQUENCE { MechTypeList | ReqFLags | Token | mechListMIC } ] 3261 * 0x30 [DER_LEN] [data] 3262 * 3263 */ 3264 negTokenInitContSize = dataLen; 3265 negTokenInitSeqSize = 1 + gssint_der_length_size(dataLen) + dataLen; 3266 dataLen = negTokenInitSeqSize; 3267 3268 /* 3269 * negTokenInitSize indicates the bytes needed to 3270 * hold the ASN.1 encoding of the entire NegTokenInit 3271 * SEQUENCE. 3272 * 0xa0 [DER_LEN] + data 3273 * 3274 */ 3275 negTokenInitSize = 1 + 3276 gssint_der_length_size(negTokenInitSeqSize) + 3277 negTokenInitSeqSize; 3278 3279 tlen = g_token_size(gss_mech_spnego, negTokenInitSize); 3280 3281 t = (unsigned char *) malloc(tlen); 3282 3283 if (t == NULL) { 3284 return (-1); 3285 } 3286 3287 ptr = t; 3288 3289 /* create the message */ 3290 if ((ret = g_make_token_header(gss_mech_spnego, negTokenInitSize, 3291 &ptr, tlen))) 3292 goto errout; 3293 3294 *ptr++ = CONTEXT; /* NegotiationToken identifier */ 3295 if ((ret = gssint_put_der_length(negTokenInitSeqSize, &ptr, tlen))) 3296 goto errout; 3297 3298 *ptr++ = SEQUENCE; 3299 if ((ret = gssint_put_der_length(negTokenInitContSize, &ptr, 3300 tlen - (int)(ptr-t)))) 3301 goto errout; 3302 3303 *ptr++ = CONTEXT | 0x00; /* MechTypeList identifier */ 3304 if ((ret = gssint_put_der_length(spnego_ctx->DER_mechTypes.length, 3305 &ptr, tlen - (int)(ptr-t)))) 3306 goto errout; 3307 3308 /* We already encoded the MechSetList */ 3309 (void) memcpy(ptr, spnego_ctx->DER_mechTypes.value, 3310 spnego_ctx->DER_mechTypes.length); 3311 3312 ptr += spnego_ctx->DER_mechTypes.length; 3313 3314 if (data != NULL) { 3315 *ptr++ = CONTEXT | 0x02; 3316 if ((ret = gssint_put_der_length(rspTokenSize, 3317 &ptr, tlen - (int)(ptr - t)))) 3318 goto errout; 3319 3320 if ((ret = put_input_token(&ptr, data, 3321 tlen - (int)(ptr - t)))) 3322 goto errout; 3323 } 3324 3325 if (mechListMIC != GSS_C_NO_BUFFER) { 3326 *ptr++ = CONTEXT | 0x03; 3327 if ((ret = gssint_put_der_length(micTokenSize, 3328 &ptr, tlen - (int)(ptr - t)))) 3329 goto errout; 3330 3331 if (negHintsCompat) { 3332 ret = put_neg_hints(&ptr, mechListMIC, 3333 tlen - (int)(ptr - t)); 3334 if (ret) 3335 goto errout; 3336 } else if ((ret = put_input_token(&ptr, mechListMIC, 3337 tlen - (int)(ptr - t)))) 3338 goto errout; 3339 } 3340 3341 errout: 3342 if (ret != 0) { 3343 if (t) 3344 free(t); 3345 t = NULL; 3346 tlen = 0; 3347 } 3348 outbuf->length = tlen; 3349 outbuf->value = (void *) t; 3350 3351 return (ret); 3352 } 3353 3354 /* 3355 * create the server side spnego token passed back to 3356 * gss_accept_sec_context and eventually up to the application program 3357 * and over to the client. 3358 */ 3359 static int 3360 make_spnego_tokenTarg_msg(OM_uint32 status, gss_OID mech_wanted, 3361 gss_buffer_t data, gss_buffer_t mechListMIC, 3362 send_token_flag sendtoken, 3363 gss_buffer_t outbuf) 3364 { 3365 unsigned int tlen = 0; 3366 unsigned int ret = 0; 3367 unsigned int NegTokenTargSize = 0; 3368 unsigned int NegTokenSize = 0; 3369 unsigned int rspTokenSize = 0; 3370 unsigned int micTokenSize = 0; 3371 unsigned int dataLen = 0; 3372 unsigned char *t; 3373 unsigned char *ptr; 3374 3375 if (outbuf == GSS_C_NO_BUFFER) 3376 return (GSS_S_DEFECTIVE_TOKEN); 3377 3378 outbuf->length = 0; 3379 outbuf->value = NULL; 3380 3381 /* 3382 * ASN.1 encoding of the negResult 3383 * ENUMERATED type is 3 bytes 3384 * ENUMERATED TAG, Length, Value, 3385 * Plus 2 bytes for the CONTEXT id and length. 3386 */ 3387 dataLen = 5; 3388 3389 /* 3390 * calculate data length 3391 * 3392 * If this is the initial token, include length of 3393 * mech_type and the negotiation result fields. 3394 */ 3395 if (sendtoken == INIT_TOKEN_SEND) { 3396 int mechlistTokenSize; 3397 /* 3398 * 1 byte for the CONTEXT ID(0xa0), 3399 * 1 byte for the OID ID(0x06) 3400 * 1 byte for OID Length field 3401 * Plus the rest... (OID Length, OID value) 3402 */ 3403 mechlistTokenSize = 3 + mech_wanted->length + 3404 gssint_der_length_size(mech_wanted->length); 3405 3406 dataLen += mechlistTokenSize; 3407 } 3408 if (data != NULL && data->length > 0) { 3409 /* Length of the inner token */ 3410 rspTokenSize = 1 + gssint_der_length_size(data->length) + 3411 data->length; 3412 3413 dataLen += rspTokenSize; 3414 3415 /* Length of the outer token */ 3416 dataLen += 1 + gssint_der_length_size(rspTokenSize); 3417 } 3418 if (mechListMIC != NULL) { 3419 3420 /* Length of the inner token */ 3421 micTokenSize = 1 + gssint_der_length_size(mechListMIC->length) + 3422 mechListMIC->length; 3423 3424 dataLen += micTokenSize; 3425 3426 /* Length of the outer token */ 3427 dataLen += 1 + gssint_der_length_size(micTokenSize); 3428 } 3429 /* 3430 * Add size of DER encoded: 3431 * NegTokenTarg [ SEQUENCE ] of 3432 * NegResult[0] ENUMERATED { 3433 * accept_completed(0), 3434 * accept_incomplete(1), 3435 * reject(2) } 3436 * supportedMech [1] MechType OPTIONAL, 3437 * responseToken [2] OCTET STRING OPTIONAL, 3438 * mechListMIC [3] OCTET STRING OPTIONAL 3439 * 3440 * size = data->length + MechListMic + SupportedMech len + 3441 * Result Length + ASN.1 overhead 3442 */ 3443 NegTokenTargSize = dataLen; 3444 dataLen += 1 + gssint_der_length_size(NegTokenTargSize); 3445 3446 /* 3447 * NegotiationToken [ CHOICE ]{ 3448 * negTokenInit [0] NegTokenInit, 3449 * negTokenTarg [1] NegTokenTarg } 3450 */ 3451 NegTokenSize = dataLen; 3452 dataLen += 1 + gssint_der_length_size(NegTokenSize); 3453 3454 tlen = dataLen; 3455 t = (unsigned char *) malloc(tlen); 3456 3457 if (t == NULL) { 3458 ret = GSS_S_DEFECTIVE_TOKEN; 3459 goto errout; 3460 } 3461 3462 ptr = t; 3463 3464 /* 3465 * Indicate that we are sending CHOICE 1 3466 * (NegTokenTarg) 3467 */ 3468 *ptr++ = CONTEXT | 0x01; 3469 if (gssint_put_der_length(NegTokenSize, &ptr, dataLen) < 0) { 3470 ret = GSS_S_DEFECTIVE_TOKEN; 3471 goto errout; 3472 } 3473 *ptr++ = SEQUENCE; 3474 if (gssint_put_der_length(NegTokenTargSize, &ptr, 3475 tlen - (int)(ptr-t)) < 0) { 3476 ret = GSS_S_DEFECTIVE_TOKEN; 3477 goto errout; 3478 } 3479 3480 /* 3481 * First field of the NegTokenTarg SEQUENCE 3482 * is the ENUMERATED NegResult. 3483 */ 3484 *ptr++ = CONTEXT; 3485 if (gssint_put_der_length(3, &ptr, 3486 tlen - (int)(ptr-t)) < 0) { 3487 ret = GSS_S_DEFECTIVE_TOKEN; 3488 goto errout; 3489 } 3490 if (put_negResult(&ptr, status, tlen - (int)(ptr - t)) < 0) { 3491 ret = GSS_S_DEFECTIVE_TOKEN; 3492 goto errout; 3493 } 3494 if (sendtoken == INIT_TOKEN_SEND) { 3495 /* 3496 * Next, is the Supported MechType 3497 */ 3498 *ptr++ = CONTEXT | 0x01; 3499 if (gssint_put_der_length(mech_wanted->length + 2, 3500 &ptr, 3501 tlen - (int)(ptr - t)) < 0) { 3502 ret = GSS_S_DEFECTIVE_TOKEN; 3503 goto errout; 3504 } 3505 if (put_mech_oid(&ptr, mech_wanted, 3506 tlen - (int)(ptr - t)) < 0) { 3507 ret = GSS_S_DEFECTIVE_TOKEN; 3508 goto errout; 3509 } 3510 } 3511 if (data != NULL && data->length > 0) { 3512 *ptr++ = CONTEXT | 0x02; 3513 if (gssint_put_der_length(rspTokenSize, &ptr, 3514 tlen - (int)(ptr - t)) < 0) { 3515 ret = GSS_S_DEFECTIVE_TOKEN; 3516 goto errout; 3517 } 3518 if (put_input_token(&ptr, data, 3519 tlen - (int)(ptr - t)) < 0) { 3520 ret = GSS_S_DEFECTIVE_TOKEN; 3521 goto errout; 3522 } 3523 } 3524 if (mechListMIC != NULL) { 3525 *ptr++ = CONTEXT | 0x03; 3526 if (gssint_put_der_length(micTokenSize, &ptr, 3527 tlen - (int)(ptr - t)) < 0) { 3528 ret = GSS_S_DEFECTIVE_TOKEN; 3529 goto errout; 3530 } 3531 if (put_input_token(&ptr, mechListMIC, 3532 tlen - (int)(ptr - t)) < 0) { 3533 ret = GSS_S_DEFECTIVE_TOKEN; 3534 goto errout; 3535 } 3536 } 3537 ret = GSS_S_COMPLETE; 3538 errout: 3539 if (ret != GSS_S_COMPLETE) { 3540 if (t) 3541 free(t); 3542 } else { 3543 outbuf->length = ptr - t; 3544 outbuf->value = (void *) t; 3545 } 3546 3547 return (ret); 3548 } 3549 3550 /* determine size of token */ 3551 static int 3552 g_token_size(gss_OID_const mech, unsigned int body_size) 3553 { 3554 int hdrsize; 3555 3556 /* 3557 * Initialize the header size to the 3558 * MECH_OID byte + the bytes needed to indicate the 3559 * length of the OID + the OID itself. 3560 * 3561 * 0x06 [MECHLENFIELD] MECHDATA 3562 */ 3563 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; 3564 3565 /* 3566 * Now add the bytes needed for the initial header 3567 * token bytes: 3568 * 0x60 + [DER_LEN] + HDRSIZE 3569 */ 3570 hdrsize += 1 + gssint_der_length_size(body_size + hdrsize); 3571 3572 return (hdrsize + body_size); 3573 } 3574 3575 /* 3576 * generate token header. 3577 * 3578 * Use DER Definite Length method per RFC2478 3579 * Use of indefinite length encoding will not be compatible 3580 * with Microsoft or others that actually follow the spec. 3581 */ 3582 static int 3583 g_make_token_header(gss_OID_const mech, 3584 unsigned int body_size, 3585 unsigned char **buf, 3586 unsigned int totallen) 3587 { 3588 int ret = 0; 3589 unsigned int hdrsize; 3590 unsigned char *p = *buf; 3591 3592 hdrsize = 1 + gssint_der_length_size(mech->length) + mech->length; 3593 3594 *(*buf)++ = HEADER_ID; 3595 if ((ret = gssint_put_der_length(hdrsize + body_size, buf, totallen))) 3596 return (ret); 3597 3598 *(*buf)++ = MECH_OID; 3599 if ((ret = gssint_put_der_length(mech->length, buf, 3600 totallen - (int)(p - *buf)))) 3601 return (ret); 3602 TWRITE_STR(*buf, mech->elements, mech->length); 3603 return (0); 3604 } 3605 3606 /* 3607 * NOTE: This checks that the length returned by 3608 * gssint_get_der_length() is not greater than the number of octets 3609 * remaining, even though gssint_get_der_length() already checks, in 3610 * theory. 3611 */ 3612 static int 3613 g_get_tag_and_length(unsigned char **buf, int tag, 3614 unsigned int buflen, unsigned int *outlen) 3615 { 3616 unsigned char *ptr = *buf; 3617 int ret = -1; /* pessimists, assume failure ! */ 3618 unsigned int encoded_len; 3619 unsigned int tmplen = 0; 3620 3621 *outlen = 0; 3622 if (buflen > 1 && *ptr == tag) { 3623 ptr++; 3624 tmplen = gssint_get_der_length(&ptr, buflen - 1, 3625 &encoded_len); 3626 if (tmplen < 0) { 3627 ret = -1; 3628 } else if (tmplen > buflen - (ptr - *buf)) { 3629 ret = -1; 3630 } else 3631 ret = 0; 3632 } 3633 *outlen = tmplen; 3634 *buf = ptr; 3635 return (ret); 3636 } 3637 3638 static int 3639 g_verify_neg_token_init(unsigned char **buf_in, unsigned int cur_size) 3640 { 3641 unsigned char *buf = *buf_in; 3642 unsigned char *endptr = buf + cur_size; 3643 unsigned int seqsize; 3644 int ret = 0; 3645 unsigned int bytes; 3646 3647 /* 3648 * Verify this is a NegotiationToken type token 3649 * - check for a0(context specific identifier) 3650 * - get length and verify that enoughd ata exists 3651 */ 3652 if (g_get_tag_and_length(&buf, CONTEXT, cur_size, &seqsize) < 0) 3653 return (G_BAD_TOK_HEADER); 3654 3655 cur_size = seqsize; /* should indicate bytes remaining */ 3656 3657 /* 3658 * Verify the next piece, it should identify this as 3659 * a strucure of type NegTokenInit. 3660 */ 3661 if (*buf++ == SEQUENCE) { 3662 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0) 3663 return (G_BAD_TOK_HEADER); 3664 /* 3665 * Make sure we have the entire buffer as described 3666 */ 3667 if (buf + seqsize > endptr) 3668 return (G_BAD_TOK_HEADER); 3669 } else { 3670 return (G_BAD_TOK_HEADER); 3671 } 3672 3673 cur_size = seqsize; /* should indicate bytes remaining */ 3674 3675 /* 3676 * Verify that the first blob is a sequence of mechTypes 3677 */ 3678 if (*buf++ == CONTEXT) { 3679 if ((seqsize = gssint_get_der_length(&buf, cur_size, &bytes)) < 0) 3680 return (G_BAD_TOK_HEADER); 3681 /* 3682 * Make sure we have the entire buffer as described 3683 */ 3684 if (buf + bytes > endptr) 3685 return (G_BAD_TOK_HEADER); 3686 } else { 3687 return (G_BAD_TOK_HEADER); 3688 } 3689 3690 /* 3691 * At this point, *buf should be at the beginning of the 3692 * DER encoded list of mech types that are to be negotiated. 3693 */ 3694 *buf_in = buf; 3695 3696 return (ret); 3697 3698 } 3699 3700 /* verify token header. */ 3701 static int 3702 g_verify_token_header(gss_OID_const mech, 3703 unsigned int *body_size, 3704 unsigned char **buf_in, 3705 int tok_type, 3706 unsigned int toksize) 3707 { 3708 unsigned char *buf = *buf_in; 3709 int seqsize; 3710 gss_OID_desc toid; 3711 int ret = 0; 3712 unsigned int bytes; 3713 3714 if (toksize-- < 1) 3715 return (G_BAD_TOK_HEADER); 3716 3717 if (*buf++ != HEADER_ID) 3718 return (G_BAD_TOK_HEADER); 3719 3720 if ((seqsize = gssint_get_der_length(&buf, toksize, &bytes)) < 0) 3721 return (G_BAD_TOK_HEADER); 3722 3723 if ((seqsize + bytes) != toksize) 3724 return (G_BAD_TOK_HEADER); 3725 3726 if (toksize-- < 1) 3727 return (G_BAD_TOK_HEADER); 3728 3729 3730 if (*buf++ != MECH_OID) 3731 return (G_BAD_TOK_HEADER); 3732 3733 if (toksize-- < 1) 3734 return (G_BAD_TOK_HEADER); 3735 3736 toid.length = *buf++; 3737 3738 if (toksize < toid.length) 3739 return (G_BAD_TOK_HEADER); 3740 else 3741 toksize -= toid.length; 3742 3743 toid.elements = buf; 3744 buf += toid.length; 3745 3746 if (!g_OID_equal(&toid, mech)) 3747 ret = G_WRONG_MECH; 3748 3749 /* 3750 * G_WRONG_MECH is not returned immediately because it's more important 3751 * to return G_BAD_TOK_HEADER if the token header is in fact bad 3752 */ 3753 if (toksize < 2) 3754 return (G_BAD_TOK_HEADER); 3755 else 3756 toksize -= 2; 3757 3758 if (!ret) { 3759 *buf_in = buf; 3760 *body_size = toksize; 3761 } 3762 3763 return (ret); 3764 } 3765 3766 /* 3767 * Return non-zero if the oid is one of the kerberos mech oids, 3768 * otherwise return zero. 3769 * 3770 * N.B. There are 3 oids that represent the kerberos mech: 3771 * RFC-specified GSS_MECH_KRB5_OID, 3772 * Old pre-RFC GSS_MECH_KRB5_OLD_OID, 3773 * Incorrect MS GSS_MECH_KRB5_WRONG_OID 3774 */ 3775 3776 static int 3777 is_kerb_mech(gss_OID oid) 3778 { 3779 int answer = 0; 3780 OM_uint32 minor; 3781 extern const gss_OID_set_desc * const gss_mech_set_krb5_both; 3782 3783 (void) gss_test_oid_set_member(&minor, 3784 oid, (gss_OID_set)gss_mech_set_krb5_both, &answer); 3785 3786 return (answer); 3787 } 3788