xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/export_cred.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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