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