1/*
2 * Copyright (c) 1997 - 2005 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 "krb5/gsskrb5_locl.h"
35
36RCSID("$Id: acquire_cred.c 22124 2007-12-04 00:03:52Z lha $");
37
38OM_uint32
39__gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
40			  krb5_context context,
41			  krb5_ccache id,
42			  krb5_principal principal,
43			  OM_uint32 *lifetime)
44{
45    krb5_creds in_cred, *out_cred;
46    krb5_const_realm realm;
47    krb5_error_code kret;
48
49    memset(&in_cred, 0, sizeof(in_cred));
50    in_cred.client = principal;
51
52    realm = krb5_principal_get_realm(context,  principal);
53    if (realm == NULL) {
54	_gsskrb5_clear_status ();
55	*minor_status = KRB5_PRINC_NOMATCH; /* XXX */
56	return GSS_S_FAILURE;
57    }
58
59    kret = krb5_make_principal(context, &in_cred.server,
60			       realm, KRB5_TGS_NAME, realm, NULL);
61    if (kret) {
62	*minor_status = kret;
63	return GSS_S_FAILURE;
64    }
65
66    kret = krb5_get_credentials(context, 0,
67				id, &in_cred, &out_cred);
68    krb5_free_principal(context, in_cred.server);
69    if (kret) {
70	*minor_status = kret;
71	return GSS_S_FAILURE;
72    }
73
74    *lifetime = out_cred->times.endtime;
75    krb5_free_creds(context, out_cred);
76
77    return GSS_S_COMPLETE;
78}
79
80
81
82
83static krb5_error_code
84get_keytab(krb5_context context, krb5_keytab *keytab)
85{
86    char kt_name[256];
87    krb5_error_code kret;
88
89    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
90
91    if (_gsskrb5_keytab != NULL) {
92	kret = krb5_kt_get_name(context,
93				_gsskrb5_keytab,
94				kt_name, sizeof(kt_name));
95	if (kret == 0)
96	    kret = krb5_kt_resolve(context, kt_name, keytab);
97    } else
98	kret = krb5_kt_default(context, keytab);
99
100    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
101
102    return (kret);
103}
104
105static OM_uint32 acquire_initiator_cred
106		  (OM_uint32 * minor_status,
107		   krb5_context context,
108		   const gss_name_t desired_name,
109		   OM_uint32 time_req,
110		   const gss_OID_set desired_mechs,
111		   gss_cred_usage_t cred_usage,
112		   gsskrb5_cred handle,
113		   gss_OID_set * actual_mechs,
114		   OM_uint32 * time_rec
115		  )
116{
117    OM_uint32 ret;
118    krb5_creds cred;
119    krb5_principal def_princ;
120    krb5_get_init_creds_opt *opt;
121    krb5_ccache ccache;
122    krb5_keytab keytab;
123    krb5_error_code kret;
124
125    keytab = NULL;
126    ccache = NULL;
127    def_princ = NULL;
128    ret = GSS_S_FAILURE;
129    memset(&cred, 0, sizeof(cred));
130
131    /* If we have a preferred principal, lets try to find it in all
132     * caches, otherwise, fall back to default cache.  Ignore
133     * errors. */
134    if (handle->principal)
135	kret = krb5_cc_cache_match (context,
136				    handle->principal,
137				    NULL,
138				    &ccache);
139
140    if (ccache == NULL) {
141	kret = krb5_cc_default(context, &ccache);
142	if (kret)
143	    goto end;
144    }
145    kret = krb5_cc_get_principal(context, ccache,
146	&def_princ);
147    if (kret != 0) {
148	/* we'll try to use a keytab below */
149	krb5_cc_destroy(context, ccache);
150	ccache = NULL;
151	kret = 0;
152    } else if (handle->principal == NULL)  {
153	kret = krb5_copy_principal(context, def_princ,
154	    &handle->principal);
155	if (kret)
156	    goto end;
157    } else if (handle->principal != NULL &&
158	krb5_principal_compare(context, handle->principal,
159	def_princ) == FALSE) {
160	/* Before failing, lets check the keytab */
161	krb5_free_principal(context, def_princ);
162	def_princ = NULL;
163    }
164    if (def_princ == NULL) {
165	/* We have no existing credentials cache,
166	 * so attempt to get a TGT using a keytab.
167	 */
168	if (handle->principal == NULL) {
169	    kret = krb5_get_default_principal(context,
170		&handle->principal);
171	    if (kret)
172		goto end;
173	}
174	kret = get_keytab(context, &keytab);
175	if (kret)
176	    goto end;
177	kret = krb5_get_init_creds_opt_alloc(context, &opt);
178	if (kret)
179	    goto end;
180	kret = krb5_get_init_creds_keytab(context, &cred,
181	    handle->principal, keytab, 0, NULL, opt);
182	krb5_get_init_creds_opt_free(context, opt);
183	if (kret)
184	    goto end;
185	kret = krb5_cc_gen_new(context, &krb5_mcc_ops,
186		&ccache);
187	if (kret)
188	    goto end;
189	kret = krb5_cc_initialize(context, ccache, cred.client);
190	if (kret)
191	    goto end;
192	kret = krb5_cc_store_cred(context, ccache, &cred);
193	if (kret)
194	    goto end;
195	handle->lifetime = cred.times.endtime;
196	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
197    } else {
198
199	ret = __gsskrb5_ccache_lifetime(minor_status,
200					context,
201					ccache,
202					handle->principal,
203					&handle->lifetime);
204	if (ret != GSS_S_COMPLETE)
205	    goto end;
206	kret = 0;
207    }
208
209    handle->ccache = ccache;
210    ret = GSS_S_COMPLETE;
211
212end:
213    if (cred.client != NULL)
214	krb5_free_cred_contents(context, &cred);
215    if (def_princ != NULL)
216	krb5_free_principal(context, def_princ);
217    if (keytab != NULL)
218	krb5_kt_close(context, keytab);
219    if (ret != GSS_S_COMPLETE) {
220	if (ccache != NULL)
221	    krb5_cc_close(context, ccache);
222	if (kret != 0) {
223	    *minor_status = kret;
224	}
225    }
226    return (ret);
227}
228
229static OM_uint32 acquire_acceptor_cred
230		  (OM_uint32 * minor_status,
231		   krb5_context context,
232		   const gss_name_t desired_name,
233		   OM_uint32 time_req,
234		   const gss_OID_set desired_mechs,
235		   gss_cred_usage_t cred_usage,
236		   gsskrb5_cred handle,
237		   gss_OID_set * actual_mechs,
238		   OM_uint32 * time_rec
239		  )
240{
241    OM_uint32 ret;
242    krb5_error_code kret;
243
244    kret = 0;
245    ret = GSS_S_FAILURE;
246    kret = get_keytab(context, &handle->keytab);
247    if (kret)
248	goto end;
249
250    /* check that the requested principal exists in the keytab */
251    if (handle->principal) {
252	krb5_keytab_entry entry;
253
254	kret = krb5_kt_get_entry(context, handle->keytab,
255				 handle->principal, 0, 0, &entry);
256	if (kret)
257	    goto end;
258	krb5_kt_free_entry(context, &entry);
259	ret = GSS_S_COMPLETE;
260    } else {
261	/*
262	 * Check if there is at least one entry in the keytab before
263	 * declaring it as an useful keytab.
264	 */
265	krb5_keytab_entry tmp;
266	krb5_kt_cursor c;
267
268	kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
269	if (kret)
270	    goto end;
271	if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
272	    krb5_kt_free_entry(context, &tmp);
273	    ret = GSS_S_COMPLETE; /* ok found one entry */
274	}
275	krb5_kt_end_seq_get (context, handle->keytab, &c);
276    }
277end:
278    if (ret != GSS_S_COMPLETE) {
279	if (handle->keytab != NULL)
280	    krb5_kt_close(context, handle->keytab);
281	if (kret != 0) {
282	    *minor_status = kret;
283	}
284    }
285    return (ret);
286}
287
288OM_uint32 _gsskrb5_acquire_cred
289(OM_uint32 * minor_status,
290 const gss_name_t desired_name,
291 OM_uint32 time_req,
292 const gss_OID_set desired_mechs,
293 gss_cred_usage_t cred_usage,
294 gss_cred_id_t * output_cred_handle,
295 gss_OID_set * actual_mechs,
296 OM_uint32 * time_rec
297    )
298{
299    krb5_context context;
300    gsskrb5_cred handle;
301    OM_uint32 ret;
302
303    if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
304	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
305	return GSS_S_FAILURE;
306    }
307
308    GSSAPI_KRB5_INIT(&context);
309
310    *output_cred_handle = NULL;
311    if (time_rec)
312	*time_rec = 0;
313    if (actual_mechs)
314	*actual_mechs = GSS_C_NO_OID_SET;
315
316    if (desired_mechs) {
317	int present = 0;
318
319	ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
320				      desired_mechs, &present);
321	if (ret)
322	    return ret;
323	if (!present) {
324	    *minor_status = 0;
325	    return GSS_S_BAD_MECH;
326	}
327    }
328
329    handle = calloc(1, sizeof(*handle));
330    if (handle == NULL) {
331	*minor_status = ENOMEM;
332        return (GSS_S_FAILURE);
333    }
334
335    HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
336
337    if (desired_name != GSS_C_NO_NAME) {
338	krb5_principal name = (krb5_principal)desired_name;
339	ret = krb5_copy_principal(context, name, &handle->principal);
340	if (ret) {
341	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
342	    *minor_status = ret;
343	    free(handle);
344	    return GSS_S_FAILURE;
345	}
346    }
347    if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
348	ret = acquire_initiator_cred(minor_status, context,
349				     desired_name, time_req,
350				     desired_mechs, cred_usage, handle,
351				     actual_mechs, time_rec);
352    	if (ret != GSS_S_COMPLETE) {
353	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
354	    krb5_free_principal(context, handle->principal);
355	    free(handle);
356	    return (ret);
357	}
358    }
359    if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
360	ret = acquire_acceptor_cred(minor_status, context,
361				    desired_name, time_req,
362				    desired_mechs, cred_usage, handle, actual_mechs, time_rec);
363	if (ret != GSS_S_COMPLETE) {
364	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
365	    krb5_free_principal(context, handle->principal);
366	    free(handle);
367	    return (ret);
368	}
369    }
370    ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
371    if (ret == GSS_S_COMPLETE)
372    	ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
373				     &handle->mechanisms);
374    if (ret == GSS_S_COMPLETE)
375    	ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle,
376				    NULL, time_rec, NULL, actual_mechs);
377    if (ret != GSS_S_COMPLETE) {
378	if (handle->mechanisms != NULL)
379	    gss_release_oid_set(NULL, &handle->mechanisms);
380	HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
381	krb5_free_principal(context, handle->principal);
382	free(handle);
383	return (ret);
384    }
385    *minor_status = 0;
386    if (time_rec) {
387	ret = _gsskrb5_lifetime_left(minor_status,
388				     context,
389				     handle->lifetime,
390				     time_rec);
391
392	if (ret)
393	    return ret;
394    }
395    handle->usage = cred_usage;
396    *output_cred_handle = (gss_cred_id_t)handle;
397    return (GSS_S_COMPLETE);
398}
399