xref: /freebsd/crypto/krb5/src/lib/gssapi/krb5/import_cred.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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
check_element(k5_json_array array,size_t idx,k5_json_tid tid)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
json_to_optional_string(k5_json_value v,char ** string_out)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
json_to_principal(krb5_context context,k5_json_value v,krb5_principal * princ_out)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
json_to_etypes(k5_json_value v,krb5_enctype ** etypes_out)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
json_to_kgname(krb5_context context,k5_json_value v,krb5_gss_name_t * name_out)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
json_to_keytab(krb5_context context,k5_json_value v,krb5_keytab * keytab_out)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
json_to_rcache(krb5_context context,k5_json_value v,krb5_rcache * rcache_out)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
json_to_keyblock(k5_json_value v,krb5_keyblock * keyblock)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
json_to_address(k5_json_value v,krb5_address ** addr_out)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
json_to_addresses(krb5_context context,k5_json_value v,krb5_address *** addresses_out)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
json_to_authdata_element(k5_json_value v,krb5_authdata ** ad_out)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
json_to_authdata(krb5_context context,k5_json_value v,krb5_authdata *** authdata_out)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
json_to_creds(krb5_context context,k5_json_value v,krb5_creds * creds)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
json_to_ccache(krb5_context context,k5_json_value v,krb5_ccache * ccache_out,krb5_boolean * new_out)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
json_to_kgcred(krb5_context context,k5_json_array array,krb5_gss_cred_id_t * cred_out)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
krb5_gss_import_cred(OM_uint32 * minor_status,gss_buffer_t token,gss_cred_id_t * cred_handle)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 }
645