2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
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
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
36 #include "mech_locl.h"
37 #include "heim_threads.h"
39 #include "krb5_locl.h"
40 #include "negoex_err.h"
42 struct mg_thread_ctx
{
45 gss_buffer_desc min_error
;
49 static HEIMDAL_MUTEX context_mutex
= HEIMDAL_MUTEX_INITIALIZER
;
50 static int created_key
;
51 static HEIMDAL_thread_key context_key
;
55 destroy_context(void *ptr
)
57 struct mg_thread_ctx
*mg
= ptr
;
63 gss_release_buffer(&junk
, &mg
->min_error
);
66 krb5_free_context(mg
->context
);
72 static struct mg_thread_ctx
*
73 _gss_mechglue_thread(void)
75 struct mg_thread_ctx
*ctx
;
78 HEIMDAL_MUTEX_lock(&context_mutex
);
81 HEIMDAL_key_create(&context_key
, destroy_context
, ret
);
83 HEIMDAL_MUTEX_unlock(&context_mutex
);
88 HEIMDAL_MUTEX_unlock(&context_mutex
);
90 ctx
= HEIMDAL_getspecific(context_key
);
93 ctx
= calloc(1, sizeof(*ctx
));
97 ret
= krb5_init_context(&ctx
->context
);
103 krb5_add_et_list(ctx
->context
, initialize_ngex_error_table_r
);
105 HEIMDAL_setspecific(context_key
, ctx
, ret
);
107 krb5_free_context(ctx
->context
);
116 _gss_mg_krb5_context(void)
118 struct mg_thread_ctx
*mg
;
120 mg
= _gss_mechglue_thread();
122 return mg
? mg
->context
: NULL
;
126 _gss_mg_get_error(const gss_OID mech
,
130 struct mg_thread_ctx
*mg
;
132 mg
= _gss_mechglue_thread();
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
;
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
)
164 mg
= _gss_mechglue_thread();
168 gss_release_buffer(&minor_status
, &mg
->min_error
);
170 mg
->mech
= &m
->gm_mech_oid
;
173 major_status
= m
->gm_display_status(&minor_status
,
179 if (major_status
!= GSS_S_COMPLETE
) {
180 _mg_buffer_zero(&mg
->min_error
);
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
);
189 gss_mg_collect_error(gss_OID mech
, OM_uint32 maj
, OM_uint32 min
)
191 gssapi_mech_interface m
= __gss_get_mechanism(mech
);
194 _gss_mg_error(m
, min
);
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
;
208 mg
= _gss_mechglue_thread();
213 vasprintf_ret
= vasprintf(&str
, fmt
, ap
);
216 if (vasprintf_ret
>= 0 && str
) {
217 gss_release_buffer(&junk
, &mg
->min_error
);
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
,
232 static void *log_ctx
= NULL
;
233 static void (*log_func
)(void *ctx
, int level
, const char *fmt
, va_list) = NULL
;
236 gss_set_log_function(void *ctx
, void (*func
)(void * ctx
, int level
, const char *fmt
, va_list))
238 if (log_func
== NULL
) {
245 _gss_mg_log_level(int level
)
247 struct mg_thread_ctx
*mg
;
249 mg
= _gss_mechglue_thread();
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.
261 _gss_mg_log(int level
, const char *fmt
, ...)
263 struct mg_thread_ctx
*mg
;
266 if (!_gss_mg_log_level(level
))
269 mg
= _gss_mechglue_thread();
273 if (mg
->context
&& _krb5_have_debug(mg
->context
, level
)) {
275 krb5_vlog(mg
->context
, heim_get_debug_dest(mg
->context
->hcontext
),
282 log_func(log_ctx
, level
, fmt
, ap
);
288 _gss_mg_log_name(int level
,
289 struct _gss_name
*name
,
291 const char *fmt
, ...)
293 struct _gss_mechanism_name
*mn
= NULL
;
294 gssapi_mech_interface m
;
297 if (!_gss_mg_log_level(level
))
300 m
= __gss_get_mechanism(mech_type
);
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
;
310 namebuf
.value
= "no name";
311 namebuf
.length
= strlen((char *)namebuf
.value
);
313 maj_stat
= m
->gm_display_name(&junk
, mn
->gmn_name
,
316 if (maj_stat
== GSS_S_COMPLETE
) {
321 ret
= vasprintf(&str
, fmt
, ap
);
325 _gss_mg_log(level
, "%s %.*s", str
,
326 (int)namebuf
.length
, (char *)namebuf
.value
);
329 gss_release_buffer(&junk
, &namebuf
);
336 _gss_mg_log_cred(int level
,
337 struct _gss_cred
*cred
,
338 const char *fmt
, ...)
340 struct _gss_mechanism_cred
*mc
;
345 if (!_gss_mg_log_level(level
))
349 ret
= vasprintf(&str
, fmt
, 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
);
357 _gss_mg_log(1, "%s: GSS_C_NO_CREDENTIAL", str
);