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