1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * lib/kdb/kdb_db2.c 10 * 11 * Copyright 1997,2006 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 */ 34 35 /* 36 * Copyright (C) 1998 by the FundsXpress, INC. 37 * 38 * All rights reserved. 39 * 40 * Export of this software from the United States of America may require 41 * a specific license from the United States Government. It is the 42 * responsibility of any person or organization contemplating export to 43 * obtain such a license before exporting. 44 * 45 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 46 * distribute this software and its documentation for any purpose and 47 * without fee is hereby granted, provided that the above copyright 48 * notice appear in all copies and that both that copyright notice and 49 * this permission notice appear in supporting documentation, and that 50 * the name of FundsXpress. not be used in advertising or publicity pertaining 51 * to distribution of the software without specific, written prior 52 * permission. FundsXpress makes no representations about the suitability of 53 * this software for any purpose. It is provided "as is" without express 54 * or implied warranty. 55 * 56 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 57 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 58 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 59 */ 60 61 #include "k5-int.h" 62 #include <kdb_log.h> 63 64 #if HAVE_UNISTD_H 65 #include <unistd.h> 66 #endif 67 68 #include <db.h> 69 #include <stdio.h> 70 #include <errno.h> 71 #include <utime.h> 72 #include <kdb5.h> 73 #include "kdb_db2.h" 74 #include "kdb_xdr.h" 75 #include "policy_db.h" 76 #include <libintl.h> 77 78 #define KDB_DB2_DATABASE_NAME "database_name" 79 80 #include "kdb_db2.h" 81 82 static char *gen_dbsuffix(char *, char *); 83 84 static krb5_error_code krb5_db2_db_start_update(krb5_context); 85 static krb5_error_code krb5_db2_db_end_update(krb5_context); 86 87 static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int); 88 89 krb5_error_code krb5_db2_db_lock(krb5_context, int); 90 91 static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int); 92 93 static char default_db_name[] = DEFAULT_KDB_FILE; 94 95 /* 96 * Locking: 97 * 98 * There are two distinct locking protocols used. One is designed to 99 * lock against processes (the admin_server, for one) which make 100 * incremental changes to the database; the other is designed to lock 101 * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the 102 * entire database in one fell swoop. 103 * 104 * The first locking protocol is implemented using flock() in the 105 * krb_dbl_lock() and krb_dbl_unlock routines. 106 * 107 * The second locking protocol is necessary because DBM "files" are 108 * actually implemented as two separate files, and it is impossible to 109 * atomically rename two files simultaneously. It assumes that the 110 * database is replaced only very infrequently in comparison to the time 111 * needed to do a database read operation. 112 * 113 * A third file is used as a "version" semaphore; the modification 114 * time of this file is the "version number" of the database. 115 * At the start of a read operation, the reader checks the version 116 * number; at the end of the read operation, it checks again. If the 117 * version number changed, or if the semaphore was nonexistant at 118 * either time, the reader sleeps for a second to let things 119 * stabilize, and then tries again; if it does not succeed after 120 * KRB5_DBM_MAX_RETRY attempts, it gives up. 121 * 122 * On update, the semaphore file is deleted (if it exists) before any 123 * update takes place; at the end of the update, it is replaced, with 124 * a version number strictly greater than the version number which 125 * existed at the start of the update. 126 * 127 * If the system crashes in the middle of an update, the semaphore 128 * file is not automatically created on reboot; this is a feature, not 129 * a bug, since the database may be inconsistant. Note that the 130 * absence of a semaphore file does not prevent another _update_ from 131 * taking place later. Database replacements take place automatically 132 * only on slave servers; a crash in the middle of an update will be 133 * fixed by the next slave propagation. A crash in the middle of an 134 * update on the master would be somewhat more serious, but this would 135 * likely be noticed by an administrator, who could fix the problem and 136 * retry the operation. 137 */ 138 139 #define free_dbsuffix(name) free(name) 140 141 /* 142 * Routines to deal with context. 143 */ 144 #define k5db2_inited(c) (c && c->db_context \ 145 && ((kdb5_dal_handle*)c->db_context)->db_context \ 146 && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited) 147 148 static krb5_error_code 149 krb5_db2_get_db_opt(char *input, char **opt, char **val) 150 { 151 char *pos = strchr(input, '='); 152 if (pos == NULL) { 153 *opt = NULL; 154 *val = strdup(input); 155 if (*val == NULL) { 156 return ENOMEM; 157 } 158 } else { 159 *opt = malloc((pos - input) + 1); 160 *val = strdup(pos + 1); 161 if (!*opt || !*val) { 162 return ENOMEM; 163 } 164 memcpy(*opt, input, pos - input); 165 (*opt)[pos - input] = '\0'; 166 } 167 return (0); 168 169 } 170 171 /* 172 * Restore the default context. 173 */ 174 static void 175 k5db2_clear_context(krb5_db2_context *dbctx) 176 { 177 /* 178 * Free any dynamically allocated memory. File descriptors and locks 179 * are the caller's problem. 180 */ 181 if (dbctx->db_lf_name) 182 free(dbctx->db_lf_name); 183 if (dbctx->db_name && (dbctx->db_name != default_db_name)) 184 free(dbctx->db_name); 185 /* 186 * Clear the structure and reset the defaults. 187 */ 188 memset((char *) dbctx, 0, sizeof(krb5_db2_context)); 189 dbctx->db_name = default_db_name; 190 dbctx->db_nb_locks = FALSE; 191 dbctx->tempdb = FALSE; 192 } 193 194 static krb5_error_code 195 k5db2_init_context(krb5_context context) 196 { 197 krb5_db2_context *db_ctx; 198 kdb5_dal_handle *dal_handle; 199 200 dal_handle = (kdb5_dal_handle *) context->db_context; 201 202 if (dal_handle->db_context == NULL) { 203 db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context)); 204 if (db_ctx == NULL) 205 return ENOMEM; 206 else { 207 memset((char *) db_ctx, 0, sizeof(krb5_db2_context)); 208 k5db2_clear_context((krb5_db2_context *) db_ctx); 209 dal_handle->db_context = (void *) db_ctx; 210 } 211 } 212 return (0); 213 } 214 215 /* 216 * Utility routine: generate name of database file. 217 */ 218 219 static char * 220 gen_dbsuffix(char *db_name, char *sfx) 221 { 222 char *dbsuffix; 223 224 if (sfx == NULL) 225 return ((char *) NULL); 226 227 dbsuffix = malloc(strlen(db_name) + strlen(sfx) + 1); 228 if (!dbsuffix) 229 return (0); 230 /*LINTED*/ 231 (void) strcpy(dbsuffix, db_name); 232 /*LINTED*/ 233 (void) strcat(dbsuffix, sfx); 234 return dbsuffix; 235 } 236 237 static DB * 238 k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb) 239 { 240 DB *db; 241 BTREEINFO bti; 242 HASHINFO hashi; 243 bti.flags = 0; 244 bti.cachesize = 0; 245 bti.psize = 4096; 246 bti.lorder = 0; 247 bti.minkeypage = 0; 248 bti.compare = NULL; 249 bti.prefix = NULL; 250 251 if (tempdb) { 252 fname = gen_dbsuffix(fname, "~"); 253 } else { 254 fname = strdup(fname); 255 } 256 if (fname == NULL) 257 { 258 errno = ENOMEM; 259 return NULL; 260 } 261 262 263 hashi.bsize = 4096; 264 hashi.cachesize = 0; 265 hashi.ffactor = 40; 266 hashi.hash = NULL; 267 hashi.lorder = 0; 268 hashi.nelem = 1; 269 270 db = dbopen(fname, flags, mode, 271 dbc->hashfirst ? DB_HASH : DB_BTREE, 272 dbc->hashfirst ? (void *) &hashi : (void *) &bti); 273 if (db != NULL) { 274 free(fname); 275 return db; 276 } 277 switch (errno) { 278 #ifdef EFTYPE 279 case EFTYPE: 280 #endif 281 case EINVAL: 282 db = dbopen(fname, flags, mode, 283 dbc->hashfirst ? DB_BTREE : DB_HASH, 284 dbc->hashfirst ? (void *) &bti : (void *) &hashi); 285 if (db != NULL) 286 dbc->hashfirst = !dbc->hashfirst; 287 default: 288 free(fname); 289 return db; 290 } 291 } 292 293 static krb5_error_code 294 krb5_db2_db_set_hashfirst(krb5_context context, int hashfirst) 295 { 296 krb5_db2_context *dbc; 297 kdb5_dal_handle *dal_handle; 298 299 if (k5db2_inited(context)) 300 return KRB5_KDB_DBNOTINITED; 301 dal_handle = (kdb5_dal_handle *) context->db_context; 302 dbc = (krb5_db2_context *) dal_handle->db_context; 303 dbc->hashfirst = hashfirst; 304 return 0; 305 } 306 307 /* 308 * initialization for data base routines. 309 */ 310 311 krb5_error_code 312 krb5_db2_db_init(krb5_context context) 313 { 314 char *filename = NULL; 315 krb5_db2_context *db_ctx; 316 krb5_error_code retval; 317 kdb5_dal_handle *dal_handle; 318 char policy_db_name[1024], policy_lock_name[1024]; 319 320 if (k5db2_inited(context)) 321 return 0; 322 323 /* Check for presence of our context, if not present, allocate one. */ 324 if ((retval = k5db2_init_context(context))) 325 return (retval); 326 327 dal_handle = (kdb5_dal_handle *) context->db_context; 328 db_ctx = dal_handle->db_context; 329 db_ctx->db = NULL; 330 331 if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb 332 ?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT))) 333 return ENOMEM; 334 db_ctx->db_lf_name = filename; /* so it gets freed by clear_context */ 335 336 /* 337 * should be opened read/write so that write locking can work with 338 * POSIX systems 339 */ 340 if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) { 341 if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) { 342 retval = errno; 343 goto err_out; 344 } 345 } 346 db_ctx->db_inited++; 347 348 if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time))) 349 goto err_out; 350 351 sprintf(policy_db_name, db_ctx->tempdb ? "%s~.kadm5" : "%s.kadm5", 352 db_ctx->db_name); 353 sprintf(policy_lock_name, "%s.lock", policy_db_name); 354 355 if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name, 356 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC))) 357 { 358 goto err_out; 359 } 360 return 0; 361 362 err_out: 363 db_ctx->db = NULL; 364 k5db2_clear_context(db_ctx); 365 return (retval); 366 } 367 368 /* 369 * gracefully shut down database--must be called by ANY program that does 370 * a krb5_db2_db_init 371 */ 372 krb5_error_code 373 krb5_db2_db_fini(krb5_context context) 374 { 375 krb5_error_code retval = 0; 376 krb5_db2_context *db_ctx; 377 kdb5_dal_handle *dal_handle; 378 379 dal_handle = (kdb5_dal_handle *) context->db_context; 380 if (dal_handle == NULL) { 381 return 0; 382 } 383 384 db_ctx = (krb5_db2_context *) dal_handle->db_context; 385 386 if (k5db2_inited(context)) { 387 if (close(db_ctx->db_lf_file)) 388 retval = errno; 389 else 390 retval = 0; 391 } 392 if (db_ctx) { 393 if (db_ctx->policy_db) { 394 retval = 395 osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC); 396 if (retval) 397 return retval; 398 } 399 400 k5db2_clear_context(db_ctx); 401 /* free(dal_handle->db_context); */ 402 dal_handle->db_context = NULL; 403 } 404 return retval; 405 } 406 407 /* 408 * Set/Get the master key associated with the database 409 */ 410 krb5_error_code 411 krb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key) 412 { 413 krb5_db2_context *db_ctx; 414 kdb5_dal_handle *dal_handle; 415 416 if (!k5db2_inited(context)) 417 return (KRB5_KDB_DBNOTINITED); 418 419 dal_handle = (kdb5_dal_handle *) context->db_context; 420 db_ctx = dal_handle->db_context; 421 db_ctx->db_master_key = key; 422 return 0; 423 } 424 425 krb5_error_code 426 krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key) 427 { 428 krb5_db2_context *db_ctx; 429 kdb5_dal_handle *dal_handle; 430 431 if (!k5db2_inited(context)) 432 return (KRB5_KDB_DBNOTINITED); 433 434 dal_handle = (kdb5_dal_handle *) context->db_context; 435 db_ctx = dal_handle->db_context; 436 *key = db_ctx->db_master_key; 437 438 return 0; 439 } 440 441 /* 442 * Set the "name" of the current database to some alternate value. 443 * 444 * Passing a null pointer as "name" will set back to the default. 445 * If the alternate database doesn't exist, nothing is changed. 446 * 447 * XXX rethink this 448 */ 449 450 static krb5_error_code 451 krb5_db2_db_set_name(krb5_context context, char *name, int tempdb) 452 { 453 DB *db; 454 krb5_db2_context *db_ctx; 455 krb5_error_code kret; 456 kdb5_dal_handle *dal_handle; 457 458 if (k5db2_inited(context)) 459 return KRB5_KDB_DBINITED; 460 461 /* Check for presence of our context, if not present, allocate one. */ 462 if ((kret = k5db2_init_context(context))) 463 return (kret); 464 465 if (name == NULL) 466 name = default_db_name; 467 468 dal_handle = (kdb5_dal_handle *) context->db_context; 469 db_ctx = dal_handle->db_context; 470 db_ctx->tempdb = tempdb; 471 db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb); 472 if (db == NULL) 473 return errno; 474 475 db_ctx->db_name = strdup(name); 476 if (db_ctx->db_name == NULL) { 477 (*db->close) (db); 478 return ENOMEM; 479 } 480 (*db->close) (db); 481 return 0; 482 } 483 484 /* 485 * Return the last modification time of the database. 486 * 487 * Think about using fstat. 488 */ 489 490 krb5_error_code 491 krb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age) 492 { 493 krb5_db2_context *db_ctx; 494 kdb5_dal_handle *dal_handle; 495 struct stat st; 496 497 if (!k5db2_inited(context)) 498 return (KRB5_KDB_DBNOTINITED); 499 dal_handle = (kdb5_dal_handle *) context->db_context; 500 db_ctx = (krb5_db2_context *) dal_handle->db_context; 501 502 if (fstat(db_ctx->db_lf_file, &st) < 0) 503 *age = -1; 504 else 505 *age = st.st_mtime; 506 return 0; 507 } 508 509 /* 510 * Remove the semaphore file; indicates that database is currently 511 * under renovation. 512 * 513 * This is only for use when moving the database out from underneath 514 * the server (for example, during slave updates). 515 */ 516 517 static krb5_error_code 518 krb5_db2_db_start_update(krb5_context context) 519 { 520 return 0; 521 } 522 523 static krb5_error_code 524 krb5_db2_db_end_update(krb5_context context) 525 { 526 krb5_error_code retval; 527 krb5_db2_context *db_ctx; 528 kdb5_dal_handle *dal_handle; 529 struct stat st; 530 time_t now; 531 struct utimbuf utbuf; 532 533 if (!k5db2_inited(context)) 534 return (KRB5_KDB_DBNOTINITED); 535 536 retval = 0; 537 dal_handle = (kdb5_dal_handle *) context->db_context; 538 db_ctx = dal_handle->db_context; 539 now = time((time_t *) NULL); 540 if (fstat(db_ctx->db_lf_file, &st) == 0) { 541 if (st.st_mtime >= now) { 542 utbuf.actime = st.st_mtime + 1; 543 utbuf.modtime = st.st_mtime + 1; 544 if (utime(db_ctx->db_lf_name, &utbuf)) 545 retval = errno; 546 } else { 547 if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL)) 548 retval = errno; 549 } 550 } else 551 retval = errno; 552 if (!retval) { 553 if (fstat(db_ctx->db_lf_file, &st) == 0) 554 db_ctx->db_lf_time = st.st_mtime; 555 else 556 retval = errno; 557 } 558 return (retval); 559 } 560 561 #define MAX_LOCK_TRIES 5 562 563 krb5_error_code 564 krb5_db2_db_lock(krb5_context context, int in_mode) 565 { 566 krb5_db2_context *db_ctx; 567 int krb5_lock_mode; 568 DB *db; 569 krb5_error_code retval; 570 time_t mod_time; 571 kdb5_dal_handle *dal_handle; 572 int mode, gotlock, tries; 573 574 switch (in_mode) { 575 case KRB5_DB_LOCKMODE_PERMANENT: 576 mode = KRB5_DB_LOCKMODE_EXCLUSIVE; 577 break; 578 case KRB5_DB_LOCKMODE_EXCLUSIVE: 579 mode = KRB5_LOCKMODE_EXCLUSIVE; 580 break; 581 582 case KRB5_DB_LOCKMODE_SHARED: 583 mode = KRB5_LOCKMODE_SHARED; 584 break; 585 default: 586 return EINVAL; 587 } 588 589 if (!k5db2_inited(context)) 590 return KRB5_KDB_DBNOTINITED; 591 592 dal_handle = (kdb5_dal_handle *) context->db_context; 593 db_ctx = (krb5_db2_context *) dal_handle->db_context; 594 if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) { 595 /* No need to upgrade lock, just return */ 596 db_ctx->db_locks_held++; 597 goto policy_lock; 598 } 599 600 if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE)) 601 return KRB5_KDB_BADLOCKMODE; 602 603 krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK; 604 for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { 605 retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode); 606 if (retval == 0) { 607 gotlock++; 608 break; 609 } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) 610 /* tried to exclusive-lock something we don't have */ 611 /* write access to */ 612 return KRB5_KDB_CANTLOCK_DB; 613 sleep(1); 614 } 615 if (retval == EACCES) 616 return KRB5_KDB_CANTLOCK_DB; 617 else if (retval == EAGAIN || retval == EWOULDBLOCK) 618 return OSA_ADB_CANTLOCK_DB; 619 else if (retval != 0) 620 return retval; 621 622 if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time))) 623 goto lock_error; 624 625 db = k5db2_dbopen(db_ctx, db_ctx->db_name, 626 mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb); 627 if (db) { 628 db_ctx->db_lf_time = mod_time; 629 db_ctx->db = db; 630 } else { 631 retval = errno; 632 db_ctx->db = NULL; 633 goto lock_error; 634 } 635 636 db_ctx->db_lock_mode = mode; 637 db_ctx->db_locks_held++; 638 639 policy_lock: 640 if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) { 641 krb5_db2_db_unlock(context); 642 } 643 return retval; 644 645 lock_error:; 646 db_ctx->db_lock_mode = 0; 647 db_ctx->db_locks_held = 0; 648 krb5_db2_db_unlock(context); 649 return retval; 650 } 651 652 krb5_error_code 653 krb5_db2_db_unlock(krb5_context context) 654 { 655 krb5_db2_context *db_ctx; 656 kdb5_dal_handle *dal_handle; 657 DB *db; 658 krb5_error_code retval; 659 660 if (!k5db2_inited(context)) 661 return KRB5_KDB_DBNOTINITED; 662 663 dal_handle = (kdb5_dal_handle *) context->db_context; 664 db_ctx = (krb5_db2_context *) dal_handle->db_context; 665 666 if ((retval = osa_adb_release_lock(db_ctx->policy_db))) { 667 return retval; 668 } 669 670 if (!db_ctx->db_locks_held) /* lock already unlocked */ 671 return KRB5_KDB_NOTLOCKED; 672 db = db_ctx->db; 673 if (--(db_ctx->db_locks_held) == 0) { 674 (*db->close) (db); 675 db_ctx->db = NULL; 676 677 retval = krb5_lock_file(context, db_ctx->db_lf_file, 678 KRB5_LOCKMODE_UNLOCK); 679 db_ctx->db_lock_mode = 0; 680 return (retval); 681 } 682 return 0; 683 } 684 685 /* 686 * Create the database, assuming it's not there. 687 */ 688 krb5_error_code 689 krb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags) 690 { 691 register krb5_error_code retval = 0; 692 kdb5_dal_handle *dal_handle; 693 char *okname; 694 char *db_name2 = NULL; 695 int fd; 696 krb5_db2_context *db_ctx; 697 DB *db; 698 char policy_db_name[1024], policy_lock_name[1024]; 699 700 if ((retval = k5db2_init_context(context))) 701 return (retval); 702 703 dal_handle = (kdb5_dal_handle *) context->db_context; 704 db_ctx = (krb5_db2_context *) dal_handle->db_context; 705 switch (flags) { 706 case KRB5_KDB_CREATE_HASH: 707 if ((retval = krb5_db2_db_set_hashfirst(context, TRUE))) 708 return retval; 709 break; 710 case KRB5_KDB_CREATE_BTREE: 711 case 0: 712 if ((retval = krb5_db2_db_set_hashfirst(context, FALSE))) 713 return retval; 714 break; 715 default: 716 return KRB5_KDB_BAD_CREATEFLAGS; 717 } 718 db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb); 719 if (db == NULL) 720 retval = errno; 721 else 722 (*db->close) (db); 723 if (retval == 0) { 724 725 db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name); 726 if (db_name2 == NULL) 727 return ENOMEM; 728 okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT); 729 if (!okname) 730 retval = ENOMEM; 731 else { 732 fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600); 733 if (fd < 0) 734 retval = errno; 735 else 736 close(fd); 737 free_dbsuffix(okname); 738 } 739 } 740 741 sprintf(policy_db_name, "%s.kadm5", db_name2); 742 sprintf(policy_lock_name, "%s.lock", policy_db_name); 743 744 retval = osa_adb_create_db(policy_db_name, 745 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); 746 free(db_name2); 747 return retval; 748 } 749 750 /* 751 * Destroy the database. Zero's out all of the files, just to be sure. 752 */ 753 static krb5_error_code 754 destroy_file_suffix(char *dbname, char *suffix) 755 { 756 char *filename; 757 struct stat statb; 758 int nb, fd; 759 unsigned int j; 760 off_t pos; 761 char buf[BUFSIZ]; 762 char zbuf[BUFSIZ]; 763 int dowrite; 764 765 filename = gen_dbsuffix(dbname, suffix); 766 if (filename == 0) 767 return ENOMEM; 768 if ((fd = open(filename, O_RDWR, 0)) < 0) { 769 free(filename); 770 return errno; 771 } 772 /* fstat() will probably not fail unless using a remote filesystem 773 * (which is inappropriate for the kerberos database) so this check 774 * is mostly paranoia. */ 775 if (fstat(fd, &statb) == -1) { 776 int retval = errno; 777 free(filename); 778 return retval; 779 } 780 /* 781 * Stroll through the file, reading in BUFSIZ chunks. If everything 782 * is zero, then we're done for that block, otherwise, zero the block. 783 * We would like to just blast through everything, but some DB 784 * implementations make holey files and writing data to the holes 785 * causes actual blocks to be allocated which is no good, since 786 * we're just about to unlink it anyways. 787 */ 788 memset(zbuf, 0, BUFSIZ); 789 pos = 0; 790 while (pos < statb.st_size) { 791 dowrite = 0; 792 nb = read(fd, buf, BUFSIZ); 793 if (nb < 0) { 794 int retval = errno; 795 free(filename); 796 return retval; 797 } 798 for (j = 0; j < nb; j++) { 799 if (buf[j] != '\0') { 800 dowrite = 1; 801 break; 802 } 803 } 804 /* For signedness */ 805 j = nb; 806 if (dowrite) { 807 lseek(fd, pos, SEEK_SET); 808 nb = write(fd, zbuf, j); 809 if (nb < 0) { 810 int retval = errno; 811 free(filename); 812 return retval; 813 } 814 } 815 pos += nb; 816 } 817 /* ??? Is fsync really needed? I don't know of any non-networked 818 * filesystem which will discard queued writes to disk if a file 819 * is deleted after it is closed. --jfc */ 820 #ifndef NOFSYNC 821 fsync(fd); 822 #endif 823 close(fd); 824 825 if (unlink(filename)) { 826 free(filename); 827 return (errno); 828 } 829 free(filename); 830 return (0); 831 } 832 833 /* 834 * Since the destroy operation happens outside the init/fini bracket, we 835 * have some tomfoolery to undergo here. If we're operating under no 836 * database context, then we initialize with the default. If the caller 837 * wishes a different context (e.g. different dispatch table), it's their 838 * responsibility to call kdb5_db_set_dbops() before this call. That will 839 * set up the right dispatch table values (e.g. name extensions). 840 * 841 * Not quite valid due to ripping out of dbops... 842 */ 843 krb5_error_code 844 krb5_db2_db_destroy(krb5_context context, char *dbname) 845 { 846 krb5_error_code retval1, retval2; 847 krb5_boolean tmpcontext; 848 char policy_db_name[1024], policy_lock_name[1024]; 849 850 tmpcontext = 0; 851 if (!context->db_context 852 || !((kdb5_dal_handle *) context->db_context)->db_context) { 853 tmpcontext = 1; 854 if ((retval1 = k5db2_init_context(context))) 855 return (retval1); 856 } 857 858 retval1 = retval2 = 0; 859 retval1 = destroy_file_suffix(dbname, ""); 860 retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT); 861 862 if (tmpcontext) { 863 k5db2_clear_context((krb5_db2_context *) ((kdb5_dal_handle *) context-> 864 db_context)->db_context); 865 free(((kdb5_dal_handle *) context->db_context)->db_context); 866 ((kdb5_dal_handle *) context->db_context)->db_context = NULL; 867 } 868 869 if (retval1 || retval2) 870 return (retval1 ? retval1 : retval2); 871 872 873 assert (strlen(dbname) + strlen("%s.kadm5") < sizeof(policy_db_name)); 874 sprintf(policy_db_name, "%s.kadm5", dbname); 875 /* XXX finish this */ 876 sprintf(policy_lock_name, "%s.lock", policy_db_name); 877 878 retval1 = osa_adb_destroy_db(policy_db_name, 879 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); 880 881 return retval1; 882 } 883 884 /* 885 * look up a principal in the data base. 886 * returns number of entries found, and whether there were 887 * more than requested. 888 */ 889 890 krb5_error_code 891 krb5_db2_db_get_principal(krb5_context context, 892 krb5_const_principal searchfor, 893 krb5_db_entry *entries, /* filled in */ 894 int *nentries, /* how much room/how many found */ 895 krb5_boolean *more) /* are there more? */ 896 { 897 krb5_db2_context *db_ctx; 898 krb5_error_code retval; 899 DB *db; 900 DBT key, contents; 901 krb5_data keydata, contdata; 902 int trynum, dbret; 903 kdb5_dal_handle *dal_handle; 904 905 *more = FALSE; 906 *nentries = 0; 907 908 if (!k5db2_inited(context)) 909 return KRB5_KDB_DBNOTINITED; 910 911 dal_handle = (kdb5_dal_handle *) context->db_context; 912 db_ctx = (krb5_db2_context *) dal_handle->db_context; 913 914 for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) { 915 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) { 916 if (db_ctx->db_nb_locks) 917 return (retval); 918 sleep(1); 919 continue; 920 } 921 break; 922 } 923 if (trynum == KRB5_DB2_MAX_RETRY) 924 return KRB5_KDB_DB_INUSE; 925 926 /* XXX deal with wildcard lookups */ 927 retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); 928 if (retval) 929 goto cleanup; 930 key.data = keydata.data; 931 key.size = keydata.length; 932 933 db = db_ctx->db; 934 dbret = (*db->get) (db, &key, &contents, 0); 935 retval = errno; 936 krb5_free_data_contents(context, &keydata); 937 switch (dbret) { 938 case 1: 939 retval = 0; 940 /*LINTED*/ 941 case -1: 942 default: 943 *nentries = 0; 944 goto cleanup; 945 case 0: 946 contdata.data = contents.data; 947 contdata.length = contents.size; 948 retval = krb5_decode_princ_contents(context, &contdata, entries); 949 if (!retval) 950 *nentries = 1; 951 break; 952 } 953 954 cleanup: 955 (void) krb5_db2_db_unlock(context); /* unlock read lock */ 956 return retval; 957 } 958 959 /* 960 Free stuff returned by krb5_db2_db_get_principal. 961 */ 962 krb5_error_code 963 krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries, 964 int nentries) 965 { 966 register int i; 967 for (i = 0; i < nentries; i++) 968 krb5_dbe_free_contents(context, &entries[i]); 969 return 0; 970 } 971 972 /* 973 Stores the *"nentries" entry structures pointed to by "entries" in the 974 database. 975 976 *"nentries" is updated upon return to reflect the number of records 977 acutally stored; the first *"nstored" records will have been stored in the 978 database (even if an error occurs). 979 980 */ 981 982 krb5_error_code 983 krb5_db2_db_put_principal(krb5_context context, 984 krb5_db_entry *entries, 985 int *nentries, /* number of entry structs to update */ 986 char **db_args) 987 { 988 int i, n, dbret; 989 DB *db; 990 DBT key, contents; 991 krb5_data contdata, keydata; 992 krb5_error_code retval; 993 krb5_db2_context *db_ctx; 994 kdb5_dal_handle *dal_handle; 995 kdb_incr_update_t *upd, *fupd; 996 char *princ_name = NULL; 997 kdb_log_context *log_ctx; 998 999 krb5_clear_error_message (context); 1000 if (db_args) { 1001 /* DB2 does not support db_args DB arguments for principal */ 1002 krb5_set_error_message(context, EINVAL, 1003 gettext("Unsupported argument \"%s\" for db2"), 1004 db_args[0]); 1005 return EINVAL; 1006 } 1007 1008 log_ctx = context->kdblog_context; 1009 1010 n = *nentries; 1011 *nentries = 0; 1012 if (!k5db2_inited(context)) 1013 return KRB5_KDB_DBNOTINITED; 1014 1015 dal_handle = (kdb5_dal_handle *) context->db_context; 1016 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1017 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1018 return retval; 1019 1020 /* 1021 * Solaris Kerberos: We need the lock since ulog_conv_2logentry() does a get 1022 */ 1023 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1024 if (!(upd = (kdb_incr_update_t *) 1025 malloc(sizeof (kdb_incr_update_t)*n))) { 1026 retval = errno; 1027 goto err_lock; 1028 } 1029 fupd = upd; 1030 1031 (void) memset(upd, 0, sizeof(kdb_incr_update_t)*n); 1032 1033 if ((retval = ulog_conv_2logentry(context, entries, upd, n))) { 1034 goto err_lock; 1035 } 1036 } 1037 1038 db = db_ctx->db; 1039 if ((retval = krb5_db2_db_start_update(context))) { 1040 (void) krb5_db2_db_unlock(context); 1041 goto err_lock; 1042 } 1043 1044 /* for each one, stuff temps, and do replace/append */ 1045 for (i = 0; i < n; i++) { 1046 /* 1047 * Solaris Kerberos: We'll be sharing the same locks as db for logging 1048 */ 1049 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1050 if ((retval = krb5_unparse_name(context, entries->princ, 1051 &princ_name))) 1052 goto err_lock; 1053 1054 upd->kdb_princ_name.utf8str_t_val = princ_name; 1055 upd->kdb_princ_name.utf8str_t_len = strlen(princ_name); 1056 1057 if (retval = ulog_add_update(context, upd)) 1058 goto err_lock; 1059 } 1060 1061 retval = krb5_encode_princ_contents(context, &contdata, entries); 1062 if (retval) 1063 break; 1064 contents.data = contdata.data; 1065 contents.size = contdata.length; 1066 retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ); 1067 if (retval) { 1068 krb5_free_data_contents(context, &contdata); 1069 break; 1070 } 1071 1072 key.data = keydata.data; 1073 key.size = keydata.length; 1074 dbret = (*db->put) (db, &key, &contents, 0); 1075 retval = dbret ? errno : 0; 1076 krb5_free_data_contents(context, &keydata); 1077 krb5_free_data_contents(context, &contdata); 1078 if (retval) 1079 break; 1080 else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1081 /* 1082 * We need to make sure the db record is synced before we mark 1083 * it as committed via finish_update. 1084 */ 1085 dbret = (*db->sync)(db, 0); 1086 if (dbret) { 1087 retval = errno; 1088 goto err_lock; 1089 } 1090 (void) ulog_finish_update(context, upd); 1091 upd++; 1092 } 1093 entries++; /* bump to next struct */ 1094 } 1095 1096 (void) krb5_db2_db_end_update(context); 1097 1098 err_lock: 1099 (void) krb5_db2_db_unlock(context); /* unlock database */ 1100 1101 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) 1102 ulog_free_entries(fupd, n); 1103 1104 *nentries = i; 1105 return (retval); 1106 } 1107 1108 /* 1109 * delete a principal from the data base. 1110 * returns number of entries removed 1111 */ 1112 1113 krb5_error_code 1114 krb5_db2_db_delete_principal(krb5_context context, 1115 krb5_const_principal searchfor, 1116 int *nentries) /* how many found & deleted */ 1117 { 1118 krb5_error_code retval; 1119 krb5_db_entry entry; 1120 krb5_db2_context *db_ctx; 1121 DB *db; 1122 DBT key, contents; 1123 krb5_data keydata, contdata; 1124 int i, dbret; 1125 kdb5_dal_handle *dal_handle; 1126 kdb_incr_update_t upd; 1127 char *princ_name = NULL; 1128 kdb_log_context *log_ctx; 1129 1130 log_ctx = context->kdblog_context; 1131 1132 if (!k5db2_inited(context)) 1133 return KRB5_KDB_DBNOTINITED; 1134 1135 dal_handle = (kdb5_dal_handle *) context->db_context; 1136 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1137 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1138 return (retval); 1139 1140 if ((retval = krb5_db2_db_start_update(context))) { 1141 (void) krb5_db2_db_unlock(context); /* unlock write lock */ 1142 return (retval); 1143 } 1144 1145 if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) 1146 goto cleanup; 1147 key.data = keydata.data; 1148 key.size = keydata.length; 1149 1150 db = db_ctx->db; 1151 dbret = (*db->get) (db, &key, &contents, 0); 1152 retval = errno; 1153 switch (dbret) { 1154 case 1: 1155 retval = KRB5_KDB_NOENTRY; 1156 /*LINTED*/ 1157 case -1: 1158 default: 1159 *nentries = 0; 1160 goto cleankey; 1161 case 0: 1162 ; 1163 } 1164 /* 1165 * Solaris Kerberos: We'll be sharing the same locks as db for logging 1166 */ 1167 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1168 if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) { 1169 (void) krb5_db2_db_unlock(context); 1170 return retval; 1171 } 1172 1173 (void) memset(&upd, 0, sizeof (kdb_incr_update_t)); 1174 1175 upd.kdb_princ_name.utf8str_t_val = princ_name; 1176 upd.kdb_princ_name.utf8str_t_len = strlen(princ_name); 1177 1178 if (retval = ulog_delete_update(context, &upd)) { 1179 free(princ_name); 1180 (void) krb5_db2_db_unlock(context); 1181 return retval; 1182 } 1183 1184 free(princ_name); 1185 } 1186 1187 memset((char *) &entry, 0, sizeof(entry)); 1188 contdata.data = contents.data; 1189 contdata.length = contents.size; 1190 retval = krb5_decode_princ_contents(context, &contdata, &entry); 1191 if (retval) 1192 goto cleankey; 1193 *nentries = 1; 1194 1195 /* Clear encrypted key contents */ 1196 for (i = 0; i < entry.n_key_data; i++) { 1197 if (entry.key_data[i].key_data_length[0]) { 1198 memset((char *) entry.key_data[i].key_data_contents[0], 0, 1199 (unsigned) entry.key_data[i].key_data_length[0]); 1200 } 1201 } 1202 1203 retval = krb5_encode_princ_contents(context, &contdata, &entry); 1204 krb5_dbe_free_contents(context, &entry); 1205 if (retval) 1206 goto cleankey; 1207 1208 contents.data = contdata.data; 1209 contents.size = contdata.length; 1210 dbret = (*db->put) (db, &key, &contents, 0); 1211 retval = dbret ? errno : 0; 1212 krb5_free_data_contents(context, &contdata); 1213 if (retval) 1214 goto cleankey; 1215 dbret = (*db->del) (db, &key, 0); 1216 retval = dbret ? errno : 0; 1217 1218 /* 1219 * We need to commit our update upon success 1220 */ 1221 if (!retval) 1222 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) 1223 (void) ulog_finish_update(context, &upd); 1224 1225 cleankey: 1226 krb5_free_data_contents(context, &keydata); 1227 1228 cleanup: 1229 (void) krb5_db2_db_end_update(context); 1230 (void) krb5_db2_db_unlock(context); /* unlock write lock */ 1231 return retval; 1232 } 1233 1234 krb5_error_code 1235 krb5_db2_db_iterate_ext(krb5_context context, 1236 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), 1237 krb5_pointer func_arg, 1238 int backwards, int recursive) 1239 { 1240 krb5_db2_context *db_ctx; 1241 DB *db; 1242 DBT key, contents; 1243 krb5_data contdata; 1244 krb5_db_entry entries; 1245 krb5_error_code retval; 1246 kdb5_dal_handle *dal_handle; 1247 int dbret; 1248 void *cookie; 1249 1250 cookie = NULL; 1251 if (!k5db2_inited(context)) 1252 return KRB5_KDB_DBNOTINITED; 1253 1254 dal_handle = (kdb5_dal_handle *) context->db_context; 1255 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1256 retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED); 1257 1258 if (retval) 1259 return retval; 1260 1261 db = db_ctx->db; 1262 if (recursive && db->type != DB_BTREE) { 1263 (void) krb5_db2_db_unlock(context); 1264 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1265 } 1266 1267 if (!recursive) { 1268 dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST); 1269 } else { 1270 #ifdef HAVE_BT_RSEQ 1271 dbret = bt_rseq(db, &key, &contents, &cookie, 1272 backwards ? R_LAST : R_FIRST); 1273 #else 1274 (void) krb5_db2_db_unlock(context); 1275 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1276 #endif 1277 } 1278 while (dbret == 0) { 1279 contdata.data = contents.data; 1280 contdata.length = contents.size; 1281 retval = krb5_decode_princ_contents(context, &contdata, &entries); 1282 if (retval) 1283 break; 1284 retval = (*func) (func_arg, &entries); 1285 krb5_dbe_free_contents(context, &entries); 1286 if (retval) 1287 break; 1288 if (!recursive) { 1289 dbret = (*db->seq) (db, &key, &contents, 1290 backwards ? R_PREV : R_NEXT); 1291 } else { 1292 #ifdef HAVE_BT_RSEQ 1293 dbret = bt_rseq(db, &key, &contents, &cookie, 1294 backwards ? R_PREV : R_NEXT); 1295 #else 1296 (void) krb5_db2_db_unlock(context); 1297 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1298 #endif 1299 } 1300 } 1301 switch (dbret) { 1302 case 1: 1303 case 0: 1304 break; 1305 case -1: 1306 default: 1307 retval = errno; 1308 } 1309 (void) krb5_db2_db_unlock(context); 1310 return retval; 1311 } 1312 1313 krb5_error_code 1314 krb5_db2_db_iterate(krb5_context context, 1315 char *match_expr, 1316 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), 1317 krb5_pointer func_arg) 1318 { 1319 return krb5_db2_db_iterate_ext(context, func, func_arg, 0, 0); 1320 } 1321 1322 krb5_boolean 1323 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode) 1324 { 1325 krb5_boolean old; 1326 krb5_db2_context *db_ctx; 1327 kdb5_dal_handle *dal_handle; 1328 1329 dal_handle = (kdb5_dal_handle *) context->db_context; 1330 old = mode; 1331 if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) { 1332 old = db_ctx->db_nb_locks; 1333 db_ctx->db_nb_locks = mode; 1334 } 1335 return old; 1336 } 1337 1338 /* 1339 * DAL API functions 1340 */ 1341 krb5_error_code 1342 krb5_db2_lib_init() 1343 { 1344 return 0; 1345 } 1346 1347 krb5_error_code 1348 krb5_db2_lib_cleanup() 1349 { 1350 /* right now, no cleanup required */ 1351 return 0; 1352 } 1353 1354 krb5_error_code 1355 krb5_db2_open(krb5_context kcontext, 1356 char *conf_section, char **db_args, int mode) 1357 { 1358 krb5_error_code status = 0; 1359 char **t_ptr = db_args; 1360 int db_name_set = 0, tempdb=0; 1361 char *dbname = NULL; 1362 1363 krb5_clear_error_message (kcontext); 1364 1365 if (k5db2_inited(kcontext)) 1366 return 0; 1367 1368 while (t_ptr && *t_ptr) { 1369 char *opt = NULL, *val = NULL; 1370 1371 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1372 if (opt && !strcmp(opt, "dbname")) { 1373 if (dbname) free(dbname); 1374 dbname = strdup(val); 1375 } 1376 else if (!opt && !strcmp(val, "temporary") ) { 1377 tempdb = 1; 1378 } 1379 /* ignore hash argument. Might have been passed from create */ 1380 else if (!opt || strcmp(opt, "hash")) { 1381 krb5_set_error_message(kcontext, EINVAL, 1382 gettext("Unsupported argument \"%s\" for db2"), 1383 opt ? opt : val); 1384 free(opt); 1385 free(val); 1386 return EINVAL; 1387 } 1388 1389 free(opt); 1390 free(val); 1391 t_ptr++; 1392 } 1393 1394 if(dbname) { 1395 status = krb5_db2_db_set_name(kcontext, dbname, tempdb); 1396 free(dbname); 1397 if (status) { 1398 goto clean_n_exit; 1399 } 1400 db_name_set = 1; 1401 } 1402 if (!db_name_set) { 1403 char *value = NULL; 1404 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1405 NULL, &value); 1406 1407 if (value == NULL) { 1408 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1409 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1410 default_db_name, &value); 1411 if (status) { 1412 goto clean_n_exit; 1413 } 1414 } 1415 1416 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1417 profile_release_string(value); 1418 if (status) { 1419 goto clean_n_exit; 1420 } 1421 1422 } 1423 1424 status = krb5_db2_db_init(kcontext); 1425 1426 clean_n_exit: 1427 return status; 1428 } 1429 1430 krb5_error_code 1431 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args) 1432 { 1433 krb5_error_code status = 0; 1434 char **t_ptr = db_args; 1435 int db_name_set = 0, tempdb=0; 1436 krb5_int32 flags = KRB5_KDB_CREATE_BTREE; 1437 char *db_name = NULL; 1438 1439 krb5_clear_error_message (kcontext); 1440 1441 if (k5db2_inited(kcontext)) 1442 return 0; 1443 1444 while (t_ptr && *t_ptr) { 1445 char *opt = NULL, *val = NULL; 1446 1447 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1448 if (opt && !strcmp(opt, "dbname")) { 1449 db_name = strdup(val); 1450 if (db_name == NULL) 1451 return ENOMEM; 1452 } 1453 else if (!opt && !strcmp(val, "temporary")) { 1454 tempdb = 1; 1455 } 1456 else if (opt && !strcmp(opt, "hash")) { 1457 flags = KRB5_KDB_CREATE_HASH; 1458 } else { 1459 krb5_set_error_message(kcontext, EINVAL, 1460 gettext("Unsupported argument \"%s\" for db2"), 1461 opt ? opt : val); 1462 free(opt); 1463 free(val); 1464 return EINVAL; 1465 } 1466 1467 free(opt); 1468 free(val); 1469 t_ptr++; 1470 } 1471 if (db_name) { 1472 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1473 if (!status) { 1474 status = EEXIST; 1475 goto clean_n_exit; 1476 } 1477 db_name_set = 1; 1478 } 1479 if (!db_name_set) { 1480 char *value = NULL; 1481 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1482 KDB_MODULE_SECTION, conf_section, 1483 /* under given conf section */ 1484 KDB_DB2_DATABASE_NAME, NULL, &value); 1485 1486 if (value == NULL) { 1487 /* Special case for db2. We might actually be looking at 1488 * old type config file where database is specified as 1489 * part of realm. */ 1490 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1491 KDB_REALM_SECTION, 1492 KRB5_DB_GET_REALM(kcontext), 1493 /* under given realm */ 1494 KDB_DB2_DATABASE_NAME, 1495 default_db_name, &value); 1496 if (status) { 1497 goto clean_n_exit; 1498 } 1499 } 1500 1501 db_name = strdup(value); 1502 /* Solaris Kerberos: for safety */ 1503 if (db_name == NULL) { 1504 status = ENOMEM; 1505 goto clean_n_exit; 1506 } 1507 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1508 profile_release_string(value); 1509 if (!status) { 1510 status = EEXIST; 1511 goto clean_n_exit; 1512 } 1513 1514 } 1515 1516 status = krb5_db2_db_create(kcontext, db_name, flags); 1517 if (status) 1518 goto clean_n_exit; 1519 /* db2 has a problem of needing to close and open the database again. This removes that need */ 1520 status = krb5_db2_db_fini(kcontext); 1521 if (status) 1522 goto clean_n_exit; 1523 1524 status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW); 1525 1526 clean_n_exit: 1527 if (db_name) 1528 free(db_name); 1529 return status; 1530 } 1531 1532 krb5_error_code 1533 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args) 1534 { 1535 krb5_error_code status = 0; 1536 char **t_ptr = db_args; 1537 int db_name_set = 0, tempdb=0; 1538 char *db_name = NULL; 1539 1540 while (t_ptr && *t_ptr) { 1541 char *opt = NULL, *val = NULL; 1542 1543 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1544 if (opt && !strcmp(opt, "dbname")) { 1545 db_name = strdup(val); 1546 if (db_name == NULL) 1547 return ENOMEM; 1548 } 1549 else if (!opt && !strcmp(val, "temporary")) { 1550 tempdb = 1; 1551 } 1552 /* ignore hash argument. Might have been passed from create */ 1553 else if (!opt || strcmp(opt, "hash")) { 1554 free(opt); 1555 free(val); 1556 return EINVAL; 1557 } 1558 1559 free(opt); 1560 free(val); 1561 t_ptr++; 1562 } 1563 1564 if (db_name) { 1565 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1566 if (status) { 1567 goto clean_n_exit; 1568 } 1569 db_name_set = 1; 1570 } 1571 if (!db_name_set) { 1572 char *value = NULL; 1573 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1574 NULL, &value); 1575 1576 if (value == NULL) { 1577 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1578 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1579 default_db_name, &value); 1580 if (status) { 1581 goto clean_n_exit; 1582 } 1583 } 1584 1585 db_name = strdup(value); 1586 if (db_name == NULL) { 1587 status = ENOMEM; 1588 goto clean_n_exit; 1589 } 1590 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1591 profile_release_string(value); 1592 if (status) { 1593 goto clean_n_exit; 1594 } 1595 1596 } 1597 1598 status = krb5_db2_db_destroy(kcontext, db_name); 1599 1600 clean_n_exit: 1601 if (db_name) 1602 free(db_name); 1603 return status; 1604 } 1605 1606 krb5_error_code 1607 krb5_db2_set_master_key_ext(krb5_context kcontext, 1608 char *pwd, krb5_keyblock * key) 1609 { 1610 return krb5_db2_db_set_mkey(kcontext, key); 1611 } 1612 1613 krb5_error_code 1614 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value) 1615 { 1616 krb5_error_code status = 0; 1617 krb5_boolean oldval; 1618 krb5_db2_context *db_ctx; 1619 kdb5_dal_handle *dal_handle; 1620 1621 if (!k5db2_inited(kcontext)) 1622 return KRB5_KDB_DBNOTINITED; 1623 1624 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1625 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1626 1627 1628 switch (option) { 1629 case KRB5_KDB_OPT_SET_DB_NAME: 1630 status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb); 1631 break; 1632 1633 case KRB5_KDB_OPT_SET_LOCK_MODE: 1634 oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value)); 1635 *((krb5_boolean *) value) = oldval; 1636 break; 1637 1638 default: 1639 status = -1; /* TBD */ 1640 break; 1641 } 1642 1643 return status; 1644 } 1645 1646 void * 1647 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size) 1648 { 1649 return realloc(ptr, size); 1650 } 1651 1652 void 1653 krb5_db2_free(krb5_context kcontext, void *ptr) 1654 { 1655 free(ptr); 1656 } 1657 1658 /* policy functions */ 1659 krb5_error_code 1660 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy) 1661 { 1662 kdb5_dal_handle *dal_handle; 1663 krb5_db2_context *dbc; 1664 1665 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1666 dbc = (krb5_db2_context *) dal_handle->db_context; 1667 1668 return osa_adb_create_policy(dbc->policy_db, policy); 1669 } 1670 1671 krb5_error_code 1672 krb5_db2_get_policy(krb5_context kcontext, 1673 char *name, osa_policy_ent_t * policy, int *cnt) 1674 { 1675 kdb5_dal_handle *dal_handle; 1676 krb5_db2_context *dbc; 1677 1678 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1679 dbc = (krb5_db2_context *) dal_handle->db_context; 1680 1681 return osa_adb_get_policy(dbc->policy_db, name, policy, cnt); 1682 } 1683 1684 krb5_error_code 1685 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy) 1686 { 1687 kdb5_dal_handle *dal_handle; 1688 krb5_db2_context *dbc; 1689 1690 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1691 dbc = (krb5_db2_context *) dal_handle->db_context; 1692 1693 return osa_adb_put_policy(dbc->policy_db, policy); 1694 } 1695 1696 krb5_error_code 1697 krb5_db2_iter_policy(krb5_context kcontext, 1698 char *match_entry, 1699 osa_adb_iter_policy_func func, void *data) 1700 { 1701 kdb5_dal_handle *dal_handle; 1702 krb5_db2_context *dbc; 1703 1704 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1705 dbc = (krb5_db2_context *) dal_handle->db_context; 1706 1707 return osa_adb_iter_policy(dbc->policy_db, func, data); 1708 } 1709 1710 krb5_error_code 1711 krb5_db2_delete_policy(krb5_context kcontext, char *policy) 1712 { 1713 kdb5_dal_handle *dal_handle; 1714 krb5_db2_context *dbc; 1715 1716 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1717 dbc = (krb5_db2_context *) dal_handle->db_context; 1718 1719 return osa_adb_destroy_policy(dbc->policy_db, policy); 1720 } 1721 1722 void 1723 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry) 1724 { 1725 osa_free_policy_ent(entry); 1726 } 1727 1728 1729 /* */ 1730 1731 krb5_error_code 1732 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args) 1733 { 1734 krb5_error_code status = 0; 1735 char *db_name = NULL; 1736 char *temp_db_name = NULL; 1737 1738 krb5_clear_error_message (kcontext); 1739 1740 { 1741 kdb5_dal_handle *dal_handle = kcontext->db_context; 1742 krb5_db2_context *db_ctx = dal_handle->db_context; 1743 db_name = strdup(db_ctx->db_name); 1744 if (db_name == NULL) { 1745 status = ENOMEM; 1746 goto clean_n_exit; 1747 } 1748 } 1749 1750 assert(kcontext->db_context != NULL); 1751 temp_db_name = gen_dbsuffix(db_name, "~"); 1752 if (temp_db_name == NULL) { 1753 status = ENOMEM; 1754 goto clean_n_exit; 1755 } 1756 1757 status = krb5_db2_db_rename (kcontext, temp_db_name, db_name); 1758 1759 clean_n_exit: 1760 if (db_name) 1761 free(db_name); 1762 if (temp_db_name) 1763 free(temp_db_name); 1764 return status; 1765 } 1766 1767 /* Retrieved from pre-DAL code base. */ 1768 /* 1769 * "Atomically" rename the database in a way that locks out read 1770 * access in the middle of the rename. 1771 * 1772 * Not perfect; if we crash in the middle of an update, we don't 1773 * necessarily know to complete the transaction the rename, but... 1774 * 1775 * Since the rename operation happens outside the init/fini bracket, we 1776 * have to go through the same stuff that we went through up in db_destroy. 1777 */ 1778 krb5_error_code 1779 krb5_db2_db_rename(context, from, to) 1780 krb5_context context; 1781 char *from; 1782 char *to; 1783 { 1784 char *fromok; 1785 krb5_error_code retval; 1786 krb5_db2_context *s_context, *db_ctx; 1787 kdb5_dal_handle *dal_handle = context->db_context; 1788 1789 s_context = dal_handle->db_context; 1790 dal_handle->db_context = NULL; 1791 if ((retval = k5db2_init_context(context))) 1792 return retval; 1793 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1794 1795 /* 1796 * Create the database if it does not already exist; the 1797 * files must exist because krb5_db2_db_lock, called below, 1798 * will fail otherwise. 1799 */ 1800 { 1801 struct stat statbuf; 1802 1803 if (stat(to, &statbuf) == -1) { 1804 if (errno == ENOENT) { 1805 retval = krb5_db2_db_create(context, to, 1806 KRB5_KDB_CREATE_BTREE); 1807 if (retval) 1808 goto errout; 1809 } 1810 else { 1811 /* 1812 * XXX assuming we should bail if there is some other stat error 1813 */ 1814 retval = errno; 1815 goto errout; 1816 } 1817 } 1818 } 1819 /* 1820 * Set the database to the target, so that other processes sharing 1821 * the target will stop their activity, and notice the new database. 1822 */ 1823 retval = krb5_db2_db_set_name(context, to, 0); 1824 if (retval) 1825 goto errout; 1826 1827 retval = krb5_db2_db_init(context); 1828 if (retval) 1829 goto errout; 1830 1831 /* XXX WAF this needs to be redone (not lock safe)!!! */ 1832 { 1833 /* Ugly brute force hack. 1834 1835 Should be going through nice friendly helper routines for 1836 this, but it's a mess of jumbled so-called interfaces right 1837 now. */ 1838 char policy[2048], new_policy[2048]; 1839 assert (strlen(db_ctx->db_name) < 2000); 1840 /*LINTED*/ 1841 sprintf(policy, "%s.kadm5", db_ctx->db_name); 1842 /*LINTED*/ 1843 sprintf(new_policy, "%s~.kadm5", db_ctx->db_name); 1844 if (0 != rename(new_policy, policy)) { 1845 retval = errno; 1846 goto errout; 1847 } 1848 strcat(new_policy, ".lock"); 1849 (void) unlink(new_policy); 1850 } 1851 1852 retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time); 1853 if (retval) 1854 goto errout; 1855 1856 fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); 1857 if (fromok == NULL) { 1858 retval = ENOMEM; 1859 goto errout; 1860 } 1861 1862 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1863 goto errfromok; 1864 1865 if ((retval = krb5_db2_db_start_update(context))) 1866 goto errfromok; 1867 1868 if (rename(from, to)) { 1869 retval = errno; 1870 goto errfromok; 1871 } 1872 if (unlink(fromok)) { 1873 retval = errno; 1874 goto errfromok; 1875 } 1876 retval = krb5_db2_db_end_update(context); 1877 errfromok: 1878 free_dbsuffix(fromok); 1879 errout: 1880 if (dal_handle->db_context) { 1881 if (db_ctx->db_lf_file >= 0) { 1882 krb5_db2_db_unlock(context); 1883 close(db_ctx->db_lf_file); 1884 } 1885 k5db2_clear_context((krb5_db2_context *) dal_handle->db_context); 1886 free(dal_handle->db_context); 1887 } 1888 1889 dal_handle->db_context = s_context; 1890 (void) krb5_db2_db_unlock(context); /* unlock saved context db */ 1891 1892 return retval; 1893 } 1894