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