Imported Upstream version 1.10.2
[platform/upstream/krb5.git] / src / lib / gssapi / krb5 / s4u_gss_glue.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  * Copyright 2009  by the Massachusetts Institute of Technology.
4  * All Rights Reserved.
5  *
6  * Export of this software from the United States of America may
7  *   require a specific license from the United States Government.
8  *   It is the responsibility of any person or organization contemplating
9  *   export to obtain such a license before exporting.
10  *
11  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
12  * distribute this software and its documentation for any purpose and
13  * without fee is hereby granted, provided that the above copyright
14  * notice appear in all copies and that both that copyright notice and
15  * this permission notice appear in supporting documentation, and that
16  * the name of M.I.T. not be used in advertising or publicity pertaining
17  * to distribution of the software without specific, written prior
18  * permission.  Furthermore if you modify this software you must label
19  * your software as modified software and not distribute it in such a
20  * fashion that it might be confused with the original M.I.T. software.
21  * M.I.T. makes no representations about the suitability of
22  * this software for any purpose.  It is provided "as is" without express
23  * or implied warranty.
24  */
25 #include "k5-int.h"
26 #include "gssapiP_krb5.h"
27 #ifdef HAVE_MEMORY_H
28 #include <memory.h>
29 #endif
30 #include <assert.h>
31
32 static int
33 kg_is_initiator_cred(krb5_gss_cred_id_t cred)
34 {
35     return (cred->usage == GSS_C_INITIATE || cred->usage == GSS_C_BOTH) &&
36         (cred->ccache != NULL);
37 }
38
39 static OM_uint32
40 kg_impersonate_name(OM_uint32 *minor_status,
41                     const krb5_gss_cred_id_t impersonator_cred,
42                     const krb5_gss_name_t user,
43                     OM_uint32 time_req,
44                     krb5_gss_cred_id_t *output_cred,
45                     OM_uint32 *time_rec,
46                     krb5_context context)
47 {
48     OM_uint32 major_status;
49     krb5_error_code code;
50     krb5_creds in_creds, *out_creds = NULL;
51
52     *output_cred = NULL;
53     memset(&in_creds, 0, sizeof(in_creds));
54
55     in_creds.client = user->princ;
56     in_creds.server = impersonator_cred->name->princ;
57
58     if (impersonator_cred->req_enctypes != NULL)
59         in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0];
60
61     code = k5_mutex_lock(&user->lock);
62     if (code != 0) {
63         *minor_status = code;
64         return GSS_S_FAILURE;
65     }
66
67     if (user->ad_context != NULL) {
68         code = krb5_authdata_export_authdata(context,
69                                              user->ad_context,
70                                              AD_USAGE_TGS_REQ,
71                                              &in_creds.authdata);
72         if (code != 0) {
73             k5_mutex_unlock(&user->lock);
74             *minor_status = code;
75             return GSS_S_FAILURE;
76         }
77     }
78
79     k5_mutex_unlock(&user->lock);
80
81     code = krb5_get_credentials_for_user(context,
82                                          KRB5_GC_CANONICALIZE | KRB5_GC_NO_STORE,
83                                          impersonator_cred->ccache,
84                                          &in_creds,
85                                          NULL, &out_creds);
86     if (code != 0) {
87         krb5_free_authdata(context, in_creds.authdata);
88         *minor_status = code;
89         return GSS_S_FAILURE;
90     }
91
92     major_status = kg_compose_deleg_cred(minor_status,
93                                          impersonator_cred,
94                                          out_creds,
95                                          time_req,
96                                          output_cred,
97                                          time_rec,
98                                          context);
99
100     krb5_free_authdata(context, in_creds.authdata);
101     krb5_free_creds(context, out_creds);
102
103     return major_status;
104 }
105
106 /* The mechglue always passes null desired_mechs and actual_mechs. */
107 OM_uint32 KRB5_CALLCONV
108 krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
109                                        const gss_cred_id_t impersonator_cred_handle,
110                                        const gss_name_t desired_name,
111                                        OM_uint32 time_req,
112                                        const gss_OID_set desired_mechs,
113                                        gss_cred_usage_t cred_usage,
114                                        gss_cred_id_t *output_cred_handle,
115                                        gss_OID_set *actual_mechs,
116                                        OM_uint32 *time_rec)
117 {
118     OM_uint32 major_status;
119     krb5_error_code code;
120     krb5_gss_cred_id_t cred;
121     krb5_context context;
122
123     if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL)
124         return GSS_S_CALL_INACCESSIBLE_READ;
125
126     if (desired_name == GSS_C_NO_NAME)
127         return GSS_S_CALL_INACCESSIBLE_READ;
128
129     if (output_cred_handle == NULL)
130         return GSS_S_CALL_INACCESSIBLE_WRITE;
131
132     if (cred_usage != GSS_C_INITIATE) {
133         *minor_status = (OM_uint32)G_BAD_USAGE;
134         return GSS_S_FAILURE;
135     }
136
137     *output_cred_handle = GSS_C_NO_CREDENTIAL;
138     if (time_rec != NULL)
139         *time_rec = 0;
140
141     code = krb5_gss_init_context(&context);
142     if (code != 0) {
143         *minor_status = code;
144         return GSS_S_FAILURE;
145     }
146
147     major_status = kg_cred_resolve(minor_status, context,
148                                    impersonator_cred_handle, NULL);
149     if (GSS_ERROR(major_status)) {
150         krb5_free_context(context);
151         return major_status;
152     }
153
154     major_status = kg_impersonate_name(minor_status,
155                                        (krb5_gss_cred_id_t)impersonator_cred_handle,
156                                        (krb5_gss_name_t)desired_name,
157                                        time_req,
158                                        &cred,
159                                        time_rec,
160                                        context);
161
162     if (!GSS_ERROR(major_status))
163         *output_cred_handle = (gss_cred_id_t)cred;
164
165     k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock);
166     krb5_free_context(context);
167
168     return major_status;
169
170 }
171
172 OM_uint32
173 kg_compose_deleg_cred(OM_uint32 *minor_status,
174                       krb5_gss_cred_id_t impersonator_cred,
175                       krb5_creds *subject_creds,
176                       OM_uint32 time_req,
177                       krb5_gss_cred_id_t *output_cred,
178                       OM_uint32 *time_rec,
179                       krb5_context context)
180 {
181     OM_uint32 major_status;
182     krb5_error_code code;
183     krb5_gss_cred_id_t cred = NULL;
184
185     *output_cred = NULL;
186     k5_mutex_assert_locked(&impersonator_cred->lock);
187
188     if (!kg_is_initiator_cred(impersonator_cred) ||
189         impersonator_cred->name == NULL ||
190         impersonator_cred->proxy_cred) {
191         code = G_BAD_USAGE;
192         goto cleanup;
193     }
194
195     assert(impersonator_cred->name->princ != NULL);
196
197     assert(subject_creds != NULL);
198     assert(subject_creds->client != NULL);
199
200     cred = xmalloc(sizeof(*cred));
201     if (cred == NULL) {
202         code = ENOMEM;
203         goto cleanup;
204     }
205     memset(cred, 0, sizeof(*cred));
206
207     code = k5_mutex_init(&cred->lock);
208     if (code != 0)
209         goto cleanup;
210
211     /*
212      * Only return a "proxy" credential for use with constrained
213      * delegation if the subject credentials are forwardable.
214      * Submitting non-forwardable credentials to the KDC for use
215      * with constrained delegation will only return an error.
216      */
217     cred->usage = GSS_C_INITIATE;
218     cred->proxy_cred = !!(subject_creds->ticket_flags & TKT_FLG_FORWARDABLE);
219
220     cred->tgt_expire = subject_creds->times.endtime;
221
222     code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0,
223                         &cred->name);
224     if (code != 0)
225         goto cleanup;
226
227     code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
228     if (code != 0)
229         goto cleanup;
230     cred->destroy_ccache = 1;
231
232     code = krb5_cc_initialize(context, cred->ccache,
233                               cred->proxy_cred ? impersonator_cred->name->princ :
234                               subject_creds->client);
235     if (code != 0)
236         goto cleanup;
237
238     if (cred->proxy_cred) {
239         /* Impersonator's TGT will be necessary for S4U2Proxy */
240         code = krb5_cc_copy_creds(context, impersonator_cred->ccache,
241                                   cred->ccache);
242         if (code != 0)
243             goto cleanup;
244     }
245
246     code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
247     if (code != 0)
248         goto cleanup;
249
250     if (time_rec != NULL) {
251         krb5_timestamp now;
252
253         code = krb5_timeofday(context, &now);
254         if (code != 0)
255             goto cleanup;
256
257         *time_rec = cred->tgt_expire - now;
258     }
259
260     major_status = GSS_S_COMPLETE;
261     *minor_status = 0;
262     *output_cred = cred;
263
264 cleanup:
265     if (code != 0) {
266         *minor_status = code;
267         major_status = GSS_S_FAILURE;
268     }
269
270     if (GSS_ERROR(major_status) && cred != NULL) {
271         k5_mutex_destroy(&cred->lock);
272         krb5_cc_destroy(context, cred->ccache);
273         kg_release_name(context, &cred->name);
274         xfree(cred);
275     }
276
277     return major_status;
278 }