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 the Institute nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#include "gsskrb5_locl.h"
35
36OM_uint32 GSSAPI_CALLCONV
37_gsskrb5_export_cred(OM_uint32 *minor_status,
38		     gss_cred_id_t cred_handle,
39		     gss_buffer_t cred_token)
40{
41    gsskrb5_cred handle = (gsskrb5_cred)cred_handle;
42    krb5_context context;
43    krb5_error_code ret;
44    krb5_storage *sp;
45    krb5_data data, mech;
46    char *str;
47
48    GSSAPI_KRB5_INIT (&context);
49
50    if (handle->usage != GSS_C_INITIATE && handle->usage != GSS_C_BOTH) {
51	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
52	return GSS_S_FAILURE;
53    }
54
55    sp = krb5_storage_emem();
56    if (sp == NULL) {
57	*minor_status = ENOMEM;
58	return GSS_S_FAILURE;
59    }
60
61    /*
62    type = krb5_cc_get_type(context, handle->ccache);
63     *
64     * XXX Always use reference keys since that makes it easier to
65     * transport between processing in seprate authentication domain.
66     *
67     * We should encrypt credentials in KCM though using the kcm
68     * session key.
69     */
70    if (1 /*strcmp(type, "MEMORY") == 0 */) {
71	krb5_creds *creds;
72	ret = krb5_store_uint32(sp, 0);
73	if (ret) {
74	    krb5_storage_free(sp);
75	    *minor_status = ret;
76	    return GSS_S_FAILURE;
77	}
78
79	ret = _krb5_get_krbtgt(context, handle->ccache,
80			       handle->principal->realm,
81			       &creds);
82	if (ret) {
83	    krb5_storage_free(sp);
84	    *minor_status = ret;
85	    return GSS_S_FAILURE;
86	}
87
88	ret = krb5_store_creds(sp, creds);
89	krb5_free_creds(context, creds);
90	if (ret) {
91	    krb5_storage_free(sp);
92	    *minor_status = ret;
93	    return GSS_S_FAILURE;
94	}
95
96    } else {
97	ret = krb5_store_uint32(sp, 1);
98	if (ret) {
99	    krb5_storage_free(sp);
100	    *minor_status = ret;
101	    return GSS_S_FAILURE;
102	}
103
104	ret = krb5_cc_get_full_name(context, handle->ccache, &str);
105	if (ret) {
106	    krb5_storage_free(sp);
107	    *minor_status = ret;
108	    return GSS_S_FAILURE;
109	}
110
111	ret = krb5_store_string(sp, str);
112	free(str);
113	if (ret) {
114	    krb5_storage_free(sp);
115	    *minor_status = ret;
116	    return GSS_S_FAILURE;
117	}
118    }
119    ret = krb5_storage_to_data(sp, &data);
120    krb5_storage_free(sp);
121    if (ret) {
122	*minor_status = ret;
123	return GSS_S_FAILURE;
124    }
125    sp = krb5_storage_emem();
126    if (sp == NULL) {
127	krb5_data_free(&data);
128	*minor_status = ENOMEM;
129	return GSS_S_FAILURE;
130    }
131
132    mech.data = GSS_KRB5_MECHANISM->elements;
133    mech.length = GSS_KRB5_MECHANISM->length;
134
135    ret = krb5_store_data(sp, mech);
136    if (ret) {
137	krb5_data_free(&data);
138	krb5_storage_free(sp);
139	*minor_status = ret;
140	return GSS_S_FAILURE;
141    }
142
143    ret = krb5_store_data(sp, data);
144    krb5_data_free(&data);
145    if (ret) {
146	krb5_storage_free(sp);
147	*minor_status = ret;
148	return GSS_S_FAILURE;
149    }
150
151    ret = krb5_storage_to_data(sp, &data);
152    krb5_storage_free(sp);
153    if (ret) {
154	*minor_status = ret;
155	return GSS_S_FAILURE;
156    }
157
158    cred_token->value = data.data;
159    cred_token->length = data.length;
160
161    return GSS_S_COMPLETE;
162}
163
164OM_uint32 GSSAPI_CALLCONV
165_gsskrb5_import_cred(OM_uint32 * minor_status,
166		     gss_buffer_t cred_token,
167		     gss_cred_id_t * cred_handle)
168{
169    krb5_context context;
170    krb5_error_code ret;
171    gsskrb5_cred handle;
172    krb5_ccache id;
173    krb5_storage *sp;
174    char *str;
175    uint32_t type;
176    int flags = 0;
177
178    *cred_handle = GSS_C_NO_CREDENTIAL;
179
180    GSSAPI_KRB5_INIT (&context);
181
182    sp = krb5_storage_from_mem(cred_token->value, cred_token->length);
183    if (sp == NULL) {
184	*minor_status = ENOMEM;
185	return GSS_S_FAILURE;
186    }
187
188    ret = krb5_ret_uint32(sp, &type);
189    if (ret) {
190	krb5_storage_free(sp);
191	*minor_status = ret;
192	return GSS_S_FAILURE;
193    }
194    switch (type) {
195    case 0: {
196	krb5_creds creds;
197
198	ret = krb5_ret_creds(sp, &creds);
199	krb5_storage_free(sp);
200	if (ret) {
201	    *minor_status = ret;
202	    return GSS_S_FAILURE;
203	}
204
205	ret = krb5_cc_new_unique(context, "API", NULL, &id);
206	if (ret) {
207	    *minor_status = ret;
208	    return GSS_S_FAILURE;
209	}
210
211	ret = krb5_cc_initialize(context, id, creds.client);
212	if (ret) {
213	    krb5_cc_destroy(context, id);
214	    *minor_status = ret;
215	    return GSS_S_FAILURE;
216	}
217
218	ret = krb5_cc_store_cred(context, id, &creds);
219	krb5_free_cred_contents(context, &creds);
220	if (ret) {
221	    krb5_cc_destroy(context, id);
222	    *minor_status = ret;
223	    return GSS_S_FAILURE;
224	}
225
226	flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
227
228	break;
229    }
230    case 1:
231	ret = krb5_ret_string(sp, &str);
232	krb5_storage_free(sp);
233	if (ret) {
234	    *minor_status = ret;
235	    return GSS_S_FAILURE;
236	}
237
238	ret = krb5_cc_resolve(context, str, &id);
239	krb5_xfree(str);
240	if (ret) {
241	    *minor_status = ret;
242	    return GSS_S_FAILURE;
243	}
244	break;
245
246    default:
247	krb5_storage_free(sp);
248	*minor_status = 0;
249	return GSS_S_NO_CRED;
250    }
251
252    handle = calloc(1, sizeof(*handle));
253    if (handle == NULL) {
254	krb5_cc_close(context, id);
255	*minor_status = ENOMEM;
256	return GSS_S_FAILURE;
257    }
258
259    handle->usage = GSS_C_INITIATE;
260    krb5_cc_get_principal(context, id, &handle->principal);
261    handle->ccache = id;
262    handle->cred_flags = flags;
263
264    if (handle->principal)
265      __gsskrb5_ccache_lifetime(minor_status, context,
266				id, handle->principal,
267				&handle->endtime);
268
269    *cred_handle = (gss_cred_id_t)handle;
270
271    return GSS_S_COMPLETE;
272}
273
274OM_uint32
275_gsskrb5_destroy_cred(OM_uint32 *minor_status,
276		      gss_cred_id_t *cred_handle)
277{
278    gsskrb5_cred cred = (gsskrb5_cred)*cred_handle;
279    cred->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
280    return _gsskrb5_release_cred(minor_status, cred_handle);
281}
282
283static OM_uint32
284change_hold(OM_uint32 *minor_status, gsskrb5_cred cred,
285	    krb5_error_code (*func)(krb5_context, krb5_ccache))
286{
287    krb5_error_code ret;
288    krb5_context context;
289    krb5_data data;
290
291    *minor_status = 0;
292    krb5_data_zero(&data);
293
294    GSSAPI_KRB5_INIT (&context);
295
296    if (cred == NULL)
297	return GSS_S_COMPLETE;
298
299    if (cred->usage != GSS_C_INITIATE && cred->usage != GSS_C_BOTH) {
300	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
301	return GSS_S_FAILURE;
302    }
303
304    /* XXX only refcount nah-created credentials */
305    ret = krb5_cc_get_config(context, cred->ccache, NULL, "nah-created", &data);
306    if (ret) {
307	*minor_status = ret;
308	return GSS_S_FAILURE;
309    }
310    krb5_data_free(&data);
311
312    ret = func(context, cred->ccache);
313
314    if (ret) {
315	*minor_status = ret;
316	return GSS_S_FAILURE;
317    }
318
319    return GSS_S_COMPLETE;
320}
321
322OM_uint32
323_gsskrb5_cred_hold(OM_uint32 *minor_status, gss_cred_id_t cred)
324{
325    return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_hold);
326}
327
328OM_uint32
329_gsskrb5_cred_unhold(OM_uint32 *minor_status, gss_cred_id_t cred)
330{
331    return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_unhold);
332}
333
334OM_uint32
335_gsskrb5_cred_label_get(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
336			const char *label, gss_buffer_t value)
337{
338    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
339    krb5_context context;
340    krb5_error_code ret;
341    krb5_data data;
342
343    GSSAPI_KRB5_INIT (&context);
344
345    if (cred == NULL)
346	return GSS_S_COMPLETE;
347
348    if (cred->ccache == NULL) {
349	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
350	return GSS_S_FAILURE;
351    }
352
353    ret = krb5_cc_get_config(context, cred->ccache, NULL, label, &data);
354    if (ret) {
355	*minor_status = ret;
356	return GSS_S_FAILURE;
357    }
358
359    value->value = data.data;
360    value->length = data.length;
361
362    return GSS_S_COMPLETE;
363}
364
365OM_uint32
366_gsskrb5_cred_label_set(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
367			const char *label, gss_buffer_t value)
368{
369    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
370    krb5_context context;
371    krb5_error_code ret;
372    krb5_data data, *datap = NULL;
373
374    GSSAPI_KRB5_INIT (&context);
375
376    if (cred == NULL)
377	return GSS_S_COMPLETE;
378
379    if (cred->ccache == NULL) {
380	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
381	return GSS_S_FAILURE;
382    }
383
384    if (value) {
385	data.data = value->value;
386	data.length = value->length;
387	datap = &data;
388    }
389
390    ret = krb5_cc_set_config(context, cred->ccache, NULL, label, datap);
391    if (ret) {
392	*minor_status = ret;
393	return GSS_S_FAILURE;
394    }
395
396    return GSS_S_COMPLETE;
397}
398
399
400OM_uint32
401_gsskrb5_appl_change_password(OM_uint32 *minor_status,
402			      gss_name_t name,
403			      const char *oldpw,
404			      const char *newpw)
405{
406    krb5_data result_code_string, result_string;
407    krb5_get_init_creds_opt *opt = NULL;
408    krb5_context context;
409    krb5_principal principal = (krb5_principal)name;
410    krb5_creds kcred;
411    krb5_error_code ret;
412    int result_code;
413
414    GSSAPI_KRB5_INIT (&context);
415
416    memset(&kcred, 0, sizeof(kcred));
417
418    ret = krb5_get_init_creds_opt_alloc(context, &opt);
419    if (ret)
420	goto out;
421
422    krb5_get_init_creds_opt_set_tkt_life(opt, 300);
423    krb5_get_init_creds_opt_set_forwardable(opt, FALSE);
424    krb5_get_init_creds_opt_set_proxiable(opt, FALSE);
425
426    ret = krb5_get_init_creds_password(context,
427				       &kcred,
428				       principal,
429				       oldpw,
430				       NULL,
431				       NULL,
432				       0,
433				       "kadmin/changepw",
434				       opt);
435    if (ret)
436	goto out;
437
438    ret = krb5_set_password(context, &kcred, newpw, NULL,
439			    &result_code, &result_code_string, &result_string);
440    if (ret)
441	goto out;
442
443    krb5_data_free(&result_string);
444    krb5_data_free(&result_code_string);
445
446    if (result_code) {
447	krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY,
448			       "Failed to change invalid password: %d", result_code);
449	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
450	goto out;
451    }
452
453 out:
454    if (opt)
455	krb5_get_init_creds_opt_free(context, opt);
456    krb5_free_cred_contents(context, &kcred);
457
458    *minor_status = ret;
459
460    return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
461}
462
463
464