1/*- 2 * Copyright (c) 2005 Doug Rabson 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 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * $FreeBSD: src/lib/libgssapi/gss_init_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $ 29 */ 30 31#include "mech_locl.h" 32#include <krb5.h> 33#include <gssapi_plugin.h> 34 35static gss_cred_id_t 36_gss_mech_cred_find(gss_cred_id_t cred_handle, gss_OID mech_type) 37{ 38 struct _gss_cred *cred = (struct _gss_cred *)cred_handle; 39 struct _gss_mechanism_cred *mc; 40 41 if (cred == NULL) 42 return GSS_C_NO_CREDENTIAL; 43 44 HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) { 45 if (gss_oid_equal(mech_type, mc->gmc_mech_oid)) 46 return mc->gmc_cred; 47 } 48 return GSS_C_NO_CREDENTIAL; 49} 50 51/* 52 * Plugin support to select credentials 53 */ 54 55struct iscrc { 56 gss_cred_id_t found; 57 unsigned long pluginflags; 58 OM_uint32 flags; 59 gss_name_t target; 60 gss_OID mech_type; 61 gss_cred_id_t initiator_cred_handle; 62}; 63 64static krb5_error_code 65replace_cred_fun(krb5_context context, 66 const void *plug, void *plugctx, void *userctx) 67{ 68 const gssapi_plugin_ftable *plugin = plug; 69 struct iscrc *ctx = userctx; 70 gss_name_t dupname = GSS_C_NO_NAME; 71 OM_uint32 maj_stat, junk; 72 73 if (ctx->found || plugin->isc_replace_cred == NULL) 74 return KRB5_PLUGIN_NO_HANDLE; 75 76 /* check if the plugin support required flags */ 77 if ((plugin->flags & ctx->pluginflags) != ctx->pluginflags) 78 return KRB5_PLUGIN_NO_HANDLE; 79 80 /* 81 * Duplicate name since the replace handler might ruin the name 82 * with canonicalization (gss_canonicalize_name). The name might 83 * be canonicalization differently when mech mech have access to 84 * theaccess to the peer/network. 85 */ 86 maj_stat = gss_duplicate_name(&junk, ctx->target, &dupname); 87 if (maj_stat != GSS_S_COMPLETE) 88 return KRB5_PLUGIN_NO_HANDLE; 89 90 _gss_mg_log(1, "gss_isc running plugin %s", plugin->name); 91 ctx->found = plugin->isc_replace_cred(dupname, ctx->mech_type, ctx->initiator_cred_handle, ctx->flags); 92 _gss_mg_log(1, "gss_isc plugin %s done (%s replacement)", 93 plugin->name, ctx->found ? "found" : "no"); 94 95 gss_release_name(&junk, &dupname); 96 97 if (ctx->found == NULL) 98 return KRB5_PLUGIN_NO_HANDLE; 99 100 _gss_mg_log_cred(1, (struct _gss_cred *)ctx->found, "gss_isc %s replace the credential to", plugin->name); 101 102 return 0; 103} 104 105static gss_cred_id_t 106check_replace_cred(OM_uint32 *minor_status, 107 gss_name_t target, 108 gss_OID mech_type, 109 gss_cred_id_t initiator_cred_handle) 110{ 111 krb5_context context; 112 krb5_error_code ret; 113 struct iscrc ctx; 114 115 _gss_mg_log(1, "gss_isc running replace plugins"); 116 117 _gss_load_plugins(); 118 119 ctx.found = GSS_C_NO_CREDENTIAL; 120 ctx.pluginflags = 0; 121 ctx.flags = 0; 122 ctx.target = target; 123 ctx.mech_type = mech_type; 124 ctx.initiator_cred_handle = initiator_cred_handle; 125 126 if (!krb5_homedir_access(NULL)) { 127 ctx.pluginflags |= GPT_SYSTEM_ONLY; 128 ctx.flags = GPT_IRC_F_SYSTEM_ONLY; 129 } 130 131 ret = krb5_init_context(&context); 132 if (ret) 133 return NULL; 134 135 krb5_plugin_run_f(context, "gss", 136 GSSAPI_PLUGIN, 137 GSSAPI_PLUGIN_VERSION_1, 138 0, &ctx, replace_cred_fun); 139 krb5_free_context(context); 140 return ctx.found; 141} 142 143static void 144log_init_sec_context(struct _gss_context *ctx, 145 struct _gss_name *target, 146 OM_uint32 req_flags, 147 struct _gss_cred *cred, 148 gss_OID mech_type, 149 gss_buffer_t input_token) 150{ 151 gssapi_mech_interface m; 152 153 if (ctx) 154 m = ctx->gc_mech; 155 else 156 m = __gss_get_mechanism(mech_type); 157 if (m == NULL) 158 return; 159 160 mech_type = &m->gm_mech_oid; 161 162 _gss_mg_log(1, "gss_isc: %s %sfirst flags %08x, %s cred, %stoken", 163 m->gm_name, 164 (ctx == NULL) ? "" : "not ", 165 req_flags, 166 (cred != NULL) ? "specific" : "default", 167 (input_token != NULL && input_token->length) ? "" : "no "); 168 169 _gss_mg_log_cred(1, cred, "gss_isc cred"); 170 171 /* print target name */ 172 _gss_mg_log_name(1, target, mech_type, "gss_isc: target"); 173} 174 175/** 176 * As the initiator build a context with an acceptor. 177 * 178 * This function is blocking and should not be used on threads blocking UI updates. 179 * 180 * Returns in the major 181 * - GSS_S_COMPLETE - if the context if build 182 * - GSS_S_CONTINUE_NEEDED - if the caller needs to continue another 183 * round of gss_i nit_sec_context 184 * - error code - any other error code 185 * 186 * @param minor_status minor status code. 187 * 188 * @param initiator_cred_handle the credential to use when building 189 * the context, if GSS_C_NO_CREDENTIAL is passed, the default 190 * credential for the mechanism will be used. 191 * 192 * @param context_handle a pointer to a context handle, will be 193 * returned as long as there is not an error. 194 * 195 * @param target_name the target name of acceptor, created using 196 * gss_import_name(). The name is can be of any name types the 197 * mechanism supports, check supported name types with 198 * gss_inquire_names_for_mech(). 199 * 200 * @param input_mech_type mechanism type to use, if GSS_C_NO_OID is 201 * used, Kerberos (GSS_KRB5_MECHANISM) will be tried. Other 202 * available mechanism are listed in the @ref gssapi_mechs_intro 203 * section. 204 * 205 * @param req_flags flags using when building the context, see @ref 206 * gssapi_context_flags 207 * 208 * @param time_req time requested this context should be valid in 209 * seconds, common used value is GSS_C_INDEFINITE 210 * 211 * @param input_chan_bindings Channel bindings used, if not exepected 212 * otherwise, used GSS_C_NO_CHANNEL_BINDINGS 213 * 214 * @param input_token input token sent from the acceptor, for the 215 * initial packet the buffer of { NULL, 0 } should be used. 216 * 217 * @param actual_mech_type the actual mech used, MUST NOT be freed 218 * since it pointing to static memory. 219 * 220 * @param output_token if there is an output token, regardless of 221 * complete, continue_needed, or error it should be sent to the 222 * acceptor 223 * 224 * @param ret_flags return what flags was negotitated, caller should 225 * check if they are accetable. For example, if 226 * GSS_C_MUTUAL_FLAG was negotiated with the acceptor or not. 227 * 228 * @param time_rec amount of time this context is valid for 229 * 230 * @returns a gss_error code, see gss_display_status() about printing 231 * the error code. 232 * 233 * @ingroup gssapi 234 */ 235 236 237 238GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL 239gss_init_sec_context(OM_uint32 * minor_status, 240 const gss_cred_id_t initiator_cred_handle, 241 gss_ctx_id_t * context_handle, 242 const gss_name_t target_name, 243 const gss_OID input_mech_type, 244 OM_uint32 req_flags, 245 OM_uint32 time_req, 246 const gss_channel_bindings_t input_chan_bindings, 247 const gss_buffer_t input_token, 248 gss_OID * actual_mech_type, 249 gss_buffer_t output_token, 250 OM_uint32 * ret_flags, 251 OM_uint32 * time_rec) 252{ 253 OM_uint32 major_status; 254 gssapi_mech_interface m; 255 struct _gss_name *name = (struct _gss_name *) target_name; 256 struct _gss_mechanism_name *mn; 257 struct _gss_context *ctx = (struct _gss_context *) *context_handle; 258 gss_cred_id_t cred_handle; 259 int allocated_ctx; 260 gss_OID mech_type = input_mech_type; 261 262 *minor_status = 0; 263 264 _mg_buffer_zero(output_token); 265 if (actual_mech_type) 266 *actual_mech_type = GSS_C_NO_OID; 267 if (ret_flags) 268 *ret_flags = 0; 269 if (time_rec) 270 *time_rec = 0; 271 272 if (mech_type == NULL) 273 mech_type = GSS_KRB5_MECHANISM; 274 275 HEIM_WARN_BLOCKING("gss_init_sec_context", warn_once); 276 277 _gss_mg_check_name(target_name); 278 _gss_mg_check_credential(initiator_cred_handle); 279 280 if (_gss_mg_log_level(1)) 281 log_init_sec_context(ctx, name, req_flags, 282 (struct _gss_cred *)initiator_cred_handle, 283 input_mech_type, input_token); 284 285 286 cred_handle = initiator_cred_handle; 287 288 /* 289 * If we haven't allocated a context yet, do so now and lookup 290 * the mechanism switch table. If we have one already, make 291 * sure we use the same mechanism switch as before. 292 */ 293 if (!ctx) { 294 295 ctx = malloc(sizeof(struct _gss_context)); 296 if (!ctx) { 297 *minor_status = ENOMEM; 298 return (GSS_S_FAILURE); 299 } 300 memset(ctx, 0, sizeof(struct _gss_context)); 301 m = ctx->gc_mech = __gss_get_mechanism(mech_type); 302 if (!m) { 303 free(ctx); 304 *minor_status = 0; 305 gss_mg_set_error_string(mech_type, GSS_S_BAD_MECH, 306 *minor_status, 307 "Asked for mechanism isn'ted supported"); 308 return GSS_S_BAD_MECH; 309 } 310 allocated_ctx = 1; 311 312 /* 313 * Check if a plugin wants to replace the initiator_cred_handle with something else 314 */ 315 316 ctx->gc_replaced_cred = 317 check_replace_cred(minor_status, target_name, 318 mech_type, cred_handle); 319 320 if (ctx->gc_replaced_cred) 321 _gss_mg_log_cred(1, (struct _gss_cred *)ctx->gc_replaced_cred, 322 "gss_isc replacement cred"); 323 } else { 324 m = ctx->gc_mech; 325 mech_type = &ctx->gc_mech->gm_mech_oid; 326 allocated_ctx = 0; 327 } 328 329 if (ctx->gc_replaced_cred) 330 cred_handle = ctx->gc_replaced_cred; 331 332 /* 333 * Find the MN for this mechanism. 334 */ 335 major_status = _gss_find_mn(minor_status, name, mech_type, &mn); 336 if (major_status != GSS_S_COMPLETE) { 337 if (allocated_ctx) 338 free(ctx); 339 return major_status; 340 } 341 342 /* 343 * If we have a cred, find the cred for this mechanism. 344 */ 345 if ((m->gm_flags & GM_USE_MG_CRED) == 0 && cred_handle) { 346 cred_handle = _gss_mech_cred_find(cred_handle, mech_type); 347 if (cred_handle == GSS_C_NO_CREDENTIAL) { 348 *minor_status = 0; 349 if (allocated_ctx) 350 free(ctx); 351 gss_mg_set_error_string(mech_type, GSS_S_UNAVAILABLE, 352 *minor_status, 353 "Credential for asked mech-type " 354 "mech not found in the " 355 "credential handle"); 356 return GSS_S_UNAVAILABLE; 357 } 358 } 359 360 major_status = m->gm_init_sec_context(minor_status, 361 cred_handle, 362 &ctx->gc_ctx, 363 mn ? mn->gmn_name : GSS_C_NO_NAME, 364 mech_type, 365 req_flags, 366 time_req, 367 input_chan_bindings, 368 input_token, 369 actual_mech_type, 370 output_token, 371 ret_flags, 372 time_rec); 373 374 if (major_status != GSS_S_COMPLETE 375 && major_status != GSS_S_CONTINUE_NEEDED) { 376 if (allocated_ctx) 377 free(ctx); 378 _mg_buffer_zero(output_token); 379 _gss_mg_error(m, *minor_status); 380 } else { 381 *context_handle = (gss_ctx_id_t) ctx; 382 } 383 384 _gss_mg_log(1, "gss_isc: %s maj_stat: %d/%d", 385 m->gm_name, (int)major_status, (int)*minor_status); 386 387 return (major_status); 388} 389