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