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