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 } 645