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