f0a0373bfb7918d82d92526c507a73941b20e953
[platform/upstream/krb5.git] / src / lib / gssapi / krb5 / import_cred.c
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/gssapi/krb5/import_cred.c - krb5 import_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 the idx element of array if it is of type tid; otherwise return
38  * NULL.  The caller is responsible for checking the array length. */
39 static k5_json_value
40 check_element(k5_json_array array, size_t idx, k5_json_tid tid)
41 {
42     k5_json_value v;
43
44     v = k5_json_array_get(array, idx);
45     return (k5_json_get_tid(v) == tid) ? v : NULL;
46 }
47
48 /* All of the json_to_x functions return 0 on success, -1 on failure (either
49  * from running out of memory or from defective input). */
50
51 /* Convert a JSON value to a C string or to NULL. */
52 static int
53 json_to_optional_string(k5_json_value v, char **string_out)
54 {
55     *string_out = NULL;
56     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
57         return 0;
58     if (k5_json_get_tid(v) != K5_JSON_TID_STRING)
59         return -1;
60     *string_out = strdup(k5_json_string_utf8(v));
61     return (*string_out == NULL) ? -1 : 0;
62 }
63
64 /* Convert a JSON value to a principal or to NULL. */
65 static int
66 json_to_principal(krb5_context context, k5_json_value v,
67                   krb5_principal *princ_out)
68 {
69     *princ_out = NULL;
70     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
71         return 0;
72     if (k5_json_get_tid(v) != K5_JSON_TID_STRING)
73         return -1;
74     if (krb5_parse_name(context, k5_json_string_utf8(v), princ_out))
75         return -1;
76     return 0;
77 }
78
79 /* Convert a JSON value to a zero-terminated enctypes list or to NULL. */
80 static int
81 json_to_etypes(k5_json_value v, krb5_enctype **etypes_out)
82 {
83     krb5_enctype *etypes = NULL;
84     k5_json_array array;
85     k5_json_number n;
86     size_t len, i;
87
88     *etypes_out = NULL;
89     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
90         return 0;
91     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
92         return -1;
93     array = v;
94     len = k5_json_array_length(array);
95     etypes = calloc(len + 1, sizeof(*etypes));
96     for (i = 0; i < len; i++) {
97         n = check_element(array, i, K5_JSON_TID_NUMBER);
98         if (n == NULL)
99             goto invalid;
100         etypes[i] = k5_json_number_value(n);
101     }
102     *etypes_out = etypes;
103     return 0;
104
105 invalid:
106     free(etypes);
107     return -1;
108 }
109
110 /* Convert a JSON value to a krb5 GSS name or to NULL. */
111 static int
112 json_to_kgname(krb5_context context, k5_json_value v,
113                krb5_gss_name_t *name_out)
114 {
115     k5_json_array array;
116     krb5_gss_name_t name = NULL;
117
118     *name_out = NULL;
119     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
120         return 0;
121     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
122         return -1;
123     array = v;
124     if (k5_json_array_length(array) != 3)
125         return -1;
126     name = calloc(1, sizeof(*name));
127     if (name == NULL)
128         return -1;
129     if (k5_mutex_init(&name->lock)) {
130         free(name);
131         return -1;
132     }
133
134     if (json_to_principal(context, k5_json_array_get(array, 0), &name->princ))
135         goto invalid;
136     if (json_to_optional_string(k5_json_array_get(array, 1), &name->service))
137         goto invalid;
138     if (json_to_optional_string(k5_json_array_get(array, 2), &name->host))
139         goto invalid;
140
141     *name_out = name;
142     return 0;
143
144 invalid:
145     kg_release_name(context, &name);
146     return -1;
147 }
148
149 /* Convert a JSON value to a keytab handle or to NULL. */
150 static int
151 json_to_keytab(krb5_context context, k5_json_value v, krb5_keytab *keytab_out)
152 {
153     *keytab_out = NULL;
154     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
155         return 0;
156     if (k5_json_get_tid(v) != K5_JSON_TID_STRING)
157         return -1;
158     if (krb5_kt_resolve(context, k5_json_string_utf8(v), keytab_out))
159         return -1;
160     return 0;
161 }
162
163 /* Convert a JSON value to an rcache handle or to NULL. */
164 static int
165 json_to_rcache(krb5_context context, k5_json_value v, krb5_rcache *rcache_out)
166 {
167     krb5_rcache rcache;
168
169     *rcache_out = NULL;
170     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
171         return 0;
172     if (k5_json_get_tid(v) != K5_JSON_TID_STRING)
173         return -1;
174     if (krb5_rc_resolve_full(context, &rcache, (char *)k5_json_string_utf8(v)))
175         return -1;
176     if (krb5_rc_recover_or_initialize(context, rcache, context->clockskew)) {
177         krb5_rc_close(context, rcache);
178         return -1;
179     }
180     *rcache_out = rcache;
181     return 0;
182 }
183
184 /* Convert a JSON value to a keyblock, filling in keyblock. */
185 static int
186 json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock)
187 {
188     k5_json_array array;
189     k5_json_number n;
190     k5_json_string s;
191     size_t len;
192
193     memset(keyblock, 0, sizeof(*keyblock));
194     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
195         return -1;
196     array = v;
197     if (k5_json_array_length(array) != 2)
198         return -1;
199
200     n = check_element(array, 0, K5_JSON_TID_NUMBER);
201     if (n == NULL)
202         return -1;
203     keyblock->enctype = k5_json_number_value(n);
204
205     s = check_element(array, 1, K5_JSON_TID_STRING);
206     if (s == NULL)
207         return -1;
208     if (k5_json_string_unbase64(s, &keyblock->contents, &len))
209         return -1;
210     keyblock->length = len;
211     keyblock->magic = KV5M_KEYBLOCK;
212     return 0;
213 }
214
215 /* Convert a JSON value to a krb5 address. */
216 static int
217 json_to_address(k5_json_value v, krb5_address **addr_out)
218 {
219     k5_json_array array;
220     krb5_address *addr = NULL;
221     k5_json_number n;
222     k5_json_string s;
223     size_t len;
224
225     *addr_out = NULL;
226     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
227         return -1;
228     array = v;
229     if (k5_json_array_length(array) != 2)
230         return -1;
231
232     n = check_element(array, 0, K5_JSON_TID_NUMBER);
233     if (n == NULL)
234         return -1;
235     s = check_element(array, 1, K5_JSON_TID_STRING);
236     if (s == NULL)
237         return -1;
238
239     addr = malloc(sizeof(*addr));
240     if (addr == NULL)
241         return -1;
242     addr->addrtype = k5_json_number_value(n);
243     if (k5_json_string_unbase64(s, &addr->contents, &len)) {
244         free(addr);
245         return -1;
246     }
247     addr->length = len;
248     addr->magic = KV5M_ADDRESS;
249     *addr_out = addr;
250     return 0;
251 }
252
253 /* Convert a JSON value to a null-terminated list of krb5 addresses or to
254  * NULL. */
255 static int
256 json_to_addresses(krb5_context context, k5_json_value v,
257                   krb5_address ***addresses_out)
258 {
259     k5_json_array array;
260     krb5_address **addrs = NULL;
261     size_t len, i;
262
263     *addresses_out = NULL;
264     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
265         return 0;
266     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
267         return -1;
268     array = v;
269     len = k5_json_array_length(array);
270     addrs = calloc(len + 1, sizeof(*addrs));
271     for (i = 0; i < len; i++) {
272         if (json_to_address(k5_json_array_get(array, i), &addrs[i]))
273             goto invalid;
274     }
275     addrs[i] = NULL;
276     *addresses_out = addrs;
277     return 0;
278
279 invalid:
280     krb5_free_addresses(context, addrs);
281     return -1;
282 }
283
284 /* Convert a JSON value to an authdata element. */
285 static int
286 json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out)
287 {
288     k5_json_array array;
289     krb5_authdata *ad = NULL;
290     k5_json_number n;
291     k5_json_string s;
292     size_t len;
293
294     *ad_out = NULL;
295     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
296         return -1;
297     array = v;
298     if (k5_json_array_length(array) != 2)
299         return -1;
300
301     n = check_element(array, 0, K5_JSON_TID_NUMBER);
302     if (n == NULL)
303         return -1;
304     s = check_element(array, 1, K5_JSON_TID_STRING);
305     if (s == NULL)
306         return -1;
307
308     ad = malloc(sizeof(*ad));
309     if (ad == NULL)
310         return -1;
311     ad->ad_type = k5_json_number_value(n);
312     if (k5_json_string_unbase64(s, &ad->contents, &len)) {
313         free(ad);
314         return -1;
315     }
316     ad->length = len;
317     ad->magic = KV5M_AUTHDATA;
318     *ad_out = ad;
319     return 0;
320 }
321
322 /* Convert a JSON value to a null-terminated authdata list or to NULL. */
323 static int
324 json_to_authdata(krb5_context context, k5_json_value v,
325                  krb5_authdata ***authdata_out)
326 {
327     k5_json_array array;
328     krb5_authdata **authdata = NULL;
329     size_t len, i;
330
331     *authdata_out = NULL;
332     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
333         return 0;
334     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
335         return -1;
336     array = v;
337     len = k5_json_array_length(array);
338     authdata = calloc(len + 1, sizeof(*authdata));
339     for (i = 0; i < len; i++) {
340         if (json_to_authdata_element(k5_json_array_get(array, i),
341                                      &authdata[i]))
342             goto invalid;
343     }
344     authdata[i] = NULL;
345     *authdata_out = authdata;
346     return 0;
347
348 invalid:
349     krb5_free_authdata(context, authdata);
350     return -1;
351 }
352
353 /* Convert a JSON value to a krb5 credential structure, filling in creds. */
354 static int
355 json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
356 {
357     k5_json_array array;
358     k5_json_number n;
359     k5_json_bool b;
360     k5_json_string s;
361     unsigned char *data;
362     size_t len;
363
364     memset(creds, 0, sizeof(*creds));
365     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
366         return -1;
367     array = v;
368     if (k5_json_array_length(array) != 13)
369         return -1;
370
371     if (json_to_principal(context, k5_json_array_get(array, 0),
372                           &creds->client))
373         goto invalid;
374
375     if (json_to_principal(context, k5_json_array_get(array, 1),
376                           &creds->server))
377         goto invalid;
378
379     if (json_to_keyblock(k5_json_array_get(array, 2), &creds->keyblock))
380         goto invalid;
381
382     n = check_element(array, 3, K5_JSON_TID_NUMBER);
383     if (n == NULL)
384         goto invalid;
385     creds->times.authtime = k5_json_number_value(n);
386
387     n = check_element(array, 4, K5_JSON_TID_NUMBER);
388     if (n == NULL)
389         goto invalid;
390     creds->times.starttime = k5_json_number_value(n);
391
392     n = check_element(array, 5, K5_JSON_TID_NUMBER);
393     if (n == NULL)
394         goto invalid;
395     creds->times.endtime = k5_json_number_value(n);
396
397     n = check_element(array, 6, K5_JSON_TID_NUMBER);
398     if (n == NULL)
399         goto invalid;
400     creds->times.renew_till = k5_json_number_value(n);
401
402     b = check_element(array, 7, K5_JSON_TID_BOOL);
403     if (b == NULL)
404         goto invalid;
405     creds->is_skey = k5_json_bool_value(b);
406
407     n = check_element(array, 8, K5_JSON_TID_NUMBER);
408     if (n == NULL)
409         goto invalid;
410     creds->ticket_flags = k5_json_number_value(n);
411
412     if (json_to_addresses(context, k5_json_array_get(array, 9),
413                           &creds->addresses))
414         goto invalid;
415
416     s = check_element(array, 10, K5_JSON_TID_STRING);
417     if (s == NULL)
418         goto invalid;
419     if (k5_json_string_unbase64(s, &data, &len))
420         goto invalid;
421     creds->ticket.data = (char *)data;
422     creds->ticket.length = len;
423
424     s = check_element(array, 11, K5_JSON_TID_STRING);
425     if (s == NULL)
426         goto invalid;
427     if (k5_json_string_unbase64(s, &data, &len))
428         goto invalid;
429     creds->second_ticket.data = (char *)data;
430     creds->second_ticket.length = len;
431
432     if (json_to_authdata(context, k5_json_array_get(array, 12),
433                          &creds->authdata))
434         goto invalid;
435
436     creds->magic = KV5M_CREDS;
437     return 0;
438
439 invalid:
440     krb5_free_cred_contents(context, creds);
441     memset(creds, 0, sizeof(*creds));
442     return -1;
443 }
444
445 /* Convert a JSON value to a ccache handle or to NULL.  Set *new_out to true if
446  * the ccache handle is a newly created memory ccache, false otherwise. */
447 static int
448 json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out,
449                krb5_boolean *new_out)
450 {
451     krb5_error_code ret;
452     krb5_ccache ccache = NULL;
453     krb5_principal princ;
454     krb5_creds creds;
455     k5_json_array array;
456     size_t i, len;
457
458     *ccache_out = NULL;
459     *new_out = FALSE;
460     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
461         return 0;
462     if (k5_json_get_tid(v) == K5_JSON_TID_STRING) {
463         /* We got a reference to an external ccache; just resolve it. */
464         return krb5_cc_resolve(context, k5_json_string_utf8(v), ccache_out) ?
465             -1 : 0;
466     }
467
468     /* We got the contents of a memory ccache. */
469     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
470         return -1;
471     array = v;
472     len = k5_json_array_length(array);
473     if (len < 1)
474         return -1;
475
476     /* Initialize a new memory ccache using the principal in the first array
477      * entry.*/
478     if (krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))
479         return -1;
480     if (json_to_principal(context, k5_json_array_get(array, 0), &princ))
481         goto invalid;
482     ret = krb5_cc_initialize(context, ccache, princ);
483     krb5_free_principal(context, princ);
484     if (ret)
485         goto invalid;
486
487     /* Add remaining array entries to the ccache as credentials. */
488     for (i = 1; i < len; i++) {
489         if (json_to_creds(context, k5_json_array_get(array, i), &creds))
490             goto invalid;
491         ret = krb5_cc_store_cred(context, ccache, &creds);
492         krb5_free_cred_contents(context, &creds);
493         if (ret)
494             goto invalid;
495     }
496
497     *ccache_out = ccache;
498     *new_out = TRUE;
499     return 0;
500
501 invalid:
502     (void)krb5_cc_destroy(context, ccache);
503     return -1;
504 }
505
506 /* Convert a JSON array value to a krb5 GSS credential. */
507 static int
508 json_to_kgcred(krb5_context context, k5_json_array array,
509                krb5_gss_cred_id_t *cred_out)
510 {
511     krb5_gss_cred_id_t cred;
512     k5_json_number n;
513     k5_json_bool b;
514     krb5_boolean is_new;
515     OM_uint32 tmp;
516
517     *cred_out = NULL;
518     if (k5_json_array_length(array) != 14)
519         return -1;
520
521     cred = calloc(1, sizeof(*cred));
522     if (cred == NULL)
523         return -1;
524     if (k5_mutex_init(&cred->lock)) {
525         free(cred);
526         return -1;
527     }
528
529     n = check_element(array, 0, K5_JSON_TID_NUMBER);
530     if (n == NULL)
531         goto invalid;
532     cred->usage = k5_json_number_value(n);
533
534     if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name))
535         goto invalid;
536
537     if (json_to_principal(context, k5_json_array_get(array, 2),
538                           &cred->impersonator))
539         goto invalid;
540
541     b = check_element(array, 3, K5_JSON_TID_BOOL);
542     if (b == NULL)
543         goto invalid;
544     cred->default_identity = k5_json_bool_value(b);
545
546     b = check_element(array, 4, K5_JSON_TID_BOOL);
547     if (b == NULL)
548         goto invalid;
549     cred->iakerb_mech = k5_json_bool_value(b);
550
551     if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab))
552         goto invalid;
553
554     if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache))
555         goto invalid;
556
557     if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache,
558                        &is_new))
559         goto invalid;
560     cred->destroy_ccache = is_new;
561
562     if (json_to_keytab(context, k5_json_array_get(array, 8),
563                        &cred->client_keytab))
564         goto invalid;
565
566     b = check_element(array, 9, K5_JSON_TID_BOOL);
567     if (b == NULL)
568         goto invalid;
569     cred->have_tgt = k5_json_bool_value(b);
570
571     n = check_element(array, 10, K5_JSON_TID_NUMBER);
572     if (n == NULL)
573         goto invalid;
574     cred->expire = k5_json_number_value(n);
575
576     n = check_element(array, 11, K5_JSON_TID_NUMBER);
577     if (n == NULL)
578         goto invalid;
579     cred->refresh_time = k5_json_number_value(n);
580
581     if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes))
582         goto invalid;
583
584     if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password))
585         goto invalid;
586
587     *cred_out = cred;
588     return 0;
589
590 invalid:
591     (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred);
592     return -1;
593 }
594
595 OM_uint32 KRB5_CALLCONV
596 krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token,
597                      gss_cred_id_t *cred_handle)
598 {
599     OM_uint32 status = GSS_S_COMPLETE;
600     krb5_context context;
601     krb5_error_code ret;
602     krb5_gss_cred_id_t cred;
603     k5_json_value v = NULL;
604     k5_json_array array;
605     k5_json_string str;
606     char *copy = NULL;
607
608     ret = krb5_gss_init_context(&context);
609     if (ret) {
610         *minor_status = ret;
611         return GSS_S_FAILURE;
612     }
613
614     /* Decode token. */
615     copy = k5memdup0(token->value, token->length, &ret);
616     if (copy == NULL) {
617         status = GSS_S_FAILURE;
618         *minor_status = ret;
619         goto cleanup;
620     }
621     if (k5_json_decode(copy, &v))
622         goto invalid;
623
624     /* Decode the CRED_EXPORT_MAGIC array wrapper. */
625     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
626         goto invalid;
627     array = v;
628     if (k5_json_array_length(array) != 2)
629         goto invalid;
630     str = check_element(array, 0, K5_JSON_TID_STRING);
631     if (str == NULL ||
632         strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0)
633         goto invalid;
634     if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred))
635         goto invalid;
636
637     *cred_handle = (gss_cred_id_t)cred;
638
639 cleanup:
640     free(copy);
641     k5_json_release(v);
642     krb5_free_context(context);
643     return status;
644
645 invalid:
646     status = GSS_S_DEFECTIVE_TOKEN;
647     goto cleanup;
648 }