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