r19604: This is a massive commit, and I appologise in advance for it's size.
[samba.git] / source4 / 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 "krb5/gsskrb5_locl.h"
35
36 RCSID("$Id: acquire_cred.c,v 1.31 2006/10/07 22:13:55 lha Exp $");
37
38 OM_uint32
39 __gsskrb5_ccache_lifetime(OM_uint32 *minor_status,
40                              krb5_ccache id,
41                              krb5_principal principal,
42                              OM_uint32 *lifetime)
43 {
44     krb5_creds in_cred, *out_cred;
45     krb5_const_realm realm;
46     krb5_error_code kret;
47
48     memset(&in_cred, 0, sizeof(in_cred));
49     in_cred.client = principal;
50         
51     realm = krb5_principal_get_realm(_gsskrb5_context,  principal);
52     if (realm == NULL) {
53         _gsskrb5_clear_status ();
54         *minor_status = KRB5_PRINC_NOMATCH; /* XXX */
55         return GSS_S_FAILURE;
56     }
57
58     kret = krb5_make_principal(_gsskrb5_context, &in_cred.server, 
59                                realm, KRB5_TGS_NAME, realm, NULL);
60     if (kret) {
61         _gsskrb5_set_error_string();
62         *minor_status = kret;
63         return GSS_S_FAILURE;
64     }
65
66     kret = krb5_get_credentials(_gsskrb5_context, 0, 
67                                 id, &in_cred, &out_cred);
68     krb5_free_principal(_gsskrb5_context, in_cred.server);
69     if (kret) {
70         _gsskrb5_set_error_string();
71         *minor_status = kret;
72         return GSS_S_FAILURE;
73     }
74
75     *lifetime = out_cred->times.endtime;
76     krb5_free_creds(_gsskrb5_context, out_cred);
77
78     return GSS_S_COMPLETE;
79 }
80
81
82
83
84 static krb5_error_code
85 get_keytab(krb5_keytab *keytab)
86 {
87     char kt_name[256];
88     krb5_error_code kret;
89
90     HEIMDAL_MUTEX_lock(&gssapi_keytab_mutex);
91
92     if (_gsskrb5_keytab != NULL) {
93         kret = krb5_kt_get_name(_gsskrb5_context,
94                                 _gsskrb5_keytab,
95                                 kt_name, sizeof(kt_name));
96         if (kret == 0)
97             kret = krb5_kt_resolve(_gsskrb5_context, kt_name, keytab);
98     } else
99         kret = krb5_kt_default(_gsskrb5_context, keytab);
100
101     HEIMDAL_MUTEX_unlock(&gssapi_keytab_mutex);
102
103     return (kret);
104 }
105
106 static OM_uint32 acquire_initiator_cred
107                   (OM_uint32 * minor_status,
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 (_gsskrb5_context,
136                                     handle->principal,
137                                     NULL,
138                                     &ccache);
139     
140     if (ccache == NULL) {
141         kret = krb5_cc_default(_gsskrb5_context, &ccache);
142         if (kret)
143             goto end;
144     }
145     kret = krb5_cc_get_principal(_gsskrb5_context, ccache,
146         &def_princ);
147     if (kret != 0) {
148         /* we'll try to use a keytab below */
149         krb5_cc_destroy(_gsskrb5_context, ccache);
150         ccache = NULL;
151         kret = 0;
152     } else if (handle->principal == NULL)  {
153         kret = krb5_copy_principal(_gsskrb5_context, def_princ,
154             &handle->principal);
155         if (kret)
156             goto end;
157     } else if (handle->principal != NULL &&
158         krb5_principal_compare(_gsskrb5_context, handle->principal,
159         def_princ) == FALSE) {
160         /* Before failing, lets check the keytab */
161         krb5_free_principal(_gsskrb5_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(_gsskrb5_context,
170                 &handle->principal);
171             if (kret)
172                 goto end;
173         }
174         kret = get_keytab(&keytab);
175         if (kret)
176             goto end;
177         kret = krb5_get_init_creds_opt_alloc(_gsskrb5_context, &opt);
178         if (kret)
179             goto end;
180         kret = krb5_get_init_creds_keytab(_gsskrb5_context, &cred,
181             handle->principal, keytab, 0, NULL, opt);
182         krb5_get_init_creds_opt_free(opt);
183         if (kret)
184             goto end;
185         kret = krb5_cc_gen_new(_gsskrb5_context, &krb5_mcc_ops,
186                 &ccache);
187         if (kret)
188             goto end;
189         kret = krb5_cc_initialize(_gsskrb5_context, ccache, cred.client);
190         if (kret)
191             goto end;
192         kret = krb5_cc_store_cred(_gsskrb5_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                                            ccache,
201                                            handle->principal,
202                                            &handle->lifetime);
203         if (ret != GSS_S_COMPLETE)
204             goto end;
205         kret = 0;
206     }
207
208     handle->ccache = ccache;
209     ret = GSS_S_COMPLETE;
210
211 end:
212     if (cred.client != NULL)
213         krb5_free_cred_contents(_gsskrb5_context, &cred);
214     if (def_princ != NULL)
215         krb5_free_principal(_gsskrb5_context, def_princ);
216     if (keytab != NULL)
217         krb5_kt_close(_gsskrb5_context, keytab);
218     if (ret != GSS_S_COMPLETE) {
219         if (ccache != NULL)
220             krb5_cc_close(_gsskrb5_context, ccache);
221         if (kret != 0) {
222             *minor_status = kret;
223             _gsskrb5_set_error_string ();
224         }
225     }
226     return (ret);
227 }
228
229 static OM_uint32 acquire_acceptor_cred
230                   (OM_uint32 * minor_status,
231                    const gss_name_t desired_name,
232                    OM_uint32 time_req,
233                    const gss_OID_set desired_mechs,
234                    gss_cred_usage_t cred_usage,
235                    gsskrb5_cred handle,
236                    gss_OID_set * actual_mechs,
237                    OM_uint32 * time_rec
238                   )
239 {
240     OM_uint32 ret;
241     krb5_error_code kret;
242
243     kret = 0;
244     ret = GSS_S_FAILURE;
245     kret = get_keytab(&handle->keytab);
246     if (kret)
247         goto end;
248     
249     /* check that the requested principal exists in the keytab */
250     if (handle->principal) {
251         krb5_keytab_entry entry;
252
253         kret = krb5_kt_get_entry(_gsskrb5_context, handle->keytab, 
254                                  handle->principal, 0, 0, &entry);
255         if (kret)
256             goto end;
257         krb5_kt_free_entry(_gsskrb5_context, &entry);
258     }
259     ret = GSS_S_COMPLETE;
260  
261 end:
262     if (ret != GSS_S_COMPLETE) {
263         if (handle->keytab != NULL)
264             krb5_kt_close(_gsskrb5_context, handle->keytab);
265         if (kret != 0) {
266             *minor_status = kret;
267             _gsskrb5_set_error_string ();
268         }
269     }
270     return (ret);
271 }
272
273 OM_uint32 _gsskrb5_acquire_cred
274 (OM_uint32 * minor_status,
275  const gss_name_t desired_name,
276  OM_uint32 time_req,
277  const gss_OID_set desired_mechs,
278  gss_cred_usage_t cred_usage,
279  gss_cred_id_t * output_cred_handle,
280  gss_OID_set * actual_mechs,
281  OM_uint32 * time_rec
282     )
283 {
284     gsskrb5_cred handle;
285     OM_uint32 ret;
286
287     if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) {
288         *minor_status = GSS_KRB5_S_G_BAD_USAGE;
289         return GSS_S_FAILURE;
290     }
291
292     GSSAPI_KRB5_INIT ();
293
294     *output_cred_handle = NULL;
295     if (time_rec)
296         *time_rec = 0;
297     if (actual_mechs)
298         *actual_mechs = GSS_C_NO_OID_SET;
299
300     if (desired_mechs) {
301         int present = 0;
302
303         ret = _gsskrb5_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
304                                            desired_mechs, &present); 
305         if (ret)
306             return ret;
307         if (!present) {
308             *minor_status = 0;
309             return GSS_S_BAD_MECH;
310         }
311     }
312
313     handle = calloc(1, sizeof(*handle));
314     if (handle == NULL) {
315         *minor_status = ENOMEM;
316         return (GSS_S_FAILURE);
317     }
318
319     HEIMDAL_MUTEX_init(&handle->cred_id_mutex);
320
321     if (desired_name != GSS_C_NO_NAME) {
322         krb5_principal name = (krb5_principal)desired_name;
323         ret = krb5_copy_principal(_gsskrb5_context, name, &handle->principal);
324         if (ret) {
325             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
326             _gsskrb5_set_error_string();
327             *minor_status = ret;
328             free(handle);
329             return GSS_S_FAILURE;
330         }
331     }
332     if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) {
333         ret = acquire_initiator_cred(minor_status, desired_name, time_req,
334                                      desired_mechs, cred_usage, handle, actual_mechs, time_rec);
335         if (ret != GSS_S_COMPLETE) {
336             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
337             krb5_free_principal(_gsskrb5_context, handle->principal);
338             free(handle);
339             return (ret);
340         }
341     }
342     if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) {
343         ret = acquire_acceptor_cred(minor_status, desired_name, time_req,
344                                     desired_mechs, cred_usage, handle, actual_mechs, time_rec);
345         if (ret != GSS_S_COMPLETE) {
346             HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
347             krb5_free_principal(_gsskrb5_context, handle->principal);
348             free(handle);
349             return (ret);
350         }
351     }
352     ret = _gsskrb5_create_empty_oid_set(minor_status, &handle->mechanisms);
353     if (ret == GSS_S_COMPLETE)
354         ret = _gsskrb5_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM,
355                                           &handle->mechanisms);
356     if (ret == GSS_S_COMPLETE)
357         ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)handle, 
358                                     NULL, time_rec, NULL, actual_mechs);
359     if (ret != GSS_S_COMPLETE) {
360         if (handle->mechanisms != NULL)
361             _gsskrb5_release_oid_set(NULL, &handle->mechanisms);
362         HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex);
363         krb5_free_principal(_gsskrb5_context, handle->principal);
364         free(handle);
365         return (ret);
366     } 
367     *minor_status = 0;
368     if (time_rec) {
369         ret = _gsskrb5_lifetime_left(minor_status,
370                                    handle->lifetime,
371                                    time_rec);
372
373         if (ret)
374             return ret;
375     }
376     handle->usage = cred_usage;
377     *output_cred_handle = (gss_cred_id_t)handle;
378     return (GSS_S_COMPLETE);
379 }