1/* 2 * Copyright (c) 2004 - 2007 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * 3. Neither the name of the Institute nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 */ 33 34#include "mit-CredentialsCache.h" 35#include <string.h> 36#include <syslog.h> 37#include <stdlib.h> 38#include <errno.h> 39#include <stdarg.h> 40#include <stdio.h> 41 42#include "heim.h" 43#include "heim-sym.h" 44 45static cc_time_t context_change_time = 0; 46 47void 48update_time(cc_time_t *change_time) 49{ 50 cc_time_t now = time(NULL); 51 if (*change_time >= now) 52 *change_time += 1; 53 else 54 *change_time = now; 55} 56 57static cc_int32 58string_release(cc_string_t io_string) 59{ 60 free((char *)io_string->data); 61 free(io_string); 62 return ccNoError; 63} 64 65 66cc_string_f string_functions = { 67 .release = string_release 68}; 69 70static cc_string_t 71create_string(const char *string) 72{ 73 cc_string_t s; 74 s = mshim_malloc(sizeof(*s)); 75 s->functions = &string_functions; 76 s->data = strdup(string); 77 return s; 78} 79 80 81#define KRB5_CCAPI_TKT_FLG_FORWARDABLE 0x40000000 82#define KRB5_CCAPI_TKT_FLG_FORWARDED 0x20000000 83#define KRB5_CCAPI_TKT_FLG_PROXIABLE 0x10000000 84#define KRB5_CCAPI_TKT_FLG_PROXY 0x08000000 85#define KRB5_CCAPI_TKT_FLG_MAY_POSTDATE 0x04000000 86#define KRB5_CCAPI_TKT_FLG_POSTDATED 0x02000000 87#define KRB5_CCAPI_TKT_FLG_INVALID 0x01000000 88#define KRB5_CCAPI_TKT_FLG_RENEWABLE 0x00800000 89#define KRB5_CCAPI_TKT_FLG_INITIAL 0x00400000 90#define KRB5_CCAPI_TKT_FLG_PRE_AUTH 0x00200000 91#define KRB5_CCAPI_TKT_FLG_HW_AUTH 0x00100000 92#define KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED 0x00080000 93#define KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE 0x00040000 94#define KRB5_CCAPI_TKT_FLG_ANONYMOUS 0x00020000 95 96static krb5_error_code 97make_cred_from_ccred(krb5_context context, 98 const cc_credentials_v5_t *incred, 99 krb5_creds *cred) 100{ 101 krb5_error_code ret; 102 unsigned int i; 103 104 memset(cred, 0, sizeof(*cred)); 105 106 ret = heim_krb5_parse_name(context, incred->client, &cred->client); 107 if (ret) 108 goto fail; 109 110 ret = heim_krb5_parse_name(context, incred->server, &cred->server); 111 if (ret) 112 goto fail; 113 114 cred->session.keytype = incred->keyblock.type; 115 cred->session.keyvalue.length = incred->keyblock.length; 116 cred->session.keyvalue.data = malloc(incred->keyblock.length); 117 if (cred->session.keyvalue.data == NULL) 118 goto nomem; 119 memcpy(cred->session.keyvalue.data, incred->keyblock.data, 120 incred->keyblock.length); 121 122 cred->times.authtime = incred->authtime; 123 cred->times.starttime = incred->starttime; 124 cred->times.endtime = incred->endtime; 125 cred->times.renew_till = incred->renew_till; 126 127 ret = heim_krb5_data_copy(&cred->ticket, 128 incred->ticket.data, 129 incred->ticket.length); 130 if (ret) 131 goto nomem; 132 133 ret = heim_krb5_data_copy(&cred->second_ticket, 134 incred->second_ticket.data, 135 incred->second_ticket.length); 136 if (ret) 137 goto nomem; 138 139 cred->authdata.val = NULL; 140 cred->authdata.len = 0; 141 142 cred->addresses.val = NULL; 143 cred->addresses.len = 0; 144 145 for (i = 0; incred->authdata && incred->authdata[i]; i++) 146 ; 147 148 if (i) { 149 cred->authdata.val = calloc(i, sizeof(cred->authdata.val[0])); 150 if (cred->authdata.val == NULL) 151 goto nomem; 152 cred->authdata.len = i; 153 for (i = 0; i < cred->authdata.len; i++) { 154 cred->authdata.val[i].ad_type = incred->authdata[i]->type; 155 ret = heim_krb5_data_copy(&cred->authdata.val[i].ad_data, 156 incred->authdata[i]->data, 157 incred->authdata[i]->length); 158 if (ret) 159 goto nomem; 160 } 161 } 162 163 for (i = 0; incred->addresses && incred->addresses[i]; i++) 164 ; 165 166 if (i) { 167 cred->addresses.val = calloc(i, sizeof(cred->addresses.val[0])); 168 if (cred->addresses.val == NULL) 169 goto nomem; 170 cred->addresses.len = i; 171 172 for (i = 0; i < cred->addresses.len; i++) { 173 cred->addresses.val[i].addr_type = incred->addresses[i]->type; 174 ret = heim_krb5_data_copy(&cred->addresses.val[i].address, 175 incred->addresses[i]->data, 176 incred->addresses[i]->length); 177 if (ret) 178 goto nomem; 179 } 180 } 181 182 cred->flags.i = 0; 183 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDABLE) 184 cred->flags.b.forwardable = 1; 185 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_FORWARDED) 186 cred->flags.b.forwarded = 1; 187 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXIABLE) 188 cred->flags.b.proxiable = 1; 189 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PROXY) 190 cred->flags.b.proxy = 1; 191 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_MAY_POSTDATE) 192 cred->flags.b.may_postdate = 1; 193 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_POSTDATED) 194 cred->flags.b.postdated = 1; 195 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INVALID) 196 cred->flags.b.invalid = 1; 197 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_RENEWABLE) 198 cred->flags.b.renewable = 1; 199 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_INITIAL) 200 cred->flags.b.initial = 1; 201 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_PRE_AUTH) 202 cred->flags.b.pre_authent = 1; 203 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_HW_AUTH) 204 cred->flags.b.hw_authent = 1; 205 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED) 206 cred->flags.b.transited_policy_checked = 1; 207 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE) 208 cred->flags.b.ok_as_delegate = 1; 209 if (incred->ticket_flags & KRB5_CCAPI_TKT_FLG_ANONYMOUS) 210 cred->flags.b.anonymous = 1; 211 212 return 0; 213 214nomem: 215 ret = ENOMEM; 216 krb5_set_error_message((mit_krb5_context)context, ret, "malloc: out of memory"); 217 218fail: 219 heim_krb5_free_cred_contents(context, cred); 220 return ret; 221} 222 223static void 224free_ccred(cc_credentials_v5_t *cred) 225{ 226 int i; 227 228 if (cred->addresses) { 229 for (i = 0; cred->addresses[i] != 0; i++) { 230 if (cred->addresses[i]->data) 231 free(cred->addresses[i]->data); 232 free(cred->addresses[i]); 233 } 234 free(cred->addresses); 235 } 236 if (cred->server) 237 free(cred->server); 238 if (cred->client) 239 free(cred->client); 240 memset(cred, 0, sizeof(*cred)); 241} 242 243static krb5_error_code 244make_ccred_from_cred(krb5_context context, 245 const krb5_creds *incred, 246 cc_credentials_v5_t *cred) 247{ 248 krb5_error_code ret; 249 int i; 250 251 memset(cred, 0, sizeof(*cred)); 252 253 ret = heim_krb5_unparse_name(context, incred->client, &cred->client); 254 if (ret) 255 goto fail; 256 257 ret = heim_krb5_unparse_name(context, incred->server, &cred->server); 258 if (ret) 259 goto fail; 260 261 cred->keyblock.type = incred->session.keytype; 262 cred->keyblock.length = incred->session.keyvalue.length; 263 cred->keyblock.data = incred->session.keyvalue.data; 264 265 cred->authtime = incred->times.authtime; 266 cred->starttime = incred->times.starttime; 267 cred->endtime = incred->times.endtime; 268 cred->renew_till = incred->times.renew_till; 269 270 cred->ticket.length = incred->ticket.length; 271 cred->ticket.data = incred->ticket.data; 272 273 cred->second_ticket.length = incred->second_ticket.length; 274 cred->second_ticket.data = incred->second_ticket.data; 275 276 /* XXX this one should also be filled in */ 277 cred->authdata = NULL; 278 279 cred->addresses = calloc(incred->addresses.len + 1, 280 sizeof(cred->addresses[0])); 281 if (cred->addresses == NULL) { 282 283 ret = ENOMEM; 284 goto fail; 285 } 286 287 for (i = 0; i < incred->addresses.len; i++) { 288 cc_data *addr; 289 addr = malloc(sizeof(*addr)); 290 if (addr == NULL) { 291 ret = ENOMEM; 292 goto fail; 293 } 294 addr->type = incred->addresses.val[i].addr_type; 295 addr->length = incred->addresses.val[i].address.length; 296 addr->data = malloc(addr->length); 297 if (addr->data == NULL) { 298 free(addr); 299 ret = ENOMEM; 300 goto fail; 301 } 302 memcpy(addr->data, incred->addresses.val[i].address.data, 303 addr->length); 304 cred->addresses[i] = addr; 305 } 306 cred->addresses[i] = NULL; 307 308 cred->ticket_flags = 0; 309 if (incred->flags.b.forwardable) 310 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDABLE; 311 if (incred->flags.b.forwarded) 312 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_FORWARDED; 313 if (incred->flags.b.proxiable) 314 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXIABLE; 315 if (incred->flags.b.proxy) 316 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PROXY; 317 if (incred->flags.b.may_postdate) 318 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_MAY_POSTDATE; 319 if (incred->flags.b.postdated) 320 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_POSTDATED; 321 if (incred->flags.b.invalid) 322 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INVALID; 323 if (incred->flags.b.renewable) 324 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_RENEWABLE; 325 if (incred->flags.b.initial) 326 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_INITIAL; 327 if (incred->flags.b.pre_authent) 328 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_PRE_AUTH; 329 if (incred->flags.b.hw_authent) 330 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_HW_AUTH; 331 if (incred->flags.b.transited_policy_checked) 332 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_TRANSIT_POLICY_CHECKED; 333 if (incred->flags.b.ok_as_delegate) 334 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_OK_AS_DELEGATE; 335 if (incred->flags.b.anonymous) 336 cred->ticket_flags |= KRB5_CCAPI_TKT_FLG_ANONYMOUS; 337 338 return 0; 339 340fail: 341 free_ccred(cred); 342 343 krb5_clear_error_message((mit_krb5_context)context); 344 return ret; 345} 346 347/* 348 * 349 */ 350 351struct cred { 352 cc_credentials_union *data; 353 cc_credentials_f *functions; 354 cc_credentials_f *otherFunctions; 355 krb5_creds cred; 356}; 357 358 359 360static cc_int32 361cred_release(cc_credentials_t io_credentials) 362{ 363 struct cred *c = (struct cred *)io_credentials; 364 heim_krb5_free_cred_contents(milcontext, &c->cred); 365 free(c->data->credentials.credentials_v5); 366 free(c->data); 367 free(c); 368 return ccNoError; 369} 370 371static cc_int32 372cred_compare (cc_credentials_t in_credentials, 373 cc_credentials_t in_compare_to_credentials, 374 cc_uint32 *out_equal) 375{ 376 *out_equal = 1; 377 return ccErrNoMem; 378} 379 380cc_credentials_f credential_functions = { 381 .release = cred_release, 382 .compare = cred_compare 383}; 384 385 386 387static cc_credentials_t 388create_credentials(krb5_creds *cred) 389{ 390 struct cred *c; 391 392 c = calloc(1, sizeof(*c)); 393 c->data = calloc(1, sizeof(*c->data)); 394 c->data->version = cc_credentials_v5; 395 c->data->credentials.credentials_v5 = calloc(1, sizeof(*c->data->credentials.credentials_v5)); 396 c->functions = &credential_functions; 397 398 heim_krb5_copy_creds_contents(milcontext, cred, &c->cred); 399 make_ccred_from_cred(milcontext, &c->cred, c->data->credentials.credentials_v5); 400 401 return (cc_credentials_t)c; 402} 403 404 405struct cred_iterator { 406 cc_credentials_iterator_d iterator; 407 krb5_ccache id; 408 krb5_cc_cursor cursor; 409}; 410 411static cc_int32 412cred_iter_release(cc_credentials_iterator_t io_credentials_iterator) 413{ 414 struct cred_iterator *ci = (struct cred_iterator *)io_credentials_iterator; 415 LOG_ENTRY(); 416 if (ci->id) 417 krb5_cc_end_seq_get ((mit_krb5_context)milcontext, 418 (mit_krb5_ccache)ci->id, 419 (mit_krb5_cc_cursor *)&ci->cursor); 420 free(ci); 421 return ccNoError; 422} 423 424cc_int32 425cred_iter_next(cc_credentials_iterator_t in_credentials_iterator, cc_credentials_t *out_credentials) 426{ 427 struct cred_iterator *ci = (struct cred_iterator *)in_credentials_iterator; 428 krb5_error_code ret; 429 krb5_creds cred; 430 LOG_ENTRY(); 431 432 ret = heim_krb5_cc_next_cred(milcontext, ci->id, &ci->cursor, &cred); 433 if (ret == KRB5_CC_END) 434 return ccIteratorEnd; 435 else if (ret) 436 return ret; /* XXX */ 437 438 *out_credentials = create_credentials(&cred); 439 heim_krb5_free_cred_contents(milcontext, &cred); 440 if (*out_credentials == NULL) 441 return ccErrNoMem; 442 443 return ccNoError; 444} 445 446cc_int32 447cred_iter_clone (cc_credentials_iterator_t in_credentials_iterator, 448 cc_credentials_iterator_t *out_credentials_iterator) 449{ 450 LOG_UNIMPLEMENTED(); 451 return ccErrNoMem; 452} 453 454 455struct cc_credentials_iterator_f cred_iter_functions = { 456 .release = cred_iter_release, 457 .next = cred_iter_next, 458 .clone = cred_iter_clone 459}; 460 461 462 463struct cc_ccache { 464 cc_ccache_d ccache; 465 krb5_ccache id; 466 cc_time_t change_time; 467 cc_time_t last_default_time; 468}; 469 470cc_int32 471ccache_release(cc_ccache_t io_ccache) 472{ 473 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 474 LOG_ENTRY(); 475 if (c->id) 476 heim_krb5_cc_close(milcontext, c->id); 477 free(c); 478 return ccNoError; 479} 480 481static cc_int32 482ccache_destroy(cc_ccache_t io_ccache) 483{ 484 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 485 LOG_ENTRY(); 486 update_time(&context_change_time); 487 if (c->id) { 488 krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)c->id); 489 c->id = NULL; 490 } 491 return ccNoError; 492} 493 494static cc_int32 495ccache_set_default(cc_ccache_t io_ccache) 496{ 497 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 498 LOG_ENTRY(); 499 if (io_ccache == NULL || c->id == NULL) 500 return ccErrBadParam; 501 heim_krb5_cc_switch(milcontext, c->id); 502 update_time(&c->change_time); 503 update_time(&c->last_default_time); 504 return ccNoError; 505} 506 507static cc_int32 508ccache_get_credentials_version(cc_ccache_t in_ccache, cc_uint32 *out_credentials_version) 509{ 510 if (out_credentials_version == NULL) 511 return ccErrBadParam; 512 *out_credentials_version = cc_credentials_v5; 513 return ccNoError; 514} 515 516static cc_int32 517ccache_get_name(cc_ccache_t in_ccache, cc_string_t *out_name) 518{ 519 struct cc_ccache *c = (struct cc_ccache *)in_ccache; 520 const char *name; 521 LOG_ENTRY(); 522 523 if (out_name == NULL) 524 return ccErrBadParam; 525 if (c->id == NULL) 526 return ccErrInvalidCCache; 527 528 name = heim_krb5_cc_get_name(milcontext, c->id); 529 if (name == NULL) 530 return ccErrInvalidCCache; 531 *out_name = create_string(name); 532 533 return ccNoError; 534} 535 536static cc_int32 537ccache_get_principal(cc_ccache_t in_ccache, cc_uint32 in_credentials_version, cc_string_t *out_principal) 538{ 539 struct cc_ccache *c = (struct cc_ccache *)in_ccache; 540 krb5_principal princ; 541 krb5_error_code ret; 542 char *name; 543 544 LOG_ENTRY(); 545 546 if (out_principal == NULL) 547 return ccErrBadParam; 548 if (in_credentials_version != cc_credentials_v5) 549 return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version"); 550 if (c->id == NULL) 551 return ccErrInvalidCCache; 552 553 ret = heim_krb5_cc_get_principal(milcontext, c->id, &princ); 554 if (ret) 555 return LOG_FAILURE(ret, "get principal"); 556 ret = heim_krb5_unparse_name(milcontext, princ, &name); 557 heim_krb5_free_principal(milcontext, princ); 558 if (ret) 559 return LOG_FAILURE(ret, "unparse name"); 560 *out_principal = create_string(name); 561 free(name); 562 563 return ccNoError; 564} 565 566static cc_int32 567ccache_set_principal(cc_ccache_t io_ccache, cc_uint32 in_credentials_version, const char *in_principal) 568{ 569 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 570 krb5_error_code ret; 571 krb5_principal p; 572 LOG_ENTRY(); 573 574 if (in_principal == NULL) 575 return ccErrBadParam; 576 if (in_credentials_version != cc_credentials_v5) 577 return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version"); 578 579 update_time(&c->change_time); 580 update_time(&context_change_time); 581 582 ret = heim_krb5_parse_name(milcontext, in_principal, &p); 583 if (ret) 584 return LOG_FAILURE(ccErrBadParam, "parse name"); 585 586 ret = heim_krb5_cc_initialize(milcontext, c->id, p); 587 heim_krb5_free_principal(milcontext, p); 588 if (ret) 589 return LOG_FAILURE(ccErrInvalidCCache, "init cache"); 590 591 return ccNoError; 592} 593 594static cc_int32 595ccache_store_credentials(cc_ccache_t io_ccache, const cc_credentials_union *in_credentials_union) 596{ 597 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 598 krb5_error_code ret; 599 krb5_creds hcred; 600 LOG_ENTRY(); 601 602 if (in_credentials_union == NULL) 603 return ccErrBadParam; 604 if (in_credentials_union->version != cc_credentials_v5) 605 return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version"); 606 if (in_credentials_union->credentials.credentials_v5->client == NULL) 607 return ccErrBadParam; 608 609 update_time(&c->change_time); 610 update_time(&context_change_time); 611 612 make_cred_from_ccred(milcontext, in_credentials_union->credentials.credentials_v5, &hcred); 613 614 ret = heim_krb5_cc_store_cred(milcontext, c->id, &hcred); 615 heim_krb5_free_cred_contents(milcontext, &hcred); 616 if (ret) 617 return LOG_FAILURE(ccErrInvalidCCache, "store cred"); 618 619 return ccNoError; 620} 621 622static cc_int32 623ccache_remove_credentials(cc_ccache_t io_ccache, cc_credentials_t in_credentials) 624{ 625 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 626 const cc_credentials_v5_t *incred; 627 krb5_creds cred; 628 krb5_error_code ret; 629 630 LOG_ENTRY(); 631 632 update_time(&c->change_time); 633 update_time(&context_change_time); 634 635 memset(&cred, 0, sizeof(cred)); 636 637 if (c->id == NULL) 638 return LOG_FAILURE(ccErrBadParam, "bad argument"); 639 640 if (in_credentials == NULL || in_credentials->data == NULL) 641 return LOG_FAILURE(ccErrBadParam, "remove with no cred?"); 642 if (in_credentials->data->version != cc_credentials_v5) 643 return LOG_FAILURE(ccErrBadParam, "wrong version"); 644 645 incred = in_credentials->data->credentials.credentials_v5; 646 if (incred->client == NULL) 647 return LOG_FAILURE(ccErrBadParam, "no client to remove"); 648 if (incred->server == NULL) 649 return LOG_FAILURE(ccErrBadParam, "no server to remove"); 650 651 ret = heim_krb5_parse_name(milcontext, incred->client, &cred.client); 652 if (ret) 653 goto fail; 654 ret = heim_krb5_parse_name(milcontext, incred->server, &cred.server); 655 if (ret) 656 goto fail; 657 658 ret = heim_krb5_cc_remove_cred(milcontext, c->id, 0, &cred); 659 660 update_time(&context_change_time); 661 fail: 662 heim_krb5_free_cred_contents(milcontext, &cred); 663 if (ret) 664 return ccErrCredentialsNotFound; 665 return ccNoError; 666} 667 668static cc_int32 669ccache_new_credentials_iterator(cc_ccache_t in_ccache, cc_credentials_iterator_t *out_credentials_iterator) 670{ 671 struct cc_ccache *c = (struct cc_ccache *)in_ccache; 672 struct cred_iterator *ci; 673 krb5_error_code ret; 674 LOG_ENTRY(); 675 676 if (c == NULL || c->id == NULL) 677 return ccErrInvalidCCache; 678 if (out_credentials_iterator == NULL) 679 return ccErrBadParam; 680 681 ci = calloc(1, sizeof(*ci)); 682 ci->iterator.functions = &cred_iter_functions; 683 ci->id = c->id; 684 ret = krb5_cc_start_seq_get((mit_krb5_context)milcontext, 685 (mit_krb5_ccache)c->id, 686 (mit_krb5_cc_cursor *)&ci->cursor); 687 if (ret) { 688 free(ci); 689 return LOG_FAILURE(ccErrInvalidCCache, "start seq"); 690 } 691 *out_credentials_iterator = (cc_credentials_iterator_t)ci; 692 return ccNoError; 693} 694 695static cc_int32 696ccache_move(cc_ccache_t io_source_ccache, cc_ccache_t io_destination_ccache) 697{ 698 struct cc_ccache *s = (struct cc_ccache *)io_source_ccache; 699 struct cc_ccache *d = (struct cc_ccache *)io_destination_ccache; 700 krb5_error_code ret; 701 702 if (s->id == NULL) 703 return ccErrInvalidCCache; 704 if (d == NULL) 705 return ccErrBadParam; 706 707 if (d->id == NULL) { 708 ret = heim_krb5_cc_new_unique(milcontext, 709 heim_krb5_cc_get_type(milcontext, s->id), 710 NULL, &d->id); 711 if (ret) 712 return ccErrInvalidCCache; 713 } 714 715 ret = heim_krb5_cc_move(milcontext, s->id, d->id); 716 if (ret) 717 return LOG_FAILURE(ret, "move cache"); 718 s->id = NULL; 719 720 return ccNoError; 721} 722 723static cc_int32 724ccache_lock(cc_ccache_t io_ccache, cc_uint32 in_lock_type, cc_uint32 in_block) 725{ 726 LOG_ENTRY(); 727 return ccNoError; 728} 729 730static cc_int32 731ccache_unlock(cc_ccache_t io_ccache) 732{ 733 LOG_ENTRY(); 734 return ccNoError; 735} 736 737static cc_int32 738ccache_get_last_default_time(cc_ccache_t in_ccache, cc_time_t *out_last_default_time) 739{ 740 struct cc_ccache *s = (struct cc_ccache *)in_ccache; 741 LOG_ENTRY(); 742 743 if (out_last_default_time == NULL) 744 return ccErrBadParam; 745 if (s->id == NULL) 746 return ccErrInvalidCCache; 747 if (s->last_default_time == 0) 748 return ccErrNeverDefault; 749 750 *out_last_default_time = s->last_default_time; 751 return ccNoError; 752} 753 754static cc_int32 755ccache_get_change_time(cc_ccache_t in_ccache, cc_time_t *out_change_time) 756{ 757 struct cc_ccache *s = (struct cc_ccache *)in_ccache; 758 LOG_ENTRY(); 759 760 if (out_change_time == NULL) 761 return ccErrBadParam; 762 *out_change_time = s->change_time; 763 return ccNoError; 764} 765 766static cc_int32 767ccache_compare(cc_ccache_t in_ccache, cc_ccache_t in_compare_to_ccache, cc_uint32 *out_equal) 768{ 769 struct cc_ccache *s1 = (struct cc_ccache *)in_ccache; 770 struct cc_ccache *s2 = (struct cc_ccache *)in_compare_to_ccache; 771 krb5_error_code ret; 772 char *n1, *n2; 773 774 LOG_ENTRY(); 775 776 if (out_equal == NULL || s2 == NULL) 777 return ccErrBadParam; 778 if (s1 == s2) { 779 *out_equal = 1; 780 return ccNoError; 781 } 782 if (s1->id == NULL || s2->id == NULL) 783 return ccErrInvalidCCache; 784 785 ret = heim_krb5_cc_get_full_name(milcontext, s1->id, &n1); 786 if (ret) 787 return ccErrInvalidCCache; 788 ret = heim_krb5_cc_get_full_name(milcontext, s2->id, &n2); 789 if (ret) { 790 free(n1); 791 return ccErrInvalidCCache; 792 } 793 794 *out_equal = (strcmp(n1, n2) == 0); 795 796 free(n1); 797 free(n2); 798 799 return ccNoError; 800} 801 802static cc_int32 803ccache_get_kdc_time_offset(cc_ccache_t in_ccache, 804 cc_uint32 in_credentials_version, 805 cc_time_t *out_time_offset) 806{ 807 struct cc_ccache *c = (struct cc_ccache *)in_ccache; 808 krb5_deltat sec = 0; 809 810 LOG_ENTRY(); 811 812 if (c->id == NULL) 813 return LOG_FAILURE(ccErrBadParam, "bad credential"); 814 if (in_credentials_version != cc_credentials_v5) 815 return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version"); 816 if (out_time_offset == NULL) 817 return LOG_FAILURE(ccErrBadParam, "bad argument"); 818 819 heim_krb5_cc_get_kdc_offset(milcontext, c->id, &sec); 820 *out_time_offset = sec; 821 822 return ccNoError; 823} 824 825static cc_int32 826ccache_set_kdc_time_offset(cc_ccache_t io_ccache, cc_uint32 in_credentials_version, cc_time_t in_time_offset) 827{ 828 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 829 LOG_ENTRY(); 830 831 if (c->id == NULL) 832 return LOG_FAILURE(ccErrBadParam, "bad credential"); 833 834 if (in_credentials_version != cc_credentials_v5) 835 return LOG_FAILURE(ccErrBadCredentialsVersion, "wrong version"); 836 837 heim_krb5_cc_set_kdc_offset(milcontext, c->id, in_time_offset); 838 839 return ccNoError; 840} 841 842static cc_int32 843ccache_clear_kdc_time_offset(cc_ccache_t io_ccache, cc_uint32 in_credentials_version) 844{ 845 struct cc_ccache *c = (struct cc_ccache *)io_ccache; 846 LOG_ENTRY(); 847 848 if (c->id == NULL) 849 return LOG_FAILURE(ccErrBadParam, "bad credential"); 850 851 heim_krb5_cc_set_kdc_offset(milcontext, c->id, 0); 852 853 return ccNoError; 854} 855 856static cc_int32 857ccache_wait_for_change(cc_ccache_t in_ccache) 858{ 859 LOG_UNIMPLEMENTED(); 860 return ccErrNoMem; 861} 862 863static cc_ccache_f ccache_functions = { 864 .release = ccache_release, 865 .destroy = ccache_destroy, 866 .set_default = ccache_set_default, 867 .get_credentials_version = ccache_get_credentials_version, 868 .get_name = ccache_get_name, 869 .get_principal = ccache_get_principal, 870 .set_principal = ccache_set_principal, 871 .store_credentials = ccache_store_credentials, 872 .remove_credentials = ccache_remove_credentials, 873 .new_credentials_iterator = ccache_new_credentials_iterator, 874 .move = ccache_move, 875 .lock = ccache_lock, 876 .unlock = ccache_unlock, 877 .get_last_default_time = ccache_get_last_default_time, 878 .get_change_time = ccache_get_change_time, 879 .compare = ccache_compare, 880 .get_kdc_time_offset = ccache_get_kdc_time_offset, 881 .set_kdc_time_offset = ccache_set_kdc_time_offset, 882 .clear_kdc_time_offset = ccache_clear_kdc_time_offset, 883 .wait_for_change = ccache_wait_for_change 884}; 885 886static cc_ccache_t 887create_ccache(krb5_ccache id) 888{ 889 struct cc_ccache *c; 890 891 c = mshim_malloc(sizeof(*c)); 892 c->ccache.functions = &ccache_functions; 893 c->id = id; 894 update_time(&c->change_time); 895 return (cc_ccache_t)c; 896} 897 898struct cc_iter { 899 cc_ccache_iterator_d iterator; 900 mit_krb5_cccol_cursor cursor; 901}; 902 903static cc_int32 904cc_iterator_release(cc_ccache_iterator_t io_ccache_iterator) 905{ 906 struct cc_iter *c = (struct cc_iter *)io_ccache_iterator; 907 LOG_ENTRY(); 908 krb5_cccol_cursor_free((mit_krb5_context)milcontext, &c->cursor); 909 free(c); 910 return ccNoError; 911} 912 913cc_int32 914cc_iterator_next(cc_ccache_iterator_t in_ccache_iterator, 915 cc_ccache_t *out_ccache) 916{ 917 struct cc_iter *c = (struct cc_iter *)in_ccache_iterator; 918 krb5_error_code ret; 919 krb5_ccache id; 920 921 LOG_ENTRY(); 922 923 if (out_ccache == NULL) 924 return ccErrBadParam; 925 926 927 while (1) { 928 ret = krb5_cccol_cursor_next((mit_krb5_context)milcontext, c->cursor, (mit_krb5_ccache *)&id); 929 if (ret == KRB5_CC_END || id == NULL) 930 return ccIteratorEnd; 931 else if (ret) 932 return LOG_FAILURE(ret, "ccol next cursor"); 933 934 const char *type = heim_krb5_cc_get_type(milcontext, id); 935 if (strcmp(type, "API") == 0 || strcmp(type, "KCM") == 0) 936 break; 937 heim_krb5_cc_close(milcontext, id); 938 } 939 *out_ccache = create_ccache(id); 940 941 return ccNoError; 942} 943 944static cc_int32 945cc_iterator_clone(cc_ccache_iterator_t in_ccache_iterator, 946 cc_ccache_iterator_t *out_ccache_iterator) 947{ 948 LOG_UNIMPLEMENTED(); 949 if (out_ccache_iterator == NULL) 950 return ccErrBadParam; 951 return ccErrNoMem; 952} 953 954static cc_ccache_iterator_f ccache_iterator_functions = { 955 .release = cc_iterator_release, 956 .next = cc_iterator_next, 957 .clone = cc_iterator_clone 958}; 959 960 961 962 963static cc_int32 964context_release(cc_context_t io_context) 965{ 966 LOG_ENTRY(); 967 memset(io_context, 0, sizeof(*io_context)); 968 free(io_context); 969 970 return ccNoError; 971} 972 973static cc_int32 974context_get_change_time(cc_context_t in_context, 975 cc_time_t *out_time) 976{ 977 LOG_ENTRY(); 978 if (out_time == NULL) 979 return ccErrBadParam; 980 *out_time = context_change_time; 981 return ccNoError; 982} 983 984static cc_int32 985context_get_default_ccache_name(cc_context_t in_context, 986 cc_string_t *out_name) 987{ 988 const char *name; 989 name = krb5_cc_default_name((mit_krb5_context)milcontext); 990 if (name == NULL) 991 return ccErrNoMem; /* XXX */ 992 if (out_name == NULL) 993 return ccErrBadParam; 994 if (strncmp("API:", name, 4) == 0) 995 name += 4; 996 997 *out_name = create_string(name); 998 999 return ccNoError; 1000} 1001 1002/* 1003 * Probe for client principal to make sure the cache really 1004 * exists. 1005 */ 1006 1007static cc_int32 1008check_exists(krb5_ccache id) 1009{ 1010 krb5_principal princ; 1011 int ret; 1012 1013 ret = heim_krb5_cc_get_principal(milcontext, id, &princ); 1014 if (ret) 1015 return 0; 1016 heim_krb5_free_principal(milcontext, princ); 1017 1018 return 1; 1019} 1020 1021 1022 1023static cc_int32 1024context_open_ccache (cc_context_t in_context, 1025 const char *in_name, 1026 cc_ccache_t *out_ccache) 1027{ 1028 char *name; 1029 krb5_error_code ret; 1030 krb5_ccache id; 1031 1032 if (out_ccache == NULL || in_name == NULL || in_context == NULL) 1033 return ccErrBadParam; 1034 1035 asprintf(&name, "API:%s", in_name); 1036 1037 ret = heim_krb5_cc_resolve(milcontext, name, &id); 1038 free(name); 1039 if (ret) 1040 return LOG_FAILURE(ret, "open cache"); 1041 1042 if (!check_exists(id)) { 1043 heim_krb5_cc_close(milcontext, id); 1044 return ccErrCCacheNotFound; 1045 } 1046 1047 *out_ccache = create_ccache(id); 1048 1049 return ccNoError; 1050} 1051 1052static cc_int32 1053context_open_default_ccache(cc_context_t in_context, 1054 cc_ccache_t *out_ccache) 1055{ 1056 krb5_error_code ret; 1057 krb5_ccache id; 1058 1059 LOG_ENTRY(); 1060 1061 if (out_ccache == NULL) 1062 return ccErrBadParam; 1063 1064 ret = heim_krb5_cc_default(milcontext, &id); 1065 if (ret) 1066 return LOG_FAILURE(ret, "cc default"); 1067 1068 if (!check_exists(id)) { 1069 heim_krb5_cc_close(milcontext, id); 1070 return ccErrCCacheNotFound; 1071 } 1072 1073 *out_ccache = create_ccache(id); 1074 1075 return ccNoError; 1076} 1077 1078static cc_int32 1079context_create_ccache(cc_context_t in_context, 1080 const char *in_name, 1081 cc_uint32 in_cred_vers, 1082 const char *in_principal, 1083 cc_ccache_t *out_ccache) 1084{ 1085 krb5_principal principal; 1086 krb5_error_code ret; 1087 krb5_ccache id; 1088 1089 if (in_cred_vers != cc_credentials_v5) 1090 return ccErrBadCredentialsVersion; 1091 if (out_ccache == NULL || in_name == NULL || in_context == NULL || in_principal == NULL) 1092 return ccErrBadParam; 1093 1094 update_time(&context_change_time); 1095 1096 ret = heim_krb5_parse_name(milcontext, in_principal, &principal); 1097 if (ret) 1098 return LOG_FAILURE(ret, "parse name"); 1099 1100 ret = heim_krb5_cc_resolve(milcontext, in_name, &id); 1101 if (ret) { 1102 heim_krb5_free_principal(milcontext, principal); 1103 return LOG_FAILURE(ret, "open cache"); 1104 } 1105 1106 ret = heim_krb5_cc_initialize(milcontext, id, principal); 1107 heim_krb5_free_principal(milcontext, principal); 1108 if (ret) { 1109 krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id); 1110 return LOG_FAILURE(ret, "cc init"); 1111 } 1112 1113 *out_ccache = create_ccache(id); 1114 1115 return ccNoError; 1116} 1117 1118static cc_int32 1119context_create_default_ccache(cc_context_t in_context, 1120 cc_uint32 in_cred_vers, 1121 const char *in_principal, 1122 cc_ccache_t *out_ccache) 1123{ 1124 krb5_principal principal; 1125 krb5_error_code ret; 1126 struct cc_ccache *c; 1127 krb5_ccache id; 1128 1129 LOG_ENTRY(); 1130 1131 if (in_cred_vers != cc_credentials_v5) 1132 return ccErrBadCredentialsVersion; 1133 if (out_ccache == NULL || in_principal == NULL) 1134 return ccErrBadParam; 1135 1136 *out_ccache = NULL; 1137 1138 update_time(&context_change_time); 1139 1140 ret = heim_krb5_cc_default(milcontext, &id); 1141 if (ret) 1142 return LOG_FAILURE(ret, "cc default"); 1143 1144 ret = heim_krb5_parse_name(milcontext, in_principal, &principal); 1145 if (ret) { 1146 heim_krb5_cc_close(milcontext, id); 1147 return LOG_FAILURE(ret, "parse name"); 1148 } 1149 1150 ret = heim_krb5_cc_initialize(milcontext, id, principal); 1151 heim_krb5_free_principal(milcontext, principal); 1152 if (ret) { 1153 krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id); 1154 return LOG_FAILURE(ret, "cc init"); 1155 } 1156 1157 c = (struct cc_ccache *)create_ccache(id); 1158 1159 update_time(&c->last_default_time); 1160 1161 *out_ccache = (cc_ccache_t)c; 1162 1163 return ccNoError; 1164} 1165 1166static cc_int32 1167context_create_new_ccache(cc_context_t in_context, 1168 cc_uint32 in_cred_vers, 1169 const char *in_principal, 1170 cc_ccache_t *out_ccache) 1171{ 1172 krb5_principal principal; 1173 krb5_error_code ret; 1174 krb5_ccache id; 1175 1176 LOG_ENTRY(); 1177 1178 if (in_cred_vers != cc_credentials_v5) 1179 return ccErrBadCredentialsVersion; 1180 1181 if (out_ccache == NULL || in_principal == NULL) 1182 return ccErrBadParam; 1183 1184 update_time(&context_change_time); 1185 1186 ret = heim_krb5_parse_name(milcontext, in_principal, &principal); 1187 if (ret) 1188 return LOG_FAILURE(ret, "parse name"); 1189 1190 ret = heim_krb5_cc_new_unique(milcontext, NULL, NULL, &id); 1191 if (ret) { 1192 heim_krb5_free_principal(milcontext, principal); 1193 return LOG_FAILURE(ret, "new unique"); 1194 } 1195 1196 ret = heim_krb5_cc_initialize(milcontext, id, principal); 1197 heim_krb5_free_principal(milcontext, principal); 1198 if (ret) { 1199 krb5_cc_destroy((mit_krb5_context)milcontext, (mit_krb5_ccache)id); 1200 return LOG_FAILURE(ret, "cc init"); 1201 } 1202 1203 *out_ccache = create_ccache(id); 1204 1205 return ccNoError; 1206} 1207 1208static cc_int32 1209context_new_ccache_iterator(cc_context_t in_context, 1210 cc_ccache_iterator_t *out_iterator) 1211{ 1212 LOG_ENTRY(); 1213 1214 krb5_error_code ret; 1215 struct cc_iter *c; 1216 1217 if (out_iterator == NULL) 1218 return ccErrBadParam; 1219 1220 c = calloc(1, sizeof(*c)); 1221 c->iterator.functions = &ccache_iterator_functions; 1222 1223 ret = krb5_cccol_cursor_new((mit_krb5_context)milcontext, &c->cursor); 1224 if (ret) { 1225 free(c); 1226 return ccErrNoMem; 1227 } 1228 1229 *out_iterator = (cc_ccache_iterator_t)c; 1230 1231 return ccNoError; 1232} 1233 1234static cc_int32 1235context_lock(cc_context_t in_context, 1236 cc_uint32 in_lock_type, 1237 cc_uint32 in_block) 1238{ 1239 LOG_UNIMPLEMENTED(); 1240 return ccNoError; 1241} 1242 1243static cc_int32 1244context_unlock(cc_context_t in_context) 1245{ 1246 LOG_UNIMPLEMENTED(); 1247 return ccNoError; 1248} 1249 1250static cc_int32 1251context_compare(cc_context_t in_cc_context, 1252 cc_context_t in_compare_to_context, 1253 cc_uint32 *out_equal) 1254{ 1255 LOG_UNIMPLEMENTED(); 1256 if (out_equal == NULL || in_compare_to_context == NULL) 1257 return ccErrBadParam; 1258 *out_equal = (in_cc_context == in_compare_to_context); 1259 return 0; 1260} 1261 1262static cc_int32 1263context_wait_for_change(cc_context_t in_cc_context) 1264{ 1265 LOG_UNIMPLEMENTED(); 1266 return ccErrNoMem; 1267} 1268 1269 1270 1271cc_context_f cc_functions = { 1272 .release = context_release, 1273 .get_change_time = context_get_change_time, 1274 .get_default_ccache_name = context_get_default_ccache_name, 1275 .open_ccache = context_open_ccache, 1276 .open_default_ccache = context_open_default_ccache, 1277 .create_ccache = context_create_ccache, 1278 .create_default_ccache = context_create_default_ccache, 1279 .create_new_ccache = context_create_new_ccache, 1280 .new_ccache_iterator = context_new_ccache_iterator, 1281 .lock = context_lock, 1282 .unlock = context_unlock, 1283 .compare = context_compare, 1284 .wait_for_change = context_wait_for_change 1285}; 1286 1287 1288cc_int32 1289cc_initialize(cc_context_t *out_context, 1290 cc_int32 in_version, 1291 cc_int32 *out_supported_version, 1292 char const **out_vendor) 1293{ 1294 LOG_ENTRY(); 1295 1296 update_time(&context_change_time); 1297 1298 if (in_version < ccapi_version_3 || in_version > ccapi_version_7) 1299 return ccErrBadAPIVersion; 1300 if (out_context == NULL) 1301 return ccErrBadParam; 1302 1303 *out_context = calloc(1, sizeof(**out_context)); 1304 (*out_context)->functions = &cc_functions; 1305 1306 if (out_supported_version) 1307 *out_supported_version = ccapi_version_7; 1308 if (out_vendor) 1309 *out_vendor = "Apple Heimdal shim layer"; 1310 1311 return 0; 1312} 1313 1314 1315