xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/s4u_gss_glue.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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
kg_is_initiator_cred(krb5_gss_cred_id_t cred)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
kg_impersonate_name(OM_uint32 * minor_status,const krb5_gss_cred_id_t impersonator_cred,const krb5_gss_name_t user,OM_uint32 time_req,krb5_gss_cred_id_t * output_cred,OM_uint32 * time_rec,krb5_context context)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     krb5_data *subject_cert = NULL;
52 
53     *output_cred = NULL;
54     memset(&in_creds, 0, sizeof(in_creds));
55 
56     if (user->is_cert)
57         subject_cert = user->princ->data;
58     else
59         in_creds.client = user->princ;
60     in_creds.server = impersonator_cred->name->princ;
61 
62     if (impersonator_cred->req_enctypes != NULL)
63         in_creds.keyblock.enctype = impersonator_cred->req_enctypes[0];
64 
65     k5_mutex_lock(&user->lock);
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, subject_cert, &out_creds);
85     if (code != 0) {
86         krb5_free_authdata(context, in_creds.authdata);
87         *minor_status = code;
88         return GSS_S_FAILURE;
89     }
90 
91     major_status = kg_compose_deleg_cred(minor_status,
92                                          impersonator_cred,
93                                          out_creds,
94                                          time_req,
95                                          output_cred,
96                                          time_rec,
97                                          context);
98 
99     krb5_free_authdata(context, in_creds.authdata);
100     krb5_free_creds(context, out_creds);
101 
102     return major_status;
103 }
104 
105 /* The mechglue always passes null desired_mechs and actual_mechs. */
106 OM_uint32 KRB5_CALLCONV
krb5_gss_acquire_cred_impersonate_name(OM_uint32 * minor_status,const gss_cred_id_t impersonator_cred_handle,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)107 krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status,
108                                        const gss_cred_id_t impersonator_cred_handle,
109                                        const gss_name_t desired_name,
110                                        OM_uint32 time_req,
111                                        const gss_OID_set desired_mechs,
112                                        gss_cred_usage_t cred_usage,
113                                        gss_cred_id_t *output_cred_handle,
114                                        gss_OID_set *actual_mechs,
115                                        OM_uint32 *time_rec)
116 {
117     OM_uint32 major_status;
118     krb5_error_code code;
119     krb5_gss_cred_id_t imp_cred = (krb5_gss_cred_id_t)impersonator_cred_handle;
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     if (imp_cred->usage != GSS_C_INITIATE && imp_cred->usage != GSS_C_BOTH) {
138         *minor_status = 0;
139         return GSS_S_NO_CRED;
140     }
141 
142     *output_cred_handle = GSS_C_NO_CREDENTIAL;
143     if (time_rec != NULL)
144         *time_rec = 0;
145 
146     code = krb5_gss_init_context(&context);
147     if (code != 0) {
148         *minor_status = code;
149         return GSS_S_FAILURE;
150     }
151 
152     major_status = kg_cred_resolve(minor_status, context,
153                                    impersonator_cred_handle, NULL);
154     if (GSS_ERROR(major_status)) {
155         krb5_free_context(context);
156         return major_status;
157     }
158 
159     major_status = kg_impersonate_name(minor_status,
160                                        imp_cred,
161                                        (krb5_gss_name_t)desired_name,
162                                        time_req,
163                                        &cred,
164                                        time_rec,
165                                        context);
166 
167     if (!GSS_ERROR(major_status))
168         *output_cred_handle = (gss_cred_id_t)cred;
169 
170     k5_mutex_unlock(&imp_cred->lock);
171     krb5_free_context(context);
172 
173     return major_status;
174 
175 }
176 
177 /*
178  * Set up cred to be an S4U2Proxy credential by copying in the impersonator's
179  * creds, setting a cache config variable with the impersonator principal name,
180  * and saving the impersonator principal name in the cred structure.
181  */
182 static krb5_error_code
make_proxy_cred(krb5_context context,krb5_gss_cred_id_t cred,krb5_gss_cred_id_t impersonator_cred)183 make_proxy_cred(krb5_context context, krb5_gss_cred_id_t cred,
184                 krb5_gss_cred_id_t impersonator_cred)
185 {
186     krb5_error_code code;
187     krb5_data data;
188     char *str;
189 
190     code = krb5_cc_copy_creds(context, impersonator_cred->ccache,
191                               cred->ccache);
192     if (code)
193         return code;
194 
195     code = krb5_unparse_name(context, impersonator_cred->name->princ, &str);
196     if (code)
197         return code;
198 
199     data = string2data(str);
200     code = krb5_cc_set_config(context, cred->ccache, NULL,
201                               KRB5_CC_CONF_PROXY_IMPERSONATOR, &data);
202     krb5_free_unparsed_name(context, str);
203     if (code)
204         return code;
205 
206     return krb5_copy_principal(context, impersonator_cred->name->princ,
207                                &cred->impersonator);
208 }
209 
210 OM_uint32
kg_compose_deleg_cred(OM_uint32 * minor_status,krb5_gss_cred_id_t impersonator_cred,krb5_creds * subject_creds,OM_uint32 time_req,krb5_gss_cred_id_t * output_cred,OM_uint32 * time_rec,krb5_context context)211 kg_compose_deleg_cred(OM_uint32 *minor_status,
212                       krb5_gss_cred_id_t impersonator_cred,
213                       krb5_creds *subject_creds,
214                       OM_uint32 time_req,
215                       krb5_gss_cred_id_t *output_cred,
216                       OM_uint32 *time_rec,
217                       krb5_context context)
218 {
219     OM_uint32 major_status;
220     krb5_error_code code;
221     krb5_gss_cred_id_t cred = NULL;
222 
223     *output_cred = NULL;
224     k5_mutex_assert_locked(&impersonator_cred->lock);
225 
226     if (!kg_is_initiator_cred(impersonator_cred) ||
227         impersonator_cred->name == NULL ||
228         impersonator_cred->impersonator != NULL) {
229         code = G_BAD_USAGE;
230         goto cleanup;
231     }
232 
233     assert(impersonator_cred->name->princ != NULL);
234 
235     assert(subject_creds != NULL);
236     assert(subject_creds->client != NULL);
237 
238     cred = xmalloc(sizeof(*cred));
239     if (cred == NULL) {
240         code = ENOMEM;
241         goto cleanup;
242     }
243     memset(cred, 0, sizeof(*cred));
244 
245     code = k5_mutex_init(&cred->lock);
246     if (code != 0)
247         goto cleanup;
248 
249     cred->usage = GSS_C_INITIATE;
250 
251     cred->expire = subject_creds->times.endtime;
252 
253     code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0,
254                         &cred->name);
255     if (code != 0)
256         goto cleanup;
257 
258     code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache);
259     if (code != 0)
260         goto cleanup;
261     cred->destroy_ccache = 1;
262 
263     code = krb5_cc_initialize(context, cred->ccache, subject_creds->client);
264     if (code != 0)
265         goto cleanup;
266 
267     code = make_proxy_cred(context, cred, impersonator_cred);
268     if (code != 0)
269         goto cleanup;
270 
271     code = krb5_cc_store_cred(context, cred->ccache, subject_creds);
272     if (code != 0)
273         goto cleanup;
274 
275     if (time_rec != NULL) {
276         krb5_timestamp now;
277 
278         code = krb5_timeofday(context, &now);
279         if (code != 0)
280             goto cleanup;
281 
282         *time_rec = ts_interval(now, cred->expire);
283     }
284 
285     major_status = GSS_S_COMPLETE;
286     *minor_status = 0;
287     *output_cred = cred;
288 
289 cleanup:
290     if (code != 0) {
291         *minor_status = code;
292         major_status = GSS_S_FAILURE;
293     }
294 
295     if (GSS_ERROR(major_status) && cred != NULL) {
296         k5_mutex_destroy(&cred->lock);
297         krb5_cc_destroy(context, cred->ccache);
298         kg_release_name(context, &cred->name);
299         xfree(cred);
300     }
301 
302     return major_status;
303 }
304