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