1178825Sdfr/*
2233294Sstas * Copyright (c) 1997 - 2005 Kungliga Tekniska H��gskolan
3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden).
4233294Sstas * All rights reserved.
5178825Sdfr *
6233294Sstas * Redistribution and use in source and binary forms, with or without
7233294Sstas * modification, are permitted provided that the following conditions
8233294Sstas * are met:
9178825Sdfr *
10233294Sstas * 1. Redistributions of source code must retain the above copyright
11233294Sstas *    notice, this list of conditions and the following disclaimer.
12178825Sdfr *
13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright
14233294Sstas *    notice, this list of conditions and the following disclaimer in the
15233294Sstas *    documentation and/or other materials provided with the distribution.
16178825Sdfr *
17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors
18233294Sstas *    may be used to endorse or promote products derived from this software
19233294Sstas *    without specific prior written permission.
20178825Sdfr *
21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24233294Sstas * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31233294Sstas * SUCH DAMAGE.
32178825Sdfr */
33178825Sdfr
34233294Sstas#include "gsskrb5_locl.h"
35178825Sdfr
36178825SdfrOM_uint32
37178825Sdfr__gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
38178825Sdfr			  krb5_context context,
39178825Sdfr			  krb5_ccache id,
40178825Sdfr			  krb5_principal principal,
41178825Sdfr			  OM_uint32 *lifetime)
42178825Sdfr{
43233294Sstas    krb5_creds in_cred, out_cred;
44178825Sdfr    krb5_const_realm realm;
45178825Sdfr    krb5_error_code kret;
46178825Sdfr
47178825Sdfr    memset(&in_cred, 0, sizeof(in_cred));
48178825Sdfr    in_cred.client = principal;
49233294Sstas
50178825Sdfr    realm = krb5_principal_get_realm(context,  principal);
51178825Sdfr    if (realm == NULL) {
52178825Sdfr	_gsskrb5_clear_status ();
53178825Sdfr	*minor_status = KRB5_PRINC_NOMATCH; /* XXX */
54178825Sdfr	return GSS_S_FAILURE;
55178825Sdfr    }
56178825Sdfr
57233294Sstas    kret = krb5_make_principal(context, &in_cred.server,
58178825Sdfr			       realm, KRB5_TGS_NAME, realm, NULL);
59178825Sdfr    if (kret) {
60178825Sdfr	*minor_status = kret;
61178825Sdfr	return GSS_S_FAILURE;
62178825Sdfr    }
63178825Sdfr
64233294Sstas    kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred);
65178825Sdfr    krb5_free_principal(context, in_cred.server);
66178825Sdfr    if (kret) {
67233294Sstas	*minor_status = 0;
68233294Sstas	*lifetime = 0;
69233294Sstas	return GSS_S_COMPLETE;
70178825Sdfr    }
71178825Sdfr
72233294Sstas    *lifetime = out_cred.times.endtime;
73233294Sstas    krb5_free_cred_contents(context, &out_cred);
74178825Sdfr
75178825Sdfr    return GSS_S_COMPLETE;
76178825Sdfr}
77178825Sdfr
78178825Sdfr
79178825Sdfr
80178825Sdfr
81178825Sdfrstatic krb5_error_code
82178825Sdfrget_keytab(krb5_context context, krb5_keytab *keytab)
83178825Sdfr{
84178825Sdfr    krb5_error_code kret;
85178825Sdfr
86178825Sdfr    HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
87178825Sdfr
88178825Sdfr    if (_gsskrb5_keytab != NULL) {
89233294Sstas	char *name = NULL;
90233294Sstas
91233294Sstas	kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
92233294Sstas	if (kret == 0) {
93233294Sstas	    kret = krb5_kt_resolve(context, name, keytab);
94233294Sstas	    krb5_xfree(name);
95233294Sstas	}
96178825Sdfr    } else
97178825Sdfr	kret = krb5_kt_default(context, keytab);
98178825Sdfr
99178825Sdfr    HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
100178825Sdfr
101178825Sdfr    return (kret);
102178825Sdfr}
103178825Sdfr
104178825Sdfrstatic OM_uint32 acquire_initiator_cred
105178825Sdfr		  (OM_uint32 * minor_status,
106178825Sdfr		   krb5_context context,
107233294Sstas		   gss_const_OID credential_type,
108233294Sstas		   const void *credential_data,
109178825Sdfr		   const gss_name_t desired_name,
110178825Sdfr		   OM_uint32 time_req,
111233294Sstas		   gss_const_OID desired_mech,
112178825Sdfr		   gss_cred_usage_t cred_usage,
113233294Sstas		   gsskrb5_cred handle
114178825Sdfr		  )
115178825Sdfr{
116178825Sdfr    OM_uint32 ret;
117178825Sdfr    krb5_creds cred;
118178825Sdfr    krb5_principal def_princ;
119178825Sdfr    krb5_get_init_creds_opt *opt;
120178825Sdfr    krb5_ccache ccache;
121178825Sdfr    krb5_keytab keytab;
122178825Sdfr    krb5_error_code kret;
123178825Sdfr
124178825Sdfr    keytab = NULL;
125178825Sdfr    ccache = NULL;
126178825Sdfr    def_princ = NULL;
127178825Sdfr    ret = GSS_S_FAILURE;
128178825Sdfr    memset(&cred, 0, sizeof(cred));
129178825Sdfr
130233294Sstas    /*
131233294Sstas     * If we have a preferred principal, lets try to find it in all
132233294Sstas     * caches, otherwise, fall back to default cache, ignore all
133233294Sstas     * errors while searching.
134233294Sstas     */
135233294Sstas
136233294Sstas    if (credential_type != GSS_C_NO_OID &&
137233294Sstas	!gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
138233294Sstas	kret = KRB5_NOCREDS_SUPPLIED; /* XXX */
139233294Sstas	goto end;
140233294Sstas    }
141233294Sstas
142233294Sstas    if (handle->principal) {
143178825Sdfr	kret = krb5_cc_cache_match (context,
144178825Sdfr				    handle->principal,
145178825Sdfr				    &ccache);
146233294Sstas	if (kret == 0) {
147233294Sstas	    ret = GSS_S_COMPLETE;
148233294Sstas	    goto found;
149233294Sstas	}
150233294Sstas    }
151233294Sstas
152178825Sdfr    if (ccache == NULL) {
153178825Sdfr	kret = krb5_cc_default(context, &ccache);
154178825Sdfr	if (kret)
155178825Sdfr	    goto end;
156178825Sdfr    }
157233294Sstas    kret = krb5_cc_get_principal(context, ccache, &def_princ);
158178825Sdfr    if (kret != 0) {
159178825Sdfr	/* we'll try to use a keytab below */
160233294Sstas	krb5_cc_close(context, ccache);
161233294Sstas	def_princ = NULL;
162178825Sdfr	kret = 0;
163178825Sdfr    } else if (handle->principal == NULL)  {
164233294Sstas	kret = krb5_copy_principal(context, def_princ, &handle->principal);
165178825Sdfr	if (kret)
166178825Sdfr	    goto end;
167178825Sdfr    } else if (handle->principal != NULL &&
168233294Sstas	       krb5_principal_compare(context, handle->principal,
169233294Sstas				      def_princ) == FALSE) {
170178825Sdfr	krb5_free_principal(context, def_princ);
171178825Sdfr	def_princ = NULL;
172233294Sstas	krb5_cc_close(context, ccache);
173233294Sstas	ccache = NULL;
174178825Sdfr    }
175178825Sdfr    if (def_princ == NULL) {
176178825Sdfr	/* We have no existing credentials cache,
177178825Sdfr	 * so attempt to get a TGT using a keytab.
178178825Sdfr	 */
179178825Sdfr	if (handle->principal == NULL) {
180233294Sstas	    kret = krb5_get_default_principal(context, &handle->principal);
181178825Sdfr	    if (kret)
182178825Sdfr		goto end;
183178825Sdfr	}
184178825Sdfr	kret = krb5_get_init_creds_opt_alloc(context, &opt);
185178825Sdfr	if (kret)
186178825Sdfr	    goto end;
187233294Sstas	if (credential_type != GSS_C_NO_OID &&
188233294Sstas	    gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
189233294Sstas	    gss_buffer_t password = (gss_buffer_t)credential_data;
190233294Sstas
191233294Sstas	    /* XXX are we requiring password to be NUL terminated? */
192233294Sstas
193233294Sstas	    kret = krb5_get_init_creds_password(context, &cred,
194233294Sstas						handle->principal,
195233294Sstas						password->value,
196233294Sstas						NULL, NULL, 0, NULL, opt);
197233294Sstas	} else {
198233294Sstas	    kret = get_keytab(context, &keytab);
199233294Sstas	    if (kret) {
200233294Sstas		krb5_get_init_creds_opt_free(context, opt);
201233294Sstas		goto end;
202233294Sstas	    }
203233294Sstas	    kret = krb5_get_init_creds_keytab(context, &cred,
204233294Sstas					      handle->principal, keytab,
205233294Sstas					      0, NULL, opt);
206233294Sstas	}
207178825Sdfr	krb5_get_init_creds_opt_free(context, opt);
208178825Sdfr	if (kret)
209178825Sdfr	    goto end;
210233294Sstas	kret = krb5_cc_new_unique(context, krb5_cc_type_memory,
211233294Sstas				  NULL, &ccache);
212178825Sdfr	if (kret)
213178825Sdfr	    goto end;
214178825Sdfr	kret = krb5_cc_initialize(context, ccache, cred.client);
215233294Sstas	if (kret) {
216233294Sstas	    krb5_cc_destroy(context, ccache);
217178825Sdfr	    goto end;
218233294Sstas	}
219178825Sdfr	kret = krb5_cc_store_cred(context, ccache, &cred);
220233294Sstas	if (kret) {
221233294Sstas	    krb5_cc_destroy(context, ccache);
222178825Sdfr	    goto end;
223233294Sstas	}
224178825Sdfr	handle->lifetime = cred.times.endtime;
225178825Sdfr	handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
226178825Sdfr    } else {
227178825Sdfr
228178825Sdfr	ret = __gsskrb5_ccache_lifetime(minor_status,
229178825Sdfr					context,
230178825Sdfr					ccache,
231178825Sdfr					handle->principal,
232178825Sdfr					&handle->lifetime);
233233294Sstas	if (ret != GSS_S_COMPLETE) {
234233294Sstas	    krb5_cc_close(context, ccache);
235178825Sdfr	    goto end;
236233294Sstas	}
237178825Sdfr	kret = 0;
238178825Sdfr    }
239233294Sstas found:
240178825Sdfr    handle->ccache = ccache;
241178825Sdfr    ret = GSS_S_COMPLETE;
242178825Sdfr
243178825Sdfrend:
244178825Sdfr    if (cred.client != NULL)
245178825Sdfr	krb5_free_cred_contents(context, &cred);
246178825Sdfr    if (def_princ != NULL)
247178825Sdfr	krb5_free_principal(context, def_princ);
248178825Sdfr    if (keytab != NULL)
249178825Sdfr	krb5_kt_close(context, keytab);
250233294Sstas    if (ret != GSS_S_COMPLETE && kret != 0)
251233294Sstas	*minor_status = kret;
252178825Sdfr    return (ret);
253178825Sdfr}
254178825Sdfr
255178825Sdfrstatic OM_uint32 acquire_acceptor_cred
256178825Sdfr		  (OM_uint32 * minor_status,
257178825Sdfr		   krb5_context context,
258233294Sstas		   gss_const_OID credential_type,
259233294Sstas		   const void *credential_data,
260178825Sdfr		   const gss_name_t desired_name,
261178825Sdfr		   OM_uint32 time_req,
262233294Sstas		   gss_const_OID desired_mech,
263178825Sdfr		   gss_cred_usage_t cred_usage,
264233294Sstas		   gsskrb5_cred handle
265178825Sdfr		  )
266178825Sdfr{
267178825Sdfr    OM_uint32 ret;
268178825Sdfr    krb5_error_code kret;
269178825Sdfr
270178825Sdfr    ret = GSS_S_FAILURE;
271233294Sstas
272233294Sstas    if (credential_type != GSS_C_NO_OID) {
273233294Sstas	kret = EINVAL;
274233294Sstas	goto end;
275233294Sstas    }
276233294Sstas
277178825Sdfr    kret = get_keytab(context, &handle->keytab);
278178825Sdfr    if (kret)
279178825Sdfr	goto end;
280233294Sstas
281178825Sdfr    /* check that the requested principal exists in the keytab */
282178825Sdfr    if (handle->principal) {
283178825Sdfr	krb5_keytab_entry entry;
284178825Sdfr
285233294Sstas	kret = krb5_kt_get_entry(context, handle->keytab,
286178825Sdfr				 handle->principal, 0, 0, &entry);
287178825Sdfr	if (kret)
288178825Sdfr	    goto end;
289178825Sdfr	krb5_kt_free_entry(context, &entry);
290178825Sdfr	ret = GSS_S_COMPLETE;
291178825Sdfr    } else {
292233294Sstas	/*
293178825Sdfr	 * Check if there is at least one entry in the keytab before
294178825Sdfr	 * declaring it as an useful keytab.
295178825Sdfr	 */
296178825Sdfr	krb5_keytab_entry tmp;
297178825Sdfr	krb5_kt_cursor c;
298178825Sdfr
299178825Sdfr	kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
300178825Sdfr	if (kret)
301178825Sdfr	    goto end;
302178825Sdfr	if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
303178825Sdfr	    krb5_kt_free_entry(context, &tmp);
304178825Sdfr	    ret = GSS_S_COMPLETE; /* ok found one entry */
305178825Sdfr	}
306178825Sdfr	krb5_kt_end_seq_get (context, handle->keytab, &c);
307233294Sstas    }
308178825Sdfrend:
309178825Sdfr    if (ret != GSS_S_COMPLETE) {
310178825Sdfr	if (handle->keytab != NULL)
311178825Sdfr	    krb5_kt_close(context, handle->keytab);
312178825Sdfr	if (kret != 0) {
313178825Sdfr	    *minor_status = kret;
314178825Sdfr	}
315178825Sdfr    }
316178825Sdfr    return (ret);
317178825Sdfr}
318178825Sdfr
319233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
320178825Sdfr(OM_uint32 * minor_status,
321178825Sdfr const gss_name_t desired_name,
322178825Sdfr OM_uint32 time_req,
323178825Sdfr const gss_OID_set desired_mechs,
324178825Sdfr gss_cred_usage_t cred_usage,
325178825Sdfr gss_cred_id_t * output_cred_handle,
326178825Sdfr gss_OID_set * actual_mechs,
327178825Sdfr OM_uint32 * time_rec
328178825Sdfr    )
329178825Sdfr{
330178825Sdfr    OM_uint32 ret;
331178825Sdfr
332178825Sdfr    if (desired_mechs) {
333178825Sdfr	int present = 0;
334178825Sdfr
335178825Sdfr	ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
336233294Sstas				      desired_mechs, &present);
337178825Sdfr	if (ret)
338178825Sdfr	    return ret;
339178825Sdfr	if (!present) {
340178825Sdfr	    *minor_status = 0;
341178825Sdfr	    return GSS_S_BAD_MECH;
342178825Sdfr	}
343178825Sdfr    }
344178825Sdfr
345233294Sstas    ret = _gsskrb5_acquire_cred_ext(minor_status,
346233294Sstas				    desired_name,
347233294Sstas				    GSS_C_NO_OID,
348233294Sstas				    NULL,
349233294Sstas				    time_req,
350233294Sstas				    GSS_KRB5_MECHANISM,
351233294Sstas				    cred_usage,
352233294Sstas				    output_cred_handle);
353233294Sstas    if (ret)
354233294Sstas	return ret;
355233294Sstas
356233294Sstas
357233294Sstas    ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle,
358233294Sstas				NULL, time_rec, NULL, actual_mechs);
359233294Sstas    if (ret) {
360233294Sstas	OM_uint32 tmp;
361233294Sstas	_gsskrb5_release_cred(&tmp, output_cred_handle);
362233294Sstas    }
363233294Sstas
364233294Sstas    return ret;
365233294Sstas}
366233294Sstas
367233294SstasOM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
368233294Sstas(OM_uint32 * minor_status,
369233294Sstas const gss_name_t desired_name,
370233294Sstas gss_const_OID credential_type,
371233294Sstas const void *credential_data,
372233294Sstas OM_uint32 time_req,
373233294Sstas gss_const_OID desired_mech,
374233294Sstas gss_cred_usage_t cred_usage,
375233294Sstas gss_cred_id_t * output_cred_handle
376233294Sstas    )
377233294Sstas{
378233294Sstas    krb5_context context;
379233294Sstas    gsskrb5_cred handle;
380233294Sstas    OM_uint32 ret;
381233294Sstas
382233294Sstas    cred_usage &= GSS_C_OPTION_MASK;
383233294Sstas
384233294Sstas    if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
385233294Sstas	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
386233294Sstas	return GSS_S_FAILURE;
387233294Sstas    }
388233294Sstas
389233294Sstas    GSSAPI_KRB5_INIT(&context);
390233294Sstas
391233294Sstas    *output_cred_handle = NULL;
392233294Sstas
393178825Sdfr    handle = calloc(1, sizeof(*handle));
394178825Sdfr    if (handle == NULL) {
395178825Sdfr	*minor_status = ENOMEM;
396178825Sdfr        return (GSS_S_FAILURE);
397178825Sdfr    }
398178825Sdfr
399178825Sdfr    HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
400178825Sdfr
401178825Sdfr    if (desired_name != GSS_C_NO_NAME) {
402233294Sstas	ret = _gsskrb5_canon_name(minor_status, context, 1, NULL,
403233294Sstas				  desired_name, &handle->principal);
404178825Sdfr	if (ret) {
405178825Sdfr	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
406178825Sdfr	    free(handle);
407233294Sstas	    return ret;
408178825Sdfr	}
409178825Sdfr    }
410178825Sdfr    if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
411178825Sdfr	ret = acquire_initiator_cred(minor_status, context,
412233294Sstas				     credential_type, credential_data,
413178825Sdfr				     desired_name, time_req,
414233294Sstas				     desired_mech, cred_usage, handle);
415178825Sdfr    	if (ret != GSS_S_COMPLETE) {
416178825Sdfr	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
417178825Sdfr	    krb5_free_principal(context, handle->principal);
418178825Sdfr	    free(handle);
419178825Sdfr	    return (ret);
420178825Sdfr	}
421178825Sdfr    }
422178825Sdfr    if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
423178825Sdfr	ret = acquire_acceptor_cred(minor_status, context,
424233294Sstas				    credential_type, credential_data,
425178825Sdfr				    desired_name, time_req,
426233294Sstas				    desired_mech, cred_usage, handle);
427178825Sdfr	if (ret != GSS_S_COMPLETE) {
428178825Sdfr	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
429178825Sdfr	    krb5_free_principal(context, handle->principal);
430178825Sdfr	    free(handle);
431178825Sdfr	    return (ret);
432178825Sdfr	}
433178825Sdfr    }
434178825Sdfr    ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
435178825Sdfr    if (ret == GSS_S_COMPLETE)
436178825Sdfr    	ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
437178825Sdfr				     &handle->mechanisms);
438178825Sdfr    if (ret != GSS_S_COMPLETE) {
439178825Sdfr	if (handle->mechanisms != NULL)
440178825Sdfr	    gss_release_oid_set(NULL, &handle->mechanisms);
441178825Sdfr	HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
442178825Sdfr	krb5_free_principal(context, handle->principal);
443178825Sdfr	free(handle);
444178825Sdfr	return (ret);
445178825Sdfr    }
446178825Sdfr    handle->usage = cred_usage;
447233294Sstas    *minor_status = 0;
448178825Sdfr    *output_cred_handle = (gss_cred_id_t)handle;
449178825Sdfr    return (GSS_S_COMPLETE);
450178825Sdfr}
451