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