1 /* 2 * Copyright 2008 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, char **db_args) 1318 { 1319 char **t_ptr = db_args; 1320 int backwards = 0, recursive = 0; 1321 1322 while (t_ptr && *t_ptr) { 1323 char *opt = NULL, *val = NULL; 1324 1325 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1326 1327 /* Solaris Kerberos: adding support for -rev/recurse flags */ 1328 if (val && !strcmp(val, "rev")) 1329 backwards = 1; 1330 else if (val && !strcmp(val, "recurse")) 1331 recursive = 1; 1332 else { 1333 krb5_set_error_message(context, EINVAL, 1334 gettext("Unsupported argument \"%s\" for db2"), 1335 val); 1336 free(opt); 1337 free(val); 1338 return EINVAL; 1339 } 1340 1341 free(opt); 1342 free(val); 1343 t_ptr++; 1344 } 1345 1346 /* Solaris Kerberos: adding support for -rev/recurse flags */ 1347 return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive); 1348 } 1349 1350 krb5_boolean 1351 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode) 1352 { 1353 krb5_boolean old; 1354 krb5_db2_context *db_ctx; 1355 kdb5_dal_handle *dal_handle; 1356 1357 dal_handle = (kdb5_dal_handle *) context->db_context; 1358 old = mode; 1359 if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) { 1360 old = db_ctx->db_nb_locks; 1361 db_ctx->db_nb_locks = mode; 1362 } 1363 return old; 1364 } 1365 1366 /* 1367 * DAL API functions 1368 */ 1369 krb5_error_code 1370 krb5_db2_lib_init() 1371 { 1372 return 0; 1373 } 1374 1375 krb5_error_code 1376 krb5_db2_lib_cleanup() 1377 { 1378 /* right now, no cleanup required */ 1379 return 0; 1380 } 1381 1382 krb5_error_code 1383 krb5_db2_open(krb5_context kcontext, 1384 char *conf_section, char **db_args, int mode) 1385 { 1386 krb5_error_code status = 0; 1387 char **t_ptr = db_args; 1388 int db_name_set = 0, tempdb=0; 1389 char *dbname = NULL; 1390 1391 krb5_clear_error_message (kcontext); 1392 1393 if (k5db2_inited(kcontext)) 1394 return 0; 1395 1396 while (t_ptr && *t_ptr) { 1397 char *opt = NULL, *val = NULL; 1398 1399 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1400 if (opt && !strcmp(opt, "dbname")) { 1401 if (dbname) free(dbname); 1402 dbname = strdup(val); 1403 } 1404 else if (!opt && !strcmp(val, "temporary") ) { 1405 tempdb = 1; 1406 } 1407 /* ignore hash argument. Might have been passed from create */ 1408 else if (!opt || strcmp(opt, "hash")) { 1409 krb5_set_error_message(kcontext, EINVAL, 1410 gettext("Unsupported argument \"%s\" for db2"), 1411 opt ? opt : val); 1412 free(opt); 1413 free(val); 1414 return EINVAL; 1415 } 1416 1417 free(opt); 1418 free(val); 1419 t_ptr++; 1420 } 1421 1422 if(dbname) { 1423 status = krb5_db2_db_set_name(kcontext, dbname, tempdb); 1424 free(dbname); 1425 if (status) { 1426 goto clean_n_exit; 1427 } 1428 db_name_set = 1; 1429 } 1430 if (!db_name_set) { 1431 char *value = NULL; 1432 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1433 NULL, &value); 1434 1435 if (value == NULL) { 1436 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1437 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1438 default_db_name, &value); 1439 if (status) { 1440 goto clean_n_exit; 1441 } 1442 } 1443 1444 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1445 profile_release_string(value); 1446 if (status) { 1447 goto clean_n_exit; 1448 } 1449 1450 } 1451 1452 status = krb5_db2_db_init(kcontext); 1453 1454 clean_n_exit: 1455 return status; 1456 } 1457 1458 krb5_error_code 1459 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args) 1460 { 1461 krb5_error_code status = 0; 1462 char **t_ptr = db_args; 1463 int db_name_set = 0, tempdb=0; 1464 krb5_int32 flags = KRB5_KDB_CREATE_BTREE; 1465 char *db_name = NULL; 1466 1467 krb5_clear_error_message (kcontext); 1468 1469 if (k5db2_inited(kcontext)) 1470 return 0; 1471 1472 while (t_ptr && *t_ptr) { 1473 char *opt = NULL, *val = NULL; 1474 1475 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1476 if (opt && !strcmp(opt, "dbname")) { 1477 db_name = strdup(val); 1478 if (db_name == NULL) 1479 return ENOMEM; 1480 } 1481 else if (!opt && !strcmp(val, "temporary")) { 1482 tempdb = 1; 1483 } 1484 else if (opt && !strcmp(opt, "hash")) { 1485 flags = KRB5_KDB_CREATE_HASH; 1486 } else { 1487 krb5_set_error_message(kcontext, EINVAL, 1488 gettext("Unsupported argument \"%s\" for db2"), 1489 opt ? opt : val); 1490 free(opt); 1491 free(val); 1492 return EINVAL; 1493 } 1494 1495 free(opt); 1496 free(val); 1497 t_ptr++; 1498 } 1499 if (db_name) { 1500 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1501 if (!status) { 1502 status = EEXIST; 1503 goto clean_n_exit; 1504 } 1505 db_name_set = 1; 1506 } 1507 if (!db_name_set) { 1508 char *value = NULL; 1509 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1510 KDB_MODULE_SECTION, conf_section, 1511 /* under given conf section */ 1512 KDB_DB2_DATABASE_NAME, NULL, &value); 1513 1514 if (value == NULL) { 1515 /* Special case for db2. We might actually be looking at 1516 * old type config file where database is specified as 1517 * part of realm. */ 1518 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1519 KDB_REALM_SECTION, 1520 KRB5_DB_GET_REALM(kcontext), 1521 /* under given realm */ 1522 KDB_DB2_DATABASE_NAME, 1523 default_db_name, &value); 1524 if (status) { 1525 goto clean_n_exit; 1526 } 1527 } 1528 1529 db_name = strdup(value); 1530 /* Solaris Kerberos: for safety */ 1531 if (db_name == NULL) { 1532 status = ENOMEM; 1533 goto clean_n_exit; 1534 } 1535 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1536 profile_release_string(value); 1537 if (!status) { 1538 status = EEXIST; 1539 goto clean_n_exit; 1540 } 1541 1542 } 1543 1544 status = krb5_db2_db_create(kcontext, db_name, flags); 1545 if (status) 1546 goto clean_n_exit; 1547 /* db2 has a problem of needing to close and open the database again. This removes that need */ 1548 status = krb5_db2_db_fini(kcontext); 1549 if (status) 1550 goto clean_n_exit; 1551 1552 status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW); 1553 1554 clean_n_exit: 1555 if (db_name) 1556 free(db_name); 1557 return status; 1558 } 1559 1560 krb5_error_code 1561 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args) 1562 { 1563 krb5_error_code status = 0; 1564 char **t_ptr = db_args; 1565 int db_name_set = 0, tempdb=0; 1566 char *db_name = NULL; 1567 1568 while (t_ptr && *t_ptr) { 1569 char *opt = NULL, *val = NULL; 1570 1571 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1572 if (opt && !strcmp(opt, "dbname")) { 1573 db_name = strdup(val); 1574 if (db_name == NULL) 1575 return ENOMEM; 1576 } 1577 else if (!opt && !strcmp(val, "temporary")) { 1578 tempdb = 1; 1579 } 1580 /* ignore hash argument. Might have been passed from create */ 1581 else if (!opt || strcmp(opt, "hash")) { 1582 free(opt); 1583 free(val); 1584 return EINVAL; 1585 } 1586 1587 free(opt); 1588 free(val); 1589 t_ptr++; 1590 } 1591 1592 if (db_name) { 1593 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1594 if (status) { 1595 goto clean_n_exit; 1596 } 1597 db_name_set = 1; 1598 } 1599 if (!db_name_set) { 1600 char *value = NULL; 1601 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1602 NULL, &value); 1603 1604 if (value == NULL) { 1605 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1606 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1607 default_db_name, &value); 1608 if (status) { 1609 goto clean_n_exit; 1610 } 1611 } 1612 1613 db_name = strdup(value); 1614 if (db_name == NULL) { 1615 status = ENOMEM; 1616 goto clean_n_exit; 1617 } 1618 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1619 profile_release_string(value); 1620 if (status) { 1621 goto clean_n_exit; 1622 } 1623 1624 } 1625 1626 status = krb5_db2_db_destroy(kcontext, db_name); 1627 1628 clean_n_exit: 1629 if (db_name) 1630 free(db_name); 1631 return status; 1632 } 1633 1634 krb5_error_code 1635 krb5_db2_set_master_key_ext(krb5_context kcontext, 1636 char *pwd, krb5_keyblock * key) 1637 { 1638 return krb5_db2_db_set_mkey(kcontext, key); 1639 } 1640 1641 krb5_error_code 1642 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value) 1643 { 1644 krb5_error_code status = 0; 1645 krb5_boolean oldval; 1646 krb5_db2_context *db_ctx; 1647 kdb5_dal_handle *dal_handle; 1648 1649 if (!k5db2_inited(kcontext)) 1650 return KRB5_KDB_DBNOTINITED; 1651 1652 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1653 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1654 1655 1656 switch (option) { 1657 case KRB5_KDB_OPT_SET_DB_NAME: 1658 status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb); 1659 break; 1660 1661 case KRB5_KDB_OPT_SET_LOCK_MODE: 1662 oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value)); 1663 *((krb5_boolean *) value) = oldval; 1664 break; 1665 1666 default: 1667 status = -1; /* TBD */ 1668 break; 1669 } 1670 1671 return status; 1672 } 1673 1674 void * 1675 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size) 1676 { 1677 return realloc(ptr, size); 1678 } 1679 1680 void 1681 krb5_db2_free(krb5_context kcontext, void *ptr) 1682 { 1683 free(ptr); 1684 } 1685 1686 /* policy functions */ 1687 krb5_error_code 1688 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy) 1689 { 1690 kdb5_dal_handle *dal_handle; 1691 krb5_db2_context *dbc; 1692 1693 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1694 dbc = (krb5_db2_context *) dal_handle->db_context; 1695 1696 return osa_adb_create_policy(dbc->policy_db, policy); 1697 } 1698 1699 krb5_error_code 1700 krb5_db2_get_policy(krb5_context kcontext, 1701 char *name, osa_policy_ent_t * policy, int *cnt) 1702 { 1703 kdb5_dal_handle *dal_handle; 1704 krb5_db2_context *dbc; 1705 1706 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1707 dbc = (krb5_db2_context *) dal_handle->db_context; 1708 1709 return osa_adb_get_policy(dbc->policy_db, name, policy, cnt); 1710 } 1711 1712 krb5_error_code 1713 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy) 1714 { 1715 kdb5_dal_handle *dal_handle; 1716 krb5_db2_context *dbc; 1717 1718 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1719 dbc = (krb5_db2_context *) dal_handle->db_context; 1720 1721 return osa_adb_put_policy(dbc->policy_db, policy); 1722 } 1723 1724 krb5_error_code 1725 krb5_db2_iter_policy(krb5_context kcontext, 1726 char *match_entry, 1727 osa_adb_iter_policy_func func, void *data) 1728 { 1729 kdb5_dal_handle *dal_handle; 1730 krb5_db2_context *dbc; 1731 1732 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1733 dbc = (krb5_db2_context *) dal_handle->db_context; 1734 1735 return osa_adb_iter_policy(dbc->policy_db, func, data); 1736 } 1737 1738 krb5_error_code 1739 krb5_db2_delete_policy(krb5_context kcontext, char *policy) 1740 { 1741 kdb5_dal_handle *dal_handle; 1742 krb5_db2_context *dbc; 1743 1744 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1745 dbc = (krb5_db2_context *) dal_handle->db_context; 1746 1747 return osa_adb_destroy_policy(dbc->policy_db, policy); 1748 } 1749 1750 void 1751 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry) 1752 { 1753 osa_free_policy_ent(entry); 1754 } 1755 1756 1757 /* */ 1758 1759 krb5_error_code 1760 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args) 1761 { 1762 krb5_error_code status = 0; 1763 char *db_name = NULL; 1764 char *temp_db_name = NULL; 1765 1766 krb5_clear_error_message (kcontext); 1767 1768 { 1769 kdb5_dal_handle *dal_handle = kcontext->db_context; 1770 krb5_db2_context *db_ctx = dal_handle->db_context; 1771 db_name = strdup(db_ctx->db_name); 1772 if (db_name == NULL) { 1773 status = ENOMEM; 1774 goto clean_n_exit; 1775 } 1776 } 1777 1778 assert(kcontext->db_context != NULL); 1779 temp_db_name = gen_dbsuffix(db_name, "~"); 1780 if (temp_db_name == NULL) { 1781 status = ENOMEM; 1782 goto clean_n_exit; 1783 } 1784 1785 status = krb5_db2_db_rename (kcontext, temp_db_name, db_name); 1786 1787 clean_n_exit: 1788 if (db_name) 1789 free(db_name); 1790 if (temp_db_name) 1791 free(temp_db_name); 1792 return status; 1793 } 1794 1795 /* Retrieved from pre-DAL code base. */ 1796 /* 1797 * "Atomically" rename the database in a way that locks out read 1798 * access in the middle of the rename. 1799 * 1800 * Not perfect; if we crash in the middle of an update, we don't 1801 * necessarily know to complete the transaction the rename, but... 1802 * 1803 * Since the rename operation happens outside the init/fini bracket, we 1804 * have to go through the same stuff that we went through up in db_destroy. 1805 */ 1806 krb5_error_code 1807 krb5_db2_db_rename(context, from, to) 1808 krb5_context context; 1809 char *from; 1810 char *to; 1811 { 1812 char *fromok; 1813 krb5_error_code retval; 1814 krb5_db2_context *s_context, *db_ctx; 1815 kdb5_dal_handle *dal_handle = context->db_context; 1816 1817 s_context = dal_handle->db_context; 1818 dal_handle->db_context = NULL; 1819 if ((retval = k5db2_init_context(context))) 1820 return retval; 1821 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1822 1823 /* 1824 * Create the database if it does not already exist; the 1825 * files must exist because krb5_db2_db_lock, called below, 1826 * will fail otherwise. 1827 */ 1828 { 1829 struct stat statbuf; 1830 1831 if (stat(to, &statbuf) == -1) { 1832 if (errno == ENOENT) { 1833 retval = krb5_db2_db_create(context, to, 1834 KRB5_KDB_CREATE_BTREE); 1835 if (retval) 1836 goto errout; 1837 } 1838 else { 1839 /* 1840 * XXX assuming we should bail if there is some other stat error 1841 */ 1842 retval = errno; 1843 goto errout; 1844 } 1845 } 1846 } 1847 /* 1848 * Set the database to the target, so that other processes sharing 1849 * the target will stop their activity, and notice the new database. 1850 */ 1851 retval = krb5_db2_db_set_name(context, to, 0); 1852 if (retval) 1853 goto errout; 1854 1855 retval = krb5_db2_db_init(context); 1856 if (retval) 1857 goto errout; 1858 1859 /* XXX WAF this needs to be redone (not lock safe)!!! */ 1860 { 1861 /* Ugly brute force hack. 1862 1863 Should be going through nice friendly helper routines for 1864 this, but it's a mess of jumbled so-called interfaces right 1865 now. */ 1866 char policy[2048], new_policy[2048]; 1867 assert (strlen(db_ctx->db_name) < 2000); 1868 /*LINTED*/ 1869 sprintf(policy, "%s.kadm5", db_ctx->db_name); 1870 /*LINTED*/ 1871 sprintf(new_policy, "%s~.kadm5", db_ctx->db_name); 1872 if (0 != rename(new_policy, policy)) { 1873 retval = errno; 1874 goto errout; 1875 } 1876 strcat(new_policy, ".lock"); 1877 (void) unlink(new_policy); 1878 } 1879 1880 retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time); 1881 if (retval) 1882 goto errout; 1883 1884 fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); 1885 if (fromok == NULL) { 1886 retval = ENOMEM; 1887 goto errout; 1888 } 1889 1890 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1891 goto errfromok; 1892 1893 if ((retval = krb5_db2_db_start_update(context))) 1894 goto errfromok; 1895 1896 if (rename(from, to)) { 1897 retval = errno; 1898 goto errfromok; 1899 } 1900 if (unlink(fromok)) { 1901 retval = errno; 1902 goto errfromok; 1903 } 1904 retval = krb5_db2_db_end_update(context); 1905 errfromok: 1906 free_dbsuffix(fromok); 1907 errout: 1908 if (dal_handle->db_context) { 1909 if (db_ctx->db_lf_file >= 0) { 1910 krb5_db2_db_unlock(context); 1911 close(db_ctx->db_lf_file); 1912 } 1913 k5db2_clear_context((krb5_db2_context *) dal_handle->db_context); 1914 free(dal_handle->db_context); 1915 } 1916 1917 dal_handle->db_context = s_context; 1918 (void) krb5_db2_db_unlock(context); /* unlock saved context db */ 1919 1920 return retval; 1921 } 1922