1/*-
2 * Copyright (c) 2005 Doug Rabson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD: src/lib/libgssapi/gss_init_sec_context.c,v 1.1 2005/12/29 14:40:20 dfr Exp $
27 */
28
29#include "mech_locl.h"
30
31static gss_cred_id_t
32_gss_mech_cred_find(gss_cred_id_t cred_handle, gss_OID mech_type)
33{
34	struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
35	struct _gss_mechanism_cred *mc;
36
37	if (cred == NULL)
38		return GSS_C_NO_CREDENTIAL;
39
40	HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
41		if (gss_oid_equal(mech_type, mc->gmc_mech_oid))
42			return mc->gmc_cred;
43	}
44	return GSS_C_NO_CREDENTIAL;
45}
46
47/**
48 * As the initiator build a context with an acceptor.
49 *
50 * Returns in the major
51 * - GSS_S_COMPLETE - if the context if build
52 * - GSS_S_CONTINUE_NEEDED -  if the caller needs  to continue another
53 *	round of gss_i nit_sec_context
54 * - error code - any other error code
55 *
56 * @param minor_status minor status code.
57 *
58 * @param initiator_cred_handle the credential to use when building
59 *        the context, if GSS_C_NO_CREDENTIAL is passed, the default
60 *        credential for the mechanism will be used.
61 *
62 * @param context_handle a pointer to a context handle, will be
63 * 	  returned as long as there is not an error.
64 *
65 * @param target_name the target name of acceptor, created using
66 * 	  gss_import_name(). The name is can be of any name types the
67 * 	  mechanism supports, check supported name types with
68 * 	  gss_inquire_names_for_mech().
69 *
70 * @param input_mech_type mechanism type to use, if GSS_C_NO_OID is
71 *        used, Kerberos (GSS_KRB5_MECHANISM) will be tried. Other
72 *        available mechanism are listed in the @ref gssapi_mechs_intro
73 *        section.
74 *
75 * @param req_flags flags using when building the context, see @ref
76 *        gssapi_context_flags
77 *
78 * @param time_req time requested this context should be valid in
79 *        seconds, common used value is GSS_C_INDEFINITE
80 *
81 * @param input_chan_bindings Channel bindings used, if not exepected
82 *        otherwise, used GSS_C_NO_CHANNEL_BINDINGS
83 *
84 * @param input_token input token sent from the acceptor, for the
85 * 	  initial packet the buffer of { NULL, 0 } should be used.
86 *
87 * @param actual_mech_type the actual mech used, MUST NOT be freed
88 *        since it pointing to static memory.
89 *
90 * @param output_token if there is an output token, regardless of
91 * 	  complete, continue_needed, or error it should be sent to the
92 * 	  acceptor
93 *
94 * @param ret_flags return what flags was negotitated, caller should
95 * 	  check if they are accetable. For example, if
96 * 	  GSS_C_MUTUAL_FLAG was negotiated with the acceptor or not.
97 *
98 * @param time_rec amount of time this context is valid for
99 *
100 * @returns a gss_error code, see gss_display_status() about printing
101 *          the error code.
102 *
103 * @ingroup gssapi
104 */
105
106
107
108GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
109gss_init_sec_context(OM_uint32 * minor_status,
110    const gss_cred_id_t initiator_cred_handle,
111    gss_ctx_id_t * context_handle,
112    const gss_name_t target_name,
113    const gss_OID input_mech_type,
114    OM_uint32 req_flags,
115    OM_uint32 time_req,
116    const gss_channel_bindings_t input_chan_bindings,
117    const gss_buffer_t input_token,
118    gss_OID * actual_mech_type,
119    gss_buffer_t output_token,
120    OM_uint32 * ret_flags,
121    OM_uint32 * time_rec)
122{
123	OM_uint32 major_status;
124	gssapi_mech_interface m;
125	struct _gss_name *name = (struct _gss_name *) target_name;
126	struct _gss_mechanism_name *mn;
127	struct _gss_context *ctx = (struct _gss_context *) *context_handle;
128	gss_cred_id_t cred_handle;
129	int allocated_ctx;
130	gss_OID mech_type = input_mech_type;
131
132	*minor_status = 0;
133
134	_mg_buffer_zero(output_token);
135	if (actual_mech_type)
136	    *actual_mech_type = GSS_C_NO_OID;
137	if (ret_flags)
138	    *ret_flags = 0;
139	if (time_rec)
140	    *time_rec = 0;
141
142	/*
143	 * If we haven't allocated a context yet, do so now and lookup
144	 * the mechanism switch table. If we have one already, make
145	 * sure we use the same mechanism switch as before.
146	 */
147	if (!ctx) {
148		if (mech_type == NULL)
149			mech_type = GSS_KRB5_MECHANISM;
150
151		ctx = malloc(sizeof(struct _gss_context));
152		if (!ctx) {
153			*minor_status = ENOMEM;
154			return (GSS_S_FAILURE);
155		}
156		memset(ctx, 0, sizeof(struct _gss_context));
157		m = ctx->gc_mech = __gss_get_mechanism(mech_type);
158		if (!m) {
159			free(ctx);
160			return (GSS_S_BAD_MECH);
161		}
162		allocated_ctx = 1;
163	} else {
164		m = ctx->gc_mech;
165		mech_type = &ctx->gc_mech->gm_mech_oid;
166		allocated_ctx = 0;
167	}
168
169	/*
170	 * Find the MN for this mechanism.
171	 */
172	major_status = _gss_find_mn(minor_status, name, mech_type, &mn);
173	if (major_status != GSS_S_COMPLETE) {
174		if (allocated_ctx)
175			free(ctx);
176		return major_status;
177	}
178
179	/*
180	 * If we have a cred, find the cred for this mechanism.
181	 */
182	if (m->gm_flags & GM_USE_MG_CRED)
183		cred_handle = initiator_cred_handle;
184	else
185		cred_handle = _gss_mech_cred_find(initiator_cred_handle, mech_type);
186
187	major_status = m->gm_init_sec_context(minor_status,
188	    cred_handle,
189	    &ctx->gc_ctx,
190	    mn->gmn_name,
191	    mech_type,
192	    req_flags,
193	    time_req,
194	    input_chan_bindings,
195	    input_token,
196	    actual_mech_type,
197	    output_token,
198	    ret_flags,
199	    time_rec);
200
201	if (major_status != GSS_S_COMPLETE
202	    && major_status != GSS_S_CONTINUE_NEEDED) {
203		if (allocated_ctx)
204			free(ctx);
205		_mg_buffer_zero(output_token);
206		_gss_mg_error(m, major_status, *minor_status);
207	} else {
208		*context_handle = (gss_ctx_id_t) ctx;
209	}
210
211	return (major_status);
212}
213