]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - crypto/heimdal/lib/gssapi/krb5/acquire_cred.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / crypto / heimdal / lib / gssapi / krb5 / acquire_cred.c
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 "gsskrb5_locl.h"
35
36 OM_uint32
37 __gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
38                           krb5_context context,
39                           krb5_ccache id,
40                           krb5_principal principal,
41                           OM_uint32 *lifetime)
42 {
43     krb5_creds in_cred, out_cred;
44     krb5_const_realm realm;
45     krb5_error_code kret;
46
47     memset(&in_cred, 0, sizeof(in_cred));
48     in_cred.client = principal;
49
50     realm = krb5_principal_get_realm(context,  principal);
51     if (realm == NULL) {
52         _gsskrb5_clear_status ();
53         *minor_status = KRB5_PRINC_NOMATCH; /* XXX */
54         return GSS_S_FAILURE;
55     }
56
57     kret = krb5_make_principal(context, &in_cred.server,
58                                realm, KRB5_TGS_NAME, realm, NULL);
59     if (kret) {
60         *minor_status = kret;
61         return GSS_S_FAILURE;
62     }
63
64     kret = krb5_cc_retrieve_cred(context, id, 0, &in_cred, &out_cred);
65     krb5_free_principal(context, in_cred.server);
66     if (kret) {
67         *minor_status = 0;
68         *lifetime = 0;
69         return GSS_S_COMPLETE;
70     }
71
72     *lifetime = out_cred.times.endtime;
73     krb5_free_cred_contents(context, &out_cred);
74
75     return GSS_S_COMPLETE;
76 }
77
78
79
80
81 static krb5_error_code
82 get_keytab(krb5_context context, krb5_keytab *keytab)
83 {
84     krb5_error_code kret;
85
86     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
87
88     if (_gsskrb5_keytab != NULL) {
89         char *name = NULL;
90
91         kret = krb5_kt_get_full_name(context, _gsskrb5_keytab, &name);
92         if (kret == 0) {
93             kret = krb5_kt_resolve(context, name, keytab);
94             krb5_xfree(name);
95         }
96     } else
97         kret = krb5_kt_default(context, keytab);
98
99     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
100
101     return (kret);
102 }
103
104 static OM_uint32 acquire_initiator_cred
105                   (OM_uint32 * minor_status,
106                    krb5_context context,
107                    gss_const_OID credential_type,
108                    const void *credential_data,
109                    const gss_name_t desired_name,
110                    OM_uint32 time_req,
111                    gss_const_OID desired_mech,
112                    gss_cred_usage_t cred_usage,
113                    gsskrb5_cred handle
114                   )
115 {
116     OM_uint32 ret;
117     krb5_creds cred;
118     krb5_principal def_princ;
119     krb5_get_init_creds_opt *opt;
120     krb5_ccache ccache;
121     krb5_keytab keytab;
122     krb5_error_code kret;
123
124     keytab = NULL;
125     ccache = NULL;
126     def_princ = NULL;
127     ret = GSS_S_FAILURE;
128     memset(&cred, 0, sizeof(cred));
129
130     /*
131      * If we have a preferred principal, lets try to find it in all
132      * caches, otherwise, fall back to default cache, ignore all
133      * errors while searching.
134      */
135
136     if (credential_type != GSS_C_NO_OID &&
137         !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
138         kret = KRB5_NOCREDS_SUPPLIED; /* XXX */
139         goto end;
140     }
141
142     if (handle->principal) {
143         kret = krb5_cc_cache_match (context,
144                                     handle->principal,
145                                     &ccache);
146         if (kret == 0) {
147             ret = GSS_S_COMPLETE;
148             goto found;
149         }
150     }
151
152     if (ccache == NULL) {
153         kret = krb5_cc_default(context, &ccache);
154         if (kret)
155             goto end;
156     }
157     kret = krb5_cc_get_principal(context, ccache, &def_princ);
158     if (kret != 0) {
159         /* we'll try to use a keytab below */
160         krb5_cc_close(context, ccache);
161         def_princ = NULL;
162         kret = 0;
163     } else if (handle->principal == NULL)  {
164         kret = krb5_copy_principal(context, def_princ, &handle->principal);
165         if (kret)
166             goto end;
167     } else if (handle->principal != NULL &&
168                krb5_principal_compare(context, handle->principal,
169                                       def_princ) == FALSE) {
170         krb5_free_principal(context, def_princ);
171         def_princ = NULL;
172         krb5_cc_close(context, ccache);
173         ccache = NULL;
174     }
175     if (def_princ == NULL) {
176         /* We have no existing credentials cache,
177          * so attempt to get a TGT using a keytab.
178          */
179         if (handle->principal == NULL) {
180             kret = krb5_get_default_principal(context, &handle->principal);
181             if (kret)
182                 goto end;
183         }
184         kret = krb5_get_init_creds_opt_alloc(context, &opt);
185         if (kret)
186             goto end;
187         if (credential_type != GSS_C_NO_OID &&
188             gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) {
189             gss_buffer_t password = (gss_buffer_t)credential_data;
190
191             /* XXX are we requiring password to be NUL terminated? */
192
193             kret = krb5_get_init_creds_password(context, &cred,
194                                                 handle->principal,
195                                                 password->value,
196                                                 NULL, NULL, 0, NULL, opt);
197         } else {
198             kret = get_keytab(context, &keytab);
199             if (kret) {
200                 krb5_get_init_creds_opt_free(context, opt);
201                 goto end;
202             }
203             kret = krb5_get_init_creds_keytab(context, &cred,
204                                               handle->principal, keytab,
205                                               0, NULL, opt);
206         }
207         krb5_get_init_creds_opt_free(context, opt);
208         if (kret)
209             goto end;
210         kret = krb5_cc_new_unique(context, krb5_cc_type_memory,
211                                   NULL, &ccache);
212         if (kret)
213             goto end;
214         kret = krb5_cc_initialize(context, ccache, cred.client);
215         if (kret) {
216             krb5_cc_destroy(context, ccache);
217             goto end;
218         }
219         kret = krb5_cc_store_cred(context, ccache, &cred);
220         if (kret) {
221             krb5_cc_destroy(context, ccache);
222             goto end;
223         }
224         handle->lifetime = cred.times.endtime;
225         handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE;
226     } else {
227
228         ret = __gsskrb5_ccache_lifetime(minor_status,
229                                         context,
230                                         ccache,
231                                         handle->principal,
232                                         &handle->lifetime);
233         if (ret != GSS_S_COMPLETE) {
234             krb5_cc_close(context, ccache);
235             goto end;
236         }
237         kret = 0;
238     }
239  found:
240     handle->ccache = ccache;
241     ret = GSS_S_COMPLETE;
242
243 end:
244     if (cred.client != NULL)
245         krb5_free_cred_contents(context, &cred);
246     if (def_princ != NULL)
247         krb5_free_principal(context, def_princ);
248     if (keytab != NULL)
249         krb5_kt_close(context, keytab);
250     if (ret != GSS_S_COMPLETE && kret != 0)
251         *minor_status = kret;
252     return (ret);
253 }
254
255 static OM_uint32 acquire_acceptor_cred
256                   (OM_uint32 * minor_status,
257                    krb5_context context,
258                    gss_const_OID credential_type,
259                    const void *credential_data,
260                    const gss_name_t desired_name,
261                    OM_uint32 time_req,
262                    gss_const_OID desired_mech,
263                    gss_cred_usage_t cred_usage,
264                    gsskrb5_cred handle
265                   )
266 {
267     OM_uint32 ret;
268     krb5_error_code kret;
269
270     ret = GSS_S_FAILURE;
271
272     if (credential_type != GSS_C_NO_OID) {
273         kret = EINVAL;
274         goto end;
275     }
276
277     kret = get_keytab(context, &handle->keytab);
278     if (kret)
279         goto end;
280
281     /* check that the requested principal exists in the keytab */
282     if (handle->principal) {
283         krb5_keytab_entry entry;
284
285         kret = krb5_kt_get_entry(context, handle->keytab,
286                                  handle->principal, 0, 0, &entry);
287         if (kret)
288             goto end;
289         krb5_kt_free_entry(context, &entry);
290         ret = GSS_S_COMPLETE;
291     } else {
292         /*
293          * Check if there is at least one entry in the keytab before
294          * declaring it as an useful keytab.
295          */
296         krb5_keytab_entry tmp;
297         krb5_kt_cursor c;
298
299         kret = krb5_kt_start_seq_get (context, handle->keytab, &c);
300         if (kret)
301             goto end;
302         if (krb5_kt_next_entry(context, handle->keytab, &tmp, &c) == 0) {
303             krb5_kt_free_entry(context, &tmp);
304             ret = GSS_S_COMPLETE; /* ok found one entry */
305         }
306         krb5_kt_end_seq_get (context, handle->keytab, &c);
307     }
308 end:
309     if (ret != GSS_S_COMPLETE) {
310         if (handle->keytab != NULL)
311             krb5_kt_close(context, handle->keytab);
312         if (kret != 0) {
313             *minor_status = kret;
314         }
315     }
316     return (ret);
317 }
318
319 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred
320 (OM_uint32 * minor_status,
321  const gss_name_t desired_name,
322  OM_uint32 time_req,
323  const gss_OID_set desired_mechs,
324  gss_cred_usage_t cred_usage,
325  gss_cred_id_t * output_cred_handle,
326  gss_OID_set * actual_mechs,
327  OM_uint32 * time_rec
328     )
329 {
330     OM_uint32 ret;
331
332     if (desired_mechs) {
333         int present = 0;
334
335         ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
336                                       desired_mechs, &present);
337         if (ret)
338             return ret;
339         if (!present) {
340             *minor_status = 0;
341             return GSS_S_BAD_MECH;
342         }
343     }
344
345     ret = _gsskrb5_acquire_cred_ext(minor_status,
346                                     desired_name,
347                                     GSS_C_NO_OID,
348                                     NULL,
349                                     time_req,
350                                     GSS_KRB5_MECHANISM,
351                                     cred_usage,
352                                     output_cred_handle);
353     if (ret)
354         return ret;
355
356
357     ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle,
358                                 NULL, time_rec, NULL, actual_mechs);
359     if (ret) {
360         OM_uint32 tmp;
361         _gsskrb5_release_cred(&tmp, output_cred_handle);
362     }
363
364     return ret;
365 }
366
367 OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext
368 (OM_uint32 * minor_status,
369  const gss_name_t desired_name,
370  gss_const_OID credential_type,
371  const void *credential_data,
372  OM_uint32 time_req,
373  gss_const_OID desired_mech,
374  gss_cred_usage_t cred_usage,
375  gss_cred_id_t * output_cred_handle
376     )
377 {
378     krb5_context context;
379     gsskrb5_cred handle;
380     OM_uint32 ret;
381
382     cred_usage &= GSS_C_OPTION_MASK;
383
384     if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
385         *minor_status = GSS_KRB5_S_G_BAD_USAGE;
386         return GSS_S_FAILURE;
387     }
388
389     GSSAPI_KRB5_INIT(&context);
390
391     *output_cred_handle = NULL;
392
393     handle = calloc(1, sizeof(*handle));
394     if (handle == NULL) {
395         *minor_status = ENOMEM;
396         return (GSS_S_FAILURE);
397     }
398
399     HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
400
401     if (desired_name != GSS_C_NO_NAME) {
402         ret = _gsskrb5_canon_name(minor_status, context, 1, NULL,
403                                   desired_name, &handle->principal);
404         if (ret) {
405             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
406             free(handle);
407             return ret;
408         }
409     }
410     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
411         ret = acquire_initiator_cred(minor_status, context,
412                                      credential_type, credential_data,
413                                      desired_name, time_req,
414                                      desired_mech, cred_usage, handle);
415         if (ret != GSS_S_COMPLETE) {
416             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
417             krb5_free_principal(context, handle->principal);
418             free(handle);
419             return (ret);
420         }
421     }
422     if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
423         ret = acquire_acceptor_cred(minor_status, context,
424                                     credential_type, credential_data,
425                                     desired_name, time_req,
426                                     desired_mech, cred_usage, handle);
427         if (ret != GSS_S_COMPLETE) {
428             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
429             krb5_free_principal(context, handle->principal);
430             free(handle);
431             return (ret);
432         }
433     }
434     ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms);
435     if (ret == GSS_S_COMPLETE)
436         ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
437                                      &handle->mechanisms);
438     if (ret != GSS_S_COMPLETE) {
439         if (handle->mechanisms != NULL)
440             gss_release_oid_set(NULL, &handle->mechanisms);
441         HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
442         krb5_free_principal(context, handle->principal);
443         free(handle);
444         return (ret);
445     }
446     handle->usage = cred_usage;
447     *minor_status = 0;
448     *output_cred_handle = (gss_cred_id_t)handle;
449     return (GSS_S_COMPLETE);
450 }