0bea5e6c6b5d8b12b2e9dd9330602d3e7b2eabaa
[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 (k5_rc_resolve(context, (char *)k5_json_string_utf8(v), &rcache))
175         return -1;
176     *rcache_out = rcache;
177     return 0;
178 }
179
180 /* Convert a JSON value to a keyblock, filling in keyblock. */
181 static int
182 json_to_keyblock(k5_json_value v, krb5_keyblock *keyblock)
183 {
184     k5_json_array array;
185     k5_json_number n;
186     k5_json_string s;
187     size_t len;
188
189     memset(keyblock, 0, sizeof(*keyblock));
190     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
191         return -1;
192     array = v;
193     if (k5_json_array_length(array) != 2)
194         return -1;
195
196     n = check_element(array, 0, K5_JSON_TID_NUMBER);
197     if (n == NULL)
198         return -1;
199     keyblock->enctype = k5_json_number_value(n);
200
201     s = check_element(array, 1, K5_JSON_TID_STRING);
202     if (s == NULL)
203         return -1;
204     if (k5_json_string_unbase64(s, &keyblock->contents, &len))
205         return -1;
206     keyblock->length = len;
207     keyblock->magic = KV5M_KEYBLOCK;
208     return 0;
209 }
210
211 /* Convert a JSON value to a krb5 address. */
212 static int
213 json_to_address(k5_json_value v, krb5_address **addr_out)
214 {
215     k5_json_array array;
216     krb5_address *addr = NULL;
217     k5_json_number n;
218     k5_json_string s;
219     size_t len;
220
221     *addr_out = NULL;
222     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
223         return -1;
224     array = v;
225     if (k5_json_array_length(array) != 2)
226         return -1;
227
228     n = check_element(array, 0, K5_JSON_TID_NUMBER);
229     if (n == NULL)
230         return -1;
231     s = check_element(array, 1, K5_JSON_TID_STRING);
232     if (s == NULL)
233         return -1;
234
235     addr = malloc(sizeof(*addr));
236     if (addr == NULL)
237         return -1;
238     addr->addrtype = k5_json_number_value(n);
239     if (k5_json_string_unbase64(s, &addr->contents, &len)) {
240         free(addr);
241         return -1;
242     }
243     addr->length = len;
244     addr->magic = KV5M_ADDRESS;
245     *addr_out = addr;
246     return 0;
247 }
248
249 /* Convert a JSON value to a null-terminated list of krb5 addresses or to
250  * NULL. */
251 static int
252 json_to_addresses(krb5_context context, k5_json_value v,
253                   krb5_address ***addresses_out)
254 {
255     k5_json_array array;
256     krb5_address **addrs = NULL;
257     size_t len, i;
258
259     *addresses_out = NULL;
260     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
261         return 0;
262     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
263         return -1;
264     array = v;
265     len = k5_json_array_length(array);
266     addrs = calloc(len + 1, sizeof(*addrs));
267     for (i = 0; i < len; i++) {
268         if (json_to_address(k5_json_array_get(array, i), &addrs[i]))
269             goto invalid;
270     }
271     addrs[i] = NULL;
272     *addresses_out = addrs;
273     return 0;
274
275 invalid:
276     krb5_free_addresses(context, addrs);
277     return -1;
278 }
279
280 /* Convert a JSON value to an authdata element. */
281 static int
282 json_to_authdata_element(k5_json_value v, krb5_authdata **ad_out)
283 {
284     k5_json_array array;
285     krb5_authdata *ad = NULL;
286     k5_json_number n;
287     k5_json_string s;
288     size_t len;
289
290     *ad_out = NULL;
291     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
292         return -1;
293     array = v;
294     if (k5_json_array_length(array) != 2)
295         return -1;
296
297     n = check_element(array, 0, K5_JSON_TID_NUMBER);
298     if (n == NULL)
299         return -1;
300     s = check_element(array, 1, K5_JSON_TID_STRING);
301     if (s == NULL)
302         return -1;
303
304     ad = malloc(sizeof(*ad));
305     if (ad == NULL)
306         return -1;
307     ad->ad_type = k5_json_number_value(n);
308     if (k5_json_string_unbase64(s, &ad->contents, &len)) {
309         free(ad);
310         return -1;
311     }
312     ad->length = len;
313     ad->magic = KV5M_AUTHDATA;
314     *ad_out = ad;
315     return 0;
316 }
317
318 /* Convert a JSON value to a null-terminated authdata list or to NULL. */
319 static int
320 json_to_authdata(krb5_context context, k5_json_value v,
321                  krb5_authdata ***authdata_out)
322 {
323     k5_json_array array;
324     krb5_authdata **authdata = NULL;
325     size_t len, i;
326
327     *authdata_out = NULL;
328     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
329         return 0;
330     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
331         return -1;
332     array = v;
333     len = k5_json_array_length(array);
334     authdata = calloc(len + 1, sizeof(*authdata));
335     for (i = 0; i < len; i++) {
336         if (json_to_authdata_element(k5_json_array_get(array, i),
337                                      &authdata[i]))
338             goto invalid;
339     }
340     authdata[i] = NULL;
341     *authdata_out = authdata;
342     return 0;
343
344 invalid:
345     krb5_free_authdata(context, authdata);
346     return -1;
347 }
348
349 /* Convert a JSON value to a krb5 credential structure, filling in creds. */
350 static int
351 json_to_creds(krb5_context context, k5_json_value v, krb5_creds *creds)
352 {
353     k5_json_array array;
354     k5_json_number n;
355     k5_json_bool b;
356     k5_json_string s;
357     unsigned char *data;
358     size_t len;
359
360     memset(creds, 0, sizeof(*creds));
361     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
362         return -1;
363     array = v;
364     if (k5_json_array_length(array) != 13)
365         return -1;
366
367     if (json_to_principal(context, k5_json_array_get(array, 0),
368                           &creds->client))
369         goto invalid;
370
371     if (json_to_principal(context, k5_json_array_get(array, 1),
372                           &creds->server))
373         goto invalid;
374
375     if (json_to_keyblock(k5_json_array_get(array, 2), &creds->keyblock))
376         goto invalid;
377
378     n = check_element(array, 3, K5_JSON_TID_NUMBER);
379     if (n == NULL)
380         goto invalid;
381     creds->times.authtime = k5_json_number_value(n);
382
383     n = check_element(array, 4, K5_JSON_TID_NUMBER);
384     if (n == NULL)
385         goto invalid;
386     creds->times.starttime = k5_json_number_value(n);
387
388     n = check_element(array, 5, K5_JSON_TID_NUMBER);
389     if (n == NULL)
390         goto invalid;
391     creds->times.endtime = k5_json_number_value(n);
392
393     n = check_element(array, 6, K5_JSON_TID_NUMBER);
394     if (n == NULL)
395         goto invalid;
396     creds->times.renew_till = k5_json_number_value(n);
397
398     b = check_element(array, 7, K5_JSON_TID_BOOL);
399     if (b == NULL)
400         goto invalid;
401     creds->is_skey = k5_json_bool_value(b);
402
403     n = check_element(array, 8, K5_JSON_TID_NUMBER);
404     if (n == NULL)
405         goto invalid;
406     creds->ticket_flags = k5_json_number_value(n);
407
408     if (json_to_addresses(context, k5_json_array_get(array, 9),
409                           &creds->addresses))
410         goto invalid;
411
412     s = check_element(array, 10, K5_JSON_TID_STRING);
413     if (s == NULL)
414         goto invalid;
415     if (k5_json_string_unbase64(s, &data, &len))
416         goto invalid;
417     creds->ticket.data = (char *)data;
418     creds->ticket.length = len;
419
420     s = check_element(array, 11, K5_JSON_TID_STRING);
421     if (s == NULL)
422         goto invalid;
423     if (k5_json_string_unbase64(s, &data, &len))
424         goto invalid;
425     creds->second_ticket.data = (char *)data;
426     creds->second_ticket.length = len;
427
428     if (json_to_authdata(context, k5_json_array_get(array, 12),
429                          &creds->authdata))
430         goto invalid;
431
432     creds->magic = KV5M_CREDS;
433     return 0;
434
435 invalid:
436     krb5_free_cred_contents(context, creds);
437     memset(creds, 0, sizeof(*creds));
438     return -1;
439 }
440
441 /* Convert a JSON value to a ccache handle or to NULL.  Set *new_out to true if
442  * the ccache handle is a newly created memory ccache, false otherwise. */
443 static int
444 json_to_ccache(krb5_context context, k5_json_value v, krb5_ccache *ccache_out,
445                krb5_boolean *new_out)
446 {
447     krb5_error_code ret;
448     krb5_ccache ccache = NULL;
449     krb5_principal princ;
450     krb5_creds creds;
451     k5_json_array array;
452     size_t i, len;
453
454     *ccache_out = NULL;
455     *new_out = FALSE;
456     if (k5_json_get_tid(v) == K5_JSON_TID_NULL)
457         return 0;
458     if (k5_json_get_tid(v) == K5_JSON_TID_STRING) {
459         /* We got a reference to an external ccache; just resolve it. */
460         return krb5_cc_resolve(context, k5_json_string_utf8(v), ccache_out) ?
461             -1 : 0;
462     }
463
464     /* We got the contents of a memory ccache. */
465     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
466         return -1;
467     array = v;
468     len = k5_json_array_length(array);
469     if (len < 1)
470         return -1;
471
472     /* Initialize a new memory ccache using the principal in the first array
473      * entry.*/
474     if (krb5_cc_new_unique(context, "MEMORY", NULL, &ccache))
475         return -1;
476     if (json_to_principal(context, k5_json_array_get(array, 0), &princ))
477         goto invalid;
478     ret = krb5_cc_initialize(context, ccache, princ);
479     krb5_free_principal(context, princ);
480     if (ret)
481         goto invalid;
482
483     /* Add remaining array entries to the ccache as credentials. */
484     for (i = 1; i < len; i++) {
485         if (json_to_creds(context, k5_json_array_get(array, i), &creds))
486             goto invalid;
487         ret = krb5_cc_store_cred(context, ccache, &creds);
488         krb5_free_cred_contents(context, &creds);
489         if (ret)
490             goto invalid;
491     }
492
493     *ccache_out = ccache;
494     *new_out = TRUE;
495     return 0;
496
497 invalid:
498     (void)krb5_cc_destroy(context, ccache);
499     return -1;
500 }
501
502 /* Convert a JSON array value to a krb5 GSS credential. */
503 static int
504 json_to_kgcred(krb5_context context, k5_json_array array,
505                krb5_gss_cred_id_t *cred_out)
506 {
507     krb5_gss_cred_id_t cred;
508     k5_json_number n;
509     k5_json_bool b;
510     krb5_boolean is_new;
511     OM_uint32 tmp;
512
513     *cred_out = NULL;
514     if (k5_json_array_length(array) != 14)
515         return -1;
516
517     cred = calloc(1, sizeof(*cred));
518     if (cred == NULL)
519         return -1;
520     if (k5_mutex_init(&cred->lock)) {
521         free(cred);
522         return -1;
523     }
524
525     n = check_element(array, 0, K5_JSON_TID_NUMBER);
526     if (n == NULL)
527         goto invalid;
528     cred->usage = k5_json_number_value(n);
529
530     if (json_to_kgname(context, k5_json_array_get(array, 1), &cred->name))
531         goto invalid;
532
533     if (json_to_principal(context, k5_json_array_get(array, 2),
534                           &cred->impersonator))
535         goto invalid;
536
537     b = check_element(array, 3, K5_JSON_TID_BOOL);
538     if (b == NULL)
539         goto invalid;
540     cred->default_identity = k5_json_bool_value(b);
541
542     b = check_element(array, 4, K5_JSON_TID_BOOL);
543     if (b == NULL)
544         goto invalid;
545     cred->iakerb_mech = k5_json_bool_value(b);
546
547     if (json_to_keytab(context, k5_json_array_get(array, 5), &cred->keytab))
548         goto invalid;
549
550     if (json_to_rcache(context, k5_json_array_get(array, 6), &cred->rcache))
551         goto invalid;
552
553     if (json_to_ccache(context, k5_json_array_get(array, 7), &cred->ccache,
554                        &is_new))
555         goto invalid;
556     cred->destroy_ccache = is_new;
557
558     if (json_to_keytab(context, k5_json_array_get(array, 8),
559                        &cred->client_keytab))
560         goto invalid;
561
562     b = check_element(array, 9, K5_JSON_TID_BOOL);
563     if (b == NULL)
564         goto invalid;
565     cred->have_tgt = k5_json_bool_value(b);
566
567     n = check_element(array, 10, K5_JSON_TID_NUMBER);
568     if (n == NULL)
569         goto invalid;
570     cred->expire = k5_json_number_value(n);
571
572     n = check_element(array, 11, K5_JSON_TID_NUMBER);
573     if (n == NULL)
574         goto invalid;
575     cred->refresh_time = k5_json_number_value(n);
576
577     if (json_to_etypes(k5_json_array_get(array, 12), &cred->req_enctypes))
578         goto invalid;
579
580     if (json_to_optional_string(k5_json_array_get(array, 13), &cred->password))
581         goto invalid;
582
583     *cred_out = cred;
584     return 0;
585
586 invalid:
587     (void)krb5_gss_release_cred(&tmp, (gss_cred_id_t *)&cred);
588     return -1;
589 }
590
591 OM_uint32 KRB5_CALLCONV
592 krb5_gss_import_cred(OM_uint32 *minor_status, gss_buffer_t token,
593                      gss_cred_id_t *cred_handle)
594 {
595     OM_uint32 status = GSS_S_COMPLETE;
596     krb5_context context;
597     krb5_error_code ret;
598     krb5_gss_cred_id_t cred;
599     k5_json_value v = NULL;
600     k5_json_array array;
601     k5_json_string str;
602     char *copy = NULL;
603
604     ret = krb5_gss_init_context(&context);
605     if (ret) {
606         *minor_status = ret;
607         return GSS_S_FAILURE;
608     }
609
610     /* Decode token. */
611     copy = k5memdup0(token->value, token->length, &ret);
612     if (copy == NULL) {
613         status = GSS_S_FAILURE;
614         *minor_status = ret;
615         goto cleanup;
616     }
617     if (k5_json_decode(copy, &v))
618         goto invalid;
619
620     /* Decode the CRED_EXPORT_MAGIC array wrapper. */
621     if (k5_json_get_tid(v) != K5_JSON_TID_ARRAY)
622         goto invalid;
623     array = v;
624     if (k5_json_array_length(array) != 2)
625         goto invalid;
626     str = check_element(array, 0, K5_JSON_TID_STRING);
627     if (str == NULL ||
628         strcmp(k5_json_string_utf8(str), CRED_EXPORT_MAGIC) != 0)
629         goto invalid;
630     if (json_to_kgcred(context, k5_json_array_get(array, 1), &cred))
631         goto invalid;
632
633     *cred_handle = (gss_cred_id_t)cred;
634
635 cleanup:
636     free(copy);
637     k5_json_release(v);
638     krb5_free_context(context);
639     return status;
640
641 invalid:
642     status = GSS_S_DEFECTIVE_TOKEN;
643     goto cleanup;
644 }