xref: /netbsd-src/crypto/external/bsd/heimdal/dist/lib/gssapi/krb5/acquire_cred.c (revision afab4e300d3a9fb07dd8c80daf53d0feb3345706)
1*afab4e30Schristos /*	$NetBSD: acquire_cred.c,v 1.3 2023/06/19 21:41:43 christos Exp $	*/
2ca1c9b0cSelric 
3ca1c9b0cSelric /*
4ca1c9b0cSelric  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
5ca1c9b0cSelric  * (Royal Institute of Technology, Stockholm, Sweden).
6ca1c9b0cSelric  * All rights reserved.
7ca1c9b0cSelric  *
8ca1c9b0cSelric  * Redistribution and use in source and binary forms, with or without
9ca1c9b0cSelric  * modification, are permitted provided that the following conditions
10ca1c9b0cSelric  * are met:
11ca1c9b0cSelric  *
12ca1c9b0cSelric  * 1. Redistributions of source code must retain the above copyright
13ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer.
14ca1c9b0cSelric  *
15ca1c9b0cSelric  * 2. Redistributions in binary form must reproduce the above copyright
16ca1c9b0cSelric  *    notice, this list of conditions and the following disclaimer in the
17ca1c9b0cSelric  *    documentation and/or other materials provided with the distribution.
18ca1c9b0cSelric  *
19ca1c9b0cSelric  * 3. Neither the name of the Institute nor the names of its contributors
20ca1c9b0cSelric  *    may be used to endorse or promote products derived from this software
21ca1c9b0cSelric  *    without specific prior written permission.
22ca1c9b0cSelric  *
23ca1c9b0cSelric  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24ca1c9b0cSelric  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25ca1c9b0cSelric  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26ca1c9b0cSelric  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27ca1c9b0cSelric  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28ca1c9b0cSelric  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29ca1c9b0cSelric  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30ca1c9b0cSelric  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31ca1c9b0cSelric  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32ca1c9b0cSelric  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33ca1c9b0cSelric  * SUCH DAMAGE.
34ca1c9b0cSelric  */
35ca1c9b0cSelric 
36ca1c9b0cSelric #include "gsskrb5_locl.h"
37ca1c9b0cSelric 
38ca1c9b0cSelric OM_uint32
__gsskrb5_ccache_lifetime(OM_uint32 * minor_status,krb5_context context,krb5_ccache id,krb5_principal principal,OM_uint32 * lifetime)39ca1c9b0cSelric __gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
40ca1c9b0cSelric 			  krb5_context context,
41ca1c9b0cSelric 			  krb5_ccache id,
42ca1c9b0cSelric 			  krb5_principal principal,
43ca1c9b0cSelric 			  OM_uint32 *lifetime)
44ca1c9b0cSelric {
45ca1c9b0cSelric     krb5_error_code kret;
46b9d004c6Schristos     time_t left;
47ca1c9b0cSelric 
48b9d004c6Schristos     kret = krb5_cc_get_lifetime(context, id, &left);
49ca1c9b0cSelric     if (kret) {
50ca1c9b0cSelric         *minor_status = kret;
51ca1c9b0cSelric         return GSS_S_FAILURE;
52ca1c9b0cSelric     }
53ca1c9b0cSelric 
54b9d004c6Schristos     *lifetime = left;
55ca1c9b0cSelric 
56ca1c9b0cSelric     return GSS_S_COMPLETE;
57ca1c9b0cSelric }
58ca1c9b0cSelric 
59ca1c9b0cSelric 
60ca1c9b0cSelric 
61ca1c9b0cSelric 
62ca1c9b0cSelric static krb5_error_code
get_keytab(krb5_context context,krb5_keytab * keytab)63ca1c9b0cSelric get_keytab(krb5_context context, krb5_keytab *keytab)
64ca1c9b0cSelric {
65ca1c9b0cSelric     krb5_error_code kret;
66ca1c9b0cSelric 
67ca1c9b0cSelric     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
68ca1c9b0cSelric 
69ca1c9b0cSelric     if (_gsskrb5_keytab != NULL) {
70ca1c9b0cSelric 	char *name = NULL;
71ca1c9b0cSelric 
72ca1c9b0cSelric 	kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
73ca1c9b0cSelric 	if (kret == 0) {
74ca1c9b0cSelric 	    kret = krb5_kt_resolve(context, name, keytab);
75ca1c9b0cSelric 	    krb5_xfree(name);
76ca1c9b0cSelric 	}
77ca1c9b0cSelric     } else
78ca1c9b0cSelric 	kret = krb5_kt_default(context, keytab);
79ca1c9b0cSelric 
80ca1c9b0cSelric     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
81ca1c9b0cSelric 
82ca1c9b0cSelric     return (kret);
83ca1c9b0cSelric }
84ca1c9b0cSelric 
85b9d004c6Schristos /*
86b9d004c6Schristos  * This function produces a cred with a MEMORY ccache containing a TGT
87b9d004c6Schristos  * acquired with a password.
88b9d004c6Schristos  */
89b9d004c6Schristos static OM_uint32
acquire_cred_with_password(OM_uint32 * minor_status,krb5_context context,const char * password,OM_uint32 time_req,gss_const_OID desired_mech,gss_cred_usage_t cred_usage,gsskrb5_cred handle)90b9d004c6Schristos acquire_cred_with_password(OM_uint32 *minor_status,
91ca1c9b0cSelric                            krb5_context context,
92b9d004c6Schristos                            const char *password,
93ca1c9b0cSelric                            OM_uint32 time_req,
944f77a458Spettai                            gss_const_OID desired_mech,
95ca1c9b0cSelric                            gss_cred_usage_t cred_usage,
96b9d004c6Schristos                            gsskrb5_cred handle)
97ca1c9b0cSelric {
98b9d004c6Schristos     OM_uint32 ret = GSS_S_FAILURE;
99ca1c9b0cSelric     krb5_creds cred;
100ca1c9b0cSelric     krb5_get_init_creds_opt *opt;
101b9d004c6Schristos     krb5_ccache ccache = NULL;
102ca1c9b0cSelric     krb5_error_code kret;
103b9d004c6Schristos     time_t now;
104b9d004c6Schristos     OM_uint32 left;
105*afab4e30Schristos     const char *realm;
106ca1c9b0cSelric 
107b9d004c6Schristos     if (cred_usage == GSS_C_ACCEPT) {
108b9d004c6Schristos         /*
109b9d004c6Schristos          * TODO: Here we should eventually support user2user (when we get
110b9d004c6Schristos          *       support for that via an extension to the mechanism
111b9d004c6Schristos          *       allowing for more than two security context tokens),
112b9d004c6Schristos          *       and/or new unique MEMORY keytabs (we have MEMORY keytab
113b9d004c6Schristos          *       support, but we don't have a keytab equivalent of
114b9d004c6Schristos          *       krb5_cc_new_unique()).  Either way, for now we can't
115b9d004c6Schristos          *       support this.
116b9d004c6Schristos          */
117b9d004c6Schristos         *minor_status = ENOTSUP; /* XXX Better error? */
118b9d004c6Schristos         return GSS_S_FAILURE;
119b9d004c6Schristos     }
120b9d004c6Schristos 
121ca1c9b0cSelric     memset(&cred, 0, sizeof(cred));
122ca1c9b0cSelric 
123ca1c9b0cSelric     if (handle->principal == NULL) {
124ca1c9b0cSelric         kret = krb5_get_default_principal(context, &handle->principal);
125ca1c9b0cSelric         if (kret)
126ca1c9b0cSelric             goto end;
127ca1c9b0cSelric     }
128ca1c9b0cSelric     kret = krb5_get_init_creds_opt_alloc(context, &opt);
129ca1c9b0cSelric     if (kret)
130ca1c9b0cSelric         goto end;
1314f77a458Spettai 
132*afab4e30Schristos     realm = krb5_principal_get_realm(context, handle->principal);
133*afab4e30Schristos 
134*afab4e30Schristos     krb5_get_init_creds_opt_set_default_flags(context, "gss_krb5", realm, opt);
135*afab4e30Schristos 
136b9d004c6Schristos     /*
137b9d004c6Schristos      * Get the current time before the AS exchange so we don't
138b9d004c6Schristos      * accidentally end up returning a value that puts advertised
139b9d004c6Schristos      * expiration past the real expiration.
140b9d004c6Schristos      *
141b9d004c6Schristos      * We need to do this because krb5_cc_get_lifetime() returns a
142b9d004c6Schristos      * relative time that we need to add to the current time.  We ought
143b9d004c6Schristos      * to have a version of krb5_cc_get_lifetime() that returns absolute
144b9d004c6Schristos      * time...
145b9d004c6Schristos      */
146b9d004c6Schristos     krb5_timeofday(context, &now);
1474f77a458Spettai 
148b9d004c6Schristos     kret = krb5_get_init_creds_password(context, &cred, handle->principal,
149b9d004c6Schristos                                         password, NULL, NULL, 0, NULL, opt);
150ca1c9b0cSelric     krb5_get_init_creds_opt_free(context, opt);
151ca1c9b0cSelric     if (kret)
152ca1c9b0cSelric         goto end;
153b9d004c6Schristos 
154b9d004c6Schristos     kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
155ca1c9b0cSelric     if (kret)
156ca1c9b0cSelric         goto end;
157b9d004c6Schristos 
158ca1c9b0cSelric     kret = krb5_cc_initialize(context, ccache, cred.client);
159b9d004c6Schristos     if (kret)
160ca1c9b0cSelric         goto end;
161ca1c9b0cSelric 
162b9d004c6Schristos     kret = krb5_cc_store_cred(context, ccache, &cred);
163b9d004c6Schristos     if (kret)
164ca1c9b0cSelric         goto end;
165b9d004c6Schristos 
166b9d004c6Schristos     handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
167b9d004c6Schristos 
168b9d004c6Schristos     ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache,
169b9d004c6Schristos                                     handle->principal, &left);
170b9d004c6Schristos     if (ret != GSS_S_COMPLETE)
171b9d004c6Schristos         goto end;
172b9d004c6Schristos     handle->endtime = now + left;
173ca1c9b0cSelric     handle->ccache = ccache;
174b9d004c6Schristos     ccache = NULL;
175ca1c9b0cSelric     ret = GSS_S_COMPLETE;
176b9d004c6Schristos     kret = 0;
177ca1c9b0cSelric 
178ca1c9b0cSelric end:
179b9d004c6Schristos     if (ccache != NULL)
180b9d004c6Schristos         krb5_cc_destroy(context, ccache);
181b9d004c6Schristos     if (cred.client != NULL)
182b9d004c6Schristos 	krb5_free_cred_contents(context, &cred);
183b9d004c6Schristos     if (ret != GSS_S_COMPLETE && kret != 0)
184b9d004c6Schristos 	*minor_status = kret;
185b9d004c6Schristos     return (ret);
186b9d004c6Schristos }
187b9d004c6Schristos 
188b9d004c6Schristos /*
189b9d004c6Schristos  * Acquires an initiator credential from a ccache or using a keytab.
190b9d004c6Schristos  */
191b9d004c6Schristos static OM_uint32
acquire_initiator_cred(OM_uint32 * minor_status,krb5_context context,OM_uint32 time_req,gss_const_OID desired_mech,gss_cred_usage_t cred_usage,gsskrb5_cred handle)192b9d004c6Schristos acquire_initiator_cred(OM_uint32 *minor_status,
193b9d004c6Schristos                        krb5_context context,
194b9d004c6Schristos                        OM_uint32 time_req,
195b9d004c6Schristos                        gss_const_OID desired_mech,
196b9d004c6Schristos                        gss_cred_usage_t cred_usage,
197b9d004c6Schristos                        gsskrb5_cred handle)
198b9d004c6Schristos {
199b9d004c6Schristos     OM_uint32 ret = GSS_S_FAILURE;
200b9d004c6Schristos     krb5_creds cred;
201b9d004c6Schristos     krb5_get_init_creds_opt *opt;
202b9d004c6Schristos     krb5_principal def_princ = NULL;
203b9d004c6Schristos     krb5_ccache def_ccache = NULL;
204b9d004c6Schristos     krb5_ccache ccache = NULL;  /* we may store into this ccache */
205b9d004c6Schristos     krb5_keytab keytab = NULL;
206b9d004c6Schristos     krb5_error_code kret = 0;
207b9d004c6Schristos     OM_uint32 left;
208b9d004c6Schristos     time_t lifetime = 0;
209b9d004c6Schristos     time_t now;
210b9d004c6Schristos 
211b9d004c6Schristos     memset(&cred, 0, sizeof(cred));
212b9d004c6Schristos 
213b9d004c6Schristos     /*
214b9d004c6Schristos      * Get current time early so we can set handle->endtime to a value that
215b9d004c6Schristos      * cannot accidentally be past the real endtime.  We need a variant of
216b9d004c6Schristos      * krb5_cc_get_lifetime() that returns absolute endtime.
217b9d004c6Schristos      */
218b9d004c6Schristos     krb5_timeofday(context, &now);
219b9d004c6Schristos 
220b9d004c6Schristos     /*
221b9d004c6Schristos      * First look for a ccache that has the desired_name (which may be
222b9d004c6Schristos      * the default credential name).
223b9d004c6Schristos      *
224b9d004c6Schristos      * If we don't have an unexpired credential, acquire one with a
225b9d004c6Schristos      * keytab.
226b9d004c6Schristos      *
227b9d004c6Schristos      * If we acquire one with a keytab, save it in the ccache we found
228b9d004c6Schristos      * with the expired credential, if any.
229b9d004c6Schristos      *
230b9d004c6Schristos      * If we don't have any such ccache, then use a MEMORY ccache.
231b9d004c6Schristos      */
232b9d004c6Schristos 
233b9d004c6Schristos     if (handle->principal != NULL) {
234b9d004c6Schristos         /*
235b9d004c6Schristos          * Not default credential case.  See if we can find a ccache in
236b9d004c6Schristos          * the cccol for the desired_name.
237b9d004c6Schristos          */
238b9d004c6Schristos 	kret = krb5_cc_cache_match(context,
239b9d004c6Schristos 				   handle->principal,
240b9d004c6Schristos 				   &ccache);
241b9d004c6Schristos 	if (kret == 0) {
242b9d004c6Schristos             kret = krb5_cc_get_lifetime(context, ccache, &lifetime);
243b9d004c6Schristos             if (kret == 0) {
244b9d004c6Schristos                 if (lifetime > 0)
245b9d004c6Schristos                     goto found;
246b9d004c6Schristos                 else
247b9d004c6Schristos                     goto try_keytab;
248b9d004c6Schristos             }
249b9d004c6Schristos 	}
250b9d004c6Schristos         /*
251b9d004c6Schristos          * Fall through.  We shouldn't find this in the default ccache
252b9d004c6Schristos          * either, but we'll give it a try, then we'll try using a keytab.
253b9d004c6Schristos          */
254b9d004c6Schristos     }
255b9d004c6Schristos 
256b9d004c6Schristos     /*
257b9d004c6Schristos      * Either desired_name was GSS_C_NO_NAME (default cred) or
258b9d004c6Schristos      * krb5_cc_cache_match() failed (or found expired).
259b9d004c6Schristos      */
260b9d004c6Schristos     kret = krb5_cc_default(context, &def_ccache);
261b9d004c6Schristos     if (kret != 0)
262b9d004c6Schristos         goto try_keytab;
263b9d004c6Schristos     kret = krb5_cc_get_lifetime(context, def_ccache, &lifetime);
264b9d004c6Schristos     if (kret != 0)
265b9d004c6Schristos         lifetime = 0;
266b9d004c6Schristos     kret = krb5_cc_get_principal(context, def_ccache, &def_princ);
267b9d004c6Schristos     if (kret != 0)
268b9d004c6Schristos         goto try_keytab;
269b9d004c6Schristos     /*
270b9d004c6Schristos      * Have a default ccache; see if it matches desired_name.
271b9d004c6Schristos      */
272b9d004c6Schristos     if (handle->principal == NULL ||
273b9d004c6Schristos         krb5_principal_compare(context, handle->principal,
274b9d004c6Schristos                                def_princ) == TRUE) {
275b9d004c6Schristos         /*
276b9d004c6Schristos          * It matches.
277b9d004c6Schristos          *
278b9d004c6Schristos          * If we end up trying a keytab then we can write the result to
279b9d004c6Schristos          * the default ccache.
280b9d004c6Schristos          */
281b9d004c6Schristos         if (handle->principal == NULL) {
282b9d004c6Schristos             kret = krb5_copy_principal(context, def_princ, &handle->principal);
283b9d004c6Schristos             if (kret)
284b9d004c6Schristos                 goto end;
285b9d004c6Schristos         }
286b9d004c6Schristos         if (ccache != NULL)
287b9d004c6Schristos             krb5_cc_close(context, ccache);
288b9d004c6Schristos         ccache = def_ccache;
289b9d004c6Schristos         def_ccache = NULL;
290b9d004c6Schristos         if (lifetime > 0)
291b9d004c6Schristos             goto found;
292b9d004c6Schristos         /* else we fall through and try using a keytab */
293b9d004c6Schristos     }
294b9d004c6Schristos 
295b9d004c6Schristos try_keytab:
296b9d004c6Schristos     if (handle->principal == NULL) {
297b9d004c6Schristos         /* We need to know what client principal to use */
298b9d004c6Schristos         kret = krb5_get_default_principal(context, &handle->principal);
299b9d004c6Schristos         if (kret)
300b9d004c6Schristos             goto end;
301b9d004c6Schristos     }
302b9d004c6Schristos     kret = get_keytab(context, &keytab);
303b9d004c6Schristos     if (kret)
304b9d004c6Schristos         goto end;
305b9d004c6Schristos 
306b9d004c6Schristos     kret = krb5_get_init_creds_opt_alloc(context, &opt);
307b9d004c6Schristos     if (kret)
308b9d004c6Schristos         goto end;
309b9d004c6Schristos     krb5_timeofday(context, &now);
310b9d004c6Schristos     kret = krb5_get_init_creds_keytab(context, &cred, handle->principal,
311b9d004c6Schristos                                       keytab, 0, NULL, opt);
312b9d004c6Schristos     krb5_get_init_creds_opt_free(context, opt);
313b9d004c6Schristos     if (kret)
314b9d004c6Schristos         goto end;
315b9d004c6Schristos 
316b9d004c6Schristos     /*
317b9d004c6Schristos      * We got a credential with a keytab.  Save it if we can.
318b9d004c6Schristos      */
319b9d004c6Schristos     if (ccache == NULL) {
320b9d004c6Schristos         /*
321b9d004c6Schristos          * There's no ccache we can overwrite with the credentials we acquired
322b9d004c6Schristos          * with a keytab.  We'll use a MEMORY ccache then.
323b9d004c6Schristos          *
324b9d004c6Schristos          * Note that an application that falls into this repeatedly will do an
325b9d004c6Schristos          * AS exchange every time it acquires a credential handle.  Hopefully
326b9d004c6Schristos          * this doesn't happen much.  A workaround is to kinit -k once so that
327b9d004c6Schristos          * we always re-initialize the matched/default ccache here.  I.e., once
328b9d004c6Schristos          * there's a FILE/DIR ccache, we'll keep it frash automatically if we
329b9d004c6Schristos          * have a keytab, but if there's no FILE/DIR ccache, then we'll
330b9d004c6Schristos          * get a fresh credential *every* time we're asked.
331b9d004c6Schristos          */
332b9d004c6Schristos         kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache);
333b9d004c6Schristos         if (kret)
334b9d004c6Schristos             goto end;
335b9d004c6Schristos         handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
336b9d004c6Schristos     } /* else we'll re-initialize whichever ccache we matched above */
337b9d004c6Schristos 
338b9d004c6Schristos     kret = krb5_cc_initialize(context, ccache, cred.client);
339b9d004c6Schristos     if (kret)
340b9d004c6Schristos         goto end;
341b9d004c6Schristos     kret = krb5_cc_store_cred(context, ccache, &cred);
342b9d004c6Schristos     if (kret)
343b9d004c6Schristos         goto end;
344b9d004c6Schristos 
345b9d004c6Schristos found:
346b9d004c6Schristos     assert(handle->principal != NULL);
347b9d004c6Schristos     ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache,
348b9d004c6Schristos                                     handle->principal, &left);
349b9d004c6Schristos     if (ret != GSS_S_COMPLETE)
350b9d004c6Schristos         goto end;
351b9d004c6Schristos     handle->endtime = now + left;
352b9d004c6Schristos     handle->ccache = ccache;
353b9d004c6Schristos     ccache = NULL;
354b9d004c6Schristos     ret = GSS_S_COMPLETE;
355b9d004c6Schristos     kret = 0;
356b9d004c6Schristos 
357b9d004c6Schristos end:
358b9d004c6Schristos     if (ccache != NULL) {
359b9d004c6Schristos         if ((handle->cred_flags & GSS_CF_DESTROY_CRED_ON_RELEASE) != 0)
360b9d004c6Schristos             krb5_cc_destroy(context, ccache);
361b9d004c6Schristos         else
362b9d004c6Schristos             krb5_cc_close(context, ccache);
363b9d004c6Schristos     }
364b9d004c6Schristos     if (def_ccache != NULL)
365b9d004c6Schristos         krb5_cc_close(context, def_ccache);
366ca1c9b0cSelric     if (cred.client != NULL)
367ca1c9b0cSelric 	krb5_free_cred_contents(context, &cred);
368ca1c9b0cSelric     if (def_princ != NULL)
369ca1c9b0cSelric 	krb5_free_principal(context, def_princ);
370ca1c9b0cSelric     if (keytab != NULL)
371ca1c9b0cSelric 	krb5_kt_close(context, keytab);
372ca1c9b0cSelric     if (ret != GSS_S_COMPLETE && kret != 0)
373ca1c9b0cSelric 	*minor_status = kret;
374ca1c9b0cSelric     return (ret);
375ca1c9b0cSelric }
376ca1c9b0cSelric 
377b9d004c6Schristos static OM_uint32
acquire_acceptor_cred(OM_uint32 * minor_status,krb5_context context,OM_uint32 time_req,gss_const_OID desired_mech,gss_cred_usage_t cred_usage,gsskrb5_cred handle)378b9d004c6Schristos acquire_acceptor_cred(OM_uint32 * minor_status,
379ca1c9b0cSelric                       krb5_context context,
380ca1c9b0cSelric                       OM_uint32 time_req,
3814f77a458Spettai                       gss_const_OID desired_mech,
382ca1c9b0cSelric                       gss_cred_usage_t cred_usage,
383b9d004c6Schristos                       gsskrb5_cred handle)
384ca1c9b0cSelric {
385ca1c9b0cSelric     OM_uint32 ret;
386ca1c9b0cSelric     krb5_error_code kret;
387ca1c9b0cSelric 
388ca1c9b0cSelric     ret = GSS_S_FAILURE;
3894f77a458Spettai 
390ca1c9b0cSelric     kret = get_keytab(context, &handle->keytab);
391ca1c9b0cSelric     if (kret)
392ca1c9b0cSelric 	goto end;
393ca1c9b0cSelric 
394ca1c9b0cSelric     /* check that the requested principal exists in the keytab */
395ca1c9b0cSelric     if (handle->principal) {
396ca1c9b0cSelric 	krb5_keytab_entry entry;
397ca1c9b0cSelric 
398ca1c9b0cSelric 	kret = krb5_kt_get_entry(context, handle->keytab,
399ca1c9b0cSelric 				 handle->principal, 0, 0, &entry);
400ca1c9b0cSelric 	if (kret)
401ca1c9b0cSelric 	    goto end;
402ca1c9b0cSelric 	krb5_kt_free_entry(context, &entry);
403ca1c9b0cSelric 	ret = GSS_S_COMPLETE;
404ca1c9b0cSelric     } else {
405ca1c9b0cSelric 	/*
406ca1c9b0cSelric 	 * Check if there is at least one entry in the keytab before
407ca1c9b0cSelric 	 * declaring it as an useful keytab.
408ca1c9b0cSelric 	 */
409ca1c9b0cSelric 	krb5_keytab_entry tmp;
410ca1c9b0cSelric 	krb5_kt_cursor c;
411ca1c9b0cSelric 
412ca1c9b0cSelric 	kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
413ca1c9b0cSelric 	if (kret)
414ca1c9b0cSelric 	    goto end;
415ca1c9b0cSelric 	if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
416ca1c9b0cSelric 	    krb5_kt_free_entry(context, &tmp);
417ca1c9b0cSelric 	    ret = GSS_S_COMPLETE; /* ok found one entry */
418ca1c9b0cSelric 	}
419ca1c9b0cSelric 	krb5_kt_end_seq_get (context, handle->keytab, &c);
420ca1c9b0cSelric     }
421ca1c9b0cSelric end:
422ca1c9b0cSelric     if (ret != GSS_S_COMPLETE) {
423ca1c9b0cSelric 	if (handle->keytab != NULL)
424ca1c9b0cSelric 	    krb5_kt_close(context, handle->keytab);
425ca1c9b0cSelric 	if (kret != 0) {
426ca1c9b0cSelric 	    *minor_status = kret;
427ca1c9b0cSelric 	}
428ca1c9b0cSelric     }
429ca1c9b0cSelric     return (ret);
430ca1c9b0cSelric }
431ca1c9b0cSelric 
_gsskrb5_acquire_cred(OM_uint32 * minor_status,gss_const_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)432ca1c9b0cSelric OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
433ca1c9b0cSelric (OM_uint32 * minor_status,
434b9d004c6Schristos  gss_const_name_t desired_name,
435ca1c9b0cSelric  OM_uint32 time_req,
436ca1c9b0cSelric  const gss_OID_set desired_mechs,
437ca1c9b0cSelric  gss_cred_usage_t cred_usage,
438ca1c9b0cSelric  gss_cred_id_t * output_cred_handle,
439ca1c9b0cSelric  gss_OID_set * actual_mechs,
440ca1c9b0cSelric  OM_uint32 * time_rec
441ca1c9b0cSelric     )
442ca1c9b0cSelric {
443ca1c9b0cSelric     OM_uint32 ret;
444ca1c9b0cSelric 
445ca1c9b0cSelric     if (desired_mechs) {
446ca1c9b0cSelric 	int present = 0;
447ca1c9b0cSelric 
448ca1c9b0cSelric 	ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
449ca1c9b0cSelric 				      desired_mechs, &present);
450ca1c9b0cSelric 	if (ret)
451ca1c9b0cSelric 	    return ret;
452ca1c9b0cSelric 	if (!present) {
453ca1c9b0cSelric 	    *minor_status = 0;
454ca1c9b0cSelric 	    return GSS_S_BAD_MECH;
455ca1c9b0cSelric 	}
456ca1c9b0cSelric     }
457ca1c9b0cSelric 
4584f77a458Spettai     ret = _gsskrb5_acquire_cred_ext(minor_status,
4594f77a458Spettai 				    desired_name,
4604f77a458Spettai 				    GSS_C_NO_OID,
4614f77a458Spettai 				    NULL,
4624f77a458Spettai 				    time_req,
4634f77a458Spettai 				    GSS_KRB5_MECHANISM,
4644f77a458Spettai 				    cred_usage,
4654f77a458Spettai 				    output_cred_handle);
4664f77a458Spettai     if (ret)
4674f77a458Spettai 	return ret;
4684f77a458Spettai 
4694f77a458Spettai 
4704f77a458Spettai     ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle,
4714f77a458Spettai 				NULL, time_rec, NULL, actual_mechs);
4724f77a458Spettai     if (ret) {
4734f77a458Spettai 	OM_uint32 tmp;
4744f77a458Spettai 	_gsskrb5_release_cred(&tmp, output_cred_handle);
4754f77a458Spettai     }
4764f77a458Spettai 
4774f77a458Spettai     return ret;
4784f77a458Spettai }
4794f77a458Spettai 
_gsskrb5_acquire_cred_ext(OM_uint32 * minor_status,gss_const_name_t desired_name,gss_const_OID credential_type,const void * credential_data,OM_uint32 time_req,gss_const_OID desired_mech,gss_cred_usage_t cred_usage,gss_cred_id_t * output_cred_handle)4804f77a458Spettai OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
4814f77a458Spettai (OM_uint32 * minor_status,
482b9d004c6Schristos  gss_const_name_t desired_name,
4834f77a458Spettai  gss_const_OID credential_type,
4844f77a458Spettai  const void *credential_data,
4854f77a458Spettai  OM_uint32 time_req,
4864f77a458Spettai  gss_const_OID desired_mech,
4874f77a458Spettai  gss_cred_usage_t cred_usage,
4884f77a458Spettai  gss_cred_id_t * output_cred_handle
4894f77a458Spettai     )
4904f77a458Spettai {
4914f77a458Spettai     krb5_context context;
4924f77a458Spettai     gsskrb5_cred handle;
4934f77a458Spettai     OM_uint32 ret;
4944f77a458Spettai 
4954f77a458Spettai     cred_usage &= GSS_C_OPTION_MASK;
4964f77a458Spettai 
497b9d004c6Schristos     if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE &&
498b9d004c6Schristos         cred_usage != GSS_C_BOTH) {
4994f77a458Spettai 	*minor_status = GSS_KRB5_S_G_BAD_USAGE;
5004f77a458Spettai 	return GSS_S_FAILURE;
5014f77a458Spettai     }
5024f77a458Spettai 
5034f77a458Spettai     GSSAPI_KRB5_INIT(&context);
5044f77a458Spettai 
505b9d004c6Schristos     *output_cred_handle = GSS_C_NO_CREDENTIAL;
5064f77a458Spettai 
507ca1c9b0cSelric     handle = calloc(1, sizeof(*handle));
508ca1c9b0cSelric     if (handle == NULL) {
509ca1c9b0cSelric 	*minor_status = ENOMEM;
510b9d004c6Schristos         return GSS_S_FAILURE;
511ca1c9b0cSelric     }
512ca1c9b0cSelric 
513ca1c9b0cSelric     HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
514ca1c9b0cSelric 
515ca1c9b0cSelric     if (desired_name != GSS_C_NO_NAME) {
516b9d004c6Schristos 	ret = _gsskrb5_canon_name(minor_status, context,
517ca1c9b0cSelric 				  desired_name, &handle->principal);
518ca1c9b0cSelric 	if (ret) {
519ca1c9b0cSelric 	    HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
520ca1c9b0cSelric 	    free(handle);
521ca1c9b0cSelric 	    return ret;
522ca1c9b0cSelric 	}
523ca1c9b0cSelric     }
524b9d004c6Schristos 
525b9d004c6Schristos     if (credential_type != GSS_C_NO_OID &&
526b9d004c6Schristos         gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
527b9d004c6Schristos         /* Acquire a cred with a password */
528b9d004c6Schristos         gss_const_buffer_t pwbuf = credential_data;
529b9d004c6Schristos         char *pw;
530b9d004c6Schristos 
531b9d004c6Schristos         if (pwbuf == NULL) {
532b9d004c6Schristos             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
533b9d004c6Schristos             free(handle);
534b9d004c6Schristos             *minor_status = KRB5_NOCREDS_SUPPLIED; /* see below */
535b9d004c6Schristos             return GSS_S_CALL_INACCESSIBLE_READ;
536b9d004c6Schristos         }
537b9d004c6Schristos 
538b9d004c6Schristos         /* NUL-terminate the password, if it wasn't already */
539b9d004c6Schristos         pw = strndup(pwbuf->value, pwbuf->length);
540b9d004c6Schristos         if (pw == NULL) {
541b9d004c6Schristos             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
542b9d004c6Schristos             free(handle);
543b9d004c6Schristos             *minor_status = krb5_enomem(context);
544b9d004c6Schristos             return GSS_S_CALL_INACCESSIBLE_READ;
545b9d004c6Schristos         }
546b9d004c6Schristos         ret = acquire_cred_with_password(minor_status, context, pw, time_req,
547b9d004c6Schristos                                          desired_mech, cred_usage, handle);
548b9d004c6Schristos         free(pw);
549b9d004c6Schristos         if (ret != GSS_S_COMPLETE) {
550b9d004c6Schristos             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
551b9d004c6Schristos             krb5_free_principal(context, handle->principal);
552b9d004c6Schristos             free(handle);
553b9d004c6Schristos             return (ret);
554b9d004c6Schristos         }
555b9d004c6Schristos     } else if (credential_type != GSS_C_NO_OID) {
556b9d004c6Schristos         /*
557b9d004c6Schristos          * _gss_acquire_cred_ext() called with something other than a password.
558b9d004c6Schristos          *
559b9d004c6Schristos          * Not supported.
560b9d004c6Schristos          *
561b9d004c6Schristos          * _gss_acquire_cred_ext() is not a supported public interface, so
562b9d004c6Schristos          * we don't have to try too hard as to minor status codes here.
563b9d004c6Schristos          */
564b9d004c6Schristos         HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
565b9d004c6Schristos         free(handle);
566b9d004c6Schristos         *minor_status = ENOTSUP;
567b9d004c6Schristos         return GSS_S_FAILURE;
568b9d004c6Schristos     } else {
569b9d004c6Schristos         /*
570b9d004c6Schristos          * Acquire a credential from the background credential store (ccache,
571b9d004c6Schristos          * keytab).
572b9d004c6Schristos          */
573ca1c9b0cSelric         if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
574b9d004c6Schristos             ret = acquire_initiator_cred(minor_status, context, time_req,
5754f77a458Spettai                                          desired_mech, cred_usage, handle);
576ca1c9b0cSelric             if (ret != GSS_S_COMPLETE) {
577ca1c9b0cSelric                 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
578ca1c9b0cSelric                 krb5_free_principal(context, handle->principal);
579ca1c9b0cSelric                 free(handle);
580ca1c9b0cSelric                 return (ret);
581ca1c9b0cSelric             }
582ca1c9b0cSelric         }
583ca1c9b0cSelric         if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
584b9d004c6Schristos             ret = acquire_acceptor_cred(minor_status, context, time_req,
5854f77a458Spettai                                         desired_mech, cred_usage, handle);
586ca1c9b0cSelric             if (ret != GSS_S_COMPLETE) {
587ca1c9b0cSelric                 HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
588ca1c9b0cSelric                 krb5_free_principal(context, handle->principal);
589ca1c9b0cSelric                 free(handle);
590ca1c9b0cSelric                 return (ret);
591ca1c9b0cSelric             }
592ca1c9b0cSelric         }
593b9d004c6Schristos     }
594ca1c9b0cSelric     ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
595ca1c9b0cSelric     if (ret == GSS_S_COMPLETE)
596ca1c9b0cSelric     	ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
597ca1c9b0cSelric 				     &handle->mechanisms);
598ca1c9b0cSelric     if (ret != GSS_S_COMPLETE) {
599ca1c9b0cSelric 	if (handle->mechanisms != NULL)
600ca1c9b0cSelric 	    gss_release_oid_set(NULL, &handle->mechanisms);
601ca1c9b0cSelric 	HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
602ca1c9b0cSelric 	krb5_free_principal(context, handle->principal);
603ca1c9b0cSelric 	free(handle);
604ca1c9b0cSelric 	return (ret);
605ca1c9b0cSelric     }
606ca1c9b0cSelric     handle->usage = cred_usage;
6074f77a458Spettai     *minor_status = 0;
608ca1c9b0cSelric     *output_cred_handle = (gss_cred_id_t)handle;
609ca1c9b0cSelric     return (GSS_S_COMPLETE);
610ca1c9b0cSelric }
611