1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/export_cred.c - krb5 export_cred implementation */
3 /*
4 * Copyright (C) 2012 by the Massachusetts Institute of Technology.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 *
14 * * Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in
16 * the documentation and/or other materials provided with the
17 * distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include "k5-int.h"
34 #include "k5-json.h"
35 #include "gssapiP_krb5.h"
36
37 /* Return a JSON null or array value representing princ. */
38 static krb5_error_code
json_principal(krb5_context context,krb5_principal princ,k5_json_value * val_out)39 json_principal(krb5_context context, krb5_principal princ,
40 k5_json_value *val_out)
41 {
42 krb5_error_code ret;
43 k5_json_string str = NULL;
44 char *princname;
45
46 *val_out = NULL;
47 if (princ == NULL)
48 return k5_json_null_create_val(val_out);
49 ret = krb5_unparse_name(context, princ, &princname);
50 if (ret)
51 return ret;
52 ret = k5_json_string_create(princname, &str);
53 krb5_free_unparsed_name(context, princname);
54 *val_out = str;
55 return ret;
56 }
57
58 /* Return a json null or array value representing etypes. */
59 static krb5_error_code
json_etypes(krb5_enctype * etypes,k5_json_value * val_out)60 json_etypes(krb5_enctype *etypes, k5_json_value *val_out)
61 {
62 krb5_error_code ret;
63 k5_json_number num;
64 k5_json_array array;
65
66 *val_out = NULL;
67 if (etypes == NULL)
68 return k5_json_null_create_val(val_out);
69 ret = k5_json_array_create(&array);
70 if (ret)
71 return ret;
72 for (; *etypes != 0; etypes++) {
73 ret = k5_json_number_create(*etypes, &num);
74 if (ret)
75 goto err;
76 ret = k5_json_array_add(array, num);
77 k5_json_release(num);
78 if (ret)
79 goto err;
80 }
81 *val_out = array;
82 return 0;
83 err:
84 k5_json_release(array);
85 return ret;
86 }
87
88 /* Return a JSON null or array value representing name. */
89 static krb5_error_code
json_kgname(krb5_context context,krb5_gss_name_t name,k5_json_value * val_out)90 json_kgname(krb5_context context, krb5_gss_name_t name, k5_json_value *val_out)
91 {
92 krb5_error_code ret;
93 k5_json_array array = NULL;
94 k5_json_value princ;
95
96 *val_out = NULL;
97 if (name == NULL)
98 return k5_json_null_create_val(val_out);
99 ret = json_principal(context, name->princ, &princ);
100 if (ret)
101 return ret;
102 ret = k5_json_array_fmt(&array, "vss", princ, name->service, name->host);
103 k5_json_release(princ);
104 *val_out = array;
105 return ret;
106 }
107
108 /* Return a JSON null or string value representing keytab. */
109 static krb5_error_code
json_keytab(krb5_context context,krb5_keytab keytab,k5_json_value * val_out)110 json_keytab(krb5_context context, krb5_keytab keytab, k5_json_value *val_out)
111 {
112 krb5_error_code ret;
113 k5_json_string str;
114 char name[1024];
115
116 *val_out = NULL;
117 if (keytab == NULL)
118 return k5_json_null_create_val(val_out);
119 ret = krb5_kt_get_name(context, keytab, name, sizeof(name));
120 if (ret)
121 return ret;
122 ret = k5_json_string_create(name, &str);
123 *val_out = str;
124 return ret;
125 }
126
127 /* Return a JSON null or string value representing rcache. */
128 static krb5_error_code
json_rcache(krb5_context context,krb5_rcache rcache,k5_json_value * val_out)129 json_rcache(krb5_context context, krb5_rcache rcache, k5_json_value *val_out)
130 {
131 krb5_error_code ret;
132 k5_json_string str = NULL;
133
134 if (rcache == NULL)
135 return k5_json_null_create_val(val_out);
136 ret = k5_json_string_create(k5_rc_get_name(context, rcache), &str);
137 *val_out = str;
138 return ret;
139 }
140
141 /* Return a JSON array value representing keyblock. */
142 static krb5_error_code
json_keyblock(krb5_keyblock * kb,k5_json_value * val_out)143 json_keyblock(krb5_keyblock *kb, k5_json_value *val_out)
144 {
145 krb5_error_code ret;
146 k5_json_array array;
147
148 *val_out = NULL;
149 ret = k5_json_array_fmt(&array, "iB", kb->enctype, (void *)kb->contents,
150 (size_t)kb->length);
151 if (ret)
152 return ret;
153 *val_out = array;
154 return 0;
155 }
156
157 /* Return a JSON array value representing addr. */
158 static krb5_error_code
json_address(krb5_address * addr,k5_json_value * val_out)159 json_address(krb5_address *addr, k5_json_value *val_out)
160 {
161 krb5_error_code ret;
162 k5_json_array array;
163
164 *val_out = NULL;
165 ret = k5_json_array_fmt(&array, "iB", addr->addrtype,
166 (void *)addr->contents, (size_t)addr->length);
167 if (ret)
168 return ret;
169 *val_out = array;
170 return 0;
171 }
172
173 /* Return a JSON null or array value representing addrs. */
174 static krb5_error_code
json_addresses(krb5_address ** addrs,k5_json_value * val_out)175 json_addresses(krb5_address **addrs, k5_json_value *val_out)
176 {
177 krb5_error_code ret;
178 k5_json_array array;
179 k5_json_value val;
180
181 *val_out = NULL;
182 if (addrs == NULL)
183 return k5_json_null_create_val(val_out);
184 ret = k5_json_array_create(&array);
185 if (ret)
186 return ret;
187 for (; *addrs != NULL; addrs++) {
188 ret = json_address(*addrs, &val);
189 if (ret)
190 goto err;
191 ret = k5_json_array_add(array, val);
192 k5_json_release(val);
193 if (ret)
194 goto err;
195 }
196 *val_out = array;
197 return 0;
198 err:
199 k5_json_release(array);
200 return ret;
201 }
202
203 /* Return a JSON array value representing ad. */
204 static krb5_error_code
json_authdata_element(krb5_authdata * ad,k5_json_value * val_out)205 json_authdata_element(krb5_authdata *ad, k5_json_value *val_out)
206 {
207 krb5_error_code ret;
208 k5_json_array array;
209
210 *val_out = NULL;
211 ret = k5_json_array_fmt(&array, "iB", ad->ad_type, (void *)ad->contents,
212 (size_t)ad->length);
213 if (ret)
214 return ret;
215 *val_out = array;
216 return 0;
217 }
218
219 /* Return a JSON null or array value representing authdata. */
220 static krb5_error_code
json_authdata(krb5_authdata ** authdata,k5_json_value * val_out)221 json_authdata(krb5_authdata **authdata, k5_json_value *val_out)
222 {
223 krb5_error_code ret;
224 k5_json_array array;
225 k5_json_value val;
226
227 *val_out = NULL;
228 if (authdata == NULL)
229 return k5_json_null_create_val(val_out);
230 ret = k5_json_array_create(&array);
231 if (ret)
232 return ret;
233 for (; *authdata != NULL; authdata++) {
234 ret = json_authdata_element(*authdata, &val);
235 if (ret)
236 goto err;
237 ret = k5_json_array_add(array, val);
238 k5_json_release(val);
239 if (ret)
240 goto err;
241 }
242 *val_out = array;
243 return 0;
244 err:
245 k5_json_release(array);
246 return ret;
247 }
248
249 /* Return a JSON array value representing creds. */
250 static krb5_error_code
json_creds(krb5_context context,krb5_creds * creds,k5_json_value * val_out)251 json_creds(krb5_context context, krb5_creds *creds, k5_json_value *val_out)
252 {
253 krb5_error_code ret;
254 k5_json_array array;
255 k5_json_value client = NULL, server = NULL, keyblock = NULL, addrs = NULL;
256 k5_json_value authdata = NULL;
257
258 *val_out = NULL;
259 ret = json_principal(context, creds->client, &client);
260 if (ret)
261 goto cleanup;
262 ret = json_principal(context, creds->server, &server);
263 if (ret)
264 goto cleanup;
265 ret = json_keyblock(&creds->keyblock, &keyblock);
266 if (ret)
267 goto cleanup;
268 ret = json_addresses(creds->addresses, &addrs);
269 if (ret)
270 goto cleanup;
271 ret = json_authdata(creds->authdata, &authdata);
272 if (ret)
273 goto cleanup;
274
275 ret = k5_json_array_fmt(&array, "vvviiiibivBBv", client, server, keyblock,
276 creds->times.authtime, creds->times.starttime,
277 creds->times.endtime, creds->times.renew_till,
278 creds->is_skey, creds->ticket_flags, addrs,
279 (void *)creds->ticket.data,
280 (size_t)creds->ticket.length,
281 (void *)creds->second_ticket.data,
282 (size_t)creds->second_ticket.length, authdata);
283 if (ret)
284 goto cleanup;
285 *val_out = array;
286
287 cleanup:
288 k5_json_release(client);
289 k5_json_release(server);
290 k5_json_release(keyblock);
291 k5_json_release(addrs);
292 k5_json_release(authdata);
293 return ret;
294 }
295
296 /* Return a JSON array value representing the contents of ccache. */
297 static krb5_error_code
json_ccache_contents(krb5_context context,krb5_ccache ccache,k5_json_value * val_out)298 json_ccache_contents(krb5_context context, krb5_ccache ccache,
299 k5_json_value *val_out)
300 {
301 krb5_error_code ret;
302 krb5_principal princ;
303 krb5_cc_cursor cursor;
304 krb5_creds creds;
305 k5_json_array array;
306 k5_json_value val;
307
308 *val_out = NULL;
309 ret = k5_json_array_create(&array);
310 if (ret)
311 return ret;
312
313 /* Put the principal in the first array entry. */
314 ret = krb5_cc_get_principal(context, ccache, &princ);
315 if (ret)
316 goto err;
317 ret = json_principal(context, princ, &val);
318 krb5_free_principal(context, princ);
319 if (ret)
320 goto err;
321 ret = k5_json_array_add(array, val);
322 k5_json_release(val);
323 if (ret)
324 goto err;
325
326 /* Put credentials in the remaining array entries. */
327 ret = krb5_cc_start_seq_get(context, ccache, &cursor);
328 if (ret)
329 goto err;
330 while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) {
331 ret = json_creds(context, &creds, &val);
332 krb5_free_cred_contents(context, &creds);
333 if (ret)
334 break;
335 ret = k5_json_array_add(array, val);
336 k5_json_release(val);
337 if (ret)
338 break;
339 }
340 krb5_cc_end_seq_get(context, ccache, &cursor);
341 if (ret != KRB5_CC_END)
342 goto err;
343 *val_out = array;
344 return 0;
345
346 err:
347 k5_json_release(array);
348 return ret;
349 }
350
351 /* Return a JSON null, string, or array value representing ccache. */
352 static krb5_error_code
json_ccache(krb5_context context,krb5_ccache ccache,k5_json_value * val_out)353 json_ccache(krb5_context context, krb5_ccache ccache, k5_json_value *val_out)
354 {
355 krb5_error_code ret;
356 k5_json_string str;
357 char *name;
358
359 *val_out = NULL;
360 if (ccache == NULL)
361 return k5_json_null_create_val(val_out);
362 if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) {
363 return json_ccache_contents(context, ccache, val_out);
364 } else {
365 ret = krb5_cc_get_full_name(context, ccache, &name);
366 if (ret)
367 return ret;
368 ret = k5_json_string_create(name, &str);
369 free(name);
370 *val_out = str;
371 return ret;
372 }
373 }
374
375 /* Return a JSON array value representing cred. */
376 static krb5_error_code
json_kgcred(krb5_context context,krb5_gss_cred_id_t cred,k5_json_value * val_out)377 json_kgcred(krb5_context context, krb5_gss_cred_id_t cred,
378 k5_json_value *val_out)
379 {
380 krb5_error_code ret;
381 k5_json_array array;
382 k5_json_value name = NULL, imp = NULL, keytab = NULL, rcache = NULL;
383 k5_json_value ccache = NULL, ckeytab = NULL, etypes = NULL;
384
385 *val_out = NULL;
386 ret = json_kgname(context, cred->name, &name);
387 if (ret)
388 goto cleanup;
389 ret = json_principal(context, cred->impersonator, &imp);
390 if (ret)
391 goto cleanup;
392 ret = json_keytab(context, cred->keytab, &keytab);
393 if (ret)
394 goto cleanup;
395 ret = json_rcache(context, cred->rcache, &rcache);
396 if (ret)
397 goto cleanup;
398 ret = json_ccache(context, cred->ccache, &ccache);
399 if (ret)
400 goto cleanup;
401 ret = json_keytab(context, cred->client_keytab, &ckeytab);
402 if (ret)
403 goto cleanup;
404 ret = json_etypes(cred->req_enctypes, &etypes);
405 if (ret)
406 goto cleanup;
407
408 ret = k5_json_array_fmt(&array, "ivvbbvvvvbLLvs", cred->usage, name, imp,
409 cred->default_identity, cred->iakerb_mech, keytab,
410 rcache, ccache, ckeytab, cred->have_tgt,
411 (long long)ts2tt(cred->expire),
412 (long long)ts2tt(cred->refresh_time), etypes,
413 cred->password);
414 if (ret)
415 goto cleanup;
416 *val_out = array;
417
418 cleanup:
419 k5_json_release(name);
420 k5_json_release(imp);
421 k5_json_release(keytab);
422 k5_json_release(rcache);
423 k5_json_release(ccache);
424 k5_json_release(ckeytab);
425 k5_json_release(etypes);
426 return ret;
427 }
428
429 OM_uint32 KRB5_CALLCONV
krb5_gss_export_cred(OM_uint32 * minor_status,gss_cred_id_t cred_handle,gss_buffer_t token)430 krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
431 gss_buffer_t token)
432 {
433 OM_uint32 status = GSS_S_COMPLETE;
434 krb5_context context;
435 krb5_error_code ret;
436 krb5_gss_cred_id_t cred;
437 k5_json_array array = NULL;
438 k5_json_value jcred = NULL;
439 char *str = NULL;
440 krb5_data d;
441
442 ret = krb5_gss_init_context(&context);
443 if (ret) {
444 *minor_status = ret;
445 return GSS_S_FAILURE;
446 }
447
448 /* Validate and lock cred_handle. */
449 status = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
450 if (status != GSS_S_COMPLETE) {
451 krb5_free_context(context);
452 return status;
453 }
454 cred = (krb5_gss_cred_id_t)cred_handle;
455
456 if (json_kgcred(context, cred, &jcred))
457 goto oom;
458 if (k5_json_array_fmt(&array, "sv", CRED_EXPORT_MAGIC, jcred))
459 goto oom;
460 if (k5_json_encode(array, &str))
461 goto oom;
462 d = string2data(str);
463 if (data_to_gss(&d, token))
464 goto oom;
465 str = NULL;
466
467 cleanup:
468 free(str);
469 k5_mutex_unlock(&cred->lock);
470 k5_json_release(array);
471 k5_json_release(jcred);
472 krb5_free_context(context);
473 return status;
474
475 oom:
476 *minor_status = ENOMEM;
477 status = GSS_S_FAILURE;
478 goto cleanup;
479 }
480