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