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