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 73 /* 74 * SUNW17PACresync 75 * MIT Kerb has phased out the context arg (1st one) of the functions 76 * of struct gss_config, Solaris is not there yet and all mechs 77 * will need to be changed at same time to do this cleanly. 78 * Till then, we set this def to effect mechglueP.h. 79 * Revisit for full 1.7 resync. 80 */ 81 #define GSSCONFIG_NO_CTXARG 82 #include <mglueP.h> 83 #undef GSSCONFIG_NO_CTXARG 84 85 #include "gssapiP_spnego.h" 86 #include <gssapi_err_generic.h> 87 88 89 /* 90 * SUNW17PACresync 91 * MIT has diff names for these GSS utilities. Solaris needs to change 92 * them globally to get in sync w/MIT. 93 * Revisit for full 1.7 resync. 94 */ 95 #define gssint_get_modOptions __gss_get_modOptions 96 #define gssint_der_length_size der_length_size 97 #define gssint_get_der_length get_der_length 98 #define gssint_put_der_length put_der_length 99 #define gssint_get_mechanism __gss_get_mechanism 100 #define gssint_copy_oid_set gss_copy_oid_set 101 #define gssint_get_mech_type __gss_get_mech_type 102 103 104 #undef g_token_size 105 #undef g_verify_token_header 106 #undef g_make_token_header 107 108 #define HARD_ERROR(v) ((v) != GSS_S_COMPLETE && (v) != GSS_S_CONTINUE_NEEDED) 109 typedef const gss_OID_desc *gss_OID_const; 110 111 /* der routines defined in libgss */ 112 extern unsigned int gssint_der_length_size(OM_uint32); 113 extern int gssint_get_der_length(unsigned char **, OM_uint32, OM_uint32*); 114 extern int gssint_put_der_length(OM_uint32, unsigned char **, OM_uint32); 115 116 117 /* private routines for spnego_mechanism */ 118 static spnego_token_t make_spnego_token(char *); 119 static gss_buffer_desc make_err_msg(char *); 120 static int g_token_size(gss_OID_const, unsigned int); 121 static int g_make_token_header(gss_OID_const, unsigned int, 122 unsigned char **, unsigned int); 123 static int g_verify_token_header(gss_OID_const, unsigned int *, 124 unsigned char **, 125 int, unsigned int); 126 static int g_verify_neg_token_init(unsigned char **, unsigned int); 127 static gss_OID get_mech_oid(OM_uint32 *, unsigned char **, size_t); 128 static gss_buffer_t get_input_token(unsigned char **, unsigned int); 129 static gss_OID_set get_mech_set(OM_uint32 *, unsigned char **, unsigned int); 130 static OM_uint32 get_req_flags(unsigned char **, OM_uint32, OM_uint32 *); 131 static OM_uint32 get_available_mechs(OM_uint32 *, gss_name_t, 132 gss_cred_usage_t, gss_cred_id_t *, gss_OID_set *); 133 static void release_spnego_ctx(spnego_gss_ctx_id_t *); 134 static void check_spnego_options(spnego_gss_ctx_id_t); 135 static spnego_gss_ctx_id_t create_spnego_ctx(void); 136 static int put_mech_set(gss_OID_set mechSet, gss_buffer_t buf); 137 static int put_input_token(unsigned char **, gss_buffer_t, unsigned int); 138 static int put_mech_oid(unsigned char **, gss_OID_const, unsigned int); 139 static int put_negResult(unsigned char **, OM_uint32, unsigned int); 140 141 static OM_uint32 142 process_mic(OM_uint32 *, gss_buffer_t, spnego_gss_ctx_id_t, 143 gss_buffer_t *, OM_uint32 *, send_token_flag *); 144 static OM_uint32 145 handle_mic(OM_uint32 *, gss_buffer_t, int, spnego_gss_ctx_id_t, 146 gss_buffer_t *, OM_uint32 *, send_token_flag *); 147 148 static OM_uint32 149 init_ctx_new(OM_uint32 *, gss_cred_id_t, gss_ctx_id_t *, 150 gss_OID_set *, send_token_flag *); 151 static OM_uint32 152 init_ctx_nego(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, gss_OID, 153 gss_buffer_t *, gss_buffer_t *, 154 OM_uint32 *, send_token_flag *); 155 static OM_uint32 156 init_ctx_cont(OM_uint32 *, gss_ctx_id_t *, gss_buffer_t, 157 gss_buffer_t *, gss_buffer_t *, 158 OM_uint32 *, send_token_flag *); 159 static OM_uint32 160 init_ctx_reselect(OM_uint32 *, spnego_gss_ctx_id_t, OM_uint32, 161 gss_OID, gss_buffer_t *, gss_buffer_t *, 162 OM_uint32 *, send_token_flag *); 163 static OM_uint32 164 init_ctx_call_init(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t, 165 gss_name_t, OM_uint32, OM_uint32, gss_buffer_t, 166 gss_OID *, gss_buffer_t, OM_uint32 *, OM_uint32 *, 167 OM_uint32 *, send_token_flag *); 168 169 static OM_uint32 170 acc_ctx_new(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *, 171 gss_cred_id_t, gss_buffer_t *, 172 gss_buffer_t *, OM_uint32 *, send_token_flag *); 173 static OM_uint32 174 acc_ctx_cont(OM_uint32 *, gss_buffer_t, gss_ctx_id_t *, 175 gss_buffer_t *, gss_buffer_t *, 176 OM_uint32 *, send_token_flag *); 177 static OM_uint32 178 acc_ctx_vfy_oid(OM_uint32 *, spnego_gss_ctx_id_t, gss_OID, 179 OM_uint32 *, send_token_flag *); 180 static OM_uint32 181 acc_ctx_call_acc(OM_uint32 *, spnego_gss_ctx_id_t, gss_cred_id_t, 182 gss_buffer_t, gss_OID *, gss_buffer_t, 183 OM_uint32 *, OM_uint32 *, gss_cred_id_t *, 184 OM_uint32 *, send_token_flag *); 185 186 static gss_OID 187 negotiate_mech_type(OM_uint32 *, gss_OID_set, gss_OID_set, 188 OM_uint32 *); 189 static int 190 g_get_tag_and_length(unsigned char **, int, unsigned int, unsigned int *); 191 192 static int 193 make_spnego_tokenInit_msg(spnego_gss_ctx_id_t, 194 int, 195 gss_buffer_t, 196 OM_uint32, gss_buffer_t, send_token_flag, 197 gss_buffer_t); 198 static int 199 make_spnego_tokenTarg_msg(OM_uint32, gss_OID, gss_buffer_t, 200 gss_buffer_t, send_token_flag, 201 gss_buffer_t); 202 203 static OM_uint32 204 get_negTokenInit(OM_uint32 *, gss_buffer_t, gss_buffer_t, 205 gss_OID_set *, OM_uint32 *, gss_buffer_t *, 206 gss_buffer_t *); 207 static OM_uint32 208 get_negTokenResp(OM_uint32 *, unsigned char *, unsigned int, 209 OM_uint32 *, gss_OID *, gss_buffer_t *, gss_buffer_t *); 210 211 static int 212 is_kerb_mech(gss_OID oid); 213 214 /* SPNEGO oid structure */ 215 static const gss_OID_desc spnego_oids[] = { 216 {SPNEGO_OID_LENGTH, SPNEGO_OID}, 217 }; 218 219 const gss_OID_desc * const gss_mech_spnego = spnego_oids+0; 220 static const gss_OID_set_desc spnego_oidsets[] = { 221 {1, (gss_OID) spnego_oids+0}, 222 }; 223 const gss_OID_set_desc * const gss_mech_set_spnego = spnego_oidsets+0; 224 225 static int make_NegHints(OM_uint32 *, gss_cred_id_t, gss_buffer_t *); 226 static int put_neg_hints(unsigned char **, gss_buffer_t, unsigned int); 227 static OM_uint32 228 acc_ctx_hints(OM_uint32 *, gss_ctx_id_t *, gss_cred_id_t, 229 gss_buffer_t *, OM_uint32 *, send_token_flag *); 230 231 232 /* 233 * The Mech OID for SPNEGO: 234 * { iso(1) org(3) dod(6) internet(1) security(5) 235 * mechanism(5) spnego(2) } 236 */ 237 static struct gss_config spnego_mechanism = 238 { 239 {SPNEGO_OID_LENGTH, SPNEGO_OID}, 240 NULL, 241 glue_spnego_gss_acquire_cred, 242 glue_spnego_gss_release_cred, 243 glue_spnego_gss_init_sec_context, 244 #ifndef LEAN_CLIENT 245 glue_spnego_gss_accept_sec_context, 246 #else 247 NULL, 248 #endif /* LEAN_CLIENT */ 249 NULL, /* unseal */ 250 NULL, /* gss_process_context_token */ 251 glue_spnego_gss_delete_sec_context, /* gss_delete_sec_context */ 252 NULL, /* gss_context_time - SUNW17PACresync make it NULL */ 253 glue_spnego_gss_display_status, 254 NULL, /* gss_indicate_mechs */ 255 glue_spnego_gss_compare_name, 256 glue_spnego_gss_display_name, 257 glue_spnego_gss_import_name, /* glue */ 258 glue_spnego_gss_release_name, 259 NULL, /* gss_inquire_cred */ 260 NULL, /* gss_add_cred */ 261 NULL, /* seal */ 262 #ifndef LEAN_CLIENT 263 glue_spnego_gss_export_sec_context, /* gss_export_sec_context */ 264 glue_spnego_gss_import_sec_context, /* gss_import_sec_context */ 265 #else 266 NULL, /* gss_export_sec_context */ 267 NULL, /* gss_import_sec_context */ 268 #endif /* LEAN_CLIENT */ 269 NULL, /* gss_inquire_cred_by_mech */ 270 glue_spnego_gss_inquire_names_for_mech, 271 NULL, /* gss_inquire_context - SUNW17PACresync make it NULL */ 272 NULL, /* gss_internal_release_oid */ 273 NULL, /* gss_wrap_size_limit - SUNW17PACresync make it NULL */ 274 NULL, /* pname */ 275 NULL, /* userok */ 276 NULL, /* gss_export_name */ 277 NULL, /* sign */ 278 NULL, /* verify */ 279 NULL, /* gss_store_cred */ 280 spnego_gss_inquire_sec_context_by_oid, /* gss_inquire_sec_context_by_oid */ 281 }; 282 283 #ifdef _GSS_STATIC_LINK 284 #include "mglueP.h" 285 286 static int gss_spnegomechglue_init(void) 287 { 288 struct gss_mech_config mech_spnego; 289 290 memset(&mech_spnego, 0, sizeof(mech_spnego)); 291 mech_spnego.mech = &spnego_mechanism; 292 mech_spnego.mechNameStr = "spnego"; 293 mech_spnego.mech_type = GSS_C_NO_OID; 294 295 return gssint_register_mechinfo(&mech_spnego); 296 } 297 #else 298 gss_mechanism KRB5_CALLCONV 299 gss_mech_initialize(void) 300 { 301 return (&spnego_mechanism); 302 } 303 304 #if 0 /* SUNW17PACresync */ 305 MAKE_INIT_FUNCTION(gss_krb5int_lib_init); 306 MAKE_FINI_FUNCTION(gss_krb5int_lib_fini); 307 int gss_krb5int_lib_init(void) 308 #endif 309 310 #endif /* _GSS_STATIC_LINK */ 311 312 static int gss_spnegoint_lib_init(void) 313 { 314 #ifdef _GSS_STATIC_LINK 315 return gss_spnegomechglue_init(); 316 #else 317 return 0; 318 #endif 319 } 320 321 static void gss_spnegoint_lib_fini(void) 322 { 323 } 324 325 /*ARGSUSED*/ 326 OM_uint32 327 glue_spnego_gss_acquire_cred( 328 void *context, 329 OM_uint32 *minor_status, 330 gss_name_t desired_name, 331 OM_uint32 time_req, 332 gss_OID_set desired_mechs, 333 gss_cred_usage_t cred_usage, 334 gss_cred_id_t *output_cred_handle, 335 gss_OID_set *actual_mechs, 336 OM_uint32 *time_rec) 337 { 338 return(spnego_gss_acquire_cred(minor_status, 339 desired_name, 340 time_req, 341 desired_mechs, 342 cred_usage, 343 output_cred_handle, 344 actual_mechs, 345 time_rec)); 346 } 347 348 /*ARGSUSED*/ 349 OM_uint32 350 spnego_gss_acquire_cred(OM_uint32 *minor_status, 351 gss_name_t desired_name, 352 OM_uint32 time_req, 353 gss_OID_set desired_mechs, 354 gss_cred_usage_t cred_usage, 355 gss_cred_id_t *output_cred_handle, 356 gss_OID_set *actual_mechs, 357 OM_uint32 *time_rec) 358 { 359 OM_uint32 status; 360 gss_OID_set amechs; 361 dsyslog("Entering spnego_gss_acquire_cred\n"); 362 363 if (actual_mechs) 364 *actual_mechs = NULL; 365 366 if (time_rec) 367 *time_rec = 0; 368 369 /* 370 * If the user did not specify a list of mechs, 371 * use get_available_mechs to collect a list of 372 * mechs for which creds are available. 373 */ 374 if (desired_mechs == GSS_C_NULL_OID_SET) { 375 status = get_available_mechs(minor_status, 376 desired_name, cred_usage, 377 output_cred_handle, &amechs); 378 } else { 379 /* 380 * The caller gave a specific list of mechanisms, 381 * so just get whatever creds are available. 382 * gss_acquire_creds will return the subset of mechs for 383 * which the given 'output_cred_handle' is valid. 384 */ 385 status = gss_acquire_cred(minor_status, 386 desired_name, time_req, 387 desired_mechs, cred_usage, 388 output_cred_handle, &amechs, 389 time_rec); 390 } 391 392 if (actual_mechs && amechs != GSS_C_NULL_OID_SET) { 393 (void) gssint_copy_oid_set(minor_status, amechs, actual_mechs); 394 } 395 (void) gss_release_oid_set(minor_status, &amechs); 396 397 dsyslog("Leaving spnego_gss_acquire_cred\n"); 398 return (status); 399 } 400 401 /*ARGSUSED*/ 402 OM_uint32 403 glue_spnego_gss_release_cred(void *context, 404 OM_uint32 *minor_status, 405 gss_cred_id_t *cred_handle) 406 { 407 return( spnego_gss_release_cred(minor_status, cred_handle)); 408 } 409 410 /*ARGSUSED*/ 411 OM_uint32 412 spnego_gss_release_cred(OM_uint32 *minor_status, 413 gss_cred_id_t *cred_handle) 414 { 415 OM_uint32 status; 416 417 dsyslog("Entering spnego_gss_release_cred\n"); 418 419 if (minor_status == NULL || cred_handle == NULL) 420 return (GSS_S_CALL_INACCESSIBLE_WRITE); 421 422 *minor_status = 0; 423 424 if (*cred_handle == GSS_C_NO_CREDENTIAL) 425 return (GSS_S_COMPLETE); 426 427 status = gss_release_cred(minor_status, cred_handle); 428 429 dsyslog("Leaving spnego_gss_release_cred\n"); 430 return (status); 431 } 432 433 static void 434 check_spnego_options(spnego_gss_ctx_id_t spnego_ctx) 435 { 436 spnego_ctx->optionStr = gssint_get_modOptions( 437 (const gss_OID)&spnego_oids[0]); 438 } 439 440 static spnego_gss_ctx_id_t 441 create_spnego_ctx(void) 442 { 443 spnego_gss_ctx_id_t spnego_ctx = NULL; 444 spnego_ctx = (spnego_gss_ctx_id_t) 445 malloc(sizeof (spnego_gss_ctx_id_rec)); 446 447 if (spnego_ctx == NULL) { 448 return (NULL); 449 } 450 451 spnego_ctx->magic_num = SPNEGO_MAGIC_ID; 452 spnego_ctx->ctx_handle = GSS_C_NO_CONTEXT; 453 spnego_ctx->internal_mech = NULL; 454 spnego_ctx->optionStr = NULL; 455 spnego_ctx->DER_mechTypes.length = 0; 456 spnego_ctx->DER_mechTypes.value = NULL; 457 spnego_ctx->default_cred = GSS_C_NO_CREDENTIAL; 458 spnego_ctx->mic_reqd = 0; 459 spnego_ctx->mic_sent = 0; 460 spnego_ctx->mic_rcvd = 0; 461 spnego_ctx->mech_complete = 0; 462 spnego_ctx->nego_done = 0; 463 spnego_ctx->internal_name = GSS_C_NO_NAME; 464 spnego_ctx->actual_mech = GSS_C_NO_OID; 465 466 check_spnego_options(spnego_ctx); 467 468 return (spnego_ctx); 469 } 470 471 /* 472 * Both initiator and acceptor call here to verify and/or create 473 * mechListMIC, and to consistency-check the MIC state. 474 */ 475 static OM_uint32 476 handle_mic(OM_uint32 *minor_status, gss_buffer_t mic_in, 477 int send_mechtok, spnego_gss_ctx_id_t sc, 478 gss_buffer_t *mic_out, 479 OM_uint32 *negState, send_token_flag *tokflag) 480 { 481 OM_uint32 ret; 482 483 ret = GSS_S_FAILURE; 484 *mic_out = GSS_C_NO_BUFFER; 485 if (mic_in != GSS_C_NO_BUFFER) { 486 if (sc->mic_rcvd) { 487 /* Reject MIC if we've already received a MIC. */ 488 *negState = REJECT; 489 *tokflag = ERROR_TOKEN_SEND; 490 return GSS_S_DEFECTIVE_TOKEN; 491 } 492 } else if (sc->mic_reqd && !send_mechtok) { 493 /* 494 * If the peer sends the final mechanism token, it 495 * must send the MIC with that token if the 496 * negotiation requires MICs. 497 */ 498 *negState = REJECT; 499 *tokflag = ERROR_TOKEN_SEND; 500 return GSS_S_DEFECTIVE_TOKEN; 501 } 502 ret = process_mic(minor_status, mic_in, sc, mic_out, 503 negState, tokflag); 504 if (ret != GSS_S_COMPLETE) { 505 return ret; 506 } 507 if (sc->mic_reqd) { 508 assert(sc->mic_sent || sc->mic_rcvd); 509 } 510 if (sc->mic_sent && sc->mic_rcvd) { 511 ret = GSS_S_COMPLETE; 512 *negState = ACCEPT_COMPLETE; 513 if (*mic_out == GSS_C_NO_BUFFER) { 514 /* 515 * We sent a MIC on the previous pass; we 516 * shouldn't be sending a mechanism token. 517 */ 518 assert(!send_mechtok); 519 *tokflag = NO_TOKEN_SEND; 520 } else { 521 *tokflag = CONT_TOKEN_SEND; 522 } 523 } else if (sc->mic_reqd) { 524 *negState = ACCEPT_INCOMPLETE; 525 ret = GSS_S_CONTINUE_NEEDED; 526 } else if (*negState == ACCEPT_COMPLETE) { 527 ret = GSS_S_COMPLETE; 528 } else { 529 ret = GSS_S_CONTINUE_NEEDED; 530 } 531 return ret; 532 } 533 534 /* 535 * Perform the actual verification and/or generation of mechListMIC. 536 */ 537 static OM_uint32 538 process_mic(OM_uint32 *minor_status, gss_buffer_t mic_in, 539 spnego_gss_ctx_id_t sc, gss_buffer_t *mic_out, 540 OM_uint32 *negState, send_token_flag *tokflag) 541 { 542 OM_uint32 ret, tmpmin; 543 gss_qop_t qop_state; 544 gss_buffer_desc tmpmic = GSS_C_EMPTY_BUFFER; 545 546 ret = GSS_S_FAILURE; 547 if (mic_in != GSS_C_NO_BUFFER) { 548 ret = gss_verify_mic(minor_status, sc->ctx_handle, 549 &sc->DER_mechTypes, 550 mic_in, &qop_state); 551 if (ret != GSS_S_COMPLETE) { 552 *negState = REJECT; 553 *tokflag = ERROR_TOKEN_SEND; 554 return ret; 555 } 556 /* If we got a MIC, we must send a MIC. */ 557 sc->mic_reqd = 1; 558 sc->mic_rcvd = 1; 559 } 560 if (sc->mic_reqd && !sc->mic_sent) { 561 ret = gss_get_mic(minor_status, sc->ctx_handle, 562 GSS_C_QOP_DEFAULT, 563 &sc->DER_mechTypes, 564 &tmpmic); 565 if (ret != GSS_S_COMPLETE) { 566 gss_release_buffer(&tmpmin, &tmpmic); 567 *tokflag = NO_TOKEN_SEND; 568 return ret; 569 } 570 *mic_out = malloc(sizeof(gss_buffer_desc)); 571 if (*mic_out == GSS_C_NO_BUFFER) { 572 gss_release_buffer(&tmpmin, &tmpmic); 573 *tokflag = NO_TOKEN_SEND; 574 return GSS_S_FAILURE; 575 } 576 **mic_out = tmpmic; 577 sc->mic_sent = 1; 578 } 579 return GSS_S_COMPLETE; 580 } 581 582 /* 583 * Initial call to spnego_gss_init_sec_context(). 584 */ 585 static OM_uint32 586 init_ctx_new(OM_uint32 *minor_status, 587 gss_cred_id_t cred, 588 gss_ctx_id_t *ctx, 589 gss_OID_set *mechSet, 590 send_token_flag *tokflag) 591 { 592 OM_uint32 ret, tmpmin; 593 gss_cred_id_t creds = GSS_C_NO_CREDENTIAL; 594 spnego_gss_ctx_id_t sc = NULL; 595 596 /* determine negotiation mech set */ 597 if (cred == GSS_C_NO_CREDENTIAL) { 598 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, 599 GSS_C_INITIATE, &creds, mechSet); 600 gss_release_cred(&tmpmin, &creds); 601 } else { 602 /* 603 * Use the list of mechs included in the cred that we 604 * were given. 605 */ 606 ret = gss_inquire_cred(minor_status, cred, 607 NULL, NULL, NULL, mechSet); 608 } 609 if (ret != GSS_S_COMPLETE) 610 return ret; 611 612 sc = create_spnego_ctx(); 613 if (sc == NULL) 614 return GSS_S_FAILURE; 615 616 /* 617 * need to pull the first mech from mechSet to do first 618 * gss_init_sec_context() 619 */ 620 ret = generic_gss_copy_oid(minor_status, (*mechSet)->elements, 621 &sc->internal_mech); 622 if (ret != GSS_S_COMPLETE) { 623 map_errcode(minor_status); 624 goto cleanup; 625 } 626 627 if (put_mech_set(*mechSet, &sc->DER_mechTypes) < 0) { 628 generic_gss_release_oid(&tmpmin, &sc->internal_mech); 629 ret = GSS_S_FAILURE; 630 goto cleanup; 631 } 632 /* 633 * The actual context is not yet determined, set the output 634 * context handle to refer to the spnego context itself. 635 */ 636 sc->ctx_handle = GSS_C_NO_CONTEXT; 637 *ctx = (gss_ctx_id_t)sc; 638 *tokflag = INIT_TOKEN_SEND; 639 ret = GSS_S_CONTINUE_NEEDED; 640 641 cleanup: 642 gss_release_oid_set(&tmpmin, mechSet); 643 return ret; 644 } 645 646 /* 647 * Called by second and later calls to spnego_gss_init_sec_context() 648 * to decode reply and update state. 649 */ 650 static OM_uint32 651 init_ctx_cont(OM_uint32 *minor_status, gss_ctx_id_t *ctx, gss_buffer_t buf, 652 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC, 653 OM_uint32 *negState, send_token_flag *tokflag) 654 { 655 OM_uint32 ret, tmpmin, acc_negState; 656 unsigned char *ptr; 657 spnego_gss_ctx_id_t sc; 658 gss_OID supportedMech = GSS_C_NO_OID; 659 660 sc = (spnego_gss_ctx_id_t)*ctx; 661 *negState = REJECT; 662 *tokflag = ERROR_TOKEN_SEND; 663 664 ptr = buf->value; 665 ret = get_negTokenResp(minor_status, ptr, buf->length, 666 &acc_negState, &supportedMech, 667 responseToken, mechListMIC); 668 if (ret != GSS_S_COMPLETE) 669 goto cleanup; 670 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN && 671 supportedMech == GSS_C_NO_OID && 672 *responseToken == GSS_C_NO_BUFFER && 673 *mechListMIC == GSS_C_NO_BUFFER) { 674 /* Reject "empty" token. */ 675 ret = GSS_S_DEFECTIVE_TOKEN; 676 } 677 if (acc_negState == REJECT) { 678 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 679 map_errcode(minor_status); 680 *tokflag = NO_TOKEN_SEND; 681 ret = GSS_S_FAILURE; 682 goto cleanup; 683 } 684 /* 685 * nego_done is false for the first call to init_ctx_cont() 686 */ 687 if (!sc->nego_done) { 688 ret = init_ctx_nego(minor_status, sc, 689 acc_negState, 690 supportedMech, responseToken, 691 mechListMIC, 692 negState, tokflag); 693 } else if (!sc->mech_complete && 694 *responseToken == GSS_C_NO_BUFFER) { 695 /* 696 * mech not finished and mech token missing 697 */ 698 ret = GSS_S_DEFECTIVE_TOKEN; 699 } else if (sc->mic_reqd && 700 (sc->ctx_flags & GSS_C_INTEG_FLAG)) { 701 *negState = ACCEPT_INCOMPLETE; 702 *tokflag = CONT_TOKEN_SEND; 703 ret = GSS_S_CONTINUE_NEEDED; 704 } else { 705 *negState = ACCEPT_COMPLETE; 706 *tokflag = NO_TOKEN_SEND; 707 ret = GSS_S_COMPLETE; 708 } 709 cleanup: 710 if (supportedMech != GSS_C_NO_OID) 711 generic_gss_release_oid(&tmpmin, &supportedMech); 712 return ret; 713 } 714 715 /* 716 * Consistency checking and mechanism negotiation handling for second 717 * call of spnego_gss_init_sec_context(). Call init_ctx_reselect() to 718 * update internal state if acceptor has counter-proposed. 719 */ 720 static OM_uint32 721 init_ctx_nego(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, 722 OM_uint32 acc_negState, gss_OID supportedMech, 723 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC, 724 OM_uint32 *negState, send_token_flag *tokflag) 725 { 726 OM_uint32 ret; 727 728 *negState = REJECT; 729 *tokflag = ERROR_TOKEN_SEND; 730 ret = GSS_S_DEFECTIVE_TOKEN; 731 /* 732 * Both supportedMech and negState must be present in first 733 * acceptor token. 734 */ 735 if (supportedMech == GSS_C_NO_OID) { 736 *minor_status = ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR; 737 map_errcode(minor_status); 738 return GSS_S_DEFECTIVE_TOKEN; 739 } 740 if (acc_negState == ACCEPT_DEFECTIVE_TOKEN) { 741 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 742 map_errcode(minor_status); 743 return GSS_S_DEFECTIVE_TOKEN; 744 } 745 746 /* 747 * If the mechanism we sent is not the mechanism returned from 748 * the server, we need to handle the server's counter 749 * proposal. There is a bug in SAMBA servers that always send 750 * the old Kerberos mech OID, even though we sent the new one. 751 * So we will treat all the Kerberos mech OIDS as the same. 752 */ 753 if (!(is_kerb_mech(supportedMech) && 754 is_kerb_mech(sc->internal_mech)) && 755 !g_OID_equal(supportedMech, sc->internal_mech)) { 756 ret = init_ctx_reselect(minor_status, sc, 757 acc_negState, supportedMech, 758 responseToken, mechListMIC, 759 negState, tokflag); 760 761 } else if (*responseToken == GSS_C_NO_BUFFER) { 762 if (sc->mech_complete) { 763 /* 764 * Mech completed on first call to its 765 * init_sec_context(). Acceptor sends no mech 766 * token. 767 */ 768 *negState = ACCEPT_COMPLETE; 769 *tokflag = NO_TOKEN_SEND; 770 ret = GSS_S_COMPLETE; 771 } else { 772 /* 773 * Reject missing mech token when optimistic 774 * mech selected. 775 */ 776 *minor_status = ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR; 777 map_errcode(minor_status); 778 ret = GSS_S_DEFECTIVE_TOKEN; 779 } 780 } else if (sc->mech_complete) { 781 /* Reject spurious mech token. */ 782 ret = GSS_S_DEFECTIVE_TOKEN; 783 } else { 784 *negState = ACCEPT_INCOMPLETE; 785 *tokflag = CONT_TOKEN_SEND; 786 ret = GSS_S_CONTINUE_NEEDED; 787 } 788 sc->nego_done = 1; 789 return ret; 790 } 791 792 /* 793 * Handle acceptor's counter-proposal of an alternative mechanism. 794 */ 795 static OM_uint32 796 init_ctx_reselect(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, 797 OM_uint32 acc_negState, gss_OID supportedMech, 798 gss_buffer_t *responseToken, gss_buffer_t *mechListMIC, 799 OM_uint32 *negState, send_token_flag *tokflag) 800 { 801 OM_uint32 ret, tmpmin; 802 803 generic_gss_release_oid(&tmpmin, &sc->internal_mech); 804 gss_delete_sec_context(&tmpmin, &sc->ctx_handle, 805 GSS_C_NO_BUFFER); 806 807 ret = generic_gss_copy_oid(minor_status, supportedMech, 808 &sc->internal_mech); 809 if (ret != GSS_S_COMPLETE) { 810 map_errcode(minor_status); 811 sc->internal_mech = GSS_C_NO_OID; 812 *tokflag = NO_TOKEN_SEND; 813 return ret; 814 } 815 if (*responseToken != GSS_C_NO_BUFFER) { 816 /* Reject spurious mech token. */ 817 return GSS_S_DEFECTIVE_TOKEN; 818 } 819 /* 820 * Windows 2003 and earlier don't correctly send a 821 * negState of request-mic when counter-proposing a 822 * mechanism. They probably don't handle mechListMICs 823 * properly either. 824 */ 825 if (acc_negState != REQUEST_MIC) 826 return GSS_S_DEFECTIVE_TOKEN; 827 828 sc->mech_complete = 0; 829 sc->mic_reqd = 1; 830 *negState = REQUEST_MIC; 831 *tokflag = CONT_TOKEN_SEND; 832 return GSS_S_CONTINUE_NEEDED; 833 } 834 835 /* 836 * Wrap call to mechanism gss_init_sec_context() and update state 837 * accordingly. 838 */ 839 static OM_uint32 840 init_ctx_call_init(OM_uint32 *minor_status, 841 spnego_gss_ctx_id_t sc, 842 gss_cred_id_t claimant_cred_handle, 843 gss_name_t target_name, 844 OM_uint32 req_flags, 845 OM_uint32 time_req, 846 gss_buffer_t mechtok_in, 847 gss_OID *actual_mech, 848 gss_buffer_t mechtok_out, 849 OM_uint32 *ret_flags, 850 OM_uint32 *time_rec, 851 OM_uint32 *negState, 852 send_token_flag *send_token) 853 { 854 OM_uint32 ret; 855 856 ret = gss_init_sec_context(minor_status, 857 claimant_cred_handle, 858 &sc->ctx_handle, 859 target_name, 860 sc->internal_mech, 861 (req_flags | GSS_C_INTEG_FLAG), 862 time_req, 863 GSS_C_NO_CHANNEL_BINDINGS, 864 mechtok_in, 865 &sc->actual_mech, 866 mechtok_out, 867 &sc->ctx_flags, 868 time_rec); 869 if (ret == GSS_S_COMPLETE) { 870 sc->mech_complete = 1; 871 if (ret_flags != NULL) 872 *ret_flags = sc->ctx_flags; 873 /* 874 * If this isn't the first time we've been called, 875 * we're done unless a MIC needs to be 876 * generated/handled. 877 */ 878 if (*send_token == CONT_TOKEN_SEND && 879 mechtok_out->length == 0 && 880 (!sc->mic_reqd || 881 !(sc->ctx_flags & GSS_C_INTEG_FLAG))) { 882 883 *negState = ACCEPT_COMPLETE; 884 ret = GSS_S_COMPLETE; 885 if (mechtok_out->length == 0) { 886 *send_token = NO_TOKEN_SEND; 887 } 888 } else { 889 *negState = ACCEPT_INCOMPLETE; 890 ret = GSS_S_CONTINUE_NEEDED; 891 } 892 } else if (ret != GSS_S_CONTINUE_NEEDED) { 893 if (*send_token == INIT_TOKEN_SEND) { 894 /* Don't output token on error if first call. */ 895 *send_token = NO_TOKEN_SEND; 896 } else { 897 *send_token = ERROR_TOKEN_SEND; 898 } 899 *negState = REJECT; 900 } 901 return ret; 902 } 903 904 /*ARGSUSED*/ 905 OM_uint32 906 glue_spnego_gss_init_sec_context( 907 void *context, 908 OM_uint32 *minor_status, 909 gss_cred_id_t claimant_cred_handle, 910 gss_ctx_id_t *context_handle, 911 gss_name_t target_name, 912 gss_OID mech_type, 913 OM_uint32 req_flags, 914 OM_uint32 time_req, 915 gss_channel_bindings_t input_chan_bindings, 916 gss_buffer_t input_token, 917 gss_OID *actual_mech, 918 gss_buffer_t output_token, 919 OM_uint32 *ret_flags, 920 OM_uint32 *time_rec) 921 { 922 return(spnego_gss_init_sec_context( 923 minor_status, 924 claimant_cred_handle, 925 context_handle, 926 target_name, 927 mech_type, 928 req_flags, 929 time_req, 930 input_chan_bindings, 931 input_token, 932 actual_mech, 933 output_token, 934 ret_flags, 935 time_rec)); 936 } 937 938 /*ARGSUSED*/ 939 OM_uint32 940 spnego_gss_init_sec_context( 941 OM_uint32 *minor_status, 942 gss_cred_id_t claimant_cred_handle, 943 gss_ctx_id_t *context_handle, 944 gss_name_t target_name, 945 gss_OID mech_type, 946 OM_uint32 req_flags, 947 OM_uint32 time_req, 948 gss_channel_bindings_t input_chan_bindings, 949 gss_buffer_t input_token, 950 gss_OID *actual_mech, 951 gss_buffer_t output_token, 952 OM_uint32 *ret_flags, 953 OM_uint32 *time_rec) 954 { 955 /* 956 * send_token is used to indicate in later steps 957 * what type of token, if any should be sent or processed. 958 * NO_TOKEN_SEND = no token should be sent 959 * INIT_TOKEN_SEND = initial token will be sent 960 * CONT_TOKEN_SEND = continuing tokens to be sent 961 * CHECK_MIC = no token to be sent, but have a MIC to check. 962 */ 963 send_token_flag send_token = NO_TOKEN_SEND; 964 OM_uint32 tmpmin, ret, negState; 965 gss_buffer_t mechtok_in, mechListMIC_in, mechListMIC_out; 966 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER; 967 gss_OID_set mechSet = GSS_C_NO_OID_SET; 968 spnego_gss_ctx_id_t spnego_ctx = NULL; 969 970 dsyslog("Entering init_sec_context\n"); 971 972 mechtok_in = mechListMIC_out = mechListMIC_in = GSS_C_NO_BUFFER; 973 negState = REJECT; 974 975 if (minor_status != NULL) 976 *minor_status = 0; 977 if (output_token != GSS_C_NO_BUFFER) { 978 output_token->length = 0; 979 output_token->value = NULL; 980 } 981 if (minor_status == NULL || 982 output_token == GSS_C_NO_BUFFER || 983 context_handle == NULL) 984 return GSS_S_CALL_INACCESSIBLE_WRITE; 985 986 if (actual_mech != NULL) 987 *actual_mech = GSS_C_NO_OID; 988 989 if (*context_handle == GSS_C_NO_CONTEXT) { 990 ret = init_ctx_new(minor_status, claimant_cred_handle, 991 context_handle, &mechSet, &send_token); 992 if (ret != GSS_S_CONTINUE_NEEDED) { 993 goto cleanup; 994 } 995 } else { 996 ret = init_ctx_cont(minor_status, context_handle, 997 input_token, &mechtok_in, 998 &mechListMIC_in, &negState, &send_token); 999 if (HARD_ERROR(ret)) { 1000 goto cleanup; 1001 } 1002 } 1003 spnego_ctx = (spnego_gss_ctx_id_t)*context_handle; 1004 if (!spnego_ctx->mech_complete) { 1005 ret = init_ctx_call_init( 1006 minor_status, spnego_ctx, 1007 claimant_cred_handle, 1008 target_name, req_flags, 1009 time_req, mechtok_in, 1010 actual_mech, &mechtok_out, 1011 ret_flags, time_rec, 1012 &negState, &send_token); 1013 } 1014 /* create mic/check mic */ 1015 if (!HARD_ERROR(ret) && spnego_ctx->mech_complete && 1016 (spnego_ctx->ctx_flags & GSS_C_INTEG_FLAG)) { 1017 1018 ret = handle_mic(minor_status, 1019 mechListMIC_in, 1020 (mechtok_out.length != 0), 1021 spnego_ctx, &mechListMIC_out, 1022 &negState, &send_token); 1023 } 1024 cleanup: 1025 if (send_token == INIT_TOKEN_SEND) { 1026 if (make_spnego_tokenInit_msg(spnego_ctx, 1027 0, 1028 mechListMIC_out, 1029 req_flags, 1030 &mechtok_out, send_token, 1031 output_token) < 0) { 1032 ret = GSS_S_FAILURE; 1033 } 1034 } else if (send_token != NO_TOKEN_SEND) { 1035 if (make_spnego_tokenTarg_msg(negState, GSS_C_NO_OID, 1036 &mechtok_out, mechListMIC_out, 1037 send_token, 1038 output_token) < 0) { 1039 ret = GSS_S_FAILURE; 1040 } 1041 } 1042 gss_release_buffer(&tmpmin, &mechtok_out); 1043 if (ret == GSS_S_COMPLETE) { 1044 /* 1045 * Now, switch the output context to refer to the 1046 * negotiated mechanism's context. 1047 */ 1048 *context_handle = (gss_ctx_id_t)spnego_ctx->ctx_handle; 1049 if (actual_mech != NULL) 1050 *actual_mech = spnego_ctx->actual_mech; 1051 if (ret_flags != NULL) 1052 *ret_flags = spnego_ctx->ctx_flags; 1053 release_spnego_ctx(&spnego_ctx); 1054 } else if (ret != GSS_S_CONTINUE_NEEDED) { 1055 if (spnego_ctx != NULL) { 1056 gss_delete_sec_context(&tmpmin, 1057 &spnego_ctx->ctx_handle, 1058 GSS_C_NO_BUFFER); 1059 release_spnego_ctx(&spnego_ctx); 1060 } 1061 *context_handle = GSS_C_NO_CONTEXT; 1062 } 1063 if (mechtok_in != GSS_C_NO_BUFFER) { 1064 gss_release_buffer(&tmpmin, mechtok_in); 1065 free(mechtok_in); 1066 } 1067 if (mechListMIC_in != GSS_C_NO_BUFFER) { 1068 gss_release_buffer(&tmpmin, mechListMIC_in); 1069 free(mechListMIC_in); 1070 } 1071 if (mechListMIC_out != GSS_C_NO_BUFFER) { 1072 gss_release_buffer(&tmpmin, mechListMIC_out); 1073 free(mechListMIC_out); 1074 } 1075 if (mechSet != GSS_C_NO_OID_SET) { 1076 gss_release_oid_set(&tmpmin, &mechSet); 1077 } 1078 return ret; 1079 } /* init_sec_context */ 1080 1081 /* We don't want to import KRB5 headers here */ 1082 static const gss_OID_desc gss_mech_krb5_oid = 1083 { 9, "\052\206\110\206\367\022\001\002\002" }; 1084 static const gss_OID_desc gss_mech_krb5_wrong_oid = 1085 { 9, "\052\206\110\202\367\022\001\002\002" }; 1086 1087 /* 1088 * verify that the input token length is not 0. If it is, just return. 1089 * If the token length is greater than 0, der encode as a sequence 1090 * and place in buf_out, advancing buf_out. 1091 */ 1092 1093 static int 1094 put_neg_hints(unsigned char **buf_out, gss_buffer_t input_token, 1095 unsigned int buflen) 1096 { 1097 int ret; 1098 1099 /* if token length is 0, we do not want to send */ 1100 if (input_token->length == 0) 1101 return (0); 1102 1103 if (input_token->length > buflen) 1104 return (-1); 1105 1106 *(*buf_out)++ = SEQUENCE; 1107 if ((ret = gssint_put_der_length(input_token->length, buf_out, 1108 input_token->length))) 1109 return (ret); 1110 TWRITE_STR(*buf_out, input_token->value, input_token->length); 1111 return (0); 1112 } 1113 1114 /* 1115 * NegHints ::= SEQUENCE { 1116 * hintName [0] GeneralString OPTIONAL, 1117 * hintAddress [1] OCTET STRING OPTIONAL 1118 * } 1119 */ 1120 1121 #define HOST_PREFIX "host@" 1122 #define HOST_PREFIX_LEN (sizeof(HOST_PREFIX) - 1) 1123 1124 static int 1125 make_NegHints(OM_uint32 *minor_status, 1126 gss_cred_id_t cred, gss_buffer_t *outbuf) 1127 { 1128 gss_buffer_desc hintNameBuf; 1129 gss_name_t hintName = GSS_C_NO_NAME; 1130 gss_name_t hintKerberosName; 1131 gss_OID hintNameType; 1132 OM_uint32 major_status; 1133 OM_uint32 minor; 1134 unsigned int tlen = 0; 1135 unsigned int hintNameSize = 0; 1136 unsigned int negHintsSize = 0; 1137 unsigned char *ptr; 1138 unsigned char *t; 1139 1140 *outbuf = GSS_C_NO_BUFFER; 1141 1142 if (cred != GSS_C_NO_CREDENTIAL) { 1143 major_status = gss_inquire_cred(minor_status, 1144 cred, 1145 &hintName, 1146 NULL, 1147 NULL, 1148 NULL); 1149 if (major_status != GSS_S_COMPLETE) 1150 return (major_status); 1151 } 1152 1153 if (hintName == GSS_C_NO_NAME) { 1154 krb5_error_code code; 1155 krb5int_access kaccess; 1156 char hostname[HOST_PREFIX_LEN + MAXHOSTNAMELEN + 1] = HOST_PREFIX; 1157 1158 code = krb5int_accessor(&kaccess, KRB5INT_ACCESS_VERSION); 1159 if (code != 0) { 1160 *minor_status = code; 1161 return (GSS_S_FAILURE); 1162 } 1163 1164 /* this breaks mutual authentication but Samba relies on it */ 1165 code = (*kaccess.clean_hostname)(NULL, NULL, 1166 &hostname[HOST_PREFIX_LEN], 1167 MAXHOSTNAMELEN); 1168 if (code != 0) { 1169 *minor_status = code; 1170 return (GSS_S_FAILURE); 1171 } 1172 1173 major_status = gss_import_name(minor_status, 1174 &hintNameBuf, 1175 GSS_C_NT_HOSTBASED_SERVICE, 1176 &hintName); 1177 if (major_status != GSS_S_COMPLETE) { 1178 return (major_status); 1179 } 1180 } 1181 1182 hintNameBuf.value = NULL; 1183 hintNameBuf.length = 0; 1184 1185 major_status = gss_canonicalize_name(minor_status, 1186 hintName, 1187 (gss_OID)&gss_mech_krb5_oid, 1188 &hintKerberosName); 1189 if (major_status != GSS_S_COMPLETE) { 1190 gss_release_name(&minor, &hintName); 1191 return (major_status); 1192 } 1193 gss_release_name(&minor, &hintName); 1194 1195 major_status = gss_display_name(minor_status, 1196 hintKerberosName, 1197 &hintNameBuf, 1198 &hintNameType); 1199 if (major_status != GSS_S_COMPLETE) { 1200 gss_release_name(&minor, &hintName); 1201 return (major_status); 1202 } 1203 gss_release_name(&minor, &hintKerberosName); 1204 1205 /* 1206 * Now encode the name hint into a NegHints ASN.1 type 1207 */ 1208 major_status = GSS_S_FAILURE; 1209 1210 /* Length of DER encoded GeneralString */ 1211 tlen = 1 + gssint_der_length_size(hintNameBuf.length) + 1212 hintNameBuf.length; 1213 hintNameSize = tlen; 1214 1215 /* Length of DER encoded hintName */ 1216 tlen += 1 + gssint_der_length_size(hintNameSize); 1217 negHintsSize = tlen; 1218 1219 t = (unsigned char *)malloc(tlen); 1220 if (t == NULL) { 1221 *minor_status = ENOMEM; 1222 goto errout; 1223 } 1224 1225 ptr = t; 1226 1227 *ptr++ = CONTEXT | 0x00; /* hintName identifier */ 1228 if (gssint_put_der_length(hintNameSize, 1229 &ptr, tlen - (int)(ptr-t))) 1230 goto errout; 1231 1232 *ptr++ = GENERAL_STRING; 1233 if (gssint_put_der_length(hintNameBuf.length, 1234 &ptr, tlen - (int)(ptr-t))) 1235 goto errout; 1236 1237 memcpy(ptr, hintNameBuf.value, hintNameBuf.length); 1238 ptr += hintNameBuf.length; 1239 1240 *outbuf = (gss_buffer_t)malloc(sizeof(gss_buffer_desc)); 1241 if (*outbuf == NULL) { 1242 *minor_status = ENOMEM; 1243 goto errout; 1244 } 1245 (*outbuf)->value = (void *)t; 1246 (*outbuf)->length = ptr - t; 1247 1248 t = NULL; /* don't free */ 1249 1250 *minor_status = 0; 1251 major_status = GSS_S_COMPLETE; 1252 1253 errout: 1254 if (t != NULL) { 1255 free(t); 1256 } 1257 1258 gss_release_buffer(&minor, &hintNameBuf); 1259 return (major_status); 1260 } 1261 1262 static OM_uint32 1263 acc_ctx_hints(OM_uint32 *minor_status, 1264 gss_ctx_id_t *ctx, 1265 gss_cred_id_t cred, 1266 gss_buffer_t *mechListMIC, 1267 OM_uint32 *negState, 1268 send_token_flag *return_token) 1269 { 1270 OM_uint32 tmpmin, ret; 1271 gss_OID_set supported_mechSet; 1272 spnego_gss_ctx_id_t sc = NULL; 1273 1274 *mechListMIC = GSS_C_NO_BUFFER; 1275 supported_mechSet = GSS_C_NO_OID_SET; 1276 *return_token = ERROR_TOKEN_SEND; 1277 *negState = REJECT; 1278 *minor_status = 0; 1279 1280 *ctx = GSS_C_NO_CONTEXT; 1281 ret = GSS_S_DEFECTIVE_TOKEN; 1282 1283 if (cred != GSS_C_NO_CREDENTIAL) { 1284 ret = gss_inquire_cred(minor_status, cred, NULL, NULL, 1285 NULL, &supported_mechSet); 1286 if (ret != GSS_S_COMPLETE) { 1287 *return_token = NO_TOKEN_SEND; 1288 goto cleanup; 1289 } 1290 } else { 1291 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, 1292 GSS_C_ACCEPT, NULL, 1293 &supported_mechSet); 1294 if (ret != GSS_S_COMPLETE) { 1295 *return_token = NO_TOKEN_SEND; 1296 goto cleanup; 1297 } 1298 } 1299 1300 ret = make_NegHints(minor_status, cred, mechListMIC); 1301 if (ret != GSS_S_COMPLETE) { 1302 *return_token = NO_TOKEN_SEND; 1303 goto cleanup; 1304 } 1305 1306 /* 1307 * Select the best match between the list of mechs 1308 * that the initiator requested and the list that 1309 * the acceptor will support. 1310 */ 1311 sc = create_spnego_ctx(); 1312 if (sc == NULL) { 1313 ret = GSS_S_FAILURE; 1314 *return_token = NO_TOKEN_SEND; 1315 goto cleanup; 1316 } 1317 if (put_mech_set(supported_mechSet, &sc->DER_mechTypes) < 0) { 1318 ret = GSS_S_FAILURE; 1319 *return_token = NO_TOKEN_SEND; 1320 goto cleanup; 1321 } 1322 sc->internal_mech = GSS_C_NO_OID; 1323 1324 *negState = ACCEPT_INCOMPLETE; 1325 *return_token = INIT_TOKEN_SEND; 1326 sc->firstpass = 1; 1327 *ctx = (gss_ctx_id_t)sc; 1328 ret = GSS_S_COMPLETE; 1329 1330 cleanup: 1331 gss_release_oid_set(&tmpmin, &supported_mechSet); 1332 return ret; 1333 } 1334 1335 /* 1336 * Set negState to REJECT if the token is defective, else 1337 * ACCEPT_INCOMPLETE or REQUEST_MIC, depending on whether initiator's 1338 * preferred mechanism is supported. 1339 */ 1340 static OM_uint32 1341 acc_ctx_new(OM_uint32 *minor_status, 1342 gss_buffer_t buf, 1343 gss_ctx_id_t *ctx, 1344 gss_cred_id_t cred, 1345 gss_buffer_t *mechToken, 1346 gss_buffer_t *mechListMIC, 1347 OM_uint32 *negState, 1348 send_token_flag *return_token) 1349 { 1350 OM_uint32 tmpmin, ret, req_flags; 1351 gss_OID_set supported_mechSet, mechTypes; 1352 gss_buffer_desc der_mechTypes; 1353 gss_OID mech_wanted; 1354 spnego_gss_ctx_id_t sc = NULL; 1355 1356 *ctx = GSS_C_NO_CONTEXT; 1357 1358 ret = GSS_S_DEFECTIVE_TOKEN; 1359 der_mechTypes.length = 0; 1360 der_mechTypes.value = NULL; 1361 *mechToken = *mechListMIC = GSS_C_NO_BUFFER; 1362 supported_mechSet = mechTypes = GSS_C_NO_OID_SET; 1363 *return_token = ERROR_TOKEN_SEND; 1364 *negState = REJECT; 1365 *minor_status = 0; 1366 1367 ret = get_negTokenInit(minor_status, buf, &der_mechTypes, 1368 &mechTypes, &req_flags, 1369 mechToken, mechListMIC); 1370 if (ret != GSS_S_COMPLETE) { 1371 goto cleanup; 1372 } 1373 if (cred != GSS_C_NO_CREDENTIAL) { 1374 ret = gss_inquire_cred(minor_status, cred, NULL, NULL, 1375 NULL, &supported_mechSet); 1376 if (ret != GSS_S_COMPLETE) { 1377 *return_token = NO_TOKEN_SEND; 1378 goto cleanup; 1379 } 1380 } else { 1381 ret = get_available_mechs(minor_status, GSS_C_NO_NAME, 1382 GSS_C_ACCEPT, NULL, 1383 &supported_mechSet); 1384 if (ret != GSS_S_COMPLETE) { 1385 *return_token = NO_TOKEN_SEND; 1386 goto cleanup; 1387 } 1388 } 1389 /* 1390 * Select the best match between the list of mechs 1391 * that the initiator requested and the list that 1392 * the acceptor will support. 1393 */ 1394 mech_wanted = negotiate_mech_type(minor_status, 1395 supported_mechSet, 1396 mechTypes, 1397 negState); 1398 if (*negState == REJECT) { 1399 ret = GSS_S_BAD_MECH; 1400 goto cleanup; 1401 } 1402 sc = (spnego_gss_ctx_id_t)*ctx; 1403 if (sc != NULL) { 1404 gss_release_buffer(&tmpmin, &sc->DER_mechTypes); 1405 assert(mech_wanted != GSS_C_NO_OID); 1406 } else 1407 sc = create_spnego_ctx(); 1408 if (sc == NULL) { 1409 ret = GSS_S_FAILURE; 1410 *return_token = NO_TOKEN_SEND; 1411 generic_gss_release_oid(&tmpmin, &mech_wanted); 1412 goto cleanup; 1413 } 1414 sc->internal_mech = mech_wanted; 1415 sc->DER_mechTypes = der_mechTypes; 1416 der_mechTypes.length = 0; 1417 der_mechTypes.value = NULL; 1418 1419 if (*negState == REQUEST_MIC) 1420 sc->mic_reqd = 1; 1421 1422 *return_token = INIT_TOKEN_SEND; 1423 sc->firstpass = 1; 1424 *ctx = (gss_ctx_id_t)sc; 1425 ret = GSS_S_COMPLETE; 1426 cleanup: 1427 gss_release_oid_set(&tmpmin, &mechTypes); 1428 gss_release_oid_set(&tmpmin, &supported_mechSet); 1429 if (der_mechTypes.length != 0) 1430 gss_release_buffer(&tmpmin, &der_mechTypes); 1431 return ret; 1432 } 1433 1434 static OM_uint32 1435 acc_ctx_cont(OM_uint32 *minstat, 1436 gss_buffer_t buf, 1437 gss_ctx_id_t *ctx, 1438 gss_buffer_t *responseToken, 1439 gss_buffer_t *mechListMIC, 1440 OM_uint32 *negState, 1441 send_token_flag *return_token) 1442 { 1443 OM_uint32 ret, tmpmin; 1444 gss_OID supportedMech; 1445 spnego_gss_ctx_id_t sc; 1446 unsigned int len; 1447 unsigned char *ptr, *bufstart; 1448 1449 sc = (spnego_gss_ctx_id_t)*ctx; 1450 ret = GSS_S_DEFECTIVE_TOKEN; 1451 *negState = REJECT; 1452 *minstat = 0; 1453 supportedMech = GSS_C_NO_OID; 1454 *return_token = ERROR_TOKEN_SEND; 1455 *responseToken = *mechListMIC = GSS_C_NO_BUFFER; 1456 1457 ptr = bufstart = buf->value; 1458 #define REMAIN (buf->length - (ptr - bufstart)) 1459 if (REMAIN > INT_MAX) 1460 return GSS_S_DEFECTIVE_TOKEN; 1461 1462 /* 1463 * Attempt to work with old Sun SPNEGO. 1464 */ 1465 if (*ptr == HEADER_ID) { 1466 ret = g_verify_token_header(gss_mech_spnego, 1467 &len, &ptr, 0, REMAIN); 1468 if (ret) { 1469 *minstat = ret; 1470 return GSS_S_DEFECTIVE_TOKEN; 1471 } 1472 } 1473 if (*ptr != (CONTEXT | 0x01)) { 1474 return GSS_S_DEFECTIVE_TOKEN; 1475 } 1476 ret = get_negTokenResp(minstat, ptr, REMAIN, 1477 negState, &supportedMech, 1478 responseToken, mechListMIC); 1479 if (ret != GSS_S_COMPLETE) 1480 goto cleanup; 1481 1482 if (*responseToken == GSS_C_NO_BUFFER && 1483 *mechListMIC == GSS_C_NO_BUFFER) { 1484 1485 ret = GSS_S_DEFECTIVE_TOKEN; 1486 goto cleanup; 1487 } 1488 if (supportedMech != GSS_C_NO_OID) { 1489 ret = GSS_S_DEFECTIVE_TOKEN; 1490 goto cleanup; 1491 } 1492 sc->firstpass = 0; 1493 *negState = ACCEPT_INCOMPLETE; 1494 *return_token = CONT_TOKEN_SEND; 1495 cleanup: 1496 if (supportedMech != GSS_C_NO_OID) { 1497 generic_gss_release_oid(&tmpmin, &supportedMech); 1498 } 1499 return ret; 1500 #undef REMAIN 1501 } 1502 1503 /* 1504 * Verify that mech OID is either exactly the same as the negotiated 1505 * mech OID, or is a mech OID supported by the negotiated mech. MS 1506 * implementations can list a most preferred mech using an incorrect 1507 * krb5 OID while emitting a krb5 initiator mech token having the 1508 * correct krb5 mech OID. 1509 */ 1510 static OM_uint32 1511 acc_ctx_vfy_oid(OM_uint32 *minor_status, 1512 spnego_gss_ctx_id_t sc, gss_OID mechoid, 1513 OM_uint32 *negState, send_token_flag *tokflag) 1514 { 1515 OM_uint32 ret, tmpmin; 1516 gss_mechanism mech = NULL; 1517 gss_OID_set mech_set = GSS_C_NO_OID_SET; 1518 int present = 0; 1519 1520 if (g_OID_equal(sc->internal_mech, mechoid)) 1521 return GSS_S_COMPLETE; 1522 1523 /* 1524 * SUNW17PACresync 1525 * If both mechs are kerb, we are done. 1526 */ 1527 if (is_kerb_mech(mechoid) && is_kerb_mech(sc->internal_mech)) { 1528 return GSS_S_COMPLETE; 1529 } 1530 1531 mech = gssint_get_mechanism(mechoid); 1532 if (mech == NULL || mech->gss_indicate_mechs == NULL) { 1533 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 1534 map_errcode(minor_status); 1535 *negState = REJECT; 1536 *tokflag = ERROR_TOKEN_SEND; 1537 return GSS_S_BAD_MECH; 1538 } 1539 ret = mech->gss_indicate_mechs(mech->context, minor_status, &mech_set); 1540 if (ret != GSS_S_COMPLETE) { 1541 *tokflag = NO_TOKEN_SEND; 1542 map_error(minor_status, mech); 1543 goto cleanup; 1544 } 1545 ret = gss_test_oid_set_member(minor_status, sc->internal_mech, 1546 mech_set, &present); 1547 if (ret != GSS_S_COMPLETE) 1548 goto cleanup; 1549 if (!present) { 1550 *minor_status = ERR_SPNEGO_NEGOTIATION_FAILED; 1551 map_errcode(minor_status); 1552 *negState = REJECT; 1553 *tokflag = ERROR_TOKEN_SEND; 1554 ret = GSS_S_BAD_MECH; 1555 } 1556 cleanup: 1557 gss_release_oid_set(&tmpmin, &mech_set); 1558 return ret; 1559 } 1560 #ifndef LEAN_CLIENT 1561 /* 1562 * Wrap call to gss_accept_sec_context() and update state 1563 * accordingly. 1564 */ 1565 static OM_uint32 1566 acc_ctx_call_acc(OM_uint32 *minor_status, spnego_gss_ctx_id_t sc, 1567 gss_cred_id_t cred, gss_buffer_t mechtok_in, 1568 gss_OID *mech_type, gss_buffer_t mechtok_out, 1569 OM_uint32 *ret_flags, OM_uint32 *time_rec, 1570 gss_cred_id_t *delegated_cred_handle, 1571 OM_uint32 *negState, send_token_flag *tokflag) 1572 { 1573 OM_uint32 ret; 1574 gss_OID_desc mechoid; 1575 1576 if (sc->ctx_handle == GSS_C_NO_CONTEXT) { 1577 /* 1578 * mechoid is an alias; don't free it. 1579 */ 1580 ret = gssint_get_mech_type(&mechoid, mechtok_in); 1581 if (ret != GSS_S_COMPLETE) { 1582 *tokflag = NO_TOKEN_SEND; 1583 return ret; 1584 } 1585 ret = acc_ctx_vfy_oid(minor_status, sc, &mechoid, 1586 negState, tokflag); 1587 if (ret != GSS_S_COMPLETE) 1588 return ret; 1589 } 1590 1591 ret = gss_accept_sec_context(minor_status, 1592 &sc->ctx_handle, 1593 cred, 1594 mechtok_in, 1595 GSS_C_NO_CHANNEL_BINDINGS, 1596 &sc->internal_name, 1597 mech_type, 1598 mechtok_out, 1599 &sc->ctx_flags, 1600 time_rec, 1601 delegated_cred_handle); 1602 1603 if (ret == GSS_S_COMPLETE) { 1604 #ifdef MS_BUG_TEST 1605 /* 1606 * Force MIC to be not required even if we previously 1607 * requested a MIC. 1608 */ 1609 char *envstr = getenv("MS_FORCE_NO_MIC"); 1610 1611 if (envstr != NULL && strcmp(envstr, "1") == 0 && 1612 !(sc->ctx_flags & GSS_C_MUTUAL_FLAG) && 1613 sc->mic_reqd) { 1614 1615 sc->mic_reqd = 0; 1616 } 1617 #endif 1618 sc->mech_complete = 1; 1619 if (ret_flags != NULL) 1620 *ret_flags = sc->ctx_flags; 1621 1622 if (!sc->mic_reqd) { 1623 *negState = ACCEPT_COMPLETE; 1624 ret = GSS_S_COMPLETE; 1625 } else { 1626 ret = GSS_S_CONTINUE_NEEDED; 1627 } 1628 } else if (ret != GSS_S_CONTINUE_NEEDED) { 1629 *negState = REJECT; 1630 *tokflag = ERROR_TOKEN_SEND; 1631 } 1632 return ret; 1633 } 1634 1635 /*ARGSUSED*/ 1636 OM_uint32 1637 glue_spnego_gss_accept_sec_context( 1638 void *context, 1639 OM_uint32 *minor_status, 1640 gss_ctx_id_t *context_handle, 1641 gss_cred_id_t verifier_cred_handle, 1642 gss_buffer_t input_token, 1643 gss_channel_bindings_t input_chan_bindings, 1644 gss_name_t *src_name, 1645 gss_OID *mech_type, 1646 gss_buffer_t output_token, 1647 OM_uint32 *ret_flags, 1648 OM_uint32 *time_rec, 1649 gss_cred_id_t *delegated_cred_handle) 1650 { 1651 return(spnego_gss_accept_sec_context( 1652 minor_status, 1653 context_handle, 1654 verifier_cred_handle, 1655 input_token, 1656 input_chan_bindings, 1657 src_name, 1658 mech_type, 1659 output_token, 1660 ret_flags, 1661 time_rec, 1662 delegated_cred_handle)); 1663 } 1664 1665 /*ARGSUSED*/ 1666 OM_uint32 1667 spnego_gss_accept_sec_context( 1668 OM_uint32 *minor_status, 1669 gss_ctx_id_t *context_handle, 1670 gss_cred_id_t verifier_cred_handle, 1671 gss_buffer_t input_token, 1672 gss_channel_bindings_t input_chan_bindings, 1673 gss_name_t *src_name, 1674 gss_OID *mech_type, 1675 gss_buffer_t output_token, 1676 OM_uint32 *ret_flags, 1677 OM_uint32 *time_rec, 1678 gss_cred_id_t *delegated_cred_handle) 1679 { 1680 OM_uint32 ret, tmpmin, negState; 1681 send_token_flag return_token; 1682 gss_buffer_t mechtok_in, mic_in, mic_out; 1683 gss_buffer_desc mechtok_out = GSS_C_EMPTY_BUFFER; 1684 spnego_gss_ctx_id_t sc = NULL; 1685 OM_uint32 mechstat = GSS_S_FAILURE; 1686 int sendTokenInit = 0; 1687 1688 mechtok_in = mic_in = mic_out = GSS_C_NO_BUFFER; 1689 1690 if (minor_status != NULL) 1691 *minor_status = 0; 1692 if (output_token != GSS_C_NO_BUFFER) { 1693 output_token->length = 0; 1694 output_token->value = NULL; 1695 } 1696 1697 1698 if (minor_status == NULL || 1699 output_token == GSS_C_NO_BUFFER || 1700 context_handle == NULL) { 1701 return GSS_S_CALL_INACCESSIBLE_WRITE; 1702 } 1703 1704 if (input_token == GSS_C_NO_BUFFER) { 1705 return GSS_S_CALL_INACCESSIBLE_READ; 1706 } 1707 1708 sc = (spnego_gss_ctx_id_t)*context_handle; 1709 if (sc == NULL || sc->internal_mech == GSS_C_NO_OID) { 1710 if (src_name != NULL) 1711 *src_name = GSS_C_NO_NAME; 1712 if (mech_type != NULL) 1713 *mech_type = GSS_C_NO_OID; 1714 if (time_rec != NULL) 1715 *time_rec = 0; 1716 if (ret_flags != NULL) 1717 *ret_flags = 0; 1718 if (delegated_cred_handle != NULL) 1719 *delegated_cred_handle = GSS_C_NO_CREDENTIAL; 1720 if (input_token->length == 0) { 1721 sendTokenInit = 1; 1722 ret = acc_ctx_hints(minor_status, 1723 context_handle, 1724 verifier_cred_handle, 1725 &mic_out, 1726 &negState, 1727 &return_token); 1728 if (ret != GSS_S_COMPLETE) 1729 goto cleanup; 1730 ret = GSS_S_CONTINUE_NEEDED; 1731 } else { 1732 /* Can set negState to REQUEST_MIC */ 1733 ret = acc_ctx_new(minor_status, input_token, 1734 context_handle, verifier_cred_handle, 1735 &mechtok_in, &mic_in, 1736 &negState, &return_token); 1737 if (ret != GSS_S_COMPLETE) 1738 goto cleanup; 1739 ret = GSS_S_CONTINUE_NEEDED; 1740 } 1741 } else { 1742 /* Can set negState to ACCEPT_INCOMPLETE */ 1743 ret = acc_ctx_cont(minor_status, input_token, 1744 context_handle, &mechtok_in, 1745 &mic_in, &negState, &return_token); 1746 if (ret != GSS_S_COMPLETE) 1747 goto cleanup; 1748 ret = GSS_S_CONTINUE_NEEDED; 1749 } 1750 1751 sc = (spnego_gss_ctx_id_t)*context_handle; 1752 /* 1753 * Handle mechtok_in and mic_in only if they are 1754 * present in input_token. If neither is present, whether 1755 * this is an error depends on whether this is the first 1756 * round-trip. RET is set to a default value according to 1757 * whether it is the first round-trip. 1758 */ 1759 mechstat = GSS_S_FAILURE; 1760 if (negState != REQUEST_MIC && mechtok_in != GSS_C_NO_BUFFER) { 1761 ret = acc_ctx_call_acc(minor_status, sc, 1762 verifier_cred_handle, mechtok_in, 1763 mech_type, &mechtok_out, 1764 ret_flags, time_rec, 1765 delegated_cred_handle, 1766 &negState, &return_token); 1767 } else if (negState == REQUEST_MIC) { 1768 mechstat = GSS_S_CONTINUE_NEEDED; 1769 } 1770 1771 if (!HARD_ERROR(ret) && sc->mech_complete && 1772 (sc->ctx_flags & GSS_C_INTEG_FLAG)) { 1773 1774 ret = handle_mic(minor_status, mic_in, 1775 (mechtok_out.length != 0), 1776 sc, &mic_out, 1777 &negState, &return_token); 1778 } 1779 1780 cleanup: 1781 if (return_token != NO_TOKEN_SEND && return_token != CHECK_MIC) { 1782 /* For acceptor-sends-first send a tokenInit */ 1783 int tmpret; 1784 1785 assert(sc != NULL); 1786 1787 if (sendTokenInit) { 1788 tmpret = make_spnego_tokenInit_msg(sc, 1789 1, 1790 mic_out, 1791 0, 1792 GSS_C_NO_BUFFER, 1793 return_token, 1794 output_token); 1795 } else { 1796 tmpret = make_spnego_tokenTarg_msg(negState, sc->internal_mech, 1797 &mechtok_out, mic_out, 1798 return_token, 1799 output_token); 1800 } 1801 if (tmpret < 0) 1802 ret = GSS_S_FAILURE; 1803 } 1804 if (ret == GSS_S_COMPLETE) { 1805 *context_handle = (gss_ctx_id_t)sc->ctx_handle; 1806 if (sc->internal_name != GSS_C_NO_NAME && 1807 src_name != NULL) { 1808 *src_name = sc->internal_name; 1809 } 1810 release_spnego_ctx(&sc); 1811 } 1812 gss_release_buffer(&tmpmin, &mechtok_out); 1813 if (mechtok_in != GSS_C_NO_BUFFER) { 1814 gss_release_buffer(&tmpmin, mechtok_in); 1815 free(mechtok_in); 1816 } 1817 if (mic_in != GSS_C_NO_BUFFER) { 1818 gss_release_buffer(&tmpmin, mic_in); 1819 free(mic_in); 1820 } 1821 if (mic_out != GSS_C_NO_BUFFER) { 1822 gss_release_buffer(&tmpmin, mic_out); 1823 free(mic_out); 1824 } 1825 return ret; 1826 } 1827 #endif /* LEAN_CLIENT */ 1828 1829 /*ARGSUSED*/ 1830 OM_uint32 1831 glue_spnego_gss_display_status( 1832 void *context, 1833 OM_uint32 *minor_status, 1834 OM_uint32 status_value, 1835 int status_type, 1836 gss_OID mech_type, 1837 OM_uint32 *message_context, 1838 gss_buffer_t status_string) 1839 { 1840 return (spnego_gss_display_status(minor_status, 1841 status_value, 1842 status_type, 1843 mech_type, 1844 message_context, 1845 status_string)); 1846 } 1847 1848 /*ARGSUSED*/ 1849 OM_uint32 1850 spnego_gss_display_status( 1851 OM_uint32 *minor_status, 1852 OM_uint32 status_value, 1853 int status_type, 1854 gss_OID mech_type, 1855 OM_uint32 *message_context, 1856 gss_buffer_t status_string) 1857 { 1858 dsyslog("Entering display_status\n"); 1859 1860 *message_context = 0; 1861 switch (status_value) { 1862 case ERR_SPNEGO_NO_MECHS_AVAILABLE: 1863 /* CSTYLED */ 1864 *status_string = make_err_msg("SPNEGO cannot find mechanisms to negotiate"); 1865 break; 1866 case ERR_SPNEGO_NO_CREDS_ACQUIRED: 1867 /* CSTYLED */ 1868 *status_string = make_err_msg("SPNEGO failed to acquire creds"); 1869 break; 1870 case ERR_SPNEGO_NO_MECH_FROM_ACCEPTOR: 1871 /* CSTYLED */ 1872 *status_string = make_err_msg("SPNEGO acceptor did not select a mechanism"); 1873 break; 1874 case ERR_SPNEGO_NEGOTIATION_FAILED: 1875 /* CSTYLED */ 1876 *status_string = make_err_msg("SPNEGO failed to negotiate a mechanism"); 1877 break; 1878 case ERR_SPNEGO_NO_TOKEN_FROM_ACCEPTOR: 1879 /* CSTYLED */ 1880 *status_string = make_err_msg("SPNEGO acceptor did not return a valid token"); 1881 break; 1882 default: 1883 status_string->length = 0; 1884 status_string->value = ""; 1885 break; 1886 } 1887 1888 dsyslog("Leaving display_status\n"); 1889 return (GSS_S_COMPLETE); 1890 } 1891 1892 /*ARGSUSED*/ 1893 OM_uint32 1894 glue_spnego_gss_import_name( 1895 void *context, 1896 OM_uint32 *minor_status, 1897 gss_buffer_t input_name_buffer, 1898 gss_OID input_name_type, 1899 gss_name_t *output_name) 1900 { 1901 return(spnego_gss_import_name(minor_status, 1902 input_name_buffer, 1903 input_name_type, 1904 output_name)); 1905 } 1906 1907 /*ARGSUSED*/ 1908 OM_uint32 1909 spnego_gss_import_name( 1910 OM_uint32 *minor_status, 1911 gss_buffer_t input_name_buffer, 1912 gss_OID input_name_type, 1913 gss_name_t *output_name) 1914 { 1915 OM_uint32 status; 1916 1917 dsyslog("Entering import_name\n"); 1918 1919 status = gss_import_name(minor_status, input_name_buffer, 1920 input_name_type, output_name); 1921 1922 dsyslog("Leaving import_name\n"); 1923 return (status); 1924 } 1925 1926 /*ARGSUSED*/ 1927 OM_uint32 1928 glue_spnego_gss_release_name( 1929 void *context, 1930 OM_uint32 *minor_status, 1931 gss_name_t *input_name) 1932 { 1933 return(spnego_gss_release_name(minor_status, input_name)); 1934 } 1935 1936 /*ARGSUSED*/ 1937 OM_uint32 1938 spnego_gss_release_name( 1939 OM_uint32 *minor_status, 1940 gss_name_t *input_name) 1941 { 1942 OM_uint32 status; 1943 1944 dsyslog("Entering release_name\n"); 1945 1946 status = gss_release_name(minor_status, input_name); 1947 1948 dsyslog("Leaving release_name\n"); 1949 return (status); 1950 } 1951 1952 /*ARGSUSED*/ 1953 OM_uint32 1954 glue_spnego_gss_compare_name( 1955 void *context, 1956 OM_uint32 *minor_status, 1957 const gss_name_t name1, 1958 const gss_name_t name2, 1959 int *name_equal) 1960 { 1961 return(spnego_gss_compare_name(minor_status, 1962 name1, 1963 name2, 1964 name_equal)); 1965 } 1966 /*ARGSUSED*/ 1967 OM_uint32 1968 spnego_gss_compare_name( 1969 OM_uint32 *minor_status, 1970 const gss_name_t name1, 1971 const gss_name_t name2, 1972 int *name_equal) 1973 { 1974 OM_uint32 status = GSS_S_COMPLETE; 1975 dsyslog("Entering compare_name\n"); 1976 1977 status = gss_compare_name(minor_status, name1, name2, name_equal); 1978 1979 dsyslog("Leaving compare_name\n"); 1980 return (status); 1981 } 1982 1983 /*ARGSUSED*/ 1984 OM_uint32 1985 glue_spnego_gss_display_name( 1986 void *context, 1987 OM_uint32 *minor_status, 1988 gss_name_t input_name, 1989 gss_buffer_t output_name_buffer, 1990 gss_OID *output_name_type) 1991 { 1992 return(spnego_gss_display_name( 1993 minor_status, 1994 input_name, 1995 output_name_buffer, 1996 output_name_type)); 1997 } 1998 1999 /*ARGSUSED*/ 2000 OM_uint32 2001 spnego_gss_display_name( 2002 OM_uint32 *minor_status, 2003 gss_name_t input_name, 2004 gss_buffer_t output_name_buffer, 2005 gss_OID *output_name_type) 2006 { 2007 OM_uint32 status = GSS_S_COMPLETE; 2008 dsyslog("Entering display_name\n"); 2009 2010 status = gss_display_name(minor_status, input_name, 2011 output_name_buffer, output_name_type); 2012 2013 dsyslog("Leaving display_name\n"); 2014 return (status); 2015 } 2016 2017 2018 /*ARGSUSED*/ 2019 OM_uint32 2020 glue_spnego_gss_inquire_names_for_mech( 2021 void *context, 2022 OM_uint32 *minor_status, 2023 gss_OID mechanism, 2024 gss_OID_set *name_types) 2025 { 2026 return(spnego_gss_inquire_names_for_mech(minor_status, 2027 mechanism, 2028 name_types)); 2029 } 2030 /*ARGSUSED*/ 2031 OM_uint32 2032 spnego_gss_inquire_names_for_mech( 2033 OM_uint32 *minor_status, 2034 gss_OID mechanism, 2035 gss_OID_set *name_types) 2036 { 2037 OM_uint32 major, minor; 2038 2039 dsyslog("Entering inquire_names_for_mech\n"); 2040 /* 2041 * We only know how to handle our own mechanism. 2042 */ 2043 if ((mechanism != GSS_C_NULL_OID) && 2044 !g_OID_equal(gss_mech_spnego, mechanism)) { 2045 *minor_status = 0; 2046 return (GSS_S_FAILURE); 2047 } 2048 2049 major = gss_create_empty_oid_set(minor_status, name_types); 2050 if (major == GSS_S_COMPLETE) { 2051 /* Now add our members. */ 2052 if (((major = gss_add_oid_set_member(minor_status, 2053 (gss_OID) GSS_C_NT_USER_NAME, 2054 name_types)) == GSS_S_COMPLETE) && 2055 ((major = gss_add_oid_set_member(minor_status, 2056 (gss_OID) GSS_C_NT_MACHINE_UID_NAME, 2057 name_types)) == GSS_S_COMPLETE) && 2058 ((major = gss_add_oid_set_member(minor_status, 2059 (gss_OID) GSS_C_NT_STRING_UID_NAME, 2060 name_types)) == GSS_S_COMPLETE)) { 2061 major = gss_add_oid_set_member(minor_status, 2062 (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, 2063 name_types); 2064 } 2065 2066 if (major != GSS_S_COMPLETE) 2067 (void) gss_release_oid_set(&minor, name_types); 2068 } 2069 2070 dsyslog("Leaving inquire_names_for_mech\n"); 2071 return (major); 2072 } 2073 2074 OM_uint32 2075 spnego_gss_unwrap( 2076 OM_uint32 *minor_status, 2077 gss_ctx_id_t context_handle, 2078 gss_buffer_t input_message_buffer, 2079 gss_buffer_t output_message_buffer, 2080 int *conf_state, 2081 gss_qop_t *qop_state) 2082 { 2083 OM_uint32 ret; 2084 ret = gss_unwrap(minor_status, 2085 context_handle, 2086 input_message_buffer, 2087 output_message_buffer, 2088 conf_state, 2089 qop_state); 2090 2091 return (ret); 2092 } 2093 2094 OM_uint32 2095 spnego_gss_wrap( 2096 OM_uint32 *minor_status, 2097 gss_ctx_id_t context_handle, 2098 int conf_req_flag, 2099 gss_qop_t qop_req, 2100 gss_buffer_t input_message_buffer, 2101 int *conf_state, 2102 gss_buffer_t output_message_buffer) 2103 { 2104 OM_uint32 ret; 2105 ret = gss_wrap(minor_status, 2106 context_handle, 2107 conf_req_flag, 2108 qop_req, 2109 input_message_buffer, 2110 conf_state, 2111 output_message_buffer); 2112 2113 return (ret); 2114 } 2115 2116 OM_uint32 2117 spnego_gss_process_context_token( 2118 OM_uint32 *minor_status, 2119 const gss_ctx_id_t context_handle, 2120 const gss_buffer_t token_buffer) 2121 { 2122 OM_uint32 ret; 2123 ret = gss_process_context_token(minor_status, 2124 context_handle, 2125 token_buffer); 2126 2127 return (ret); 2128 } 2129 2130 OM_uint32 2131 glue_spnego_gss_delete_sec_context( 2132 void *context, 2133 OM_uint32 *minor_status, 2134 gss_ctx_id_t *context_handle, 2135 gss_buffer_t output_token) 2136 { 2137 return(spnego_gss_delete_sec_context(minor_status, 2138 context_handle, output_token)); 2139 } 2140 2141 OM_uint32 2142 spnego_gss_delete_sec_context( 2143 OM_uint32 *minor_status, 2144 gss_ctx_id_t *context_handle, 2145 gss_buffer_t output_token) 2146 { 2147 OM_uint32 ret = GSS_S_COMPLETE; 2148 spnego_gss_ctx_id_t *ctx = 2149 (spnego_gss_ctx_id_t *)context_handle; 2150 2151 if (context_handle == NULL) 2152 return (GSS_S_FAILURE); 2153 2154 /* 2155 * If this is still an SPNEGO mech, release it locally. 2156 */ 2157 if (*ctx != NULL && 2158 (*ctx)->magic_num == SPNEGO_MAGIC_ID) { 2159 (void) release_spnego_ctx(ctx); 2160 /* SUNW17PACresync - MIT 1.7 bug (and our fix) */ 2161 if (output_token) { 2162 output_token->length = 0; 2163 output_token->value = NULL; 2164 } 2165 } else { 2166 ret = gss_delete_sec_context(minor_status, 2167 context_handle, 2168 output_token); 2169 } 2170 2171 return (ret); 2172 } 2173 2174 #if 0 /* SUNW17PACresync */ 2175 OM_uint32 2176 glue_spnego_gss_context_time( 2177 void *context, 2178 OM_uint32 *minor_status, 2179 const gss_ctx_id_t context_handle, 2180 OM_uint32 *time_rec) 2181 { 2182 return(spnego_gss_context_time(minor_status, 2183 context_handle, 2184 time_rec)); 2185 } 2186 2187 OM_uint32 2188 spnego_gss_context_time( 2189 OM_uint32 *minor_status, 2190 const gss_ctx_id_t context_handle, 2191 OM_uint32 *time_rec) 2192 { 2193 OM_uint32 ret; 2194 ret = gss_context_time(minor_status, 2195 context_handle, 2196 time_rec); 2197 return (ret); 2198 } 2199 #endif 2200 2201 #ifndef LEAN_CLIENT 2202 OM_uint32 2203 glue_spnego_gss_export_sec_context( 2204 void *context, 2205 OM_uint32 *minor_status, 2206 gss_ctx_id_t *context_handle, 2207 gss_buffer_t interprocess_token) 2208 { 2209 return(spnego_gss_export_sec_context(minor_status, 2210 context_handle, 2211 interprocess_token)); 2212 } 2213 OM_uint32 2214 spnego_gss_export_sec_context( 2215 OM_uint32 *minor_status, 2216 gss_ctx_id_t *context_handle, 2217 gss_buffer_t interprocess_token) 2218 { 2219 OM_uint32 ret; 2220 ret = gss_export_sec_context(minor_status, 2221 context_handle, 2222 interprocess_token); 2223 return (ret); 2224 } 2225 2226 OM_uint32 2227 glue_spnego_gss_import_sec_context( 2228 void *context, 2229 OM_uint32 *minor_status, 2230 const gss_buffer_t interprocess_token, 2231 gss_ctx_id_t *context_handle) 2232 { 2233 return(spnego_gss_import_sec_context(minor_status, 2234 interprocess_token, 2235 context_handle)); 2236 } 2237 OM_uint32 2238 spnego_gss_import_sec_context( 2239 OM_uint32 *minor_status, 2240 const gss_buffer_t interprocess_token, 2241 gss_ctx_id_t *context_handle) 2242 { 2243 OM_uint32 ret; 2244 ret = gss_import_sec_context(minor_status, 2245 interprocess_token, 2246 context_handle); 2247 return (ret); 2248 } 2249 #endif /* LEAN_CLIENT */ 2250 2251 #if 0 /* SUNW17PACresync */ 2252 OM_uint32 2253 glue_spnego_gss_inquire_context( 2254 void *context, 2255 OM_uint32 *minor_status, 2256 const gss_ctx_id_t context_handle, 2257 gss_name_t *src_name, 2258 gss_name_t *targ_name, 2259 OM_uint32 *lifetime_rec, 2260 gss_OID *mech_type, 2261 OM_uint32 *ctx_flags, 2262 int *locally_initiated, 2263 int *opened) 2264 { 2265 return(spnego_gss_inquire_context( 2266 minor_status, 2267 context_handle, 2268 src_name, 2269 targ_name, 2270 lifetime_rec, 2271 mech_type, 2272 ctx_flags, 2273 locally_initiated, 2274 opened)); 2275 } 2276 2277 OM_uint32 2278 spnego_gss_inquire_context( 2279 OM_uint32 *minor_status, 2280 const gss_ctx_id_t context_handle, 2281 gss_name_t *src_name, 2282 gss_name_t *targ_name, 2283 OM_uint32 *lifetime_rec, 2284 gss_OID *mech_type, 2285 OM_uint32 *ctx_flags, 2286 int *locally_initiated, 2287 int *opened) 2288 { 2289 OM_uint32 ret = GSS_S_COMPLETE; 2290 2291 ret = gss_inquire_context(minor_status, 2292 context_handle, 2293 src_name, 2294 targ_name, 2295 lifetime_rec, 2296 mech_type, 2297 ctx_flags, 2298 locally_initiated, 2299 opened); 2300 2301 return (ret); 2302 } 2303 #endif 2304 2305 #if 0 /* SUNW17PACresync */ 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 OM_uint32 2324 spnego_gss_wrap_size_limit( 2325 OM_uint32 *minor_status, 2326 const gss_ctx_id_t context_handle, 2327 int conf_req_flag, 2328 gss_qop_t qop_req, 2329 OM_uint32 req_output_size, 2330 OM_uint32 *max_input_size) 2331 { 2332 OM_uint32 ret; 2333 ret = gss_wrap_size_limit(minor_status, 2334 context_handle, 2335 conf_req_flag, 2336 qop_req, 2337 req_output_size, 2338 max_input_size); 2339 return (ret); 2340 } 2341 #endif 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