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
47    krb5_data_zero(&data);
48
49    GSSAPI_KRB5_INIT (&context);
50
51    if (handle->usage != GSS_C_INITIATE && handle->usage != GSS_C_BOTH) {
52	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
53	return GSS_S_FAILURE;
54    }
55
56    sp = krb5_storage_emem();
57    if (sp == NULL) {
58	*minor_status = ENOMEM;
59	return GSS_S_FAILURE;
60    }
61
62    /*
63    type = krb5_cc_get_type(context, handle->ccache);
64     *
65     * XXX Always use reference keys since that makes it easier to
66     * transport between processing in seprate authentication domain.
67     *
68     * We should encrypt credentials in KCM though using the kcm
69     * session key.
70     */
71    {
72	krb5_creds *creds;
73
74	if (handle->ccache == NULL)
75	    goto out;
76
77	ret = krb5_store_uint32(sp, 0);
78	if (ret) {
79	    krb5_storage_free(sp);
80	    *minor_status = ret;
81	    return GSS_S_FAILURE;
82	}
83
84	ret = _krb5_get_krbtgt(context, handle->ccache,
85			       handle->principal->realm,
86			       &creds);
87	if (ret) {
88	    krb5_storage_free(sp);
89	    *minor_status = ret;
90	    return GSS_S_FAILURE;
91	}
92
93	ret = krb5_store_creds(sp, creds);
94	krb5_free_creds(context, creds);
95	if (ret) {
96	    krb5_storage_free(sp);
97	    *minor_status = ret;
98	    return GSS_S_FAILURE;
99	}
100
101    }
102    ret = krb5_storage_to_data(sp, &data);
103    krb5_storage_free(sp);
104    if (ret) {
105	*minor_status = ret;
106	return GSS_S_FAILURE;
107    }
108    sp = krb5_storage_emem();
109    if (sp == NULL) {
110	krb5_data_free(&data);
111	*minor_status = ENOMEM;
112	return GSS_S_FAILURE;
113    }
114
115    mech.data = GSS_KRB5_MECHANISM->elements;
116    mech.length = GSS_KRB5_MECHANISM->length;
117
118    ret = krb5_store_data(sp, mech);
119    if (ret) {
120	krb5_data_free(&data);
121	krb5_storage_free(sp);
122	*minor_status = ret;
123	return GSS_S_FAILURE;
124    }
125
126    ret = krb5_store_data(sp, data);
127    krb5_data_free(&data);
128    if (ret) {
129	krb5_storage_free(sp);
130	*minor_status = ret;
131	return GSS_S_FAILURE;
132    }
133
134    ret = krb5_storage_to_data(sp, &data);
135    krb5_storage_free(sp);
136    if (ret) {
137	*minor_status = ret;
138	return GSS_S_FAILURE;
139    }
140
141 out:
142    cred_token->value = data.data;
143    cred_token->length = data.length;
144
145    return GSS_S_COMPLETE;
146}
147
148OM_uint32 GSSAPI_CALLCONV
149_gsskrb5_import_cred(OM_uint32 * minor_status,
150		     gss_buffer_t cred_token,
151		     gss_cred_id_t * cred_handle)
152{
153    krb5_context context;
154    krb5_error_code ret;
155    gsskrb5_cred handle;
156    krb5_ccache id;
157    krb5_storage *sp;
158    char *str;
159    uint32_t type;
160    int flags = 0;
161
162    *cred_handle = GSS_C_NO_CREDENTIAL;
163
164    GSSAPI_KRB5_INIT (&context);
165
166    sp = krb5_storage_from_mem(cred_token->value, cred_token->length);
167    if (sp == NULL) {
168	*minor_status = ENOMEM;
169	return GSS_S_FAILURE;
170    }
171
172    ret = krb5_ret_uint32(sp, &type);
173    if (ret) {
174	krb5_storage_free(sp);
175	*minor_status = ret;
176	return GSS_S_FAILURE;
177    }
178    switch (type) {
179    case 0: {
180	krb5_creds creds;
181
182	ret = krb5_ret_creds(sp, &creds);
183	krb5_storage_free(sp);
184	if (ret) {
185	    *minor_status = ret;
186	    return GSS_S_FAILURE;
187	}
188
189	ret = krb5_cc_new_unique(context, "API", NULL, &id);
190	if (ret) {
191	    *minor_status = ret;
192	    return GSS_S_FAILURE;
193	}
194
195	ret = krb5_cc_initialize(context, id, creds.client);
196	if (ret) {
197	    krb5_cc_destroy(context, id);
198	    *minor_status = ret;
199	    return GSS_S_FAILURE;
200	}
201
202	ret = krb5_cc_store_cred(context, id, &creds);
203	krb5_free_cred_contents(context, &creds);
204	if (ret) {
205	    krb5_cc_destroy(context, id);
206	    *minor_status = ret;
207	    return GSS_S_FAILURE;
208	}
209
210	flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
211
212	break;
213    }
214    case 1:
215	ret = krb5_ret_string(sp, &str);
216	krb5_storage_free(sp);
217	if (ret) {
218	    *minor_status = ret;
219	    return GSS_S_FAILURE;
220	}
221
222	ret = krb5_cc_resolve(context, str, &id);
223	krb5_xfree(str);
224	if (ret) {
225	    *minor_status = ret;
226	    return GSS_S_FAILURE;
227	}
228	break;
229
230    default:
231	krb5_storage_free(sp);
232	*minor_status = 0;
233	return GSS_S_NO_CRED;
234    }
235
236    handle = calloc(1, sizeof(*handle));
237    if (handle == NULL) {
238	krb5_cc_close(context, id);
239	*minor_status = ENOMEM;
240	return GSS_S_FAILURE;
241    }
242
243    handle->usage = GSS_C_INITIATE;
244    krb5_cc_get_principal(context, id, &handle->principal);
245    handle->ccache = id;
246    handle->cred_flags = flags;
247
248    if (handle->principal)
249      __gsskrb5_ccache_lifetime(minor_status, context,
250				id, handle->principal,
251				&handle->endtime);
252
253    *cred_handle = (gss_cred_id_t)handle;
254
255    return GSS_S_COMPLETE;
256}
257
258OM_uint32
259_gsskrb5_destroy_cred(OM_uint32 *minor_status,
260		      gss_cred_id_t *cred_handle)
261{
262    gsskrb5_cred cred = (gsskrb5_cred)*cred_handle;
263    cred->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
264    return _gsskrb5_release_cred(minor_status, cred_handle);
265}
266
267static OM_uint32
268change_hold(OM_uint32 *minor_status, gsskrb5_cred cred,
269	    krb5_error_code (*func)(krb5_context, krb5_ccache))
270{
271    krb5_error_code ret;
272    krb5_context context;
273    krb5_data data;
274
275    *minor_status = 0;
276    krb5_data_zero(&data);
277
278    GSSAPI_KRB5_INIT (&context);
279
280    if (cred == NULL)
281	return GSS_S_COMPLETE;
282
283    if (cred->usage != GSS_C_INITIATE && cred->usage != GSS_C_BOTH) {
284	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
285	return GSS_S_FAILURE;
286    }
287
288    /* XXX only refcount nah-created credentials */
289    ret = krb5_cc_get_config(context, cred->ccache, NULL, "nah-created", &data);
290    if (ret) {
291	*minor_status = ret;
292	return GSS_S_FAILURE;
293    }
294    krb5_data_free(&data);
295
296    ret = func(context, cred->ccache);
297
298    if (ret) {
299	*minor_status = ret;
300	return GSS_S_FAILURE;
301    }
302
303    return GSS_S_COMPLETE;
304}
305
306OM_uint32
307_gsskrb5_cred_hold(OM_uint32 *minor_status, gss_cred_id_t cred)
308{
309    return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_hold);
310}
311
312OM_uint32
313_gsskrb5_cred_unhold(OM_uint32 *minor_status, gss_cred_id_t cred)
314{
315    return change_hold(minor_status, (gsskrb5_cred)cred, krb5_cc_unhold);
316}
317
318OM_uint32
319_gsskrb5_cred_label_get(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
320			const char *label, gss_buffer_t value)
321{
322    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
323    krb5_context context;
324    krb5_error_code ret;
325    krb5_data data;
326
327    GSSAPI_KRB5_INIT (&context);
328
329    if (cred == NULL)
330	return GSS_S_COMPLETE;
331
332    if (cred->ccache == NULL) {
333	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
334	return GSS_S_FAILURE;
335    }
336
337    ret = krb5_cc_get_config(context, cred->ccache, NULL, label, &data);
338    if (ret) {
339	*minor_status = ret;
340	return GSS_S_FAILURE;
341    }
342
343    value->value = data.data;
344    value->length = data.length;
345
346    return GSS_S_COMPLETE;
347}
348
349OM_uint32
350_gsskrb5_cred_label_set(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
351			const char *label, gss_buffer_t value)
352{
353    gsskrb5_cred cred = (gsskrb5_cred)cred_handle;
354    krb5_context context;
355    krb5_error_code ret;
356    krb5_data data, *datap = NULL;
357
358    GSSAPI_KRB5_INIT (&context);
359
360    if (cred == NULL)
361	return GSS_S_COMPLETE;
362
363    if (cred->ccache == NULL) {
364	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
365	return GSS_S_FAILURE;
366    }
367
368    if (value) {
369	data.data = value->value;
370	data.length = value->length;
371	datap = &data;
372    }
373
374    ret = krb5_cc_set_config(context, cred->ccache, NULL, label, datap);
375    if (ret) {
376	*minor_status = ret;
377	return GSS_S_FAILURE;
378    }
379
380    return GSS_S_COMPLETE;
381}
382
383
384OM_uint32
385_gsskrb5_appl_change_password(OM_uint32 *minor_status,
386			      gss_name_t name,
387			      const char *oldpw,
388			      const char *newpw)
389{
390    krb5_data result_code_string, result_string;
391    krb5_get_init_creds_opt *opt = NULL;
392    krb5_context context;
393    krb5_principal principal = (krb5_principal)name;
394    krb5_creds kcred;
395    krb5_error_code ret;
396    int result_code;
397
398    GSSAPI_KRB5_INIT (&context);
399
400    memset(&kcred, 0, sizeof(kcred));
401
402    ret = krb5_get_init_creds_opt_alloc(context, &opt);
403    if (ret)
404	goto out;
405
406    krb5_get_init_creds_opt_set_tkt_life(opt, 300);
407    krb5_get_init_creds_opt_set_forwardable(opt, FALSE);
408    krb5_get_init_creds_opt_set_proxiable(opt, FALSE);
409
410    ret = krb5_get_init_creds_password(context,
411				       &kcred,
412				       principal,
413				       oldpw,
414				       NULL,
415				       NULL,
416				       0,
417				       "kadmin/changepw",
418				       opt);
419    if (ret)
420	goto out;
421
422    ret = krb5_set_password(context, &kcred, newpw, NULL,
423			    &result_code, &result_code_string, &result_string);
424    if (ret)
425	goto out;
426
427    krb5_data_free(&result_string);
428    krb5_data_free(&result_code_string);
429
430    if (result_code) {
431	krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY,
432			       "Failed to change invalid password: %d", result_code);
433	ret = KRB5KRB_AP_ERR_BAD_INTEGRITY;
434	goto out;
435    }
436
437 out:
438    if (opt)
439	krb5_get_init_creds_opt_free(context, opt);
440    krb5_free_cred_contents(context, &kcred);
441
442    *minor_status = ret;
443
444    return ret ? GSS_S_FAILURE : GSS_S_COMPLETE;
445}
446
447
448