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