1 /* 2 * Copyright (c) 2008 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 "krb5_locl.h" 35 36 #ifdef HAVE_SCC 37 38 #include <sqlite3.h> 39 40 typedef struct krb5_scache { 41 char *name; 42 char *file; 43 sqlite3 *db; 44 45 sqlite_uint64 cid; 46 47 sqlite3_stmt *icred; 48 sqlite3_stmt *dcred; 49 sqlite3_stmt *iprincipal; 50 51 sqlite3_stmt *icache; 52 sqlite3_stmt *ucachen; 53 sqlite3_stmt *ucachep; 54 sqlite3_stmt *dcache; 55 sqlite3_stmt *scache; 56 sqlite3_stmt *scache_name; 57 sqlite3_stmt *umaster; 58 59 } krb5_scache; 60 61 #define SCACHE(X) ((krb5_scache *)(X)->data.data) 62 63 #define SCACHE_DEF_NAME "Default-cache" 64 #ifdef KRB5_USE_PATH_TOKENS 65 #define KRB5_SCACHE_DB "%{TEMP}/krb5scc_%{uid}" 66 #else 67 #define KRB5_SCACHE_DB "/tmp/krb5scc_%{uid}" 68 #endif 69 #define KRB5_SCACHE_NAME "SCC:" SCACHE_DEF_NAME ":" KRB5_SCACHE_DB 70 71 #define SCACHE_INVALID_CID ((sqlite_uint64)-1) 72 73 /* 74 * 75 */ 76 77 #define SQL_CMASTER "" \ 78 "CREATE TABLE master (" \ 79 "oid INTEGER PRIMARY KEY," \ 80 "version INTEGER NOT NULL," \ 81 "defaultcache TEXT NOT NULL" \ 82 ")" 83 84 #define SQL_SETUP_MASTER \ 85 "INSERT INTO master (version,defaultcache) VALUES(2, \"" SCACHE_DEF_NAME "\")" 86 #define SQL_UMASTER "UPDATE master SET defaultcache=? WHERE version=2" 87 88 #define SQL_CCACHE "" \ 89 "CREATE TABLE caches (" \ 90 "oid INTEGER PRIMARY KEY," \ 91 "principal TEXT," \ 92 "name TEXT NOT NULL" \ 93 ")" 94 95 #define SQL_TCACHE "" \ 96 "CREATE TRIGGER CacheDropCreds AFTER DELETE ON caches " \ 97 "FOR EACH ROW BEGIN " \ 98 "DELETE FROM credentials WHERE cid=old.oid;" \ 99 "END" 100 101 #define SQL_ICACHE "INSERT INTO caches (name) VALUES(?)" 102 #define SQL_UCACHE_NAME "UPDATE caches SET name=? WHERE OID=?" 103 #define SQL_UCACHE_PRINCIPAL "UPDATE caches SET principal=? WHERE OID=?" 104 #define SQL_DCACHE "DELETE FROM caches WHERE OID=?" 105 #define SQL_SCACHE "SELECT principal,name FROM caches WHERE OID=?" 106 #define SQL_SCACHE_NAME "SELECT oid FROM caches WHERE NAME=?" 107 108 #define SQL_CCREDS "" \ 109 "CREATE TABLE credentials (" \ 110 "oid INTEGER PRIMARY KEY," \ 111 "cid INTEGER NOT NULL," \ 112 "kvno INTEGER NOT NULL," \ 113 "etype INTEGER NOT NULL," \ 114 "created_at INTEGER NOT NULL," \ 115 "cred BLOB NOT NULL" \ 116 ")" 117 118 #define SQL_TCRED "" \ 119 "CREATE TRIGGER credDropPrincipal AFTER DELETE ON credentials " \ 120 "FOR EACH ROW BEGIN " \ 121 "DELETE FROM principals WHERE credential_id=old.oid;" \ 122 "END" 123 124 #define SQL_ICRED "INSERT INTO credentials (cid, kvno, etype, cred, created_at) VALUES (?,?,?,?,?)" 125 #define SQL_DCRED "DELETE FROM credentials WHERE cid=?" 126 127 #define SQL_CPRINCIPALS "" \ 128 "CREATE TABLE principals (" \ 129 "oid INTEGER PRIMARY KEY," \ 130 "principal TEXT NOT NULL," \ 131 "type INTEGER NOT NULL," \ 132 "credential_id INTEGER NOT NULL" \ 133 ")" 134 135 #define SQL_IPRINCIPAL "INSERT INTO principals (principal, type, credential_id) VALUES (?,?,?)" 136 137 /* 138 * sqlite destructors 139 */ 140 141 static void 142 free_data(void *data) 143 { 144 free(data); 145 } 146 147 static void 148 free_krb5(void *str) 149 { 150 krb5_xfree(str); 151 } 152 153 static void 154 scc_free(krb5_scache *s) 155 { 156 if (s->file) 157 free(s->file); 158 if (s->name) 159 free(s->name); 160 161 if (s->icred) 162 sqlite3_finalize(s->icred); 163 if (s->dcred) 164 sqlite3_finalize(s->dcred); 165 if (s->iprincipal) 166 sqlite3_finalize(s->iprincipal); 167 if (s->icache) 168 sqlite3_finalize(s->icache); 169 if (s->ucachen) 170 sqlite3_finalize(s->ucachen); 171 if (s->ucachep) 172 sqlite3_finalize(s->ucachep); 173 if (s->dcache) 174 sqlite3_finalize(s->dcache); 175 if (s->scache) 176 sqlite3_finalize(s->scache); 177 if (s->scache_name) 178 sqlite3_finalize(s->scache_name); 179 if (s->umaster) 180 sqlite3_finalize(s->umaster); 181 182 if (s->db) 183 sqlite3_close(s->db); 184 free(s); 185 } 186 187 #ifdef TRACEME 188 static void 189 trace(void* ptr, const char * str) 190 { 191 printf("SQL: %s\n", str); 192 } 193 #endif 194 195 static krb5_error_code 196 prepare_stmt(krb5_context context, sqlite3 *db, 197 sqlite3_stmt **stmt, const char *str) 198 { 199 int ret; 200 201 ret = sqlite3_prepare_v2(db, str, -1, stmt, NULL); 202 if (ret != SQLITE_OK) { 203 krb5_set_error_message(context, ENOENT, 204 N_("Failed to prepare stmt %s: %s", ""), 205 str, sqlite3_errmsg(db)); 206 return ENOENT; 207 } 208 return 0; 209 } 210 211 static krb5_error_code 212 exec_stmt(krb5_context context, sqlite3 *db, const char *str, 213 krb5_error_code code) 214 { 215 int ret; 216 217 ret = sqlite3_exec(db, str, NULL, NULL, NULL); 218 if (ret != SQLITE_OK && code) { 219 krb5_set_error_message(context, code, 220 N_("scache execute %s: %s", ""), str, 221 sqlite3_errmsg(db)); 222 return code; 223 } 224 return 0; 225 } 226 227 static krb5_error_code 228 default_db(krb5_context context, sqlite3 **db) 229 { 230 char *name; 231 int ret; 232 233 ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &name); 234 if (ret) 235 return ret; 236 237 ret = sqlite3_open_v2(name, db, SQLITE_OPEN_READWRITE, NULL); 238 free(name); 239 if (ret != SQLITE_OK) { 240 krb5_clear_error_message(context); 241 return ENOENT; 242 } 243 244 #ifdef TRACEME 245 sqlite3_trace(*db, trace, NULL); 246 #endif 247 248 return 0; 249 } 250 251 static krb5_error_code 252 get_def_name(krb5_context context, char **str) 253 { 254 krb5_error_code ret; 255 sqlite3_stmt *stmt; 256 const char *name; 257 sqlite3 *db; 258 259 ret = default_db(context, &db); 260 if (ret) 261 return ret; 262 263 ret = prepare_stmt(context, db, &stmt, "SELECT defaultcache FROM master"); 264 if (ret) { 265 sqlite3_close(db); 266 return ret; 267 } 268 269 ret = sqlite3_step(stmt); 270 if (ret != SQLITE_ROW) 271 goto out; 272 273 if (sqlite3_column_type(stmt, 0) != SQLITE_TEXT) 274 goto out; 275 276 name = (const char *)sqlite3_column_text(stmt, 0); 277 if (name == NULL) 278 goto out; 279 280 *str = strdup(name); 281 if (*str == NULL) 282 goto out; 283 284 sqlite3_finalize(stmt); 285 sqlite3_close(db); 286 return 0; 287 out: 288 sqlite3_finalize(stmt); 289 sqlite3_close(db); 290 krb5_clear_error_message(context); 291 return ENOENT; 292 } 293 294 295 296 static krb5_scache * KRB5_CALLCONV 297 scc_alloc(krb5_context context, const char *name) 298 { 299 krb5_error_code ret; 300 krb5_scache *s; 301 302 ALLOC(s, 1); 303 if(s == NULL) 304 return NULL; 305 306 s->cid = SCACHE_INVALID_CID; 307 308 if (name) { 309 char *file; 310 311 if (*name == '\0') { 312 krb5_error_code ret; 313 ret = get_def_name(context, &s->name); 314 if (ret) 315 s->name = strdup(SCACHE_DEF_NAME); 316 } else 317 s->name = strdup(name); 318 319 file = strrchr(s->name, ':'); 320 if (file) { 321 *file++ = '\0'; 322 s->file = strdup(file); 323 ret = 0; 324 } else { 325 ret = _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file); 326 } 327 } else { 328 _krb5_expand_default_cc_name(context, KRB5_SCACHE_DB, &s->file); 329 ret = asprintf(&s->name, "unique-%p", s); 330 } 331 if (ret < 0 || s->file == NULL || s->name == NULL) { 332 scc_free(s); 333 return NULL; 334 } 335 336 return s; 337 } 338 339 static krb5_error_code 340 open_database(krb5_context context, krb5_scache *s, int flags) 341 { 342 int ret; 343 344 ret = sqlite3_open_v2(s->file, &s->db, SQLITE_OPEN_READWRITE|flags, NULL); 345 if (ret) { 346 if (s->db) { 347 krb5_set_error_message(context, ENOENT, 348 N_("Error opening scache file %s: %s", ""), 349 s->file, sqlite3_errmsg(s->db)); 350 sqlite3_close(s->db); 351 s->db = NULL; 352 } else 353 krb5_set_error_message(context, ENOENT, 354 N_("malloc: out of memory", "")); 355 return ENOENT; 356 } 357 return 0; 358 } 359 360 static krb5_error_code 361 create_cache(krb5_context context, krb5_scache *s) 362 { 363 int ret; 364 365 sqlite3_bind_text(s->icache, 1, s->name, -1, NULL); 366 do { 367 ret = sqlite3_step(s->icache); 368 } while (ret == SQLITE_ROW); 369 if (ret != SQLITE_DONE) { 370 krb5_set_error_message(context, KRB5_CC_IO, 371 N_("Failed to add scache: %d", ""), ret); 372 return KRB5_CC_IO; 373 } 374 sqlite3_reset(s->icache); 375 376 s->cid = sqlite3_last_insert_rowid(s->db); 377 378 return 0; 379 } 380 381 static krb5_error_code 382 make_database(krb5_context context, krb5_scache *s) 383 { 384 int created_file = 0; 385 int ret; 386 387 if (s->db) 388 return 0; 389 390 ret = open_database(context, s, 0); 391 if (ret) { 392 mode_t oldumask = umask(077); 393 ret = open_database(context, s, SQLITE_OPEN_CREATE); 394 umask(oldumask); 395 if (ret) goto out; 396 397 created_file = 1; 398 399 ret = exec_stmt(context, s->db, SQL_CMASTER, KRB5_CC_IO); 400 if (ret) goto out; 401 ret = exec_stmt(context, s->db, SQL_CCACHE, KRB5_CC_IO); 402 if (ret) goto out; 403 ret = exec_stmt(context, s->db, SQL_CCREDS, KRB5_CC_IO); 404 if (ret) goto out; 405 ret = exec_stmt(context, s->db, SQL_CPRINCIPALS, KRB5_CC_IO); 406 if (ret) goto out; 407 ret = exec_stmt(context, s->db, SQL_SETUP_MASTER, KRB5_CC_IO); 408 if (ret) goto out; 409 410 ret = exec_stmt(context, s->db, SQL_TCACHE, KRB5_CC_IO); 411 if (ret) goto out; 412 ret = exec_stmt(context, s->db, SQL_TCRED, KRB5_CC_IO); 413 if (ret) goto out; 414 } 415 416 #ifdef TRACEME 417 sqlite3_trace(s->db, trace, NULL); 418 #endif 419 420 ret = prepare_stmt(context, s->db, &s->icred, SQL_ICRED); 421 if (ret) goto out; 422 ret = prepare_stmt(context, s->db, &s->dcred, SQL_DCRED); 423 if (ret) goto out; 424 ret = prepare_stmt(context, s->db, &s->iprincipal, SQL_IPRINCIPAL); 425 if (ret) goto out; 426 ret = prepare_stmt(context, s->db, &s->icache, SQL_ICACHE); 427 if (ret) goto out; 428 ret = prepare_stmt(context, s->db, &s->ucachen, SQL_UCACHE_NAME); 429 if (ret) goto out; 430 ret = prepare_stmt(context, s->db, &s->ucachep, SQL_UCACHE_PRINCIPAL); 431 if (ret) goto out; 432 ret = prepare_stmt(context, s->db, &s->dcache, SQL_DCACHE); 433 if (ret) goto out; 434 ret = prepare_stmt(context, s->db, &s->scache, SQL_SCACHE); 435 if (ret) goto out; 436 ret = prepare_stmt(context, s->db, &s->scache_name, SQL_SCACHE_NAME); 437 if (ret) goto out; 438 ret = prepare_stmt(context, s->db, &s->umaster, SQL_UMASTER); 439 if (ret) goto out; 440 441 return 0; 442 443 out: 444 if (s->db) 445 sqlite3_close(s->db); 446 if (created_file) 447 unlink(s->file); 448 449 return ret; 450 } 451 452 static krb5_error_code 453 bind_principal(krb5_context context, 454 sqlite3 *db, 455 sqlite3_stmt *stmt, 456 int col, 457 krb5_const_principal principal) 458 { 459 krb5_error_code ret; 460 char *str; 461 462 ret = krb5_unparse_name(context, principal, &str); 463 if (ret) 464 return ret; 465 466 ret = sqlite3_bind_text(stmt, col, str, -1, free_krb5); 467 if (ret != SQLITE_OK) { 468 krb5_xfree(str); 469 krb5_set_error_message(context, ENOMEM, 470 N_("scache bind principal: %s", ""), 471 sqlite3_errmsg(db)); 472 return ENOMEM; 473 } 474 return 0; 475 } 476 477 /* 478 * 479 */ 480 481 static const char* KRB5_CALLCONV 482 scc_get_name(krb5_context context, 483 krb5_ccache id) 484 { 485 return SCACHE(id)->name; 486 } 487 488 static krb5_error_code KRB5_CALLCONV 489 scc_resolve(krb5_context context, krb5_ccache *id, const char *res) 490 { 491 krb5_scache *s; 492 int ret; 493 494 s = scc_alloc(context, res); 495 if (s == NULL) { 496 krb5_set_error_message(context, KRB5_CC_NOMEM, 497 N_("malloc: out of memory", "")); 498 return KRB5_CC_NOMEM; 499 } 500 501 ret = make_database(context, s); 502 if (ret) { 503 scc_free(s); 504 return ret; 505 } 506 507 ret = sqlite3_bind_text(s->scache_name, 1, s->name, -1, NULL); 508 if (ret != SQLITE_OK) { 509 krb5_set_error_message(context, ENOMEM, 510 "bind name: %s", sqlite3_errmsg(s->db)); 511 scc_free(s); 512 return ENOMEM; 513 } 514 515 if (sqlite3_step(s->scache_name) == SQLITE_ROW) { 516 517 if (sqlite3_column_type(s->scache_name, 0) != SQLITE_INTEGER) { 518 sqlite3_reset(s->scache_name); 519 krb5_set_error_message(context, KRB5_CC_END, 520 N_("Cache name of wrong type " 521 "for scache %s", ""), 522 s->name); 523 scc_free(s); 524 return KRB5_CC_END; 525 } 526 527 s->cid = sqlite3_column_int(s->scache_name, 0); 528 } else { 529 s->cid = SCACHE_INVALID_CID; 530 } 531 sqlite3_reset(s->scache_name); 532 533 (*id)->data.data = s; 534 (*id)->data.length = sizeof(*s); 535 536 return 0; 537 } 538 539 static krb5_error_code KRB5_CALLCONV 540 scc_gen_new(krb5_context context, krb5_ccache *id) 541 { 542 krb5_scache *s; 543 544 s = scc_alloc(context, NULL); 545 546 if (s == NULL) { 547 krb5_set_error_message(context, KRB5_CC_NOMEM, 548 N_("malloc: out of memory", "")); 549 return KRB5_CC_NOMEM; 550 } 551 552 (*id)->data.data = s; 553 (*id)->data.length = sizeof(*s); 554 555 return 0; 556 } 557 558 static krb5_error_code KRB5_CALLCONV 559 scc_initialize(krb5_context context, 560 krb5_ccache id, 561 krb5_principal primary_principal) 562 { 563 krb5_scache *s = SCACHE(id); 564 krb5_error_code ret; 565 566 ret = make_database(context, s); 567 if (ret) 568 return ret; 569 570 ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO); 571 if (ret) return ret; 572 573 if (s->cid == SCACHE_INVALID_CID) { 574 ret = create_cache(context, s); 575 if (ret) 576 goto rollback; 577 } else { 578 sqlite3_bind_int(s->dcred, 1, s->cid); 579 do { 580 ret = sqlite3_step(s->dcred); 581 } while (ret == SQLITE_ROW); 582 sqlite3_reset(s->dcred); 583 if (ret != SQLITE_DONE) { 584 ret = KRB5_CC_IO; 585 krb5_set_error_message(context, ret, 586 N_("Failed to delete old " 587 "credentials: %s", ""), 588 sqlite3_errmsg(s->db)); 589 goto rollback; 590 } 591 } 592 593 ret = bind_principal(context, s->db, s->ucachep, 1, primary_principal); 594 if (ret) 595 goto rollback; 596 sqlite3_bind_int(s->ucachep, 2, s->cid); 597 598 do { 599 ret = sqlite3_step(s->ucachep); 600 } while (ret == SQLITE_ROW); 601 sqlite3_reset(s->ucachep); 602 if (ret != SQLITE_DONE) { 603 ret = KRB5_CC_IO; 604 krb5_set_error_message(context, ret, 605 N_("Failed to bind principal to cache %s", ""), 606 sqlite3_errmsg(s->db)); 607 goto rollback; 608 } 609 610 ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO); 611 if (ret) return ret; 612 613 return 0; 614 615 rollback: 616 exec_stmt(context, s->db, "ROLLBACK", 0); 617 618 return ret; 619 620 } 621 622 static krb5_error_code KRB5_CALLCONV 623 scc_close(krb5_context context, 624 krb5_ccache id) 625 { 626 scc_free(SCACHE(id)); 627 return 0; 628 } 629 630 static krb5_error_code KRB5_CALLCONV 631 scc_destroy(krb5_context context, 632 krb5_ccache id) 633 { 634 krb5_scache *s = SCACHE(id); 635 int ret; 636 637 if (s->cid == SCACHE_INVALID_CID) 638 return 0; 639 640 sqlite3_bind_int(s->dcache, 1, s->cid); 641 do { 642 ret = sqlite3_step(s->dcache); 643 } while (ret == SQLITE_ROW); 644 sqlite3_reset(s->dcache); 645 if (ret != SQLITE_DONE) { 646 krb5_set_error_message(context, KRB5_CC_IO, 647 N_("Failed to destroy cache %s: %s", ""), 648 s->name, sqlite3_errmsg(s->db)); 649 return KRB5_CC_IO; 650 } 651 return 0; 652 } 653 654 static krb5_error_code 655 encode_creds(krb5_context context, krb5_creds *creds, krb5_data *data) 656 { 657 krb5_error_code ret; 658 krb5_storage *sp; 659 660 sp = krb5_storage_emem(); 661 if (sp == NULL) { 662 krb5_set_error_message(context, ENOMEM, 663 N_("malloc: out of memory", "")); 664 return ENOMEM; 665 } 666 667 ret = krb5_store_creds(sp, creds); 668 if (ret) { 669 krb5_set_error_message(context, ret, 670 N_("Failed to store credential in scache", "")); 671 krb5_storage_free(sp); 672 return ret; 673 } 674 675 ret = krb5_storage_to_data(sp, data); 676 krb5_storage_free(sp); 677 if (ret) 678 krb5_set_error_message(context, ret, 679 N_("Failed to encode credential in scache", "")); 680 return ret; 681 } 682 683 static krb5_error_code 684 decode_creds(krb5_context context, const void *data, size_t length, 685 krb5_creds *creds) 686 { 687 krb5_error_code ret; 688 krb5_storage *sp; 689 690 sp = krb5_storage_from_readonly_mem(data, length); 691 if (sp == NULL) { 692 krb5_set_error_message(context, ENOMEM, 693 N_("malloc: out of memory", "")); 694 return ENOMEM; 695 } 696 697 ret = krb5_ret_creds(sp, creds); 698 krb5_storage_free(sp); 699 if (ret) { 700 krb5_set_error_message(context, ret, 701 N_("Failed to read credential in scache", "")); 702 return ret; 703 } 704 return 0; 705 } 706 707 708 static krb5_error_code KRB5_CALLCONV 709 scc_store_cred(krb5_context context, 710 krb5_ccache id, 711 krb5_creds *creds) 712 { 713 sqlite_uint64 credid; 714 krb5_scache *s = SCACHE(id); 715 krb5_error_code ret; 716 krb5_data data; 717 718 ret = make_database(context, s); 719 if (ret) 720 return ret; 721 722 ret = encode_creds(context, creds, &data); 723 if (ret) 724 return ret; 725 726 sqlite3_bind_int(s->icred, 1, s->cid); 727 { 728 krb5_enctype etype = 0; 729 int kvno = 0; 730 Ticket t; 731 size_t len; 732 733 ret = decode_Ticket(creds->ticket.data, 734 creds->ticket.length, &t, &len); 735 if (ret == 0) { 736 if(t.enc_part.kvno) 737 kvno = *t.enc_part.kvno; 738 739 etype = t.enc_part.etype; 740 741 free_Ticket(&t); 742 } 743 744 sqlite3_bind_int(s->icred, 2, kvno); 745 sqlite3_bind_int(s->icred, 3, etype); 746 747 } 748 749 sqlite3_bind_blob(s->icred, 4, data.data, data.length, free_data); 750 sqlite3_bind_int(s->icred, 5, time(NULL)); 751 752 ret = exec_stmt(context, s->db, "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO); 753 if (ret) return ret; 754 755 do { 756 ret = sqlite3_step(s->icred); 757 } while (ret == SQLITE_ROW); 758 sqlite3_reset(s->icred); 759 if (ret != SQLITE_DONE) { 760 ret = KRB5_CC_IO; 761 krb5_set_error_message(context, ret, 762 N_("Failed to add credential: %s", ""), 763 sqlite3_errmsg(s->db)); 764 goto rollback; 765 } 766 767 credid = sqlite3_last_insert_rowid(s->db); 768 769 { 770 bind_principal(context, s->db, s->iprincipal, 1, creds->server); 771 sqlite3_bind_int(s->iprincipal, 2, 1); 772 sqlite3_bind_int(s->iprincipal, 3, credid); 773 774 do { 775 ret = sqlite3_step(s->iprincipal); 776 } while (ret == SQLITE_ROW); 777 sqlite3_reset(s->iprincipal); 778 if (ret != SQLITE_DONE) { 779 ret = KRB5_CC_IO; 780 krb5_set_error_message(context, ret, 781 N_("Failed to add principal: %s", ""), 782 sqlite3_errmsg(s->db)); 783 goto rollback; 784 } 785 } 786 787 { 788 bind_principal(context, s->db, s->iprincipal, 1, creds->client); 789 sqlite3_bind_int(s->iprincipal, 2, 0); 790 sqlite3_bind_int(s->iprincipal, 3, credid); 791 792 do { 793 ret = sqlite3_step(s->iprincipal); 794 } while (ret == SQLITE_ROW); 795 sqlite3_reset(s->iprincipal); 796 if (ret != SQLITE_DONE) { 797 ret = KRB5_CC_IO; 798 krb5_set_error_message(context, ret, 799 N_("Failed to add principal: %s", ""), 800 sqlite3_errmsg(s->db)); 801 goto rollback; 802 } 803 } 804 805 ret = exec_stmt(context, s->db, "COMMIT", KRB5_CC_IO); 806 if (ret) return ret; 807 808 return 0; 809 810 rollback: 811 exec_stmt(context, s->db, "ROLLBACK", 0); 812 813 return ret; 814 } 815 816 static krb5_error_code KRB5_CALLCONV 817 scc_get_principal(krb5_context context, 818 krb5_ccache id, 819 krb5_principal *principal) 820 { 821 krb5_scache *s = SCACHE(id); 822 krb5_error_code ret; 823 const char *str; 824 825 *principal = NULL; 826 827 ret = make_database(context, s); 828 if (ret) 829 return ret; 830 831 sqlite3_bind_int(s->scache, 1, s->cid); 832 833 if (sqlite3_step(s->scache) != SQLITE_ROW) { 834 sqlite3_reset(s->scache); 835 krb5_set_error_message(context, KRB5_CC_END, 836 N_("No principal for cache SCC:%s:%s", ""), 837 s->name, s->file); 838 return KRB5_CC_END; 839 } 840 841 if (sqlite3_column_type(s->scache, 0) != SQLITE_TEXT) { 842 sqlite3_reset(s->scache); 843 krb5_set_error_message(context, KRB5_CC_END, 844 N_("Principal data of wrong type " 845 "for SCC:%s:%s", ""), 846 s->name, s->file); 847 return KRB5_CC_END; 848 } 849 850 str = (const char *)sqlite3_column_text(s->scache, 0); 851 if (str == NULL) { 852 sqlite3_reset(s->scache); 853 krb5_set_error_message(context, KRB5_CC_END, 854 N_("Principal not set for SCC:%s:%s", ""), 855 s->name, s->file); 856 return KRB5_CC_END; 857 } 858 859 ret = krb5_parse_name(context, str, principal); 860 861 sqlite3_reset(s->scache); 862 863 return ret; 864 } 865 866 struct cred_ctx { 867 char *drop; 868 sqlite3_stmt *stmt; 869 sqlite3_stmt *credstmt; 870 }; 871 872 static krb5_error_code KRB5_CALLCONV 873 scc_get_first (krb5_context context, 874 krb5_ccache id, 875 krb5_cc_cursor *cursor) 876 { 877 krb5_scache *s = SCACHE(id); 878 krb5_error_code ret; 879 struct cred_ctx *ctx; 880 char *str = NULL, *name = NULL; 881 882 *cursor = NULL; 883 884 ctx = calloc(1, sizeof(*ctx)); 885 if (ctx == NULL) { 886 krb5_set_error_message(context, ENOMEM, 887 N_("malloc: out of memory", "")); 888 return ENOMEM; 889 } 890 891 ret = make_database(context, s); 892 if (ret) { 893 free(ctx); 894 return ret; 895 } 896 897 if (s->cid == SCACHE_INVALID_CID) { 898 krb5_set_error_message(context, KRB5_CC_END, 899 N_("Iterating a invalid scache %s", ""), 900 s->name); 901 free(ctx); 902 return KRB5_CC_END; 903 } 904 905 ret = asprintf(&name, "credIteration%pPid%d", 906 ctx, (int)getpid()); 907 if (ret < 0 || name == NULL) { 908 krb5_set_error_message(context, ENOMEM, 909 N_("malloc: out of memory", "")); 910 free(ctx); 911 return ENOMEM; 912 } 913 914 ret = asprintf(&ctx->drop, "DROP TABLE %s", name); 915 if (ret < 0 || ctx->drop == NULL) { 916 krb5_set_error_message(context, ENOMEM, 917 N_("malloc: out of memory", "")); 918 free(name); 919 free(ctx); 920 return ENOMEM; 921 } 922 923 ret = asprintf(&str, "CREATE TEMPORARY TABLE %s " 924 "AS SELECT oid,created_at FROM credentials WHERE cid = %lu", 925 name, (unsigned long)s->cid); 926 if (ret < 0 || str == NULL) { 927 free(ctx->drop); 928 free(name); 929 free(ctx); 930 return ENOMEM; 931 } 932 933 ret = exec_stmt(context, s->db, str, KRB5_CC_IO); 934 free(str); 935 str = NULL; 936 if (ret) { 937 free(ctx->drop); 938 free(name); 939 free(ctx); 940 return ret; 941 } 942 943 ret = asprintf(&str, "SELECT oid FROM %s ORDER BY created_at", name); 944 if (ret < 0 || str == NULL) { 945 exec_stmt(context, s->db, ctx->drop, 0); 946 free(ctx->drop); 947 free(name); 948 free(ctx); 949 return ret; 950 } 951 952 ret = prepare_stmt(context, s->db, &ctx->stmt, str); 953 free(str); 954 str = NULL; 955 free(name); 956 if (ret) { 957 exec_stmt(context, s->db, ctx->drop, 0); 958 free(ctx->drop); 959 free(ctx); 960 return ret; 961 } 962 963 ret = prepare_stmt(context, s->db, &ctx->credstmt, 964 "SELECT cred FROM credentials WHERE oid = ?"); 965 if (ret) { 966 sqlite3_finalize(ctx->stmt); 967 exec_stmt(context, s->db, ctx->drop, 0); 968 free(ctx->drop); 969 free(ctx); 970 return ret; 971 } 972 973 *cursor = ctx; 974 975 return 0; 976 } 977 978 static krb5_error_code KRB5_CALLCONV 979 scc_get_next (krb5_context context, 980 krb5_ccache id, 981 krb5_cc_cursor *cursor, 982 krb5_creds *creds) 983 { 984 struct cred_ctx *ctx = *cursor; 985 krb5_scache *s = SCACHE(id); 986 krb5_error_code ret; 987 sqlite_uint64 oid; 988 const void *data = NULL; 989 size_t len = 0; 990 991 next: 992 ret = sqlite3_step(ctx->stmt); 993 if (ret == SQLITE_DONE) { 994 krb5_clear_error_message(context); 995 return KRB5_CC_END; 996 } else if (ret != SQLITE_ROW) { 997 krb5_set_error_message(context, KRB5_CC_IO, 998 N_("scache Database failed: %s", ""), 999 sqlite3_errmsg(s->db)); 1000 return KRB5_CC_IO; 1001 } 1002 1003 oid = sqlite3_column_int64(ctx->stmt, 0); 1004 1005 /* read cred from credentials table */ 1006 1007 sqlite3_bind_int(ctx->credstmt, 1, oid); 1008 1009 ret = sqlite3_step(ctx->credstmt); 1010 if (ret != SQLITE_ROW) { 1011 sqlite3_reset(ctx->credstmt); 1012 goto next; 1013 } 1014 1015 if (sqlite3_column_type(ctx->credstmt, 0) != SQLITE_BLOB) { 1016 krb5_set_error_message(context, KRB5_CC_END, 1017 N_("credential of wrong type for SCC:%s:%s", ""), 1018 s->name, s->file); 1019 sqlite3_reset(ctx->credstmt); 1020 return KRB5_CC_END; 1021 } 1022 1023 data = sqlite3_column_blob(ctx->credstmt, 0); 1024 len = sqlite3_column_bytes(ctx->credstmt, 0); 1025 1026 ret = decode_creds(context, data, len, creds); 1027 sqlite3_reset(ctx->credstmt); 1028 return ret; 1029 } 1030 1031 static krb5_error_code KRB5_CALLCONV 1032 scc_end_get (krb5_context context, 1033 krb5_ccache id, 1034 krb5_cc_cursor *cursor) 1035 { 1036 struct cred_ctx *ctx = *cursor; 1037 krb5_scache *s = SCACHE(id); 1038 1039 sqlite3_finalize(ctx->stmt); 1040 sqlite3_finalize(ctx->credstmt); 1041 1042 exec_stmt(context, s->db, ctx->drop, 0); 1043 1044 free(ctx->drop); 1045 free(ctx); 1046 1047 return 0; 1048 } 1049 1050 static krb5_error_code KRB5_CALLCONV 1051 scc_remove_cred(krb5_context context, 1052 krb5_ccache id, 1053 krb5_flags which, 1054 krb5_creds *mcreds) 1055 { 1056 krb5_scache *s = SCACHE(id); 1057 krb5_error_code ret; 1058 sqlite3_stmt *stmt; 1059 sqlite_uint64 credid = 0; 1060 const void *data = NULL; 1061 size_t len = 0; 1062 1063 ret = make_database(context, s); 1064 if (ret) 1065 return ret; 1066 1067 ret = prepare_stmt(context, s->db, &stmt, 1068 "SELECT cred,oid FROM credentials " 1069 "WHERE cid = ?"); 1070 if (ret) 1071 return ret; 1072 1073 sqlite3_bind_int(stmt, 1, s->cid); 1074 1075 /* find credential... */ 1076 while (1) { 1077 krb5_creds creds; 1078 1079 ret = sqlite3_step(stmt); 1080 if (ret == SQLITE_DONE) { 1081 ret = 0; 1082 break; 1083 } else if (ret != SQLITE_ROW) { 1084 ret = KRB5_CC_IO; 1085 krb5_set_error_message(context, ret, 1086 N_("scache Database failed: %s", ""), 1087 sqlite3_errmsg(s->db)); 1088 break; 1089 } 1090 1091 if (sqlite3_column_type(stmt, 0) != SQLITE_BLOB) { 1092 ret = KRB5_CC_END; 1093 krb5_set_error_message(context, ret, 1094 N_("Credential of wrong type " 1095 "for SCC:%s:%s", ""), 1096 s->name, s->file); 1097 break; 1098 } 1099 1100 data = sqlite3_column_blob(stmt, 0); 1101 len = sqlite3_column_bytes(stmt, 0); 1102 1103 ret = decode_creds(context, data, len, &creds); 1104 if (ret) 1105 break; 1106 1107 ret = krb5_compare_creds(context, which, mcreds, &creds); 1108 krb5_free_cred_contents(context, &creds); 1109 if (ret) { 1110 credid = sqlite3_column_int64(stmt, 1); 1111 ret = 0; 1112 break; 1113 } 1114 } 1115 1116 sqlite3_finalize(stmt); 1117 1118 if (id) { 1119 ret = prepare_stmt(context, s->db, &stmt, 1120 "DELETE FROM credentials WHERE oid=?"); 1121 if (ret) 1122 return ret; 1123 sqlite3_bind_int(stmt, 1, credid); 1124 1125 do { 1126 ret = sqlite3_step(stmt); 1127 } while (ret == SQLITE_ROW); 1128 sqlite3_finalize(stmt); 1129 if (ret != SQLITE_DONE) { 1130 ret = KRB5_CC_IO; 1131 krb5_set_error_message(context, ret, 1132 N_("failed to delete scache credental", "")); 1133 } else 1134 ret = 0; 1135 } 1136 1137 return ret; 1138 } 1139 1140 static krb5_error_code KRB5_CALLCONV 1141 scc_set_flags(krb5_context context, 1142 krb5_ccache id, 1143 krb5_flags flags) 1144 { 1145 return 0; /* XXX */ 1146 } 1147 1148 struct cache_iter { 1149 char *drop; 1150 sqlite3 *db; 1151 sqlite3_stmt *stmt; 1152 }; 1153 1154 static krb5_error_code KRB5_CALLCONV 1155 scc_get_cache_first(krb5_context context, krb5_cc_cursor *cursor) 1156 { 1157 struct cache_iter *ctx; 1158 krb5_error_code ret; 1159 char *name = NULL, *str = NULL; 1160 1161 *cursor = NULL; 1162 1163 ctx = calloc(1, sizeof(*ctx)); 1164 if (ctx == NULL) { 1165 krb5_set_error_message(context, ENOMEM, 1166 N_("malloc: out of memory", "")); 1167 return ENOMEM; 1168 } 1169 1170 ret = default_db(context, &ctx->db); 1171 if (ctx->db == NULL) { 1172 free(ctx); 1173 return ret; 1174 } 1175 1176 ret = asprintf(&name, "cacheIteration%pPid%d", 1177 ctx, (int)getpid()); 1178 if (ret < 0 || name == NULL) { 1179 krb5_set_error_message(context, ENOMEM, 1180 N_("malloc: out of memory", "")); 1181 sqlite3_close(ctx->db); 1182 free(ctx); 1183 return ENOMEM; 1184 } 1185 1186 ret = asprintf(&ctx->drop, "DROP TABLE %s", name); 1187 if (ret < 0 || ctx->drop == NULL) { 1188 krb5_set_error_message(context, ENOMEM, 1189 N_("malloc: out of memory", "")); 1190 sqlite3_close(ctx->db); 1191 free(name); 1192 free(ctx); 1193 return ENOMEM; 1194 } 1195 1196 ret = asprintf(&str, "CREATE TEMPORARY TABLE %s AS SELECT name FROM caches", 1197 name); 1198 if (ret < 0 || str == NULL) { 1199 krb5_set_error_message(context, ENOMEM, 1200 N_("malloc: out of memory", "")); 1201 sqlite3_close(ctx->db); 1202 free(name); 1203 free(ctx->drop); 1204 free(ctx); 1205 return ENOMEM; 1206 } 1207 1208 ret = exec_stmt(context, ctx->db, str, KRB5_CC_IO); 1209 free(str); 1210 str = NULL; 1211 if (ret) { 1212 sqlite3_close(ctx->db); 1213 free(name); 1214 free(ctx->drop); 1215 free(ctx); 1216 return ret; 1217 } 1218 1219 ret = asprintf(&str, "SELECT name FROM %s", name); 1220 free(name); 1221 if (ret < 0 || str == NULL) { 1222 exec_stmt(context, ctx->db, ctx->drop, 0); 1223 sqlite3_close(ctx->db); 1224 free(name); 1225 free(ctx->drop); 1226 free(ctx); 1227 return ENOMEM; 1228 } 1229 1230 ret = prepare_stmt(context, ctx->db, &ctx->stmt, str); 1231 free(str); 1232 if (ret) { 1233 exec_stmt(context, ctx->db, ctx->drop, 0); 1234 sqlite3_close(ctx->db); 1235 free(ctx->drop); 1236 free(ctx); 1237 return ret; 1238 } 1239 1240 *cursor = ctx; 1241 1242 return 0; 1243 } 1244 1245 static krb5_error_code KRB5_CALLCONV 1246 scc_get_cache_next(krb5_context context, 1247 krb5_cc_cursor cursor, 1248 krb5_ccache *id) 1249 { 1250 struct cache_iter *ctx = cursor; 1251 krb5_error_code ret; 1252 const char *name; 1253 1254 again: 1255 ret = sqlite3_step(ctx->stmt); 1256 if (ret == SQLITE_DONE) { 1257 krb5_clear_error_message(context); 1258 return KRB5_CC_END; 1259 } else if (ret != SQLITE_ROW) { 1260 krb5_set_error_message(context, KRB5_CC_IO, 1261 N_("Database failed: %s", ""), 1262 sqlite3_errmsg(ctx->db)); 1263 return KRB5_CC_IO; 1264 } 1265 1266 if (sqlite3_column_type(ctx->stmt, 0) != SQLITE_TEXT) 1267 goto again; 1268 1269 name = (const char *)sqlite3_column_text(ctx->stmt, 0); 1270 if (name == NULL) 1271 goto again; 1272 1273 ret = _krb5_cc_allocate(context, &krb5_scc_ops, id); 1274 if (ret) 1275 return ret; 1276 1277 return scc_resolve(context, id, name); 1278 } 1279 1280 static krb5_error_code KRB5_CALLCONV 1281 scc_end_cache_get(krb5_context context, krb5_cc_cursor cursor) 1282 { 1283 struct cache_iter *ctx = cursor; 1284 1285 exec_stmt(context, ctx->db, ctx->drop, 0); 1286 sqlite3_finalize(ctx->stmt); 1287 sqlite3_close(ctx->db); 1288 free(ctx->drop); 1289 free(ctx); 1290 return 0; 1291 } 1292 1293 static krb5_error_code KRB5_CALLCONV 1294 scc_move(krb5_context context, krb5_ccache from, krb5_ccache to) 1295 { 1296 krb5_scache *sfrom = SCACHE(from); 1297 krb5_scache *sto = SCACHE(to); 1298 krb5_error_code ret; 1299 1300 if (strcmp(sfrom->file, sto->file) != 0) { 1301 krb5_set_error_message(context, KRB5_CC_BADNAME, 1302 N_("Can't handle cross database " 1303 "credential move: %s -> %s", ""), 1304 sfrom->file, sto->file); 1305 return KRB5_CC_BADNAME; 1306 } 1307 1308 ret = make_database(context, sfrom); 1309 if (ret) 1310 return ret; 1311 1312 ret = exec_stmt(context, sfrom->db, 1313 "BEGIN IMMEDIATE TRANSACTION", KRB5_CC_IO); 1314 if (ret) return ret; 1315 1316 if (sto->cid != SCACHE_INVALID_CID) { 1317 /* drop old cache entry */ 1318 1319 sqlite3_bind_int(sfrom->dcache, 1, sto->cid); 1320 do { 1321 ret = sqlite3_step(sfrom->dcache); 1322 } while (ret == SQLITE_ROW); 1323 sqlite3_reset(sfrom->dcache); 1324 if (ret != SQLITE_DONE) { 1325 krb5_set_error_message(context, KRB5_CC_IO, 1326 N_("Failed to delete old cache: %d", ""), 1327 (int)ret); 1328 goto rollback; 1329 } 1330 } 1331 1332 sqlite3_bind_text(sfrom->ucachen, 1, sto->name, -1, NULL); 1333 sqlite3_bind_int(sfrom->ucachen, 2, sfrom->cid); 1334 1335 do { 1336 ret = sqlite3_step(sfrom->ucachen); 1337 } while (ret == SQLITE_ROW); 1338 sqlite3_reset(sfrom->ucachen); 1339 if (ret != SQLITE_DONE) { 1340 krb5_set_error_message(context, KRB5_CC_IO, 1341 N_("Failed to update new cache: %d", ""), 1342 (int)ret); 1343 goto rollback; 1344 } 1345 1346 sto->cid = sfrom->cid; 1347 1348 ret = exec_stmt(context, sfrom->db, "COMMIT", KRB5_CC_IO); 1349 if (ret) return ret; 1350 1351 scc_free(sfrom); 1352 1353 return 0; 1354 1355 rollback: 1356 exec_stmt(context, sfrom->db, "ROLLBACK", 0); 1357 scc_free(sfrom); 1358 1359 return KRB5_CC_IO; 1360 } 1361 1362 static krb5_error_code KRB5_CALLCONV 1363 scc_get_default_name(krb5_context context, char **str) 1364 { 1365 krb5_error_code ret; 1366 char *name; 1367 1368 *str = NULL; 1369 1370 ret = get_def_name(context, &name); 1371 if (ret) 1372 return _krb5_expand_default_cc_name(context, KRB5_SCACHE_NAME, str); 1373 1374 ret = asprintf(str, "SCC:%s", name); 1375 free(name); 1376 if (ret < 0 || *str == NULL) { 1377 krb5_set_error_message(context, ENOMEM, 1378 N_("malloc: out of memory", "")); 1379 return ENOMEM; 1380 } 1381 return 0; 1382 } 1383 1384 static krb5_error_code KRB5_CALLCONV 1385 scc_set_default(krb5_context context, krb5_ccache id) 1386 { 1387 krb5_scache *s = SCACHE(id); 1388 krb5_error_code ret; 1389 1390 if (s->cid == SCACHE_INVALID_CID) { 1391 krb5_set_error_message(context, KRB5_CC_IO, 1392 N_("Trying to set a invalid cache " 1393 "as default %s", ""), 1394 s->name); 1395 return KRB5_CC_IO; 1396 } 1397 1398 ret = sqlite3_bind_text(s->umaster, 1, s->name, -1, NULL); 1399 if (ret) { 1400 sqlite3_reset(s->umaster); 1401 krb5_set_error_message(context, KRB5_CC_IO, 1402 N_("Failed to set name of default cache", "")); 1403 return KRB5_CC_IO; 1404 } 1405 1406 do { 1407 ret = sqlite3_step(s->umaster); 1408 } while (ret == SQLITE_ROW); 1409 sqlite3_reset(s->umaster); 1410 if (ret != SQLITE_DONE) { 1411 krb5_set_error_message(context, KRB5_CC_IO, 1412 N_("Failed to update default cache", "")); 1413 return KRB5_CC_IO; 1414 } 1415 1416 return 0; 1417 } 1418 1419 /** 1420 * Variable containing the SCC based credential cache implemention. 1421 * 1422 * @ingroup krb5_ccache 1423 */ 1424 1425 KRB5_LIB_VARIABLE const krb5_cc_ops krb5_scc_ops = { 1426 KRB5_CC_OPS_VERSION, 1427 "SCC", 1428 scc_get_name, 1429 scc_resolve, 1430 scc_gen_new, 1431 scc_initialize, 1432 scc_destroy, 1433 scc_close, 1434 scc_store_cred, 1435 NULL, /* scc_retrieve */ 1436 scc_get_principal, 1437 scc_get_first, 1438 scc_get_next, 1439 scc_end_get, 1440 scc_remove_cred, 1441 scc_set_flags, 1442 NULL, 1443 scc_get_cache_first, 1444 scc_get_cache_next, 1445 scc_end_cache_get, 1446 scc_move, 1447 scc_get_default_name, 1448 scc_set_default 1449 }; 1450 1451 #endif 1452