1/* 2 * Copyright (c) 2004, PADL Software Pty Ltd. 3 * All rights reserved. 4 * 5 * Portions Copyright (c) 2009 Apple Inc. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * 3. Neither the name of PADL Software nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY PADL SOFTWARE AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL PADL SOFTWARE OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include "spnego_locl.h" 36 37/* 38 * Apparently Microsoft got the OID wrong, and used 39 * 1.2.840.48018.1.2.2 instead. We need both this and 40 * the correct Kerberos OID here in order to deal with 41 * this. Because this is manifest in SPNEGO only I'd 42 * prefer to deal with this here rather than inside the 43 * Kerberos mechanism. 44 */ 45gss_OID_desc _gss_spnego_mskrb_mechanism_oid_desc = 46 {9, rk_UNCONST("\x2a\x86\x48\x82\xf7\x12\x01\x02\x02")}; 47 48gss_OID_desc _gss_spnego_krb5_mechanism_oid_desc = 49 {9, rk_UNCONST("\x2a\x86\x48\x86\xf7\x12\x01\x02\x02")}; 50 51/* 52 * Allocate a SPNEGO context handle 53 */ 54OM_uint32 GSSAPI_CALLCONV 55_gss_spnego_alloc_sec_context (OM_uint32 * minor_status, 56 gss_ctx_id_t *context_handle) 57{ 58 gssspnego_ctx ctx; 59 60 ctx = calloc(1, sizeof(*ctx)); 61 if (ctx == NULL) { 62 *minor_status = ENOMEM; 63 return GSS_S_FAILURE; 64 } 65 66 ctx->NegTokenInit_mech_types.value = NULL; 67 ctx->NegTokenInit_mech_types.length = 0; 68 69 ctx->preferred_mech_type = GSS_C_NO_OID; 70 ctx->selected_mech_type = GSS_C_NO_OID; 71 ctx->negotiated_mech_type = GSS_C_NO_OID; 72 73 ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; 74 75 ctx->mech_src_name = GSS_C_NO_NAME; 76 77 ctx->flags.open = 0; 78 ctx->flags.local = 0; 79 ctx->flags.peer_require_mic = 0; 80 ctx->flags.require_mic = 0; 81 ctx->flags.verified_mic = 0; 82 83 HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex); 84 85 *context_handle = (gss_ctx_id_t)ctx; 86 87 return GSS_S_COMPLETE; 88} 89 90/* 91 * Free a SPNEGO context handle. The caller must have acquired 92 * the lock before this is called. 93 */ 94OM_uint32 GSSAPI_CALLCONV _gss_spnego_internal_delete_sec_context 95 (OM_uint32 *minor_status, 96 gss_ctx_id_t *context_handle, 97 gss_buffer_t output_token 98 ) 99{ 100 gssspnego_ctx ctx; 101 OM_uint32 ret, minor; 102 103 *minor_status = 0; 104 105 if (context_handle == NULL) { 106 return GSS_S_NO_CONTEXT; 107 } 108 109 if (output_token != GSS_C_NO_BUFFER) { 110 output_token->length = 0; 111 output_token->value = NULL; 112 } 113 114 ctx = (gssspnego_ctx)*context_handle; 115 *context_handle = GSS_C_NO_CONTEXT; 116 117 if (ctx == NULL) { 118 return GSS_S_NO_CONTEXT; 119 } 120 121 if (ctx->NegTokenInit_mech_types.value) 122 free(ctx->NegTokenInit_mech_types.value); 123 124 ctx->negotiated_mech_type = GSS_C_NO_OID; 125 ctx->selected_mech_type = GSS_C_NO_OID; 126 127 gss_release_name(&minor, &ctx->target_name); 128 gss_release_name(&minor, &ctx->mech_src_name); 129 130 if (ctx->negotiated_ctx_id != GSS_C_NO_CONTEXT) { 131 ret = gss_delete_sec_context(minor_status, 132 &ctx->negotiated_ctx_id, 133 NULL); 134 ctx->negotiated_ctx_id = GSS_C_NO_CONTEXT; 135 } else { 136 ret = GSS_S_COMPLETE; 137 } 138 139 HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); 140 HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); 141 142 free(ctx); 143 144 return ret; 145} 146 147void 148_gss_spnego_fixup_ntlm(gssspnego_ctx ctx) 149{ 150 if (gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) { 151 gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; 152 OM_uint32 junk; 153 gss_inquire_sec_context_by_oid(&junk, ctx->negotiated_ctx_id, 154 GSS_C_NTLM_RESET_KEYS, 155 &buffer_set); 156 gss_release_buffer_set(&junk, &buffer_set); 157 } 158} 159 160/* 161 * For compatability with the Windows SPNEGO implementation, the 162 * default is to ignore the mechListMIC unless CFX is used and 163 * a non-preferred mechanism was negotiated 164 */ 165 166int 167_gss_spnego_require_mechlist_mic(gssspnego_ctx ctx) 168{ 169 gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; 170 int require_mic; 171 OM_uint32 minor; 172 173 require_mic = 1; 174 175 /* Acceptor requested it: mandatory to honour */ 176 if (ctx->flags.peer_require_mic) 177 return 1; 178 179 /* Protocol can't handle mechListMIC (HTTP Negotiate) */ 180 if (ctx->flags.protocol_require_no_mic) 181 return 0; 182 183 /* 184 * Check whether peer indicated implicit support for updated SPNEGO 185 * (eg. in the Kerberos case by using CFX) 186 */ 187 if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id, 188 GSS_C_PEER_HAS_UPDATED_SPNEGO, 189 &buffer_set) == GSS_S_COMPLETE) { 190 require_mic = 1; 191 gss_release_buffer_set(&minor, &buffer_set); 192 } 193 194 /* 195 * Don't require mic for NTLM because 196 * - Windows servers to have negTokenResp.negResult set for the acceptor to send the mic. 197 * - SnowLeopard smb server can't handle it 198 * So if we are the initiator and using NTLM, don't send the acceptor status. 199 */ 200 if (ctx->flags.local && gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) 201 require_mic = 0; 202 203 /* Safe-to-omit MIC rules follow */ 204 205 if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) { 206 ctx->flags.safe_omit = 1; 207 } else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) && 208 gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) { 209 ctx->flags.safe_omit = 1; 210 } 211 212 return require_mic; 213} 214 215static int 216add_mech_type(gss_OID mech_type, 217 int includeMSCompatOID, 218 MechTypeList *mechtypelist) 219{ 220 MechType mech; 221 int ret; 222 223 if (includeMSCompatOID && 224 gss_oid_equal(mech_type, &_gss_spnego_krb5_mechanism_oid_desc)) { 225 ret = der_get_oid(_gss_spnego_mskrb_mechanism_oid_desc.elements, 226 _gss_spnego_mskrb_mechanism_oid_desc.length, 227 &mech, 228 NULL); 229 if (ret) 230 return ret; 231 ret = add_MechTypeList(mechtypelist, &mech); 232 free_MechType(&mech); 233 if (ret) 234 return ret; 235 } 236 ret = der_get_oid(mech_type->elements, mech_type->length, &mech, NULL); 237 if (ret) 238 return ret; 239 ret = add_MechTypeList(mechtypelist, &mech); 240 free_MechType(&mech); 241 return ret; 242} 243 244 245OM_uint32 GSSAPI_CALLCONV 246_gss_spnego_indicate_mechtypelist(OM_uint32 *minor_status, 247 gss_name_t target_name, 248 OM_uint32 (*func)(void *, gss_name_t, const gss_cred_id_t, gss_OID), 249 void *userctx, 250 int includeMSCompatOID, 251 const gss_cred_id_t cred_handle, 252 MechTypeList *mechtypelist, 253 gss_OID *preferred_mech) 254{ 255 gss_OID_set supported_mechs = GSS_C_NO_OID_SET; 256 gss_OID first_mech = GSS_C_NO_OID; 257 OM_uint32 ret, junk; 258 int present = 0; 259 size_t i; 260 261 mechtypelist->len = 0; 262 mechtypelist->val = NULL; 263 264 if (cred_handle) { 265 ret = gss_inquire_cred(minor_status, 266 cred_handle, 267 NULL, 268 NULL, 269 NULL, 270 &supported_mechs); 271 } else { 272 ret = gss_indicate_mechs(minor_status, &supported_mechs); 273 } 274 275 if (ret != GSS_S_COMPLETE) { 276 return ret; 277 } 278 279 if (supported_mechs->count == 0) { 280 *minor_status = ENOENT; 281 gss_release_oid_set(minor_status, &supported_mechs); 282 return GSS_S_FAILURE; 283 } 284 285 /* 286 * Propose Kerberos mech first if we have Kerberos credentials/supported mechs 287 */ 288 289 ret = gss_test_oid_set_member(&junk, GSS_KRB5_MECHANISM, supported_mechs, &present); 290 if (ret == GSS_S_COMPLETE && present) { 291 ret = (*func)(userctx, target_name, cred_handle, GSS_KRB5_MECHANISM); 292 if (ret == GSS_S_COMPLETE) { 293 ret = add_mech_type(GSS_KRB5_MECHANISM, 294 includeMSCompatOID, 295 mechtypelist); 296 if (!GSS_ERROR(ret)) { 297 if (includeMSCompatOID) 298 first_mech = &_gss_spnego_mskrb_mechanism_oid_desc; 299 else 300 first_mech = GSS_KRB5_MECHANISM; 301 } 302#ifdef __APPLE_PRIVATE__ 303 (void)add_mech_type(GSS_APPL_LKDC_SUPPORTED, 0, mechtypelist); 304#endif 305 } 306 } 307 ret = GSS_S_COMPLETE; 308 309 /* 310 * Now lets, check all other mechs 311 */ 312 313 for (i = 0; i < supported_mechs->count; i++) { 314 OM_uint32 subret; 315 if (gss_oid_equal(&supported_mechs->elements[i], GSS_SPNEGO_MECHANISM)) 316 continue; 317 if (gss_oid_equal(&supported_mechs->elements[i], GSS_KRB5_MECHANISM)) 318 continue; 319 if (gss_oid_equal(&supported_mechs->elements[i], GSS_NETLOGON_MECHANISM)) 320 continue; 321 322 subret = (*func)(userctx, target_name, cred_handle, &supported_mechs->elements[i]); 323 if (subret != GSS_S_COMPLETE) 324 continue; 325 326 ret = add_mech_type(&supported_mechs->elements[i], 327 includeMSCompatOID, 328 mechtypelist); 329 if (ret != 0) { 330 *minor_status = ret; 331 ret = GSS_S_FAILURE; 332 break; 333 } 334 if (first_mech == GSS_C_NO_OID) 335 first_mech = &supported_mechs->elements[i]; 336 } 337 338 if (mechtypelist->len == 0) { 339 gss_release_oid_set(minor_status, &supported_mechs); 340 *minor_status = 0; 341 return GSS_S_BAD_MECH; 342 } 343 344 if (preferred_mech != NULL) { 345 *preferred_mech = _gss_mg_support_mechanism(first_mech); 346 } 347 gss_release_oid_set(minor_status, &supported_mechs); 348 349 return ret; 350} 351 352/* 353 * 354 */ 355 356OM_uint32 357_gss_spnego_verify_mechtypes_mic(OM_uint32 *minor_status, 358 gssspnego_ctx ctx, 359 heim_octet_string *mic) 360{ 361 gss_buffer_desc mic_buf; 362 OM_uint32 major_status; 363 364 if (ctx->flags.verified_mic) { 365 /* This doesn't make sense, we've already verified it? */ 366 *minor_status = 0; 367 return GSS_S_DUPLICATE_TOKEN; 368 } 369 370 mic_buf.length = mic->length; 371 mic_buf.value = mic->data; 372 373 major_status = gss_verify_mic(minor_status, 374 ctx->negotiated_ctx_id, 375 &ctx->NegTokenInit_mech_types, 376 &mic_buf, 377 NULL); 378 if (major_status == GSS_S_UNAVAILABLE) { 379 _gss_mg_log(10, "mech doesn't support MIC, allowing anyway"); 380 } else if (major_status) { 381 return gss_mg_set_error_string(GSS_SPNEGO_MECHANISM, 382 GSS_S_DEFECTIVE_TOKEN, *minor_status, 383 "SPNEGO peer sent invalid mechListMIC"); 384 } 385 ctx->flags.verified_mic = 1; 386 387 *minor_status = 0; 388 389 return GSS_S_COMPLETE; 390} 391