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
36 RCSID("$Id: acquire_cred.c 22124 2007-12-04 00:03:52Z lha $");
37
38 OM_uint32
__gsskrb5_ccache_lifetime(OM_uint32 * minor_status,krb5_context context,krb5_ccache id,krb5_principal principal,OM_uint32 * lifetime)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
83 static krb5_error_code
get_keytab(krb5_context context,krb5_keytab * keytab)84 get_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
acquire_initiator_cred(OM_uint32 * minor_status,krb5_context context,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gsskrb5_cred handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)105 static 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
212 end:
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
acquire_acceptor_cred(OM_uint32 * minor_status,krb5_context context,const gss_name_t desired_name,OM_uint32 time_req,const gss_OID_set desired_mechs,gss_cred_usage_t cred_usage,gsskrb5_cred handle,gss_OID_set * actual_mechs,OM_uint32 * time_rec)229 static 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 }
277 end:
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
_gsskrb5_acquire_cred(OM_uint32 * minor_status,const gss_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)288 OM_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