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