1226031Sstas#include "mech_locl.h"
2226031Sstas#include "heim_threads.h"
3226031Sstas
4226031Sstasstruct mg_thread_ctx {
5226031Sstas    gss_OID mech;
6226031Sstas    OM_uint32 maj_stat;
7226031Sstas    OM_uint32 min_stat;
8226031Sstas    gss_buffer_desc maj_error;
9226031Sstas    gss_buffer_desc min_error;
10226031Sstas};
11226031Sstas
12226031Sstasstatic HEIMDAL_MUTEX context_mutex = HEIMDAL_MUTEX_INITIALIZER;
13226031Sstasstatic int created_key;
14226031Sstasstatic HEIMDAL_thread_key context_key;
15226031Sstas
16226031Sstas
17226031Sstasstatic void
18226031Sstasdestroy_context(void *ptr)
19226031Sstas{
20226031Sstas    struct mg_thread_ctx *mg = ptr;
21226031Sstas    OM_uint32 junk;
22226031Sstas
23226031Sstas    if (mg == NULL)
24226031Sstas	return;
25226031Sstas
26226031Sstas    gss_release_buffer(&junk, &mg->maj_error);
27226031Sstas    gss_release_buffer(&junk, &mg->min_error);
28226031Sstas    free(mg);
29226031Sstas}
30226031Sstas
31226031Sstas
32226031Sstasstatic struct mg_thread_ctx *
33226031Sstas_gss_mechglue_thread(void)
34226031Sstas{
35226031Sstas    struct mg_thread_ctx *ctx;
36226031Sstas    int ret = 0;
37226031Sstas
38226031Sstas    HEIMDAL_MUTEX_lock(&context_mutex);
39226031Sstas
40226031Sstas    if (!created_key) {
41226031Sstas	HEIMDAL_key_create(&context_key, destroy_context, ret);
42226031Sstas	if (ret) {
43226031Sstas	    HEIMDAL_MUTEX_unlock(&context_mutex);
44226031Sstas	    return NULL;
45226031Sstas	}
46226031Sstas	created_key = 1;
47226031Sstas    }
48226031Sstas    HEIMDAL_MUTEX_unlock(&context_mutex);
49226031Sstas
50226031Sstas    ctx = HEIMDAL_getspecific(context_key);
51226031Sstas    if (ctx == NULL) {
52226031Sstas
53226031Sstas	ctx = calloc(1, sizeof(*ctx));
54226031Sstas	if (ctx == NULL)
55226031Sstas	    return NULL;
56226031Sstas	HEIMDAL_setspecific(context_key, ctx, ret);
57226031Sstas	if (ret) {
58226031Sstas	    free(ctx);
59226031Sstas	    return NULL;
60226031Sstas	}
61226031Sstas    }
62226031Sstas    return ctx;
63226031Sstas}
64226031Sstas
65226031SstasOM_uint32
66226031Sstas_gss_mg_get_error(const gss_OID mech, OM_uint32 type,
67226031Sstas		  OM_uint32 value, gss_buffer_t string)
68226031Sstas{
69226031Sstas    struct mg_thread_ctx *mg;
70226031Sstas
71226031Sstas    mg = _gss_mechglue_thread();
72226031Sstas    if (mg == NULL)
73226031Sstas	return GSS_S_BAD_STATUS;
74226031Sstas
75226031Sstas#if 0
76226031Sstas    /*
77226031Sstas     * We cant check the mech here since a pseudo-mech might have
78226031Sstas     * called an lower layer and then the mech info is all broken
79226031Sstas     */
80226031Sstas    if (mech != NULL && gss_oid_equal(mg->mech, mech) == 0)
81226031Sstas	return GSS_S_BAD_STATUS;
82226031Sstas#endif
83226031Sstas
84226031Sstas    switch (type) {
85226031Sstas    case GSS_C_GSS_CODE: {
86226031Sstas	if (value != mg->maj_stat || mg->maj_error.length == 0)
87226031Sstas	    break;
88226031Sstas	string->value = malloc(mg->maj_error.length + 1);
89226031Sstas	string->length = mg->maj_error.length;
90226031Sstas	memcpy(string->value, mg->maj_error.value, mg->maj_error.length);
91226031Sstas        ((char *) string->value)[string->length] = '\0';
92226031Sstas	return GSS_S_COMPLETE;
93226031Sstas    }
94226031Sstas    case GSS_C_MECH_CODE: {
95226031Sstas	if (value != mg->min_stat || mg->min_error.length == 0)
96226031Sstas	    break;
97226031Sstas	string->value = malloc(mg->min_error.length + 1);
98226031Sstas	string->length = mg->min_error.length;
99226031Sstas	memcpy(string->value, mg->min_error.value, mg->min_error.length);
100226031Sstas        ((char *) string->value)[string->length] = '\0';
101226031Sstas	return GSS_S_COMPLETE;
102226031Sstas    }
103226031Sstas    }
104226031Sstas    string->value = NULL;
105226031Sstas    string->length = 0;
106226031Sstas    return GSS_S_BAD_STATUS;
107226031Sstas}
108226031Sstas
109226031Sstasvoid
110226031Sstas_gss_mg_error(gssapi_mech_interface m, OM_uint32 maj, OM_uint32 min)
111226031Sstas{
112226031Sstas    OM_uint32 major_status, minor_status;
113226031Sstas    OM_uint32 message_content;
114226031Sstas    struct mg_thread_ctx *mg;
115226031Sstas
116226031Sstas    /*
117226031Sstas     * Mechs without gss_display_status() does
118226031Sstas     * gss_mg_collect_error() by themself.
119226031Sstas     */
120226031Sstas    if (m->gm_display_status == NULL)
121226031Sstas	return ;
122226031Sstas
123226031Sstas    mg = _gss_mechglue_thread();
124226031Sstas    if (mg == NULL)
125226031Sstas	return;
126226031Sstas
127226031Sstas    gss_release_buffer(&minor_status, &mg->maj_error);
128226031Sstas    gss_release_buffer(&minor_status, &mg->min_error);
129226031Sstas
130226031Sstas    mg->mech = &m->gm_mech_oid;
131226031Sstas    mg->maj_stat = maj;
132226031Sstas    mg->min_stat = min;
133226031Sstas
134226031Sstas    major_status = m->gm_display_status(&minor_status,
135226031Sstas					maj,
136226031Sstas					GSS_C_GSS_CODE,
137226031Sstas					&m->gm_mech_oid,
138226031Sstas					&message_content,
139226031Sstas					&mg->maj_error);
140226031Sstas    if (GSS_ERROR(major_status)) {
141226031Sstas	mg->maj_error.value = NULL;
142226031Sstas	mg->maj_error.length = 0;
143226031Sstas    }
144226031Sstas    major_status = m->gm_display_status(&minor_status,
145226031Sstas					min,
146226031Sstas					GSS_C_MECH_CODE,
147226031Sstas					&m->gm_mech_oid,
148226031Sstas					&message_content,
149226031Sstas					&mg->min_error);
150226031Sstas    if (GSS_ERROR(major_status)) {
151226031Sstas	mg->min_error.value = NULL;
152226031Sstas	mg->min_error.length = 0;
153226031Sstas    }
154226031Sstas}
155226031Sstas
156226031Sstasvoid
157226031Sstasgss_mg_collect_error(gss_OID mech, OM_uint32 maj, OM_uint32 min)
158226031Sstas{
159226031Sstas    gssapi_mech_interface m = __gss_get_mechanism(mech);
160226031Sstas    if (m == NULL)
161226031Sstas	return;
162226031Sstas    _gss_mg_error(m, maj, min);
163226031Sstas}
164