1/*	$NetBSD: acquire_cred.c,v 1.1.1.1 2011/04/13 18:14:44 elric Exp $	*/
2
3/*
4 * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "gsskrb5_locl.h"
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_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred);
67    krb5_free_principal(context, in_cred.server);
68    if (kret) {
69	*minor_status = 0;
70	*lifetime = 0;
71	return GSS_S_COMPLETE;
72    }
73
74    *lifetime = out_cred.times.endtime;
75    krb5_free_cred_contents(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    krb5_error_code kret;
87
88    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
89
90    if (_gsskrb5_keytab != NULL) {
91	char *name = NULL;
92
93	kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
94	if (kret == 0) {
95	    kret = krb5_kt_resolve(context, name, keytab);
96	    krb5_xfree(name);
97	}
98    } else
99	kret = krb5_kt_default(context, keytab);
100
101    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
102
103    return (kret);
104}
105
106static OM_uint32 acquire_initiator_cred
107		  (OM_uint32 * minor_status,
108		   krb5_context context,
109		   const gss_name_t desired_name,
110		   OM_uint32 time_req,
111		   const gss_OID_set desired_mechs,
112		   gss_cred_usage_t cred_usage,
113		   gsskrb5_cred handle,
114		   gss_OID_set * actual_mechs,
115		   OM_uint32 * time_rec
116		  )
117{
118    OM_uint32 ret;
119    krb5_creds cred;
120    krb5_principal def_princ;
121    krb5_get_init_creds_opt *opt;
122    krb5_ccache ccache;
123    krb5_keytab keytab;
124    krb5_error_code kret;
125
126    keytab = NULL;
127    ccache = NULL;
128    def_princ = NULL;
129    ret = GSS_S_FAILURE;
130    memset(&cred, 0, sizeof(cred));
131
132    /*
133     * If we have a preferred principal, lets try to find it in all
134     * caches, otherwise, fall back to default cache, ignore all
135     * errors while searching.
136     */
137
138    if (handle->principal) {
139	kret = krb5_cc_cache_match (context,
140				    handle->principal,
141				    &ccache);
142	if (kret == 0) {
143	    ret = GSS_S_COMPLETE;
144	    goto found;
145	}
146    }
147
148    if (ccache == NULL) {
149	kret = krb5_cc_default(context, &ccache);
150	if (kret)
151	    goto end;
152    }
153    kret = krb5_cc_get_principal(context, ccache, &def_princ);
154    if (kret != 0) {
155	/* we'll try to use a keytab below */
156	krb5_cc_close(context, ccache);
157	def_princ = NULL;
158	kret = 0;
159    } else if (handle->principal == NULL)  {
160	kret = krb5_copy_principal(context, def_princ, &handle->principal);
161	if (kret)
162	    goto end;
163    } else if (handle->principal != NULL &&
164	       krb5_principal_compare(context, handle->principal,
165				      def_princ) == FALSE) {
166	krb5_free_principal(context, def_princ);
167	def_princ = NULL;
168	krb5_cc_close(context, ccache);
169	ccache = NULL;
170    }
171    if (def_princ == NULL) {
172	/* We have no existing credentials cache,
173	 * so attempt to get a TGT using a keytab.
174	 */
175	if (handle->principal == NULL) {
176	    kret = krb5_get_default_principal(context, &handle->principal);
177	    if (kret)
178		goto end;
179	}
180	kret = get_keytab(context, &keytab);
181	if (kret)
182	    goto end;
183	kret = krb5_get_init_creds_opt_alloc(context, &opt);
184	if (kret)
185	    goto end;
186	kret = krb5_get_init_creds_keytab(context, &cred,
187	    handle->principal, keytab, 0, NULL, opt);
188	krb5_get_init_creds_opt_free(context, opt);
189	if (kret)
190	    goto end;
191	kret = krb5_cc_new_unique(context, krb5_cc_type_memory,
192				  NULL, &ccache);
193	if (kret)
194	    goto end;
195	kret = krb5_cc_initialize(context, ccache, cred.client);
196	if (kret) {
197	    krb5_cc_destroy(context, ccache);
198	    goto end;
199	}
200	kret = krb5_cc_store_cred(context, ccache, &cred);
201	if (kret) {
202	    krb5_cc_destroy(context, ccache);
203	    goto end;
204	}
205	handle->lifetime = cred.times.endtime;
206	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
207    } else {
208
209	ret = __gsskrb5_ccache_lifetime(minor_status,
210					context,
211					ccache,
212					handle->principal,
213					&handle->lifetime);
214	if (ret != GSS_S_COMPLETE) {
215	    krb5_cc_close(context, ccache);
216	    goto end;
217	}
218	kret = 0;
219    }
220 found:
221    handle->ccache = ccache;
222    ret = GSS_S_COMPLETE;
223
224end:
225    if (cred.client != NULL)
226	krb5_free_cred_contents(context, &cred);
227    if (def_princ != NULL)
228	krb5_free_principal(context, def_princ);
229    if (keytab != NULL)
230	krb5_kt_close(context, keytab);
231    if (ret != GSS_S_COMPLETE && kret != 0)
232	*minor_status = kret;
233    return (ret);
234}
235
236static OM_uint32 acquire_acceptor_cred
237		  (OM_uint32 * minor_status,
238		   krb5_context context,
239		   const gss_name_t desired_name,
240		   OM_uint32 time_req,
241		   const gss_OID_set desired_mechs,
242		   gss_cred_usage_t cred_usage,
243		   gsskrb5_cred handle,
244		   gss_OID_set * actual_mechs,
245		   OM_uint32 * time_rec
246		  )
247{
248    OM_uint32 ret;
249    krb5_error_code kret;
250
251    ret = GSS_S_FAILURE;
252    kret = get_keytab(context, &handle->keytab);
253    if (kret)
254	goto end;
255
256    /* check that the requested principal exists in the keytab */
257    if (handle->principal) {
258	krb5_keytab_entry entry;
259
260	kret = krb5_kt_get_entry(context, handle->keytab,
261				 handle->principal, 0, 0, &entry);
262	if (kret)
263	    goto end;
264	krb5_kt_free_entry(context, &entry);
265	ret = GSS_S_COMPLETE;
266    } else {
267	/*
268	 * Check if there is at least one entry in the keytab before
269	 * declaring it as an useful keytab.
270	 */
271	krb5_keytab_entry tmp;
272	krb5_kt_cursor c;
273
274	kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
275	if (kret)
276	    goto end;
277	if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
278	    krb5_kt_free_entry(context, &tmp);
279	    ret = GSS_S_COMPLETE; /* ok found one entry */
280	}
281	krb5_kt_end_seq_get (context, handle->keytab, &c);
282    }
283end:
284    if (ret != GSS_S_COMPLETE) {
285	if (handle->keytab != NULL)
286	    krb5_kt_close(context, handle->keytab);
287	if (kret != 0) {
288	    *minor_status = kret;
289	}
290    }
291    return (ret);
292}
293
294OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
295(OM_uint32 * minor_status,
296 const gss_name_t desired_name,
297 OM_uint32 time_req,
298 const gss_OID_set desired_mechs,
299 gss_cred_usage_t cred_usage,
300 gss_cred_id_t * output_cred_handle,
301 gss_OID_set * actual_mechs,
302 OM_uint32 * time_rec
303    )
304{
305    krb5_context context;
306    gsskrb5_cred handle;
307    OM_uint32 ret;
308
309    if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
310	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
311	return GSS_S_FAILURE;
312    }
313
314    GSSAPI_KRB5_INIT(&context);
315
316    *output_cred_handle = NULL;
317    if (time_rec)
318	*time_rec = 0;
319    if (actual_mechs)
320	*actual_mechs = GSS_C_NO_OID_SET;
321
322    if (desired_mechs) {
323	int present = 0;
324
325	ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
326				      desired_mechs, &present);
327	if (ret)
328	    return ret;
329	if (!present) {
330	    *minor_status = 0;
331	    return GSS_S_BAD_MECH;
332	}
333    }
334
335    handle = calloc(1, sizeof(*handle));
336    if (handle == NULL) {
337	*minor_status = ENOMEM;
338        return (GSS_S_FAILURE);
339    }
340
341    HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
342
343    if (desired_name != GSS_C_NO_NAME) {
344
345	ret = _gsskrb5_canon_name(minor_status, context, 1, NULL,
346				  desired_name, &handle->principal);
347	if (ret) {
348	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
349	    free(handle);
350	    return ret;
351	}
352    }
353    if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
354	ret = acquire_initiator_cred(minor_status, context,
355				     desired_name, time_req,
356				     desired_mechs, cred_usage, handle,
357				     actual_mechs, time_rec);
358    	if (ret != GSS_S_COMPLETE) {
359	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
360	    krb5_free_principal(context, handle->principal);
361	    free(handle);
362	    return (ret);
363	}
364    }
365    if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
366	ret = acquire_acceptor_cred(minor_status, context,
367				    desired_name, time_req,
368				    desired_mechs, cred_usage, handle, actual_mechs, time_rec);
369	if (ret != GSS_S_COMPLETE) {
370	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
371	    krb5_free_principal(context, handle->principal);
372	    free(handle);
373	    return (ret);
374	}
375    }
376    ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
377    if (ret == GSS_S_COMPLETE)
378    	ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
379				     &handle->mechanisms);
380    if (ret == GSS_S_COMPLETE)
381    	ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle,
382				    NULL, time_rec, NULL, actual_mechs);
383    if (ret != GSS_S_COMPLETE) {
384	if (handle->mechanisms != NULL)
385	    gss_release_oid_set(NULL, &handle->mechanisms);
386	HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
387	krb5_free_principal(context, handle->principal);
388	free(handle);
389	return (ret);
390    }
391    *minor_status = 0;
392    if (time_rec) {
393	ret = _gsskrb5_lifetime_left(minor_status,
394				     context,
395				     handle->lifetime,
396				     time_rec);
397
398	if (ret)
399	    return ret;
400    }
401    handle->usage = cred_usage;
402    *output_cred_handle = (gss_cred_id_t)handle;
403    return (GSS_S_COMPLETE);
404}
405