1 /* 2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /* 9 * lib/kdb/kdb_db2.c 10 * 11 * Copyright 1997,2006 by the Massachusetts Institute of Technology. 12 * All Rights Reserved. 13 * 14 * Export of this software from the United States of America may 15 * require a specific license from the United States Government. 16 * It is the responsibility of any person or organization contemplating 17 * export to obtain such a license before exporting. 18 * 19 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 20 * distribute this software and its documentation for any purpose and 21 * without fee is hereby granted, provided that the above copyright 22 * notice appear in all copies and that both that copyright notice and 23 * this permission notice appear in supporting documentation, and that 24 * the name of M.I.T. not be used in advertising or publicity pertaining 25 * to distribution of the software without specific, written prior 26 * permission. Furthermore if you modify this software you must label 27 * your software as modified software and not distribute it in such a 28 * fashion that it might be confused with the original M.I.T. software. 29 * M.I.T. makes no representations about the suitability of 30 * this software for any purpose. It is provided "as is" without express 31 * or implied warranty. 32 * 33 */ 34 35 /* 36 * Copyright (C) 1998 by the FundsXpress, INC. 37 * 38 * All rights reserved. 39 * 40 * Export of this software from the United States of America may require 41 * a specific license from the United States Government. It is the 42 * responsibility of any person or organization contemplating export to 43 * obtain such a license before exporting. 44 * 45 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 46 * distribute this software and its documentation for any purpose and 47 * without fee is hereby granted, provided that the above copyright 48 * notice appear in all copies and that both that copyright notice and 49 * this permission notice appear in supporting documentation, and that 50 * the name of FundsXpress. not be used in advertising or publicity pertaining 51 * to distribution of the software without specific, written prior 52 * permission. FundsXpress makes no representations about the suitability of 53 * this software for any purpose. It is provided "as is" without express 54 * or implied warranty. 55 * 56 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 57 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 58 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 59 */ 60 61 #include "k5-int.h" 62 #include <kdb_log.h> 63 64 #if HAVE_UNISTD_H 65 #include <unistd.h> 66 #endif 67 68 #include <db.h> 69 #include <stdio.h> 70 #include <errno.h> 71 #include <utime.h> 72 #include <kdb5.h> 73 #include "kdb_db2.h" 74 #include "kdb_xdr.h" 75 #include "policy_db.h" 76 #include <libintl.h> 77 78 #define KDB_DB2_DATABASE_NAME "database_name" 79 80 #include "kdb_db2.h" 81 82 static char *gen_dbsuffix(char *, char *); 83 84 static krb5_error_code krb5_db2_db_start_update(krb5_context); 85 static krb5_error_code krb5_db2_db_end_update(krb5_context); 86 87 static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int); 88 89 krb5_error_code krb5_db2_db_lock(krb5_context, int); 90 91 static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int); 92 93 /* 94 * Solaris Kerberos 95 * Extra error handling 96 */ 97 char errbuf[1024]; 98 static void krb5_db2_prepend_err_str(krb5_context , const char *, 99 krb5_error_code, krb5_error_code); 100 101 static char default_db_name[] = DEFAULT_KDB_FILE; 102 103 /* 104 * Locking: 105 * 106 * There are two distinct locking protocols used. One is designed to 107 * lock against processes (the admin_server, for one) which make 108 * incremental changes to the database; the other is designed to lock 109 * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the 110 * entire database in one fell swoop. 111 * 112 * The first locking protocol is implemented using flock() in the 113 * krb_dbl_lock() and krb_dbl_unlock routines. 114 * 115 * The second locking protocol is necessary because DBM "files" are 116 * actually implemented as two separate files, and it is impossible to 117 * atomically rename two files simultaneously. It assumes that the 118 * database is replaced only very infrequently in comparison to the time 119 * needed to do a database read operation. 120 * 121 * A third file is used as a "version" semaphore; the modification 122 * time of this file is the "version number" of the database. 123 * At the start of a read operation, the reader checks the version 124 * number; at the end of the read operation, it checks again. If the 125 * version number changed, or if the semaphore was nonexistant at 126 * either time, the reader sleeps for a second to let things 127 * stabilize, and then tries again; if it does not succeed after 128 * KRB5_DBM_MAX_RETRY attempts, it gives up. 129 * 130 * On update, the semaphore file is deleted (if it exists) before any 131 * update takes place; at the end of the update, it is replaced, with 132 * a version number strictly greater than the version number which 133 * existed at the start of the update. 134 * 135 * If the system crashes in the middle of an update, the semaphore 136 * file is not automatically created on reboot; this is a feature, not 137 * a bug, since the database may be inconsistant. Note that the 138 * absence of a semaphore file does not prevent another _update_ from 139 * taking place later. Database replacements take place automatically 140 * only on slave servers; a crash in the middle of an update will be 141 * fixed by the next slave propagation. A crash in the middle of an 142 * update on the master would be somewhat more serious, but this would 143 * likely be noticed by an administrator, who could fix the problem and 144 * retry the operation. 145 */ 146 147 #define free_dbsuffix(name) free(name) 148 149 /* 150 * Routines to deal with context. 151 */ 152 #define k5db2_inited(c) (c && c->db_context \ 153 && ((kdb5_dal_handle*)c->db_context)->db_context \ 154 && ((krb5_db2_context *) ((kdb5_dal_handle*)c->db_context)->db_context)->db_inited) 155 156 static krb5_error_code 157 krb5_db2_get_db_opt(char *input, char **opt, char **val) 158 { 159 char *pos = strchr(input, '='); 160 if (pos == NULL) { 161 *opt = NULL; 162 *val = strdup(input); 163 if (*val == NULL) { 164 return ENOMEM; 165 } 166 } else { 167 *opt = malloc((pos - input) + 1); 168 *val = strdup(pos + 1); 169 if (!*opt || !*val) { 170 return ENOMEM; 171 } 172 memcpy(*opt, input, pos - input); 173 (*opt)[pos - input] = '\0'; 174 } 175 return (0); 176 177 } 178 179 /* 180 * Restore the default context. 181 */ 182 static void 183 k5db2_clear_context(krb5_db2_context *dbctx) 184 { 185 /* 186 * Free any dynamically allocated memory. File descriptors and locks 187 * are the caller's problem. 188 */ 189 if (dbctx->db_lf_name) 190 free(dbctx->db_lf_name); 191 if (dbctx->db_name && (dbctx->db_name != default_db_name)) 192 free(dbctx->db_name); 193 /* 194 * Clear the structure and reset the defaults. 195 */ 196 memset((char *) dbctx, 0, sizeof(krb5_db2_context)); 197 dbctx->db_name = default_db_name; 198 dbctx->db_nb_locks = FALSE; 199 dbctx->tempdb = FALSE; 200 } 201 202 static krb5_error_code 203 k5db2_init_context(krb5_context context) 204 { 205 krb5_db2_context *db_ctx; 206 kdb5_dal_handle *dal_handle; 207 208 dal_handle = (kdb5_dal_handle *) context->db_context; 209 210 if (dal_handle->db_context == NULL) { 211 db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context)); 212 if (db_ctx == NULL) 213 return ENOMEM; 214 else { 215 memset((char *) db_ctx, 0, sizeof(krb5_db2_context)); 216 k5db2_clear_context((krb5_db2_context *) db_ctx); 217 dal_handle->db_context = (void *) db_ctx; 218 } 219 } 220 return (0); 221 } 222 223 /* 224 * Utility routine: generate name of database file. 225 */ 226 227 static char * 228 gen_dbsuffix(char *db_name, char *sfx) 229 { 230 char *dbsuffix; 231 232 if (sfx == NULL) 233 return ((char *) NULL); 234 235 dbsuffix = malloc(strlen(db_name) + strlen(sfx) + 1); 236 if (!dbsuffix) 237 return (0); 238 /*LINTED*/ 239 (void) strcpy(dbsuffix, db_name); 240 /*LINTED*/ 241 (void) strcat(dbsuffix, sfx); 242 return dbsuffix; 243 } 244 245 static DB * 246 k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb) 247 { 248 DB *db; 249 BTREEINFO bti; 250 HASHINFO hashi; 251 bti.flags = 0; 252 bti.cachesize = 0; 253 bti.psize = 4096; 254 bti.lorder = 0; 255 bti.minkeypage = 0; 256 bti.compare = NULL; 257 bti.prefix = NULL; 258 259 if (tempdb) { 260 fname = gen_dbsuffix(fname, "~"); 261 } else { 262 fname = strdup(fname); 263 } 264 if (fname == NULL) 265 { 266 errno = ENOMEM; 267 return NULL; 268 } 269 270 271 hashi.bsize = 4096; 272 hashi.cachesize = 0; 273 hashi.ffactor = 40; 274 hashi.hash = NULL; 275 hashi.lorder = 0; 276 hashi.nelem = 1; 277 278 db = dbopen(fname, flags, mode, 279 dbc->hashfirst ? DB_HASH : DB_BTREE, 280 dbc->hashfirst ? (void *) &hashi : (void *) &bti); 281 if (db != NULL) { 282 free(fname); 283 return db; 284 } 285 switch (errno) { 286 #ifdef EFTYPE 287 case EFTYPE: 288 #endif 289 case EINVAL: 290 db = dbopen(fname, flags, mode, 291 dbc->hashfirst ? DB_BTREE : DB_HASH, 292 dbc->hashfirst ? (void *) &bti : (void *) &hashi); 293 if (db != NULL) 294 dbc->hashfirst = !dbc->hashfirst; 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_file); 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_file); 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_file); 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_file); 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_file); 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 942 assert (strlen(dbname) + strlen("%s.kadm5") < sizeof(policy_db_name)); 943 sprintf(policy_db_name, "%s.kadm5", dbname); 944 /* XXX finish this */ 945 sprintf(policy_lock_name, "%s.lock", policy_db_name); 946 947 retval1 = osa_adb_destroy_db(policy_db_name, 948 policy_lock_name, OSA_ADB_POLICY_DB_MAGIC); 949 950 return retval1; 951 } 952 953 /* 954 * look up a principal in the data base. 955 * returns number of entries found, and whether there were 956 * more than requested. 957 */ 958 959 krb5_error_code 960 krb5_db2_db_get_principal(krb5_context context, 961 krb5_const_principal searchfor, 962 krb5_db_entry *entries, /* filled in */ 963 int *nentries, /* how much room/how many found */ 964 krb5_boolean *more) /* are there more? */ 965 { 966 krb5_db2_context *db_ctx; 967 krb5_error_code retval; 968 DB *db; 969 DBT key, contents; 970 krb5_data keydata, contdata; 971 int trynum, dbret; 972 kdb5_dal_handle *dal_handle; 973 974 *more = FALSE; 975 *nentries = 0; 976 977 if (!k5db2_inited(context)) 978 return KRB5_KDB_DBNOTINITED; 979 980 dal_handle = (kdb5_dal_handle *) context->db_context; 981 db_ctx = (krb5_db2_context *) dal_handle->db_context; 982 983 for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) { 984 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) { 985 if (db_ctx->db_nb_locks) 986 return (retval); 987 sleep(1); 988 continue; 989 } 990 break; 991 } 992 if (trynum == KRB5_DB2_MAX_RETRY) 993 return KRB5_KDB_DB_INUSE; 994 995 /* XXX deal with wildcard lookups */ 996 retval = krb5_encode_princ_dbkey(context, &keydata, searchfor); 997 if (retval) 998 goto cleanup; 999 key.data = keydata.data; 1000 key.size = keydata.length; 1001 1002 db = db_ctx->db; 1003 dbret = (*db->get) (db, &key, &contents, 0); 1004 retval = errno; 1005 krb5_free_data_contents(context, &keydata); 1006 switch (dbret) { 1007 case 1: 1008 retval = 0; 1009 /*LINTED*/ 1010 case -1: 1011 default: 1012 *nentries = 0; 1013 goto cleanup; 1014 case 0: 1015 contdata.data = contents.data; 1016 contdata.length = contents.size; 1017 retval = krb5_decode_princ_contents(context, &contdata, entries); 1018 if (!retval) 1019 *nentries = 1; 1020 break; 1021 } 1022 1023 cleanup: 1024 (void) krb5_db2_db_unlock(context); /* unlock read lock */ 1025 return retval; 1026 } 1027 1028 /* 1029 Free stuff returned by krb5_db2_db_get_principal. 1030 */ 1031 krb5_error_code 1032 krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries, 1033 int nentries) 1034 { 1035 register int i; 1036 for (i = 0; i < nentries; i++) 1037 krb5_dbe_free_contents(context, &entries[i]); 1038 return 0; 1039 } 1040 1041 /* 1042 Stores the *"nentries" entry structures pointed to by "entries" in the 1043 database. 1044 1045 *"nentries" is updated upon return to reflect the number of records 1046 acutally stored; the first *"nstored" records will have been stored in the 1047 database (even if an error occurs). 1048 1049 */ 1050 1051 krb5_error_code 1052 krb5_db2_db_put_principal(krb5_context context, 1053 krb5_db_entry *entries, 1054 int *nentries, /* number of entry structs to update */ 1055 char **db_args) 1056 { 1057 int i, n, dbret; 1058 DB *db; 1059 DBT key, contents; 1060 krb5_data contdata, keydata; 1061 krb5_error_code retval; 1062 krb5_db2_context *db_ctx; 1063 kdb5_dal_handle *dal_handle; 1064 kdb_incr_update_t *upd, *fupd; 1065 char *princ_name = NULL; 1066 kdb_log_context *log_ctx; 1067 1068 krb5_clear_error_message (context); 1069 if (db_args) { 1070 /* DB2 does not support db_args DB arguments for principal */ 1071 krb5_set_error_message(context, EINVAL, 1072 gettext("Unsupported argument \"%s\" for db2"), 1073 db_args[0]); 1074 return EINVAL; 1075 } 1076 1077 log_ctx = context->kdblog_context; 1078 1079 n = *nentries; 1080 *nentries = 0; 1081 if (!k5db2_inited(context)) 1082 return KRB5_KDB_DBNOTINITED; 1083 1084 dal_handle = (kdb5_dal_handle *) context->db_context; 1085 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1086 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1087 return retval; 1088 1089 /* 1090 * Solaris Kerberos: We need the lock since ulog_conv_2logentry() does a get 1091 */ 1092 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1093 if (!(upd = (kdb_incr_update_t *) 1094 malloc(sizeof (kdb_incr_update_t)*n))) { 1095 retval = errno; 1096 goto err_lock; 1097 } 1098 fupd = upd; 1099 1100 (void) memset(upd, 0, sizeof(kdb_incr_update_t)*n); 1101 1102 if ((retval = ulog_conv_2logentry(context, entries, upd, n))) { 1103 goto err_lock; 1104 } 1105 } 1106 1107 db = db_ctx->db; 1108 if ((retval = krb5_db2_db_start_update(context))) { 1109 (void) krb5_db2_db_unlock(context); 1110 goto err_lock; 1111 } 1112 1113 /* for each one, stuff temps, and do replace/append */ 1114 for (i = 0; i < n; i++) { 1115 /* 1116 * Solaris Kerberos: We'll be sharing the same locks as db for logging 1117 */ 1118 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1119 if ((retval = krb5_unparse_name(context, entries->princ, 1120 &princ_name))) 1121 goto err_lock; 1122 1123 upd->kdb_princ_name.utf8str_t_val = princ_name; 1124 upd->kdb_princ_name.utf8str_t_len = strlen(princ_name); 1125 1126 if (retval = ulog_add_update(context, upd)) 1127 goto err_lock; 1128 } 1129 1130 retval = krb5_encode_princ_contents(context, &contdata, entries); 1131 if (retval) 1132 break; 1133 contents.data = contdata.data; 1134 contents.size = contdata.length; 1135 retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ); 1136 if (retval) { 1137 krb5_free_data_contents(context, &contdata); 1138 break; 1139 } 1140 1141 key.data = keydata.data; 1142 key.size = keydata.length; 1143 dbret = (*db->put) (db, &key, &contents, 0); 1144 retval = dbret ? errno : 0; 1145 krb5_free_data_contents(context, &keydata); 1146 krb5_free_data_contents(context, &contdata); 1147 if (retval) 1148 break; 1149 else if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1150 /* 1151 * We need to make sure the db record is synced before we mark 1152 * it as committed via finish_update. 1153 */ 1154 dbret = (*db->sync)(db, 0); 1155 if (dbret) { 1156 retval = errno; 1157 goto err_lock; 1158 } 1159 (void) ulog_finish_update(context, upd); 1160 upd++; 1161 } 1162 entries++; /* bump to next struct */ 1163 } 1164 1165 (void) krb5_db2_db_end_update(context); 1166 1167 err_lock: 1168 (void) krb5_db2_db_unlock(context); /* unlock database */ 1169 1170 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) 1171 ulog_free_entries(fupd, n); 1172 1173 *nentries = i; 1174 return (retval); 1175 } 1176 1177 /* 1178 * delete a principal from the data base. 1179 * returns number of entries removed 1180 */ 1181 1182 krb5_error_code 1183 krb5_db2_db_delete_principal(krb5_context context, 1184 krb5_const_principal searchfor, 1185 int *nentries) /* how many found & deleted */ 1186 { 1187 krb5_error_code retval; 1188 krb5_db_entry entry; 1189 krb5_db2_context *db_ctx; 1190 DB *db; 1191 DBT key, contents; 1192 krb5_data keydata, contdata; 1193 int i, dbret; 1194 kdb5_dal_handle *dal_handle; 1195 kdb_incr_update_t upd; 1196 char *princ_name = NULL; 1197 kdb_log_context *log_ctx; 1198 1199 log_ctx = context->kdblog_context; 1200 1201 if (!k5db2_inited(context)) 1202 return KRB5_KDB_DBNOTINITED; 1203 1204 dal_handle = (kdb5_dal_handle *) context->db_context; 1205 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1206 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1207 return (retval); 1208 1209 if ((retval = krb5_db2_db_start_update(context))) { 1210 (void) krb5_db2_db_unlock(context); /* unlock write lock */ 1211 return (retval); 1212 } 1213 1214 if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor))) 1215 goto cleanup; 1216 key.data = keydata.data; 1217 key.size = keydata.length; 1218 1219 db = db_ctx->db; 1220 dbret = (*db->get) (db, &key, &contents, 0); 1221 retval = errno; 1222 switch (dbret) { 1223 case 1: 1224 retval = KRB5_KDB_NOENTRY; 1225 /*LINTED*/ 1226 case -1: 1227 default: 1228 *nentries = 0; 1229 goto cleankey; 1230 case 0: 1231 ; 1232 } 1233 /* 1234 * Solaris Kerberos: We'll be sharing the same locks as db for logging 1235 */ 1236 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) { 1237 if ((retval = krb5_unparse_name(context, searchfor, &princ_name))) { 1238 (void) krb5_db2_db_unlock(context); 1239 return retval; 1240 } 1241 1242 (void) memset(&upd, 0, sizeof (kdb_incr_update_t)); 1243 1244 upd.kdb_princ_name.utf8str_t_val = princ_name; 1245 upd.kdb_princ_name.utf8str_t_len = strlen(princ_name); 1246 1247 if (retval = ulog_delete_update(context, &upd)) { 1248 free(princ_name); 1249 (void) krb5_db2_db_unlock(context); 1250 return retval; 1251 } 1252 1253 free(princ_name); 1254 } 1255 1256 memset((char *) &entry, 0, sizeof(entry)); 1257 contdata.data = contents.data; 1258 contdata.length = contents.size; 1259 retval = krb5_decode_princ_contents(context, &contdata, &entry); 1260 if (retval) 1261 goto cleankey; 1262 *nentries = 1; 1263 1264 /* Clear encrypted key contents */ 1265 for (i = 0; i < entry.n_key_data; i++) { 1266 if (entry.key_data[i].key_data_length[0]) { 1267 memset((char *) entry.key_data[i].key_data_contents[0], 0, 1268 (unsigned) entry.key_data[i].key_data_length[0]); 1269 } 1270 } 1271 1272 retval = krb5_encode_princ_contents(context, &contdata, &entry); 1273 krb5_dbe_free_contents(context, &entry); 1274 if (retval) 1275 goto cleankey; 1276 1277 contents.data = contdata.data; 1278 contents.size = contdata.length; 1279 dbret = (*db->put) (db, &key, &contents, 0); 1280 retval = dbret ? errno : 0; 1281 krb5_free_data_contents(context, &contdata); 1282 if (retval) 1283 goto cleankey; 1284 dbret = (*db->del) (db, &key, 0); 1285 retval = dbret ? errno : 0; 1286 1287 /* 1288 * We need to commit our update upon success 1289 */ 1290 if (!retval) 1291 if (log_ctx && (log_ctx->iproprole == IPROP_MASTER)) 1292 (void) ulog_finish_update(context, &upd); 1293 1294 cleankey: 1295 krb5_free_data_contents(context, &keydata); 1296 1297 cleanup: 1298 (void) krb5_db2_db_end_update(context); 1299 (void) krb5_db2_db_unlock(context); /* unlock write lock */ 1300 return retval; 1301 } 1302 1303 krb5_error_code 1304 krb5_db2_db_iterate_ext(krb5_context context, 1305 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), 1306 krb5_pointer func_arg, 1307 int backwards, int recursive) 1308 { 1309 krb5_db2_context *db_ctx; 1310 DB *db; 1311 DBT key, contents; 1312 krb5_data contdata; 1313 krb5_db_entry entries; 1314 krb5_error_code retval; 1315 kdb5_dal_handle *dal_handle; 1316 int dbret; 1317 void *cookie; 1318 1319 cookie = NULL; 1320 if (!k5db2_inited(context)) 1321 return KRB5_KDB_DBNOTINITED; 1322 1323 dal_handle = (kdb5_dal_handle *) context->db_context; 1324 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1325 retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED); 1326 1327 if (retval) 1328 return retval; 1329 1330 db = db_ctx->db; 1331 if (recursive && db->type != DB_BTREE) { 1332 (void) krb5_db2_db_unlock(context); 1333 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1334 } 1335 1336 if (!recursive) { 1337 dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST); 1338 } else { 1339 #ifdef HAVE_BT_RSEQ 1340 dbret = bt_rseq(db, &key, &contents, &cookie, 1341 backwards ? R_LAST : R_FIRST); 1342 #else 1343 (void) krb5_db2_db_unlock(context); 1344 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1345 #endif 1346 } 1347 while (dbret == 0) { 1348 contdata.data = contents.data; 1349 contdata.length = contents.size; 1350 retval = krb5_decode_princ_contents(context, &contdata, &entries); 1351 if (retval) 1352 break; 1353 retval = (*func) (func_arg, &entries); 1354 krb5_dbe_free_contents(context, &entries); 1355 if (retval) 1356 break; 1357 if (!recursive) { 1358 dbret = (*db->seq) (db, &key, &contents, 1359 backwards ? R_PREV : R_NEXT); 1360 } else { 1361 #ifdef HAVE_BT_RSEQ 1362 dbret = bt_rseq(db, &key, &contents, &cookie, 1363 backwards ? R_PREV : R_NEXT); 1364 #else 1365 (void) krb5_db2_db_unlock(context); 1366 return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */ 1367 #endif 1368 } 1369 } 1370 switch (dbret) { 1371 case 1: 1372 case 0: 1373 break; 1374 case -1: 1375 default: 1376 retval = errno; 1377 } 1378 (void) krb5_db2_db_unlock(context); 1379 return retval; 1380 } 1381 1382 krb5_error_code 1383 krb5_db2_db_iterate(krb5_context context, 1384 char *match_expr, 1385 krb5_error_code(*func) (krb5_pointer, krb5_db_entry *), 1386 krb5_pointer func_arg, char **db_args) 1387 { 1388 char **t_ptr = db_args; 1389 int backwards = 0, recursive = 0; 1390 1391 while (t_ptr && *t_ptr) { 1392 char *opt = NULL, *val = NULL; 1393 1394 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1395 1396 /* Solaris Kerberos: adding support for -rev/recurse flags */ 1397 if (val && !strcmp(val, "rev")) 1398 backwards = 1; 1399 else if (val && !strcmp(val, "recurse")) 1400 recursive = 1; 1401 else { 1402 krb5_set_error_message(context, EINVAL, 1403 gettext("Unsupported argument \"%s\" for db2"), 1404 val); 1405 free(opt); 1406 free(val); 1407 return EINVAL; 1408 } 1409 1410 free(opt); 1411 free(val); 1412 t_ptr++; 1413 } 1414 1415 /* Solaris Kerberos: adding support for -rev/recurse flags */ 1416 return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive); 1417 } 1418 1419 krb5_boolean 1420 krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode) 1421 { 1422 krb5_boolean old; 1423 krb5_db2_context *db_ctx; 1424 kdb5_dal_handle *dal_handle; 1425 1426 dal_handle = (kdb5_dal_handle *) context->db_context; 1427 old = mode; 1428 if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) { 1429 old = db_ctx->db_nb_locks; 1430 db_ctx->db_nb_locks = mode; 1431 } 1432 return old; 1433 } 1434 1435 /* 1436 * DAL API functions 1437 */ 1438 krb5_error_code 1439 krb5_db2_lib_init() 1440 { 1441 return 0; 1442 } 1443 1444 krb5_error_code 1445 krb5_db2_lib_cleanup() 1446 { 1447 /* right now, no cleanup required */ 1448 return 0; 1449 } 1450 1451 krb5_error_code 1452 krb5_db2_open(krb5_context kcontext, 1453 char *conf_section, char **db_args, int mode) 1454 { 1455 krb5_error_code status = 0; 1456 char **t_ptr = db_args; 1457 int db_name_set = 0, tempdb=0; 1458 char *dbname = NULL; 1459 1460 krb5_clear_error_message (kcontext); 1461 1462 if (k5db2_inited(kcontext)) 1463 return 0; 1464 1465 while (t_ptr && *t_ptr) { 1466 char *opt = NULL, *val = NULL; 1467 1468 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1469 if (opt && !strcmp(opt, "dbname")) { 1470 if (dbname) free(dbname); 1471 dbname = strdup(val); 1472 } 1473 else if (!opt && !strcmp(val, "temporary") ) { 1474 tempdb = 1; 1475 } 1476 /* ignore hash argument. Might have been passed from create */ 1477 else if (!opt || strcmp(opt, "hash")) { 1478 krb5_set_error_message(kcontext, EINVAL, 1479 gettext("Unsupported argument \"%s\" for db2"), 1480 opt ? opt : val); 1481 free(opt); 1482 free(val); 1483 return EINVAL; 1484 } 1485 1486 free(opt); 1487 free(val); 1488 t_ptr++; 1489 } 1490 1491 if(dbname) { 1492 status = krb5_db2_db_set_name(kcontext, dbname, tempdb); 1493 free(dbname); 1494 if (status) { 1495 /* Solaris Kerberos: Better error logging */ 1496 snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), dbname); 1497 krb5_db2_prepend_err_str(kcontext, errbuf, status, status); 1498 1499 goto clean_n_exit; 1500 } 1501 db_name_set = 1; 1502 } 1503 if (!db_name_set) { 1504 char *value = NULL; 1505 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1506 NULL, &value); 1507 1508 if (value == NULL) { 1509 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1510 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1511 default_db_name, &value); 1512 1513 if (status) { 1514 /* Solaris Kerberos: Better error logging */ 1515 snprintf(errbuf, sizeof(errbuf), gettext("Failed when searching for " 1516 "\"%s\", \"%s\", \"%s\" in profile: "), KDB_REALM_SECTION, 1517 KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME); 1518 krb5_db2_prepend_err_str(kcontext, errbuf, status, status); 1519 1520 goto clean_n_exit; 1521 } 1522 } 1523 1524 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1525 1526 if (status) { 1527 1528 /* Solaris Kerberos: Better error logging */ 1529 snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), value); 1530 krb5_db2_prepend_err_str(kcontext, errbuf, status, status); 1531 profile_release_string(value); 1532 goto clean_n_exit; 1533 } 1534 profile_release_string(value); 1535 1536 } 1537 1538 status = krb5_db2_db_init(kcontext); 1539 if (status) { 1540 /* Solaris Kerberos: Better error logging */ 1541 snprintf(errbuf, sizeof(errbuf), gettext("Failed to initialize db2 db: ")); 1542 krb5_db2_prepend_err_str(kcontext, errbuf, status, status); 1543 } 1544 1545 clean_n_exit: 1546 return status; 1547 } 1548 1549 krb5_error_code 1550 krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args) 1551 { 1552 krb5_error_code status = 0; 1553 char **t_ptr = db_args; 1554 int db_name_set = 0, tempdb=0; 1555 krb5_int32 flags = KRB5_KDB_CREATE_BTREE; 1556 char *db_name = NULL; 1557 1558 krb5_clear_error_message (kcontext); 1559 1560 if (k5db2_inited(kcontext)) 1561 return 0; 1562 1563 while (t_ptr && *t_ptr) { 1564 char *opt = NULL, *val = NULL; 1565 1566 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1567 if (opt && !strcmp(opt, "dbname")) { 1568 db_name = strdup(val); 1569 if (db_name == NULL) 1570 return ENOMEM; 1571 } 1572 else if (!opt && !strcmp(val, "temporary")) { 1573 tempdb = 1; 1574 } 1575 else if (opt && !strcmp(opt, "hash")) { 1576 flags = KRB5_KDB_CREATE_HASH; 1577 } else { 1578 krb5_set_error_message(kcontext, EINVAL, 1579 gettext("Unsupported argument \"%s\" for db2"), 1580 opt ? opt : val); 1581 free(opt); 1582 free(val); 1583 return EINVAL; 1584 } 1585 1586 free(opt); 1587 free(val); 1588 t_ptr++; 1589 } 1590 if (db_name) { 1591 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1592 if (!status) { 1593 status = EEXIST; 1594 goto clean_n_exit; 1595 } 1596 db_name_set = 1; 1597 } 1598 if (!db_name_set) { 1599 char *value = NULL; 1600 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1601 KDB_MODULE_SECTION, conf_section, 1602 /* under given conf section */ 1603 KDB_DB2_DATABASE_NAME, NULL, &value); 1604 1605 if (value == NULL) { 1606 /* Special case for db2. We might actually be looking at 1607 * old type config file where database is specified as 1608 * part of realm. */ 1609 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), 1610 KDB_REALM_SECTION, 1611 KRB5_DB_GET_REALM(kcontext), 1612 /* under given realm */ 1613 KDB_DB2_DATABASE_NAME, 1614 default_db_name, &value); 1615 if (status) { 1616 goto clean_n_exit; 1617 } 1618 } 1619 1620 db_name = strdup(value); 1621 /* Solaris Kerberos: for safety */ 1622 if (db_name == NULL) { 1623 status = ENOMEM; 1624 goto clean_n_exit; 1625 } 1626 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1627 profile_release_string(value); 1628 if (!status) { 1629 status = EEXIST; 1630 goto clean_n_exit; 1631 } 1632 1633 } 1634 1635 status = krb5_db2_db_create(kcontext, db_name, flags); 1636 if (status) 1637 goto clean_n_exit; 1638 /* db2 has a problem of needing to close and open the database again. This removes that need */ 1639 status = krb5_db2_db_fini(kcontext); 1640 if (status) 1641 goto clean_n_exit; 1642 1643 status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW); 1644 1645 clean_n_exit: 1646 if (db_name) 1647 free(db_name); 1648 return status; 1649 } 1650 1651 krb5_error_code 1652 krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args) 1653 { 1654 krb5_error_code status = 0; 1655 char **t_ptr = db_args; 1656 int db_name_set = 0, tempdb=0; 1657 char *db_name = NULL; 1658 1659 while (t_ptr && *t_ptr) { 1660 char *opt = NULL, *val = NULL; 1661 1662 krb5_db2_get_db_opt(*t_ptr, &opt, &val); 1663 if (opt && !strcmp(opt, "dbname")) { 1664 db_name = strdup(val); 1665 if (db_name == NULL) 1666 return ENOMEM; 1667 } 1668 else if (!opt && !strcmp(val, "temporary")) { 1669 tempdb = 1; 1670 } 1671 /* ignore hash argument. Might have been passed from create */ 1672 else if (!opt || strcmp(opt, "hash")) { 1673 free(opt); 1674 free(val); 1675 return EINVAL; 1676 } 1677 1678 free(opt); 1679 free(val); 1680 t_ptr++; 1681 } 1682 1683 if (db_name) { 1684 status = krb5_db2_db_set_name(kcontext, db_name, tempdb); 1685 if (status) { 1686 goto clean_n_exit; 1687 } 1688 db_name_set = 1; 1689 } 1690 if (!db_name_set) { 1691 char *value = NULL; 1692 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */ 1693 NULL, &value); 1694 1695 if (value == NULL) { 1696 /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */ 1697 status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */ 1698 default_db_name, &value); 1699 if (status) { 1700 goto clean_n_exit; 1701 } 1702 } 1703 1704 db_name = strdup(value); 1705 if (db_name == NULL) { 1706 status = ENOMEM; 1707 goto clean_n_exit; 1708 } 1709 status = krb5_db2_db_set_name(kcontext, value, tempdb); 1710 profile_release_string(value); 1711 if (status) { 1712 goto clean_n_exit; 1713 } 1714 1715 } 1716 1717 status = krb5_db2_db_destroy(kcontext, db_name); 1718 1719 clean_n_exit: 1720 if (db_name) 1721 free(db_name); 1722 return status; 1723 } 1724 1725 krb5_error_code 1726 krb5_db2_set_master_key_ext(krb5_context kcontext, 1727 char *pwd, krb5_keyblock * key) 1728 { 1729 return krb5_db2_db_set_mkey(kcontext, key); 1730 } 1731 1732 krb5_error_code 1733 krb5_db2_db_set_option(krb5_context kcontext, int option, void *value) 1734 { 1735 krb5_error_code status = 0; 1736 krb5_boolean oldval; 1737 krb5_db2_context *db_ctx; 1738 kdb5_dal_handle *dal_handle; 1739 1740 if (!k5db2_inited(kcontext)) 1741 return KRB5_KDB_DBNOTINITED; 1742 1743 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1744 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1745 1746 1747 switch (option) { 1748 case KRB5_KDB_OPT_SET_DB_NAME: 1749 status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb); 1750 break; 1751 1752 case KRB5_KDB_OPT_SET_LOCK_MODE: 1753 oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value)); 1754 *((krb5_boolean *) value) = oldval; 1755 break; 1756 1757 default: 1758 status = -1; /* TBD */ 1759 break; 1760 } 1761 1762 return status; 1763 } 1764 1765 void * 1766 krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size) 1767 { 1768 return realloc(ptr, size); 1769 } 1770 1771 void 1772 krb5_db2_free(krb5_context kcontext, void *ptr) 1773 { 1774 free(ptr); 1775 } 1776 1777 /* policy functions */ 1778 krb5_error_code 1779 krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy) 1780 { 1781 kdb5_dal_handle *dal_handle; 1782 krb5_db2_context *dbc; 1783 1784 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1785 dbc = (krb5_db2_context *) dal_handle->db_context; 1786 1787 return osa_adb_create_policy(dbc->policy_db, policy); 1788 } 1789 1790 krb5_error_code 1791 krb5_db2_get_policy(krb5_context kcontext, 1792 char *name, osa_policy_ent_t * policy, int *cnt) 1793 { 1794 kdb5_dal_handle *dal_handle; 1795 krb5_db2_context *dbc; 1796 1797 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1798 dbc = (krb5_db2_context *) dal_handle->db_context; 1799 1800 return osa_adb_get_policy(dbc->policy_db, name, policy, cnt); 1801 } 1802 1803 krb5_error_code 1804 krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy) 1805 { 1806 kdb5_dal_handle *dal_handle; 1807 krb5_db2_context *dbc; 1808 1809 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1810 dbc = (krb5_db2_context *) dal_handle->db_context; 1811 1812 return osa_adb_put_policy(dbc->policy_db, policy); 1813 } 1814 1815 krb5_error_code 1816 krb5_db2_iter_policy(krb5_context kcontext, 1817 char *match_entry, 1818 osa_adb_iter_policy_func func, void *data) 1819 { 1820 kdb5_dal_handle *dal_handle; 1821 krb5_db2_context *dbc; 1822 1823 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1824 dbc = (krb5_db2_context *) dal_handle->db_context; 1825 1826 return osa_adb_iter_policy(dbc->policy_db, func, data); 1827 } 1828 1829 krb5_error_code 1830 krb5_db2_delete_policy(krb5_context kcontext, char *policy) 1831 { 1832 kdb5_dal_handle *dal_handle; 1833 krb5_db2_context *dbc; 1834 1835 dal_handle = (kdb5_dal_handle *) kcontext->db_context; 1836 dbc = (krb5_db2_context *) dal_handle->db_context; 1837 1838 return osa_adb_destroy_policy(dbc->policy_db, policy); 1839 } 1840 1841 void 1842 krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry) 1843 { 1844 osa_free_policy_ent(entry); 1845 } 1846 1847 1848 /* */ 1849 1850 krb5_error_code 1851 krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args) 1852 { 1853 krb5_error_code status = 0; 1854 char *db_name = NULL; 1855 char *temp_db_name = NULL; 1856 1857 krb5_clear_error_message (kcontext); 1858 1859 { 1860 kdb5_dal_handle *dal_handle = kcontext->db_context; 1861 krb5_db2_context *db_ctx = dal_handle->db_context; 1862 db_name = strdup(db_ctx->db_name); 1863 if (db_name == NULL) { 1864 status = ENOMEM; 1865 goto clean_n_exit; 1866 } 1867 } 1868 1869 assert(kcontext->db_context != NULL); 1870 temp_db_name = gen_dbsuffix(db_name, "~"); 1871 if (temp_db_name == NULL) { 1872 status = ENOMEM; 1873 goto clean_n_exit; 1874 } 1875 1876 status = krb5_db2_db_rename (kcontext, temp_db_name, db_name); 1877 1878 clean_n_exit: 1879 if (db_name) 1880 free(db_name); 1881 if (temp_db_name) 1882 free(temp_db_name); 1883 return status; 1884 } 1885 1886 /* Retrieved from pre-DAL code base. */ 1887 /* 1888 * "Atomically" rename the database in a way that locks out read 1889 * access in the middle of the rename. 1890 * 1891 * Not perfect; if we crash in the middle of an update, we don't 1892 * necessarily know to complete the transaction the rename, but... 1893 * 1894 * Since the rename operation happens outside the init/fini bracket, we 1895 * have to go through the same stuff that we went through up in db_destroy. 1896 */ 1897 krb5_error_code 1898 krb5_db2_db_rename(context, from, to) 1899 krb5_context context; 1900 char *from; 1901 char *to; 1902 { 1903 char *fromok; 1904 krb5_error_code retval; 1905 krb5_db2_context *s_context, *db_ctx; 1906 kdb5_dal_handle *dal_handle = context->db_context; 1907 1908 s_context = dal_handle->db_context; 1909 dal_handle->db_context = NULL; 1910 if ((retval = k5db2_init_context(context))) 1911 return retval; 1912 db_ctx = (krb5_db2_context *) dal_handle->db_context; 1913 1914 /* 1915 * Create the database if it does not already exist; the 1916 * files must exist because krb5_db2_db_lock, called below, 1917 * will fail otherwise. 1918 */ 1919 { 1920 struct stat statbuf; 1921 1922 if (stat(to, &statbuf) == -1) { 1923 if (errno == ENOENT) { 1924 retval = krb5_db2_db_create(context, to, 1925 KRB5_KDB_CREATE_BTREE); 1926 if (retval) 1927 goto errout; 1928 } 1929 else { 1930 /* 1931 * XXX assuming we should bail if there is some other stat error 1932 */ 1933 retval = errno; 1934 goto errout; 1935 } 1936 } 1937 } 1938 /* 1939 * Set the database to the target, so that other processes sharing 1940 * the target will stop their activity, and notice the new database. 1941 */ 1942 retval = krb5_db2_db_set_name(context, to, 0); 1943 if (retval) 1944 goto errout; 1945 1946 retval = krb5_db2_db_init(context); 1947 if (retval) 1948 goto errout; 1949 1950 /* XXX WAF this needs to be redone (not lock safe)!!! */ 1951 { 1952 /* Ugly brute force hack. 1953 1954 Should be going through nice friendly helper routines for 1955 this, but it's a mess of jumbled so-called interfaces right 1956 now. */ 1957 char policy[2048], new_policy[2048]; 1958 assert (strlen(db_ctx->db_name) < 2000); 1959 /*LINTED*/ 1960 sprintf(policy, "%s.kadm5", db_ctx->db_name); 1961 /*LINTED*/ 1962 sprintf(new_policy, "%s~.kadm5", db_ctx->db_name); 1963 if (0 != rename(new_policy, policy)) { 1964 retval = errno; 1965 goto errout; 1966 } 1967 strcat(new_policy, ".lock"); 1968 (void) unlink(new_policy); 1969 } 1970 1971 retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time); 1972 if (retval) 1973 goto errout; 1974 1975 fromok = gen_dbsuffix(from, KDB2_LOCK_EXT); 1976 if (fromok == NULL) { 1977 retval = ENOMEM; 1978 goto errout; 1979 } 1980 1981 if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE))) 1982 goto errfromok; 1983 1984 if ((retval = krb5_db2_db_start_update(context))) 1985 goto errfromok; 1986 1987 if (rename(from, to)) { 1988 retval = errno; 1989 goto errfromok; 1990 } 1991 if (unlink(fromok)) { 1992 retval = errno; 1993 goto errfromok; 1994 } 1995 retval = krb5_db2_db_end_update(context); 1996 errfromok: 1997 free_dbsuffix(fromok); 1998 errout: 1999 if (dal_handle->db_context) { 2000 if (db_ctx->db_lf_file >= 0) { 2001 krb5_db2_db_unlock(context); 2002 close(db_ctx->db_lf_file); 2003 } 2004 k5db2_clear_context((krb5_db2_context *) dal_handle->db_context); 2005 free(dal_handle->db_context); 2006 } 2007 2008 dal_handle->db_context = s_context; 2009 (void) krb5_db2_db_unlock(context); /* unlock saved context db */ 2010 2011 return retval; 2012 } 2013 2014 const char * 2015 krb5_db2_errcode_2_string(krb5_context kcontext, long err_code) 2016 { 2017 return krb5_get_error_message(kcontext, err_code); 2018 } 2019 2020 void 2021 krb5_db2_release_errcode_string(krb5_context kcontext, const char *msg) 2022 { 2023 krb5_free_error_message(kcontext, msg); 2024 } 2025 2026 2027 /* 2028 * Solaris Kerberos: 2029 * Similar to the ldap plugin. 2030 */ 2031 static void 2032 krb5_db2_prepend_err_str(krb5_context ctx, const char *str, krb5_error_code err, 2033 krb5_error_code oerr) { 2034 const char *omsg; 2035 if (oerr == 0) 2036 oerr = err; 2037 omsg = krb5_get_error_message (ctx, err); 2038 krb5_set_error_message (ctx, err, "%s %s", str, omsg); 2039 krb5_free_error_message(ctx, omsg); 2040 } 2041 2042