lib/gssapi/krb5: implement GSS_C_CHANNEL_BOUND_FLAG for gss_init_sec_context()
[heimdal.git] / lib / gssapi / mech / context.c
blob83e2cef6a7ba5178dd5d638017fe818f00a27cf5
1 /*
2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the Institute nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
36 #include "mech_locl.h"
37 #include "heim_threads.h"
38 #include <krb5.h>
39 #include "krb5_locl.h"
40 #include "negoex_err.h"
42 struct mg_thread_ctx {
43 gss_OID mech;
44 OM_uint32 min_stat;
45 gss_buffer_desc min_error;
46 krb5_context context;
49 static HEIMDAL_MUTEX context_mutex = HEIMDAL_MUTEX_INITIALIZER;
50 static int created_key;
51 static HEIMDAL_thread_key context_key;
54 static void
55 destroy_context(void *ptr)
57 struct mg_thread_ctx *mg = ptr;
58 OM_uint32 junk;
60 if (mg == NULL)
61 return;
63 gss_release_buffer(&junk, &mg->min_error);
65 if (mg->context)
66 krb5_free_context(mg->context);
68 free(mg);
72 static struct mg_thread_ctx *
73 _gss_mechglue_thread(void)
75 struct mg_thread_ctx *ctx;
76 int ret = 0;
78 HEIMDAL_MUTEX_lock(&context_mutex);
80 if (!created_key) {
81 HEIMDAL_key_create(&context_key, destroy_context, ret);
82 if (ret) {
83 HEIMDAL_MUTEX_unlock(&context_mutex);
84 return NULL;
86 created_key = 1;
88 HEIMDAL_MUTEX_unlock(&context_mutex);
90 ctx = HEIMDAL_getspecific(context_key);
91 if (ctx == NULL) {
93 ctx = calloc(1, sizeof(*ctx));
94 if (ctx == NULL)
95 return NULL;
97 ret = krb5_init_context(&ctx->context);
98 if (ret) {
99 free(ctx);
100 return NULL;
103 krb5_add_et_list(ctx->context, initialize_ngex_error_table_r);
105 HEIMDAL_setspecific(context_key, ctx, ret);
106 if (ret) {
107 krb5_free_context(ctx->context);
108 free(ctx);
109 return NULL;
112 return ctx;
115 krb5_context
116 _gss_mg_krb5_context(void)
118 struct mg_thread_ctx *mg;
120 mg = _gss_mechglue_thread();
122 return mg ? mg->context : NULL;
125 OM_uint32
126 _gss_mg_get_error(const gss_OID mech,
127 OM_uint32 value,
128 gss_buffer_t string)
130 struct mg_thread_ctx *mg;
132 mg = _gss_mechglue_thread();
133 if (mg == NULL)
134 return GSS_S_BAD_STATUS;
136 if (value != mg->min_stat || mg->min_error.length == 0) {
137 _mg_buffer_zero(string);
138 return GSS_S_BAD_STATUS;
140 string->value = malloc(mg->min_error.length);
141 if (string->value == NULL) {
142 _mg_buffer_zero(string);
143 return GSS_S_FAILURE;
145 string->length = mg->min_error.length;
146 memcpy(string->value, mg->min_error.value, mg->min_error.length);
147 return GSS_S_COMPLETE;
150 void
151 _gss_mg_error(struct gssapi_mech_interface_desc *m, OM_uint32 min)
153 OM_uint32 major_status, minor_status;
154 OM_uint32 message_content = 0;
155 struct mg_thread_ctx *mg;
158 * Mechs without gss_display_status() does
159 * gss_mg_collect_error() by themself.
161 if (m->gm_display_status == NULL)
162 return ;
164 mg = _gss_mechglue_thread();
165 if (mg == NULL)
166 return;
168 gss_release_buffer(&minor_status, &mg->min_error);
170 mg->mech = &m->gm_mech_oid;
171 mg->min_stat = min;
173 major_status = m->gm_display_status(&minor_status,
174 min,
175 GSS_C_MECH_CODE,
176 &m->gm_mech_oid,
177 &message_content,
178 &mg->min_error);
179 if (major_status != GSS_S_COMPLETE) {
180 _mg_buffer_zero(&mg->min_error);
181 } else {
182 _gss_mg_log(5, "_gss_mg_error: captured %.*s (%d) from underlying mech %s",
183 (int)mg->min_error.length, (const char *)mg->min_error.value,
184 (int)min, m->gm_name);
188 void
189 gss_mg_collect_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
191 gssapi_mech_interface m = __gss_get_mechanism(mech);
192 if (m == NULL)
193 return;
194 _gss_mg_error(m, min);
197 OM_uint32
198 gss_mg_set_error_string(gss_OID mech,
199 OM_uint32 maj, OM_uint32 min,
200 const char *fmt, ...)
202 struct mg_thread_ctx *mg;
203 char *str = NULL;
204 OM_uint32 junk;
205 va_list ap;
206 int vasprintf_ret;
208 mg = _gss_mechglue_thread();
209 if (mg == NULL)
210 return maj;
212 va_start(ap, fmt);
213 vasprintf_ret = vasprintf(&str, fmt, ap);
214 va_end(ap);
216 if (vasprintf_ret >= 0 && str) {
217 gss_release_buffer(&junk, &mg->min_error);
219 mg->mech = mech;
220 mg->min_stat = min;
222 mg->min_error.value = str;
223 mg->min_error.length = strlen(str);
225 _gss_mg_log(5, "gss_mg_set_error_string: %.*s (%d/%d)",
226 (int)mg->min_error.length, (const char *)mg->min_error.value,
227 (int)maj, (int)min);
229 return maj;
232 static void *log_ctx = NULL;
233 static void (*log_func)(void *ctx, int level, const char *fmt, va_list) = NULL;
235 void GSSAPI_LIB_CALL
236 gss_set_log_function(void *ctx, void (*func)(void * ctx, int level, const char *fmt, va_list))
238 if (log_func == NULL) {
239 log_func = func;
240 log_ctx = ctx;
245 _gss_mg_log_level(int level)
247 struct mg_thread_ctx *mg;
249 mg = _gss_mechglue_thread();
250 if (mg == NULL)
251 return 0;
253 return _krb5_have_debug(mg->context, level);
257 * TODO: refactor logging so that it no longer depends on libkrb5
258 * and can be configured independently.
260 void
261 _gss_mg_log(int level, const char *fmt, ...)
263 struct mg_thread_ctx *mg;
264 va_list ap;
266 if (!_gss_mg_log_level(level))
267 return;
269 mg = _gss_mechglue_thread();
270 if (mg == NULL)
271 return;
273 if (mg->context && _krb5_have_debug(mg->context, level)) {
274 va_start(ap, fmt);
275 krb5_vlog(mg->context, heim_get_debug_dest(mg->context->hcontext),
276 level, fmt, ap);
277 va_end(ap);
280 if (log_func) {
281 va_start(ap, fmt);
282 log_func(log_ctx, level, fmt, ap);
283 va_end(ap);
287 void
288 _gss_mg_log_name(int level,
289 struct _gss_name *name,
290 gss_OID mech_type,
291 const char *fmt, ...)
293 struct _gss_mechanism_name *mn = NULL;
294 gssapi_mech_interface m;
295 OM_uint32 junk;
297 if (!_gss_mg_log_level(level))
298 return;
300 m = __gss_get_mechanism(mech_type);
301 if (m == NULL)
302 return;
304 if (_gss_find_mn(&junk, name, mech_type, &mn) == GSS_S_COMPLETE) {
305 OM_uint32 maj_stat = GSS_S_COMPLETE;
306 gss_buffer_desc namebuf;
307 int ret;
309 if (mn == NULL) {
310 namebuf.value = "no name";
311 namebuf.length = strlen((char *)namebuf.value);
312 } else {
313 maj_stat = m->gm_display_name(&junk, mn->gmn_name,
314 &namebuf, NULL);
316 if (maj_stat == GSS_S_COMPLETE) {
317 char *str = NULL;
318 va_list ap;
320 va_start(ap, fmt);
321 ret = vasprintf(&str, fmt, ap);
322 va_end(ap);
324 if (ret >= 0 && str)
325 _gss_mg_log(level, "%s %.*s", str,
326 (int)namebuf.length, (char *)namebuf.value);
327 free(str);
328 if (mn != NULL)
329 gss_release_buffer(&junk, &namebuf);
335 void
336 _gss_mg_log_cred(int level,
337 struct _gss_cred *cred,
338 const char *fmt, ...)
340 struct _gss_mechanism_cred *mc;
341 char *str;
342 va_list ap;
343 int ret;
345 if (!_gss_mg_log_level(level))
346 return;
348 va_start(ap, fmt);
349 ret = vasprintf(&str, fmt, ap);
350 va_end(ap);
352 if (ret >=0 && cred) {
353 HEIM_TAILQ_FOREACH(mc, &cred->gc_mc, gmc_link) {
354 _gss_mg_log(1, "%s: %s", str, mc->gmc_mech->gm_name);
356 } else {
357 _gss_mg_log(1, "%s: GSS_C_NO_CREDENTIAL", str);
359 free(str);