Imported Upstream version 1.15.1
[platform/upstream/krb5.git] / src / lib / gssapi / krb5 / export_cred.c
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
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
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
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
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
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     char *name;
134
135     if (rcache == NULL)
136         return k5_json_null_create_val(val_out);
137     if (asprintf(&name, "%s:%s", krb5_rc_get_type(context, rcache),
138                  krb5_rc_get_name(context, rcache)) < 0)
139         return ENOMEM;
140     ret = k5_json_string_create(name, &str);
141     free(name);
142     *val_out = str;
143     return ret;
144 }
145
146 /* Return a JSON array value representing keyblock. */
147 static krb5_error_code
148 json_keyblock(krb5_keyblock *kb, k5_json_value *val_out)
149 {
150     krb5_error_code ret;
151     k5_json_array array;
152
153     *val_out = NULL;
154     ret = k5_json_array_fmt(&array, "iB", kb->enctype, (void *)kb->contents,
155                             (size_t)kb->length);
156     if (ret)
157         return ret;
158     *val_out = array;
159     return 0;
160 }
161
162 /* Return a JSON array value representing addr. */
163 static krb5_error_code
164 json_address(krb5_address *addr, k5_json_value *val_out)
165 {
166     krb5_error_code ret;
167     k5_json_array array;
168
169     *val_out = NULL;
170     ret = k5_json_array_fmt(&array, "iB", addr->addrtype,
171                             (void *)addr->contents, (size_t)addr->length);
172     if (ret)
173         return ret;
174     *val_out = array;
175     return 0;
176 }
177
178 /* Return a JSON null or array value representing addrs. */
179 static krb5_error_code
180 json_addresses(krb5_address **addrs, k5_json_value *val_out)
181 {
182     krb5_error_code ret;
183     k5_json_array array;
184     k5_json_value val;
185
186     *val_out = NULL;
187     if (addrs == NULL)
188         return k5_json_null_create_val(val_out);
189     ret = k5_json_array_create(&array);
190     if (ret)
191         return ret;
192     for (; *addrs != NULL; addrs++) {
193         ret = json_address(*addrs, &val);
194         if (ret)
195             goto err;
196         ret = k5_json_array_add(array, val);
197         k5_json_release(val);
198         if (ret)
199             goto err;
200     }
201     *val_out = array;
202     return 0;
203 err:
204     k5_json_release(array);
205     return ret;
206 }
207
208 /* Return a JSON array value representing ad. */
209 static krb5_error_code
210 json_authdata_element(krb5_authdata *ad, k5_json_value *val_out)
211 {
212     krb5_error_code ret;
213     k5_json_array array;
214
215     *val_out = NULL;
216     ret = k5_json_array_fmt(&array, "iB", ad->ad_type, (void *)ad->contents,
217                             (size_t)ad->length);
218     if (ret)
219         return ret;
220     *val_out = array;
221     return 0;
222 }
223
224 /* Return a JSON null or array value representing authdata. */
225 static krb5_error_code
226 json_authdata(krb5_authdata **authdata, k5_json_value *val_out)
227 {
228     krb5_error_code ret;
229     k5_json_array array;
230     k5_json_value val;
231
232     *val_out = NULL;
233     if (authdata == NULL)
234         return k5_json_null_create_val(val_out);
235     ret = k5_json_array_create(&array);
236     if (ret)
237         return ret;
238     for (; *authdata != NULL; authdata++) {
239         ret = json_authdata_element(*authdata, &val);
240         if (ret)
241             goto err;
242         ret = k5_json_array_add(array, val);
243         k5_json_release(val);
244         if (ret)
245             goto err;
246     }
247     *val_out = array;
248     return 0;
249 err:
250     k5_json_release(array);
251     return ret;
252 }
253
254 /* Return a JSON array value representing creds. */
255 static krb5_error_code
256 json_creds(krb5_context context, krb5_creds *creds, k5_json_value *val_out)
257 {
258     krb5_error_code ret;
259     k5_json_array array;
260     k5_json_value client = NULL, server = NULL, keyblock = NULL, addrs = NULL;
261     k5_json_value authdata = NULL;
262
263     *val_out = NULL;
264     ret = json_principal(context, creds->client, &client);
265     if (ret)
266         goto cleanup;
267     ret = json_principal(context, creds->server, &server);
268     if (ret)
269         goto cleanup;
270     ret = json_keyblock(&creds->keyblock, &keyblock);
271     if (ret)
272         goto cleanup;
273     ret = json_addresses(creds->addresses, &addrs);
274     if (ret)
275         goto cleanup;
276     ret = json_authdata(creds->authdata, &authdata);
277     if (ret)
278         goto cleanup;
279
280     ret = k5_json_array_fmt(&array, "vvviiiibivBBv", client, server, keyblock,
281                             creds->times.authtime, creds->times.starttime,
282                             creds->times.endtime, creds->times.renew_till,
283                             creds->is_skey, creds->ticket_flags, addrs,
284                             (void *)creds->ticket.data,
285                             (size_t)creds->ticket.length,
286                             (void *)creds->second_ticket.data,
287                             (size_t)creds->second_ticket.length, authdata);
288     if (ret)
289         goto cleanup;
290     *val_out = array;
291
292 cleanup:
293     k5_json_release(client);
294     k5_json_release(server);
295     k5_json_release(keyblock);
296     k5_json_release(addrs);
297     k5_json_release(authdata);
298     return ret;
299 }
300
301 /* Return a JSON array value representing the contents of ccache. */
302 static krb5_error_code
303 json_ccache_contents(krb5_context context, krb5_ccache ccache,
304                      k5_json_value *val_out)
305 {
306     krb5_error_code ret;
307     krb5_principal princ;
308     krb5_cc_cursor cursor;
309     krb5_creds creds;
310     k5_json_array array;
311     k5_json_value val;
312
313     *val_out = NULL;
314     ret = k5_json_array_create(&array);
315     if (ret)
316         return ret;
317
318     /* Put the principal in the first array entry. */
319     ret = krb5_cc_get_principal(context, ccache, &princ);
320     if (ret)
321         goto err;
322     ret = json_principal(context, princ, &val);
323     krb5_free_principal(context, princ);
324     if (ret)
325         goto err;
326     ret = k5_json_array_add(array, val);
327     k5_json_release(val);
328     if (ret)
329         goto err;
330
331     /* Put credentials in the remaining array entries. */
332     ret = krb5_cc_start_seq_get(context, ccache, &cursor);
333     if (ret)
334         goto err;
335     while ((ret = krb5_cc_next_cred(context, ccache, &cursor, &creds)) == 0) {
336         ret = json_creds(context, &creds, &val);
337         krb5_free_cred_contents(context, &creds);
338         if (ret)
339             break;
340         ret = k5_json_array_add(array, val);
341         k5_json_release(val);
342         if (ret)
343             break;
344     }
345     krb5_cc_end_seq_get(context, ccache, &cursor);
346     if (ret != KRB5_CC_END)
347         goto err;
348     *val_out = array;
349     return 0;
350
351 err:
352     k5_json_release(array);
353     return ret;
354 }
355
356 /* Return a JSON null, string, or array value representing ccache. */
357 static krb5_error_code
358 json_ccache(krb5_context context, krb5_ccache ccache, k5_json_value *val_out)
359 {
360     krb5_error_code ret;
361     k5_json_string str;
362     char *name;
363
364     *val_out = NULL;
365     if (ccache == NULL)
366         return k5_json_null_create_val(val_out);
367     if (strcmp(krb5_cc_get_type(context, ccache), "MEMORY") == 0) {
368         return json_ccache_contents(context, ccache, val_out);
369     } else {
370         ret = krb5_cc_get_full_name(context, ccache, &name);
371         if (ret)
372             return ret;
373         ret = k5_json_string_create(name, &str);
374         free(name);
375         *val_out = str;
376         return ret;
377     }
378 }
379
380 /* Return a JSON array value representing cred. */
381 static krb5_error_code
382 json_kgcred(krb5_context context, krb5_gss_cred_id_t cred,
383             k5_json_value *val_out)
384 {
385     krb5_error_code ret;
386     k5_json_array array;
387     k5_json_value name = NULL, imp = NULL, keytab = NULL, rcache = NULL;
388     k5_json_value ccache = NULL, ckeytab = NULL, etypes = NULL;
389
390     *val_out = NULL;
391     ret = json_kgname(context, cred->name, &name);
392     if (ret)
393         goto cleanup;
394     ret = json_principal(context, cred->impersonator, &imp);
395     if (ret)
396         goto cleanup;
397     ret = json_keytab(context, cred->keytab, &keytab);
398     if (ret)
399         goto cleanup;
400     ret = json_rcache(context, cred->rcache, &rcache);
401     if (ret)
402         goto cleanup;
403     ret = json_ccache(context, cred->ccache, &ccache);
404     if (ret)
405         goto cleanup;
406     ret = json_keytab(context, cred->client_keytab, &ckeytab);
407     if (ret)
408         goto cleanup;
409     ret = json_etypes(cred->req_enctypes, &etypes);
410     if (ret)
411         goto cleanup;
412
413     ret = k5_json_array_fmt(&array, "ivvbbvvvvbiivs", cred->usage, name, imp,
414                             cred->default_identity, cred->iakerb_mech, keytab,
415                             rcache, ccache, ckeytab, cred->have_tgt,
416                             cred->expire, cred->refresh_time, etypes,
417                             cred->password);
418     if (ret)
419         goto cleanup;
420     *val_out = array;
421
422 cleanup:
423     k5_json_release(name);
424     k5_json_release(imp);
425     k5_json_release(keytab);
426     k5_json_release(rcache);
427     k5_json_release(ccache);
428     k5_json_release(ckeytab);
429     k5_json_release(etypes);
430     return ret;
431 }
432
433 OM_uint32 KRB5_CALLCONV
434 krb5_gss_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle,
435                      gss_buffer_t token)
436 {
437     OM_uint32 status = GSS_S_COMPLETE;
438     krb5_context context;
439     krb5_error_code ret;
440     krb5_gss_cred_id_t cred;
441     k5_json_array array = NULL;
442     k5_json_value jcred = NULL;
443     char *str = NULL;
444     krb5_data d;
445
446     ret = krb5_gss_init_context(&context);
447     if (ret) {
448         *minor_status = ret;
449         return GSS_S_FAILURE;
450     }
451
452     /* Validate and lock cred_handle. */
453     status = krb5_gss_validate_cred_1(minor_status, cred_handle, context);
454     if (status != GSS_S_COMPLETE)
455         return status;
456     cred = (krb5_gss_cred_id_t)cred_handle;
457
458     if (json_kgcred(context, cred, &jcred))
459         goto oom;
460     if (k5_json_array_fmt(&array, "sv", CRED_EXPORT_MAGIC, jcred))
461         goto oom;
462     if (k5_json_encode(array, &str))
463         goto oom;
464     d = string2data(str);
465     if (data_to_gss(&d, token))
466         goto oom;
467     str = NULL;
468
469 cleanup:
470     free(str);
471     k5_mutex_unlock(&cred->lock);
472     k5_json_release(array);
473     k5_json_release(jcred);
474     krb5_free_context(context);
475     return status;
476
477 oom:
478     *minor_status = ENOMEM;
479     status = GSS_S_FAILURE;
480     goto cleanup;
481 }