1/*
2 * Copyright (c) 2009 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of KTH nor the names of its contributors may be
18 *    used to endorse or promote products derived from this software without
19 *    specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY KTH AND ITS CONTRIBUTORS ``AS IS'' AND ANY
22 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KTH OR ITS CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34#include "mech_locl.h"
35#include <krb5.h>
36
37/*
38 * format: any number of:
39 *     mech-len: int32
40 *     mech-data: char * (not alligned)
41 *     cred-len: int32
42 *     cred-data char * (not alligned)
43*/
44
45GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
46gss_export_cred(OM_uint32 * minor_status,
47		gss_cred_id_t cred_handle,
48		gss_buffer_t token)
49{
50    struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
51    struct _gss_mechanism_cred *mc;
52    gss_buffer_desc buffer;
53    krb5_error_code ret;
54    krb5_storage *sp;
55    OM_uint32 major;
56    krb5_data data;
57
58    _mg_buffer_zero(token);
59
60    if (cred == NULL) {
61	*minor_status = 0;
62	return GSS_S_NO_CRED;
63    }
64
65    HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
66	if (mc->gmc_mech->gm_export_cred == NULL) {
67	    *minor_status = 0;
68	    gss_mg_set_error_string(&mc->gmc_mech->gm_mech_oid,
69				    GSS_S_NO_CRED, *minor_status,
70				    "Credential doesn't support exporting");
71	    return GSS_S_NO_CRED;
72	}
73    }
74
75    sp = krb5_storage_emem();
76    if (sp == NULL) {
77	*minor_status = ENOMEM;
78	return GSS_S_FAILURE;
79    }
80
81    HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
82	krb5_ssize_t sret;
83
84	major = mc->gmc_mech->gm_export_cred(minor_status,
85					     mc->gmc_cred, &buffer);
86	if (major) {
87	    krb5_storage_free(sp);
88	    return major;
89	}
90
91	if (buffer.length) {
92	    sret = krb5_storage_write(sp, buffer.value, buffer.length);
93	    if (sret < 0 || (size_t)sret != buffer.length) {
94		gss_release_buffer(minor_status, &buffer);
95		krb5_storage_free(sp);
96		*minor_status = EINVAL;
97		return GSS_S_FAILURE;
98	    }
99	}
100	gss_release_buffer(minor_status, &buffer);
101    }
102
103    ret = krb5_storage_to_data(sp, &data);
104    krb5_storage_free(sp);
105    if (ret) {
106	*minor_status = ret;
107	return GSS_S_FAILURE;
108    }
109
110    if (data.length == 0)  {
111	*minor_status = 0;
112	gss_mg_set_error_string(GSS_C_NO_OID,
113				GSS_S_NO_CRED, *minor_status,
114				"Credential was not exportable");
115	return GSS_S_NO_CRED;
116    }
117
118    token->value = data.data;
119    token->length = data.length;
120
121    return GSS_S_COMPLETE;
122}
123
124GSSAPI_LIB_FUNCTION OM_uint32 GSSAPI_LIB_CALL
125gss_import_cred(OM_uint32 * minor_status,
126		gss_buffer_t token,
127		gss_cred_id_t * cred_handle)
128{
129    gssapi_mech_interface m;
130    krb5_error_code ret;
131    struct _gss_cred *cred;
132    krb5_storage *sp = NULL;
133    OM_uint32 major, junk;
134    krb5_data data;
135
136    *cred_handle = GSS_C_NO_CREDENTIAL;
137
138    if (token->length == 0) {
139	*minor_status = ENOMEM;
140	return GSS_S_FAILURE;
141    }
142
143    sp = krb5_storage_from_readonly_mem(token->value, token->length);
144    if (sp == NULL) {
145	*minor_status = ENOMEM;
146	return GSS_S_FAILURE;
147    }
148
149    cred = _gss_mg_alloc_cred();
150    if (cred == NULL) {
151	krb5_storage_free(sp);
152	*minor_status = ENOMEM;
153	return GSS_S_FAILURE;
154    }
155
156    *cred_handle = (gss_cred_id_t)cred;
157
158    while(1) {
159	struct _gss_mechanism_cred *mc;
160	gss_buffer_desc buffer;
161	gss_cred_id_t mcred;
162	gss_OID_desc oid;
163
164	ret = krb5_ret_data(sp, &data);
165	if (ret == HEIM_ERR_EOF) {
166	    break;
167	} else if (ret) {
168	    *minor_status = ret;
169	    major = GSS_S_FAILURE;
170	    goto out;
171	}
172	oid.elements = data.data;
173	oid.length = (OM_uint32)data.length;
174
175	m = __gss_get_mechanism(&oid);
176	krb5_data_free(&data);
177	if (!m) {
178	    *minor_status = 0;
179	    major = GSS_S_BAD_MECH;
180	    goto out;
181	}
182
183	if (m->gm_import_cred == NULL) {
184	    *minor_status = 0;
185	    major = GSS_S_BAD_MECH;
186	    goto out;
187	}
188
189	ret = krb5_ret_data(sp, &data);
190	if (ret) {
191	    *minor_status = ret;
192	    major = GSS_S_FAILURE;
193	    goto out;
194	}
195
196	buffer.value = data.data;
197	buffer.length = data.length;
198
199	major = m->gm_import_cred(minor_status,
200				  &buffer, &mcred);
201	krb5_data_free(&data);
202	if (major) {
203	    goto out;
204	}
205
206	mc = malloc(sizeof(struct _gss_mechanism_cred));
207	if (mc == NULL) {
208	    *minor_status = EINVAL;
209	    major = GSS_S_FAILURE;
210	    goto out;
211	}
212
213	mc->gmc_mech = m;
214	mc->gmc_mech_oid = &m->gm_mech_oid;
215	mc->gmc_cred = mcred;
216
217	HEIM_SLIST_INSERT_HEAD(&cred->gc_mc, mc, gmc_link);
218    }
219    krb5_storage_free(sp);
220    sp = NULL;
221
222    if (HEIM_SLIST_EMPTY(&cred->gc_mc)) {
223	major = GSS_S_NO_CRED;
224	goto out;
225    }
226
227    return GSS_S_COMPLETE;
228
229 out:
230    if (sp)
231	krb5_storage_free(sp);
232
233    gss_release_cred(&junk, cred_handle);
234
235    return major;
236
237}
238
239OM_uint32
240gss_cred_label_get(OM_uint32 * min_stat,
241		   gss_cred_id_t cred_handle,
242		   const char * label,
243		   gss_buffer_t value)
244{
245    struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
246    struct _gss_mechanism_cred *mc;
247    OM_uint32 maj_stat;
248
249    *min_stat = 0;
250    _mg_buffer_zero(value);
251
252    HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
253
254	if (mc->gmc_mech->gm_cred_label_get == NULL)
255	    continue;
256
257	maj_stat = mc->gmc_mech->gm_cred_label_get(min_stat, mc->gmc_cred,
258						   label, value);
259	if (maj_stat == GSS_S_COMPLETE)
260	    return GSS_S_COMPLETE;
261    }
262
263    return GSS_S_UNAVAILABLE;
264}
265
266OM_uint32
267gss_cred_label_set(OM_uint32 * min_stat,
268		   gss_cred_id_t cred_handle,
269		   const char * label,
270		   gss_buffer_t value)
271{
272    struct _gss_cred *cred = (struct _gss_cred *)cred_handle;
273    struct _gss_mechanism_cred *mc;
274
275    *min_stat = 0;
276
277    HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) {
278
279	if (mc->gmc_mech->gm_cred_label_set == NULL)
280	    continue;
281
282	(void)mc->gmc_mech->gm_cred_label_set(min_stat, mc->gmc_cred,
283					      label, value);
284    }
285
286    return GSS_S_COMPLETE;
287}
288