1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* plugins/kdb/db2/kdb_db2.c */ 3 /* 4 * Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 * 26 */ 27 28 /* 29 * Copyright (C) 1998 by the FundsXpress, INC. 30 * 31 * All rights reserved. 32 * 33 * Export of this software from the United States of America may require 34 * a specific license from the United States Government. It is the 35 * responsibility of any person or organization contemplating export to 36 * obtain such a license before exporting. 37 * 38 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 39 * distribute this software and its documentation for any purpose and 40 * without fee is hereby granted, provided that the above copyright 41 * notice appear in all copies and that both that copyright notice and 42 * this permission notice appear in supporting documentation, and that 43 * the name of FundsXpress. not be used in advertising or publicity pertaining 44 * to distribution of the software without specific, written prior 45 * permission. FundsXpress makes no representations about the suitability of 46 * this software for any purpose. It is provided "as is" without express 47 * or implied warranty. 48 * 49 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 50 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 51 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 52 */ 53 54 #include "k5-int.h" 55 56 #if HAVE_UNISTD_H 57 #include <unistd.h> 58 #endif 59 60 #include <db.h> 61 #include <stdio.h> 62 #include <errno.h> 63 #include <utime.h> 64 #include "kdb5.h" 65 #include "kdb_db2.h" 66 #include "kdb_xdr.h" 67 #include "policy_db.h" 68 69 #define KDB_DB2_DATABASE_NAME "database_name" 70 71 #define SUFFIX_DB "" 72 #define SUFFIX_LOCK ".ok" 73 #define SUFFIX_POLICY ".kadm5" 74 #define SUFFIX_POLICY_LOCK ".kadm5.lock" 75 76 /* 77 * Locking: 78 * 79 * There are two distinct locking protocols used. One is designed to 80 * lock against processes (the admin_server, for one) which make 81 * incremental changes to the database; the other is designed to lock 82 * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the 83 * entire database in one fell swoop. 84 * 85 * The first locking protocol is implemented using flock() in the 86 * krb_dbl_lock() and krb_dbl_unlock routines. 87 * 88 * The second locking protocol is necessary because DBM "files" are 89 * actually implemented as two separate files, and it is impossible to 90 * atomically rename two files simultaneously. It assumes that the 91 * database is replaced only very infrequently in comparison to the time 92 * needed to do a database read operation. 93 * 94 * A third file is used as a "version" semaphore; the modification 95 * time of this file is the "version number" of the database. 96 * At the start of a read operation, the reader checks the version 97 * number; at the end of the read operation, it checks again. If the 98 * version number changed, or if the semaphore was nonexistent at 99 * either time, the reader sleeps for a second to let things 100 * stabilize, and then tries again; if it does not succeed after 101 * KRB5_DBM_MAX_RETRY attempts, it gives up. 102 * 103 * On update, the semaphore file is deleted (if it exists) before any 104 * update takes place; at the end of the update, it is replaced, with 105 * a version number strictly greater than the version number which 106 * existed at the start of the update. 107 * 108 * If the system crashes in the middle of an update, the semaphore 109 * file is not automatically created on reboot; this is a feature, not 110 * a bug, since the database may be inconsistent. Note that the 111 * absence of a semaphore file does not prevent another _update_ from 112 * taking place later. Database replacements take place automatically 113 * only on replica servers; a crash in the middle of an update will be 114 * fixed by the next propagation. A crash in the middle of an on the 115 * master would be somewhat more serious, but this would likely be 116 * noticed by an administrator, who could fix the problem and retry 117 * the operation. 118 */ 119 120 /* Evaluate to true if the krb5_context c contains an initialized db2 121 * context. */ 122 #define inited(c) ((c)->dal_handle->db_context && \ 123 ((krb5_db2_context *)(c)->dal_handle->db_context)-> \ 124 db_inited) 125 126 static krb5_error_code 127 get_db_opt(char *input, char **opt, char **val) 128 { 129 char *pos = strchr(input, '='); 130 if (pos == NULL) { 131 *opt = NULL; 132 *val = strdup(input); 133 if (*val == NULL) { 134 return ENOMEM; 135 } 136 } else { 137 *opt = malloc((pos - input) + 1); 138 *val = strdup(pos + 1); 139 if (!*opt || !*val) { 140 free(*opt); 141 *opt = NULL; 142 free(*val); 143 *val = NULL; 144 return ENOMEM; 145 } 146 memcpy(*opt, input, pos - input); 147 (*opt)[pos - input] = '\0'; 148 } 149 return (0); 150 151 } 152 153 /* Restore dbctx to the uninitialized state. */ 154 static void 155 ctx_clear(krb5_db2_context *dbc) 156 { 157 /* 158 * Free any dynamically allocated memory. File descriptors and locks 159 * are the caller's problem. 160 */ 161 free(dbc->db_lf_name); 162 free(dbc->db_name); 163 /* 164 * Clear the structure and reset the defaults. 165 */ 166 memset(dbc, 0, sizeof(krb5_db2_context)); 167 dbc->db = NULL; 168 dbc->db_lf_name = NULL; 169 dbc->db_lf_file = -1; 170 dbc->db_name = NULL; 171 dbc->db_nb_locks = FALSE; 172 dbc->tempdb = FALSE; 173 } 174 175 /* Set *dbc_out to the db2 database context for context. If one does not 176 * exist, create one in the uninitialized state. */ 177 static krb5_error_code 178 ctx_get(krb5_context context, krb5_db2_context **dbc_out) 179 { 180 krb5_db2_context *dbc; 181 kdb5_dal_handle *dal_handle; 182 183 dal_handle = context->dal_handle; 184 185 if (dal_handle->db_context == NULL) { 186 dbc = (krb5_db2_context *) malloc(sizeof(krb5_db2_context)); 187 if (dbc == NULL) 188 return ENOMEM; 189 else { 190 memset(dbc, 0, sizeof(krb5_db2_context)); 191 ctx_clear(dbc); 192 dal_handle->db_context = dbc; 193 } 194 } 195 *dbc_out = dal_handle->db_context; 196 return 0; 197 } 198 199 /* Using db_args and the profile, initialize the configurable parameters of the 200 * DB context inside context. */ 201 static krb5_error_code 202 configure_context(krb5_context context, char *conf_section, char **db_args) 203 { 204 krb5_error_code status; 205 krb5_db2_context *dbc; 206 char **t_ptr, *opt = NULL, *val = NULL, *pval = NULL; 207 profile_t profile = KRB5_DB_GET_PROFILE(context); 208 int bval; 209 210 status = ctx_get(context, &dbc); 211 if (status != 0) 212 return status; 213 214 /* Allow unlockiter to be overridden by command line db_args. */ 215 status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section, 216 KRB5_CONF_UNLOCKITER, FALSE, &bval); 217 if (status != 0) 218 goto cleanup; 219 dbc->unlockiter = bval; 220 221 for (t_ptr = db_args; t_ptr && *t_ptr; t_ptr++) { 222 free(opt); 223 free(val); 224 status = get_db_opt(*t_ptr, &opt, &val); 225 if (opt && !strcmp(opt, "dbname")) { 226 dbc->db_name = strdup(val); 227 if (dbc->db_name == NULL) { 228 status = ENOMEM; 229 goto cleanup; 230 } 231 } 232 else if (!opt && !strcmp(val, "temporary")) { 233 dbc->tempdb = 1; 234 } else if (!opt && !strcmp(val, "merge_nra")) { 235 ; 236 } else if (opt && !strcmp(opt, "hash")) { 237 dbc->hashfirst = TRUE; 238 } else if (!opt && !strcmp(val, "unlockiter")) { 239 dbc->unlockiter = TRUE; 240 } else if (!opt && !strcmp(val, "lockiter")) { 241 dbc->unlockiter = FALSE; 242 } else { 243 status = EINVAL; 244 k5_setmsg(context, status, 245 _("Unsupported argument \"%s\" for db2"), 246 opt ? opt : val); 247 goto cleanup; 248 } 249 } 250 251 if (dbc->db_name == NULL) { 252 /* Check for database_name in the db_module section. */ 253 status = profile_get_string(profile, KDB_MODULE_SECTION, conf_section, 254 KDB_DB2_DATABASE_NAME, NULL, &pval); 255 if (status == 0 && pval == NULL) { 256 /* For compatibility, check for database_name in the realm. */ 257 status = profile_get_string(profile, KDB_REALM_SECTION, 258 KRB5_DB_GET_REALM(context), 259 KDB_DB2_DATABASE_NAME, 260 DEFAULT_KDB_FILE, &pval); 261 } 262 if (status != 0) 263 goto cleanup; 264 dbc->db_name = strdup(pval); 265 } 266 267 status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section, 268 KRB5_CONF_DISABLE_LAST_SUCCESS, FALSE, &bval); 269 if (status != 0) 270 goto cleanup; 271 dbc->disable_last_success = bval; 272 273 status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section, 274 KRB5_CONF_DISABLE_LOCKOUT, FALSE, &bval); 275 if (status != 0) 276 goto cleanup; 277 dbc->disable_lockout = bval; 278 279 cleanup: 280 free(opt); 281 free(val); 282 profile_release_string(pval); 283 return status; 284 } 285 286 /* 287 * Set *out to one of the filenames used for the DB described by dbc. sfx 288 * should be one of SUFFIX_DB, SUFFIX_LOCK, SUFFIX_POLICY, or 289 * SUFFIX_POLICY_LOCK. 290 */ 291 static krb5_error_code 292 ctx_dbsuffix(krb5_db2_context *dbc, const char *sfx, char **out) 293 { 294 char *result; 295 const char *tilde; 296 297 *out = NULL; 298 tilde = dbc->tempdb ? "~" : ""; 299 if (asprintf(&result, "%s%s%s", dbc->db_name, tilde, sfx) < 0) 300 return ENOMEM; 301 *out = result; 302 return 0; 303 } 304 305 /* Generate all four files corresponding to dbc. */ 306 static krb5_error_code 307 ctx_allfiles(krb5_db2_context *dbc, char **dbname_out, char **lockname_out, 308 char **polname_out, char **plockname_out) 309 { 310 char *a = NULL, *b = NULL, *c = NULL, *d = NULL; 311 312 *dbname_out = *lockname_out = *polname_out = *plockname_out = NULL; 313 if (ctx_dbsuffix(dbc, SUFFIX_DB, &a)) 314 goto error; 315 if (ctx_dbsuffix(dbc, SUFFIX_LOCK, &b)) 316 goto error; 317 if (ctx_dbsuffix(dbc, SUFFIX_POLICY, &c)) 318 goto error; 319 if (ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &d)) 320 goto error; 321 *dbname_out = a; 322 *lockname_out = b; 323 *polname_out = c; 324 *plockname_out = d; 325 return 0; 326 error: 327 free(a); 328 free(b); 329 free(c); 330 free(d); 331 return ENOMEM; 332 } 333 334 /* 335 * Open the DB2 database described by dbc, using the specified flags and mode, 336 * and return the resulting handle. Try both hash and btree database types; 337 * dbc->hashfirst determines which is attempted first. If dbc->hashfirst 338 * indicated the wrong type, update it to indicate the correct type. 339 */ 340 static krb5_error_code 341 open_db(krb5_context context, krb5_db2_context *dbc, int flags, int mode, 342 DB **db_out) 343 { 344 char *fname = NULL; 345 DB *db; 346 BTREEINFO bti; 347 HASHINFO hashi; 348 bti.flags = 0; 349 bti.cachesize = 0; 350 bti.psize = 4096; 351 bti.lorder = 0; 352 bti.minkeypage = 0; 353 bti.compare = NULL; 354 bti.prefix = NULL; 355 356 *db_out = NULL; 357 358 if (ctx_dbsuffix(dbc, SUFFIX_DB, &fname) != 0) 359 return ENOMEM; 360 361 hashi.bsize = 4096; 362 hashi.cachesize = 0; 363 hashi.ffactor = 40; 364 hashi.hash = NULL; 365 hashi.lorder = 0; 366 hashi.nelem = 1; 367 368 /* Try our best guess at the database type. */ 369 db = dbopen(fname, flags, mode, 370 dbc->hashfirst ? DB_HASH : DB_BTREE, 371 dbc->hashfirst ? (void *) &hashi : (void *) &bti); 372 373 if (db == NULL && IS_EFTYPE(errno)) { 374 db = dbopen(fname, flags, mode, 375 dbc->hashfirst ? DB_BTREE : DB_HASH, 376 dbc->hashfirst ? (void *) &bti : (void *) &hashi); 377 /* If that worked, update our guess for next time. */ 378 if (db != NULL) 379 dbc->hashfirst = !dbc->hashfirst; 380 } 381 382 /* Don't try unlocked iteration with a hash database. */ 383 if (db != NULL && dbc->hashfirst) 384 dbc->unlockiter = FALSE; 385 386 if (db == NULL) { 387 k5_prependmsg(context, errno, _("Cannot open DB2 database '%s'"), 388 fname); 389 } 390 391 *db_out = db; 392 free(fname); 393 return (db == NULL) ? errno : 0; 394 } 395 396 static krb5_error_code 397 ctx_unlock(krb5_context context, krb5_db2_context *dbc) 398 { 399 krb5_error_code retval, retval2; 400 DB *db; 401 402 retval = osa_adb_release_lock(dbc->policy_db); 403 404 if (!dbc->db_locks_held) /* lock already unlocked */ 405 return KRB5_KDB_NOTLOCKED; 406 407 db = dbc->db; 408 if (--(dbc->db_locks_held) == 0) { 409 db->close(db); 410 dbc->db = NULL; 411 dbc->db_lock_mode = 0; 412 413 retval2 = krb5_lock_file(context, dbc->db_lf_file, 414 KRB5_LOCKMODE_UNLOCK); 415 if (retval2) 416 return retval2; 417 } 418 419 /* We may be unlocking because osa_adb_get_lock() failed. */ 420 if (retval == OSA_ADB_NOTLOCKED) 421 return 0; 422 return retval; 423 } 424 425 static krb5_error_code 426 ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode) 427 { 428 krb5_error_code retval; 429 int kmode; 430 431 if (lockmode == KRB5_DB_LOCKMODE_PERMANENT || 432 lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE) 433 kmode = KRB5_LOCKMODE_EXCLUSIVE; 434 else if (lockmode == KRB5_DB_LOCKMODE_SHARED) 435 kmode = KRB5_LOCKMODE_SHARED; 436 else 437 return EINVAL; 438 439 if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) { 440 /* Acquire or upgrade the lock. */ 441 retval = krb5_lock_file(context, dbc->db_lf_file, kmode); 442 /* Check if we tried to lock something not open for write. */ 443 if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE) 444 return KRB5_KDB_CANTLOCK_DB; 445 else if (retval == EACCES || retval == EAGAIN || retval == EWOULDBLOCK) 446 return KRB5_KDB_CANTLOCK_DB; 447 else if (retval) 448 return retval; 449 450 /* Open the DB (or re-open it for read/write). */ 451 if (dbc->db != NULL) 452 dbc->db->close(dbc->db); 453 retval = open_db(context, dbc, 454 kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 455 0600, &dbc->db); 456 if (retval) { 457 dbc->db_locks_held = 0; 458 dbc->db_lock_mode = 0; 459 (void) osa_adb_release_lock(dbc->policy_db); 460 (void) krb5_lock_file(context, dbc->db_lf_file, 461 KRB5_LOCKMODE_UNLOCK); 462 return retval; 463 } 464 465 dbc->db_lock_mode = kmode; 466 } 467 dbc->db_locks_held++; 468 469 /* Acquire or upgrade the policy lock. */ 470 retval = osa_adb_get_lock(dbc->policy_db, lockmode); 471 if (retval) { 472 (void) ctx_unlock(context, dbc); 473 if (retval == OSA_ADB_NOEXCL_PERM || retval == OSA_ADB_CANTLOCK_DB || 474 retval == OSA_ADB_NOLOCKFILE) 475 retval = KRB5_KDB_CANTLOCK_DB; 476 } 477 return retval; 478 } 479 480 /* Initialize the lock file and policy database fields of dbc. The db_name and 481 * tempdb fields must already be set. */ 482 static krb5_error_code 483 ctx_init(krb5_db2_context *dbc) 484 { 485 krb5_error_code retval; 486 char *polname = NULL, *plockname = NULL; 487 488 retval = ctx_dbsuffix(dbc, SUFFIX_LOCK, &dbc->db_lf_name); 489 if (retval) 490 return retval; 491 492 /* 493 * should be opened read/write so that write locking can work with 494 * POSIX systems 495 */ 496 if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDWR, 0666)) < 0) { 497 if ((dbc->db_lf_file = open(dbc->db_lf_name, O_RDONLY, 0666)) < 0) { 498 retval = errno; 499 goto cleanup; 500 } 501 } 502 set_cloexec_fd(dbc->db_lf_file); 503 dbc->db_inited++; 504 505 retval = ctx_dbsuffix(dbc, SUFFIX_POLICY, &polname); 506 if (retval) 507 goto cleanup; 508 retval = ctx_dbsuffix(dbc, SUFFIX_POLICY_LOCK, &plockname); 509 if (retval) 510 goto cleanup; 511 retval = osa_adb_init_db(&dbc->policy_db, polname, plockname, 512 OSA_ADB_POLICY_DB_MAGIC); 513 514 cleanup: 515 free(polname); 516 free(plockname); 517 if (retval) 518 ctx_clear(dbc); 519 return retval; 520 } 521 522 static void 523 ctx_fini(krb5_db2_context *dbc) 524 { 525 if (dbc->db_lf_file != -1) 526 (void) close(dbc->db_lf_file); 527 if (dbc->policy_db) 528 (void) osa_adb_fini_db(dbc->policy_db, OSA_ADB_POLICY_DB_MAGIC); 529 ctx_clear(dbc); 530 free(dbc); 531 } 532 533 krb5_error_code 534 krb5_db2_fini(krb5_context context) 535 { 536 if (context->dal_handle->db_context != NULL) { 537 ctx_fini(context->dal_handle->db_context); 538 context->dal_handle->db_context = NULL; 539 } 540 return 0; 541 } 542 543 /* Return successfully if the db2 name set in context can be opened. */ 544 static krb5_error_code 545 check_openable(krb5_context context) 546 { 547 krb5_error_code retval; 548 DB *db; 549 krb5_db2_context *dbc; 550 551 dbc = context->dal_handle->db_context; 552 retval = open_db(context, dbc, O_RDONLY, 0, &db); 553 if (retval) 554 return retval; 555 db->close(db); 556 return 0; 557 } 558 559 /* 560 * Return the last modification time of the database. 561 * 562 * Think about using fstat. 563 */ 564 565 krb5_error_code 566 krb5_db2_get_age(krb5_context context, char *db_name, time_t *age) 567 { 568 krb5_db2_context *dbc; 569 struct stat st; 570 571 if (!inited(context)) 572 return (KRB5_KDB_DBNOTINITED); 573 dbc = context->dal_handle->db_context; 574 575 if (fstat(dbc->db_lf_file, &st) < 0) 576 *age = -1; 577 else 578 *age = st.st_mtime; 579 return 0; 580 } 581 582 /* Try to update the timestamp on dbc's lockfile. */ 583 static void 584 ctx_update_age(krb5_db2_context *dbc) 585 { 586 struct stat st; 587 time_t now; 588 struct utimbuf utbuf; 589 590 now = time((time_t *) NULL); 591 if (fstat(dbc->db_lf_file, &st) != 0) 592 return; 593 if (st.st_mtime >= now) { 594 utbuf.actime = st.st_mtime + 1; 595 utbuf.modtime = st.st_mtime + 1; 596 (void) utime(dbc->db_lf_name, &utbuf); 597 } else 598 (void) utime(dbc->db_lf_name, (struct utimbuf *) NULL); 599 } 600 601 krb5_error_code 602 krb5_db2_lock(krb5_context context, int lockmode) 603 { 604 if (!inited(context)) 605 return KRB5_KDB_DBNOTINITED; 606 return ctx_lock(context, context->dal_handle->db_context, lockmode); 607 } 608 609 krb5_error_code 610 krb5_db2_unlock(krb5_context context) 611 { 612 if (!inited(context)) 613 return KRB5_KDB_DBNOTINITED; 614 return ctx_unlock(context, context->dal_handle->db_context); 615 } 616 617 /* Zero out and unlink filename. */ 618 static krb5_error_code 619 destroy_file(char *filename) 620 { 621 struct stat statb; 622 int dowrite, j, nb, fd, retval; 623 off_t pos; 624 char buf[BUFSIZ], zbuf[BUFSIZ]; 625 626 fd = open(filename, O_RDWR, 0); 627 if (fd < 0) 628 return errno; 629 set_cloexec_fd(fd); 630 /* fstat() will probably not fail unless using a remote filesystem 631 * (which is inappropriate for the kerberos database) so this check 632 * is mostly paranoia. */ 633 if (fstat(fd, &statb) == -1) 634 goto error; 635 /* 636 * Stroll through the file, reading in BUFSIZ chunks. If everything 637 * is zero, then we're done for that block, otherwise, zero the block. 638 * We would like to just blast through everything, but some DB 639 * implementations make holey files and writing data to the holes 640 * causes actual blocks to be allocated which is no good, since 641 * we're just about to unlink it anyways. 642 */ 643 memset(zbuf, 0, BUFSIZ); 644 pos = 0; 645 while (pos < statb.st_size) { 646 dowrite = 0; 647 nb = read(fd, buf, BUFSIZ); 648 if (nb < 0) 649 goto error; 650 for (j = 0; j < nb; j++) { 651 if (buf[j] != '\0') { 652 dowrite = 1; 653 break; 654 } 655 } 656 /* For signedness */ 657 j = nb; 658 if (dowrite) { 659 lseek(fd, pos, SEEK_SET); 660 nb = write(fd, zbuf, j); 661 if (nb < 0) 662 goto error; 663 } 664 pos += nb; 665 } 666 /* ??? Is fsync really needed? I don't know of any non-networked 667 * filesystem which will discard queued writes to disk if a file 668 * is deleted after it is closed. --jfc */ 669 #ifndef NOFSYNC 670 fsync(fd); 671 #endif 672 close(fd); 673 674 if (unlink(filename)) 675 return errno; 676 return 0; 677 678 error: 679 retval = errno; 680 close(fd); 681 return retval; 682 } 683 684 /* Initialize dbc by locking and creating the DB. If the DB already exists, 685 * clear it out if dbc->tempdb is set; otherwise return EEXIST. */ 686 static krb5_error_code 687 ctx_create_db(krb5_context context, krb5_db2_context *dbc) 688 { 689 krb5_error_code retval = 0; 690 char *dbname = NULL, *polname = NULL, *plockname = NULL; 691 692 retval = ctx_allfiles(dbc, &dbname, &dbc->db_lf_name, &polname, 693 &plockname); 694 if (retval) 695 return retval; 696 697 dbc->db_lf_file = open(dbc->db_lf_name, O_CREAT | O_RDWR | O_TRUNC, 698 0600); 699 if (dbc->db_lf_file < 0) { 700 retval = errno; 701 goto cleanup; 702 } 703 retval = krb5_lock_file(context, dbc->db_lf_file, KRB5_LOCKMODE_EXCLUSIVE); 704 if (retval != 0) 705 goto cleanup; 706 set_cloexec_fd(dbc->db_lf_file); 707 dbc->db_lock_mode = KRB5_LOCKMODE_EXCLUSIVE; 708 dbc->db_locks_held = 1; 709 710 if (dbc->tempdb) { 711 /* Temporary DBs are locked for their whole lifetime. Since we have 712 * the lock, any remnant files can be safely destroyed. */ 713 (void) destroy_file(dbname); 714 (void) unlink(polname); 715 (void) unlink(plockname); 716 } 717 718 retval = open_db(context, dbc, O_RDWR | O_CREAT | O_EXCL, 0600, &dbc->db); 719 if (retval) 720 goto cleanup; 721 722 /* Create the policy database, initialize a handle to it, and lock it. */ 723 retval = osa_adb_create_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC); 724 if (retval) 725 goto cleanup; 726 retval = osa_adb_init_db(&dbc->policy_db, polname, plockname, 727 OSA_ADB_POLICY_DB_MAGIC); 728 if (retval) 729 goto cleanup; 730 retval = osa_adb_get_lock(dbc->policy_db, KRB5_DB_LOCKMODE_EXCLUSIVE); 731 if (retval) 732 goto cleanup; 733 734 dbc->db_inited = 1; 735 736 cleanup: 737 if (retval) { 738 if (dbc->db != NULL) 739 dbc->db->close(dbc->db); 740 if (dbc->db_locks_held > 0) { 741 (void) krb5_lock_file(context, dbc->db_lf_file, 742 KRB5_LOCKMODE_UNLOCK); 743 } 744 if (dbc->db_lf_file >= 0) 745 close(dbc->db_lf_file); 746 ctx_clear(dbc); 747 } 748 free(dbname); 749 free(polname); 750 free(plockname); 751 return retval; 752 } 753 754 krb5_error_code 755 krb5_db2_get_principal(krb5_context context, krb5_const_principal searchfor, 756 unsigned int flags, krb5_db_entry **entry) 757 { 758 krb5_db2_context *dbc; 759 krb5_error_code retval; 760 DB *db; 761 DBT key, contents; 762 krb5_data keydata, contdata; 763 int dbret; 764 765 *entry = NULL; 766 if (!inited(context)) 767 return KRB5_KDB_DBNOTINITED; 768 769 dbc = context->dal_handle->db_context; 770 771 retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED); 772 if (retval) 773 return retval; 774 775 /* XXX deal with wildcard lookups */ 776 retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); 777 if (retval) 778 goto cleanup; 779 key.data = keydata.data; 780 key.size = keydata.length; 781 782 db = dbc->db; 783 dbret = (*db->get)(db, &key, &contents, 0); 784 retval = errno; 785 krb5_free_data_contents(context, &keydata); 786 switch (dbret) { 787 case 1: 788 retval = KRB5_KDB_NOENTRY; 789 /* Fall through. */ 790 case -1: 791 default: 792 goto cleanup; 793 case 0: 794 contdata.data = contents.data; 795 contdata.length = contents.size; 796 retval = krb5_decode_princ_entry(context, &contdata, entry); 797 break; 798 } 799 800 cleanup: 801 (void) krb5_db2_unlock(context); /* unlock read lock */ 802 return retval; 803 } 804 805 krb5_error_code 806 krb5_db2_put_principal(krb5_context context, krb5_db_entry *entry, 807 char **db_args) 808 { 809 int dbret; 810 DB *db; 811 DBT key, contents; 812 krb5_data contdata, keydata; 813 krb5_error_code retval; 814 krb5_db2_context *dbc; 815 816 krb5_clear_error_message (context); 817 if (db_args) { 818 /* DB2 does not support db_args DB arguments for principal */ 819 k5_setmsg(context, EINVAL, _("Unsupported argument \"%s\" for db2"), 820 db_args[0]); 821 return EINVAL; 822 } 823 824 if (!inited(context)) 825 return KRB5_KDB_DBNOTINITED; 826 827 dbc = context->dal_handle->db_context; 828 if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE))) 829 return retval; 830 831 db = dbc->db; 832 833 retval = krb5_encode_princ_entry(context, &contdata, entry); 834 if (retval) 835 goto cleanup; 836 contents.data = contdata.data; 837 contents.size = contdata.length; 838 retval = krb5_encode_princ_dbkey(context, &keydata, entry->princ); 839 if (retval) { 840 krb5_free_data_contents(context, &contdata); 841 goto cleanup; 842 } 843 844 key.data = keydata.data; 845 key.size = keydata.length; 846 dbret = (*db->put)(db, &key, &contents, 0); 847 retval = dbret ? errno : 0; 848 krb5_free_data_contents(context, &keydata); 849 krb5_free_data_contents(context, &contdata); 850 851 cleanup: 852 ctx_update_age(dbc); 853 (void) krb5_db2_unlock(context); /* unlock database */ 854 return (retval); 855 } 856 857 krb5_error_code 858 krb5_db2_delete_principal(krb5_context context, krb5_const_principal searchfor) 859 { 860 krb5_error_code retval; 861 krb5_db_entry *entry; 862 krb5_db2_context *dbc; 863 DB *db; 864 DBT key, contents; 865 krb5_data keydata, contdata; 866 int i, dbret; 867 868 if (!inited(context)) 869 return KRB5_KDB_DBNOTINITED; 870 871 dbc = context->dal_handle->db_context; 872 if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_EXCLUSIVE))) 873 return (retval); 874 875 if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) 876 goto cleanup; 877 key.data = keydata.data; 878 key.size = keydata.length; 879 880 db = dbc->db; 881 dbret = (*db->get) (db, &key, &contents, 0); 882 retval = errno; 883 switch (dbret) { 884 case 1: 885 retval = KRB5_KDB_NOENTRY; 886 /* Fall through. */ 887 case -1: 888 default: 889 goto cleankey; 890 case 0: 891 ; 892 } 893 contdata.data = contents.data; 894 contdata.length = contents.size; 895 retval = krb5_decode_princ_entry(context, &contdata, &entry); 896 if (retval) 897 goto cleankey; 898 899 /* Clear encrypted key contents */ 900 for (i = 0; i < entry->n_key_data; i++) { 901 if (entry->key_data[i].key_data_length[0]) { 902 memset(entry->key_data[i].key_data_contents[0], 0, 903 (unsigned) entry->key_data[i].key_data_length[0]); 904 } 905 } 906 907 retval = krb5_encode_princ_entry(context, &contdata, entry); 908 krb5_db_free_principal(context, entry); 909 if (retval) 910 goto cleankey; 911 912 contents.data = contdata.data; 913 contents.size = contdata.length; 914 dbret = (*db->put) (db, &key, &contents, 0); 915 retval = dbret ? errno : 0; 916 krb5_free_data_contents(context, &contdata); 917 if (retval) 918 goto cleankey; 919 dbret = (*db->del) (db, &key, 0); 920 retval = dbret ? errno : 0; 921 cleankey: 922 krb5_free_data_contents(context, &keydata); 923 924 cleanup: 925 ctx_update_age(dbc); 926 (void) krb5_db2_unlock(context); /* unlock write lock */ 927 return retval; 928 } 929 930 typedef krb5_error_code (*ctx_iterate_cb)(krb5_pointer, krb5_db_entry *); 931 932 /* Cursor structure for ctx_iterate() */ 933 typedef struct iter_curs { 934 DBT key; 935 DBT data; 936 DBT keycopy; 937 unsigned int startflag; 938 unsigned int stepflag; 939 krb5_context ctx; 940 krb5_db2_context *dbc; 941 int lockmode; 942 krb5_boolean islocked; 943 } iter_curs; 944 945 /* Lock DB handle of curs, updating curs->islocked. */ 946 static krb5_error_code 947 curs_lock(iter_curs *curs) 948 { 949 krb5_error_code retval; 950 951 retval = ctx_lock(curs->ctx, curs->dbc, curs->lockmode); 952 if (retval) 953 return retval; 954 curs->islocked = TRUE; 955 return 0; 956 } 957 958 /* Unlock DB handle of curs, updating curs->islocked. */ 959 static void 960 curs_unlock(iter_curs *curs) 961 { 962 ctx_unlock(curs->ctx, curs->dbc); 963 curs->islocked = FALSE; 964 } 965 966 /* Set up curs and lock DB. */ 967 static krb5_error_code 968 curs_init(iter_curs *curs, krb5_context ctx, krb5_db2_context *dbc, 969 krb5_flags iterflags) 970 { 971 int isrecurse = iterflags & KRB5_DB_ITER_RECURSE; 972 unsigned int prevflag = R_PREV; 973 unsigned int nextflag = R_NEXT; 974 975 curs->keycopy.size = 0; 976 curs->keycopy.data = NULL; 977 curs->islocked = FALSE; 978 curs->ctx = ctx; 979 curs->dbc = dbc; 980 981 if (iterflags & KRB5_DB_ITER_WRITE) 982 curs->lockmode = KRB5_LOCKMODE_EXCLUSIVE; 983 else 984 curs->lockmode = KRB5_LOCKMODE_SHARED; 985 986 if (isrecurse) { 987 #ifdef R_RNEXT 988 if (dbc->hashfirst) { 989 k5_setmsg(ctx, EINVAL, _("Recursive iteration is not supported " 990 "for hash databases")); 991 return EINVAL; 992 } 993 prevflag = R_RPREV; 994 nextflag = R_RNEXT; 995 #else 996 k5_setmsg(ctx, EINVAL, _("Recursive iteration not supported " 997 "in this version of libdb")); 998 return EINVAL; 999 #endif 1000 } 1001 if (iterflags & KRB5_DB_ITER_REV) { 1002 curs->startflag = R_LAST; 1003 curs->stepflag = prevflag; 1004 } else { 1005 curs->startflag = R_FIRST; 1006 curs->stepflag = nextflag; 1007 } 1008 return curs_lock(curs); 1009 } 1010 1011 /* Get initial entry. */ 1012 static int 1013 curs_start(iter_curs *curs) 1014 { 1015 DB *db = curs->dbc->db; 1016 1017 return db->seq(db, &curs->key, &curs->data, curs->startflag); 1018 } 1019 1020 /* Save iteration state so DB can be unlocked/closed. */ 1021 static krb5_error_code 1022 curs_save(iter_curs *curs) 1023 { 1024 if (!curs->dbc->unlockiter) 1025 return 0; 1026 1027 curs->keycopy.data = malloc(curs->key.size); 1028 if (curs->keycopy.data == NULL) 1029 return ENOMEM; 1030 1031 curs->keycopy.size = curs->key.size; 1032 memcpy(curs->keycopy.data, curs->key.data, curs->key.size); 1033 return 0; 1034 } 1035 1036 /* Free allocated cursor resources */ 1037 static void 1038 curs_free(iter_curs *curs) 1039 { 1040 free(curs->keycopy.data); 1041 curs->keycopy.size = 0; 1042 curs->keycopy.data = NULL; 1043 } 1044 1045 /* Move one step of iteration (forwards or backwards as requested). Free 1046 * curs->keycopy as a side effect, if needed. */ 1047 static int 1048 curs_step(iter_curs *curs) 1049 { 1050 int dbret; 1051 krb5_db2_context *dbc = curs->dbc; 1052 1053 if (dbc->unlockiter) { 1054 /* Reacquire libdb cursor using saved copy of key. */ 1055 curs->key = curs->keycopy; 1056 dbret = dbc->db->seq(dbc->db, &curs->key, &curs->data, R_CURSOR); 1057 curs_free(curs); 1058 if (dbret) 1059 return dbret; 1060 } 1061 return dbc->db->seq(dbc->db, &curs->key, &curs->data, curs->stepflag); 1062 } 1063 1064 /* Run one invocation of the callback, unlocking the mutex and possibly the DB 1065 * around the invocation. */ 1066 static krb5_error_code 1067 curs_run_cb(iter_curs *curs, ctx_iterate_cb func, krb5_pointer func_arg) 1068 { 1069 krb5_db2_context *dbc = curs->dbc; 1070 krb5_error_code retval, lockerr; 1071 krb5_db_entry *entry; 1072 krb5_context ctx = curs->ctx; 1073 krb5_data contdata; 1074 1075 contdata = make_data(curs->data.data, curs->data.size); 1076 retval = krb5_decode_princ_entry(ctx, &contdata, &entry); 1077 if (retval) 1078 return retval; 1079 /* Save libdb key across possible DB closure. */ 1080 retval = curs_save(curs); 1081 if (retval) 1082 return retval; 1083 1084 if (dbc->unlockiter) 1085 curs_unlock(curs); 1086 1087 k5_mutex_unlock(krb5_db2_mutex); 1088 retval = (*func)(func_arg, entry); 1089 krb5_db_free_principal(ctx, entry); 1090 k5_mutex_lock(krb5_db2_mutex); 1091 if (dbc->unlockiter) { 1092 lockerr = curs_lock(curs); 1093 if (lockerr) 1094 return lockerr; 1095 } 1096 return retval; 1097 } 1098 1099 /* Free cursor resources and unlock the DB if needed. */ 1100 static void 1101 curs_fini(iter_curs *curs) 1102 { 1103 curs_free(curs); 1104 if (curs->islocked) 1105 curs_unlock(curs); 1106 } 1107 1108 static krb5_error_code 1109 ctx_iterate(krb5_context context, krb5_db2_context *dbc, 1110 ctx_iterate_cb func, krb5_pointer func_arg, krb5_flags iterflags) 1111 { 1112 krb5_error_code retval; 1113 int dbret; 1114 iter_curs curs; 1115 1116 retval = curs_init(&curs, context, dbc, iterflags); 1117 if (retval) 1118 return retval; 1119 dbret = curs_start(&curs); 1120 while (dbret == 0) { 1121 retval = curs_run_cb(&curs, func, func_arg); 1122 if (retval) 1123 goto cleanup; 1124 dbret = curs_step(&curs); 1125 } 1126 switch (dbret) { 1127 case 1: 1128 case 0: 1129 break; 1130 case -1: 1131 default: 1132 retval = errno; 1133 } 1134 cleanup: 1135 curs_fini(&curs); 1136 return retval; 1137 } 1138 1139 krb5_error_code 1140 krb5_db2_iterate(krb5_context context, char *match_expr, ctx_iterate_cb func, 1141 krb5_pointer func_arg, krb5_flags iterflags) 1142 { 1143 if (!inited(context)) 1144 return KRB5_KDB_DBNOTINITED; 1145 return ctx_iterate(context, context->dal_handle->db_context, func, 1146 func_arg, iterflags); 1147 } 1148 1149 krb5_boolean 1150 krb5_db2_set_lockmode(krb5_context context, krb5_boolean mode) 1151 { 1152 krb5_boolean old; 1153 krb5_db2_context *dbc; 1154 1155 dbc = context->dal_handle->db_context; 1156 old = mode; 1157 if (dbc) { 1158 old = dbc->db_nb_locks; 1159 dbc->db_nb_locks = mode; 1160 } 1161 return old; 1162 } 1163 1164 /* 1165 * DAL API functions 1166 */ 1167 krb5_error_code 1168 krb5_db2_lib_init() 1169 { 1170 return 0; 1171 } 1172 1173 krb5_error_code 1174 krb5_db2_lib_cleanup() 1175 { 1176 /* right now, no cleanup required */ 1177 return 0; 1178 } 1179 1180 krb5_error_code 1181 krb5_db2_open(krb5_context context, char *conf_section, char **db_args, 1182 int mode) 1183 { 1184 krb5_error_code status = 0; 1185 1186 krb5_clear_error_message(context); 1187 if (inited(context)) 1188 return 0; 1189 1190 status = configure_context(context, conf_section, db_args); 1191 if (status != 0) 1192 return status; 1193 1194 status = check_openable(context); 1195 if (status != 0) 1196 return status; 1197 1198 return ctx_init(context->dal_handle->db_context); 1199 } 1200 1201 krb5_error_code 1202 krb5_db2_create(krb5_context context, char *conf_section, char **db_args) 1203 { 1204 krb5_error_code status = 0; 1205 krb5_db2_context *dbc; 1206 1207 krb5_clear_error_message(context); 1208 if (inited(context)) 1209 return 0; 1210 1211 status = configure_context(context, conf_section, db_args); 1212 if (status != 0) 1213 return status; 1214 1215 dbc = context->dal_handle->db_context; 1216 status = ctx_create_db(context, dbc); 1217 if (status != 0) 1218 return status; 1219 1220 if (!dbc->tempdb) 1221 krb5_db2_unlock(context); 1222 1223 return 0; 1224 } 1225 1226 krb5_error_code 1227 krb5_db2_destroy(krb5_context context, char *conf_section, char **db_args) 1228 { 1229 krb5_error_code status; 1230 krb5_db2_context *dbc; 1231 char *dbname = NULL, *lockname = NULL, *polname = NULL, *plockname = NULL; 1232 1233 if (inited(context)) { 1234 status = krb5_db2_fini(context); 1235 if (status != 0) 1236 return status; 1237 } 1238 1239 krb5_clear_error_message(context); 1240 status = configure_context(context, conf_section, db_args); 1241 if (status != 0) 1242 return status; 1243 1244 status = check_openable(context); 1245 if (status != 0) 1246 return status; 1247 1248 dbc = context->dal_handle->db_context; 1249 1250 status = ctx_allfiles(dbc, &dbname, &lockname, &polname, &plockname); 1251 if (status) 1252 goto cleanup; 1253 status = destroy_file(dbname); 1254 if (status) 1255 goto cleanup; 1256 status = unlink(lockname); 1257 if (status) 1258 goto cleanup; 1259 status = osa_adb_destroy_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC); 1260 if (status) 1261 goto cleanup; 1262 1263 status = krb5_db2_fini(context); 1264 1265 cleanup: 1266 free(dbname); 1267 free(lockname); 1268 free(polname); 1269 free(plockname); 1270 return status; 1271 } 1272 1273 /* policy functions */ 1274 krb5_error_code 1275 krb5_db2_create_policy(krb5_context context, osa_policy_ent_t policy) 1276 { 1277 krb5_db2_context *dbc = context->dal_handle->db_context; 1278 1279 return osa_adb_create_policy(dbc->policy_db, policy); 1280 } 1281 1282 krb5_error_code 1283 krb5_db2_get_policy(krb5_context context, 1284 char *name, osa_policy_ent_t *policy) 1285 { 1286 krb5_db2_context *dbc = context->dal_handle->db_context; 1287 1288 return osa_adb_get_policy(dbc->policy_db, name, policy); 1289 } 1290 1291 krb5_error_code 1292 krb5_db2_put_policy(krb5_context context, osa_policy_ent_t policy) 1293 { 1294 krb5_db2_context *dbc = context->dal_handle->db_context; 1295 1296 return osa_adb_put_policy(dbc->policy_db, policy); 1297 } 1298 1299 krb5_error_code 1300 krb5_db2_iter_policy(krb5_context context, 1301 char *match_entry, 1302 osa_adb_iter_policy_func func, void *data) 1303 { 1304 krb5_db2_context *dbc = context->dal_handle->db_context; 1305 1306 return osa_adb_iter_policy(dbc->policy_db, func, data); 1307 } 1308 1309 krb5_error_code 1310 krb5_db2_delete_policy(krb5_context context, char *policy) 1311 { 1312 krb5_db2_context *dbc = context->dal_handle->db_context; 1313 1314 return osa_adb_destroy_policy(dbc->policy_db, policy); 1315 } 1316 1317 /* 1318 * Merge non-replicated attributes from src into dst, setting 1319 * changed to non-zero if dst was changed. 1320 * 1321 * Non-replicated attributes are: last_success, last_failed, 1322 * fail_auth_count, and any negative TL data values. 1323 */ 1324 static krb5_error_code 1325 krb5_db2_merge_principal(krb5_context context, 1326 krb5_db_entry *src, 1327 krb5_db_entry *dst, 1328 int *changed) 1329 { 1330 *changed = 0; 1331 1332 if (dst->last_success != src->last_success) { 1333 dst->last_success = src->last_success; 1334 (*changed)++; 1335 } 1336 1337 if (dst->last_failed != src->last_failed) { 1338 dst->last_failed = src->last_failed; 1339 (*changed)++; 1340 } 1341 1342 if (dst->fail_auth_count != src->fail_auth_count) { 1343 dst->fail_auth_count = src->fail_auth_count; 1344 (*changed)++; 1345 } 1346 1347 return 0; 1348 } 1349 1350 struct nra_context { 1351 krb5_context kcontext; 1352 krb5_db2_context *db_context; 1353 }; 1354 1355 /* 1356 * Iteration callback merges non-replicated attributes from 1357 * old database. 1358 */ 1359 static krb5_error_code 1360 krb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry) 1361 { 1362 struct nra_context *nra = (struct nra_context *)ptr; 1363 kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle; 1364 krb5_error_code retval; 1365 int changed; 1366 krb5_db_entry *s_entry; 1367 krb5_db2_context *dst_db; 1368 1369 memset(&s_entry, 0, sizeof(s_entry)); 1370 1371 dst_db = dal_handle->db_context; 1372 dal_handle->db_context = nra->db_context; 1373 1374 /* look up the new principal in the old DB */ 1375 retval = krb5_db2_get_principal(nra->kcontext, entry->princ, 0, &s_entry); 1376 if (retval != 0) { 1377 /* principal may be newly created, so ignore */ 1378 dal_handle->db_context = dst_db; 1379 return 0; 1380 } 1381 1382 /* merge non-replicated attributes from the old entry in */ 1383 krb5_db2_merge_principal(nra->kcontext, s_entry, entry, &changed); 1384 1385 dal_handle->db_context = dst_db; 1386 1387 /* if necessary, commit the modified new entry to the new DB */ 1388 if (changed) { 1389 retval = krb5_db2_put_principal(nra->kcontext, entry, NULL); 1390 } else { 1391 retval = 0; 1392 } 1393 1394 krb5_db_free_principal(nra->kcontext, s_entry); 1395 return retval; 1396 } 1397 1398 /* 1399 * Merge non-replicated attributes (that is, lockout-related 1400 * attributes and negative TL data types) from the real database 1401 * into the temporary one. 1402 */ 1403 static krb5_error_code 1404 ctx_merge_nra(krb5_context context, krb5_db2_context *dbc_temp, 1405 krb5_db2_context *dbc_real) 1406 { 1407 struct nra_context nra; 1408 1409 nra.kcontext = context; 1410 nra.db_context = dbc_real; 1411 return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra, 0); 1412 } 1413 1414 /* 1415 * In the filesystem, promote the temporary database described by dbc_temp to 1416 * the real database described by dbc_real. Both must be exclusively locked. 1417 */ 1418 static krb5_error_code 1419 ctx_promote(krb5_context context, krb5_db2_context *dbc_temp, 1420 krb5_db2_context *dbc_real) 1421 { 1422 krb5_error_code retval; 1423 char *tdb = NULL, *tlock = NULL, *tpol = NULL, *tplock = NULL; 1424 char *rdb = NULL, *rlock = NULL, *rpol = NULL, *rplock = NULL; 1425 1426 /* Generate all filenames of interest (including a few we don't need). */ 1427 retval = ctx_allfiles(dbc_temp, &tdb, &tlock, &tpol, &tplock); 1428 if (retval) 1429 return retval; 1430 retval = ctx_allfiles(dbc_real, &rdb, &rlock, &rpol, &rplock); 1431 if (retval) 1432 goto cleanup; 1433 1434 /* Rename the principal and policy databases into place. */ 1435 if (rename(tdb, rdb)) { 1436 retval = errno; 1437 goto cleanup; 1438 } 1439 if (rename(tpol, rpol)) { 1440 retval = errno; 1441 goto cleanup; 1442 } 1443 1444 ctx_update_age(dbc_real); 1445 1446 /* Release and remove the temporary DB lockfiles. */ 1447 (void) unlink(tlock); 1448 (void) unlink(tplock); 1449 1450 cleanup: 1451 free(tdb); 1452 free(tlock); 1453 free(tpol); 1454 free(tplock); 1455 free(rdb); 1456 free(rlock); 1457 free(rpol); 1458 free(rplock); 1459 return retval; 1460 } 1461 1462 krb5_error_code 1463 krb5_db2_promote_db(krb5_context context, char *conf_section, char **db_args) 1464 { 1465 krb5_error_code retval; 1466 krb5_boolean merge_nra = FALSE, real_locked = FALSE; 1467 krb5_db2_context *dbc_temp, *dbc_real = NULL; 1468 char **db_argp; 1469 1470 /* context must be initialized with an exclusively locked temp DB. */ 1471 if (!inited(context)) 1472 return KRB5_KDB_DBNOTINITED; 1473 dbc_temp = context->dal_handle->db_context; 1474 if (dbc_temp->db_lock_mode != KRB5_LOCKMODE_EXCLUSIVE) 1475 return KRB5_KDB_NOTLOCKED; 1476 if (!dbc_temp->tempdb) 1477 return EINVAL; 1478 1479 /* Check db_args for whether we should merge non-replicated attributes. */ 1480 for (db_argp = db_args; *db_argp; db_argp++) { 1481 if (!strcmp(*db_argp, "merge_nra")) { 1482 merge_nra = TRUE; 1483 break; 1484 } 1485 } 1486 1487 /* Make a db2 context for the real DB. */ 1488 dbc_real = k5alloc(sizeof(*dbc_real), &retval); 1489 if (dbc_real == NULL) 1490 return retval; 1491 ctx_clear(dbc_real); 1492 1493 /* Try creating the real DB. */ 1494 dbc_real->db_name = strdup(dbc_temp->db_name); 1495 if (dbc_real->db_name == NULL) 1496 goto cleanup; 1497 dbc_real->tempdb = FALSE; 1498 retval = ctx_create_db(context, dbc_real); 1499 if (retval == EEXIST) { 1500 /* The real database already exists, so open and lock it. */ 1501 dbc_real->db_name = strdup(dbc_temp->db_name); 1502 if (dbc_real->db_name == NULL) 1503 goto cleanup; 1504 dbc_real->tempdb = FALSE; 1505 retval = ctx_init(dbc_real); 1506 if (retval) 1507 goto cleanup; 1508 retval = ctx_lock(context, dbc_real, KRB5_DB_LOCKMODE_EXCLUSIVE); 1509 if (retval) 1510 goto cleanup; 1511 } else if (retval) 1512 goto cleanup; 1513 real_locked = TRUE; 1514 1515 if (merge_nra) { 1516 retval = ctx_merge_nra(context, dbc_temp, dbc_real); 1517 if (retval) 1518 goto cleanup; 1519 } 1520 1521 /* Perform filesystem manipulations for the promotion. */ 1522 retval = ctx_promote(context, dbc_temp, dbc_real); 1523 if (retval) 1524 goto cleanup; 1525 1526 /* Unlock and finalize context since the temp DB is gone. */ 1527 (void) krb5_db2_unlock(context); 1528 krb5_db2_fini(context); 1529 1530 cleanup: 1531 if (real_locked) 1532 (void) ctx_unlock(context, dbc_real); 1533 if (dbc_real) 1534 ctx_fini(dbc_real); 1535 return retval; 1536 } 1537 1538 krb5_error_code 1539 krb5_db2_check_policy_as(krb5_context kcontext, krb5_kdc_req *request, 1540 krb5_db_entry *client, krb5_db_entry *server, 1541 krb5_timestamp kdc_time, const char **status, 1542 krb5_pa_data ***e_data) 1543 { 1544 krb5_error_code retval; 1545 1546 retval = krb5_db2_lockout_check_policy(kcontext, client, kdc_time); 1547 if (retval == KRB5KDC_ERR_CLIENT_REVOKED) 1548 *status = "LOCKED_OUT"; 1549 return retval; 1550 } 1551 1552 void 1553 krb5_db2_audit_as_req(krb5_context kcontext, krb5_kdc_req *request, 1554 const krb5_address *local_addr, 1555 const krb5_address *remote_addr, krb5_db_entry *client, 1556 krb5_db_entry *server, krb5_timestamp authtime, 1557 krb5_error_code error_code) 1558 { 1559 (void) krb5_db2_lockout_audit(kcontext, client, authtime, error_code); 1560 } 1561