|
|
1.1 ! root 1: /* ! 2: * $Source: /usr/src/kerberosIV/kdb/RCS/krb_dbm.c,v $ ! 3: * $Author: kfall $ ! 4: * ! 5: * Copyright 1988 by the Massachusetts Institute of Technology. ! 6: * ! 7: * For copying and distribution information, please see the file ! 8: * <mit-copyright.h>. ! 9: */ ! 10: ! 11: #ifndef lint ! 12: static char rcsid_krb_dbm_c[] = ! 13: "$Header: /usr/src/kerberosIV/kdb/RCS/krb_dbm.c,v 4.10 90/06/25 20:52:39 kfall Exp $"; ! 14: #endif lint ! 15: ! 16: #include <sys/types.h> ! 17: #include <sys/uio.h> ! 18: #include <sys/time.h> ! 19: #include <sys/stat.h> ! 20: #include <sys/resource.h> ! 21: #include <sys/errno.h> ! 22: #include <sys/file.h> ! 23: #include <netinet/in.h> ! 24: #include <mit-copyright.h> ! 25: #include <stdio.h> ! 26: #include <string.h> ! 27: #include <des.h> ! 28: #include <krb.h> ! 29: #include <krb_db.h> ! 30: #include <ndbm.h> ! 31: ! 32: #define KERB_DB_MAX_RETRY 5 ! 33: ! 34: #ifdef DEBUG ! 35: extern int debug; ! 36: extern long kerb_debug; ! 37: extern char *progname; ! 38: #endif ! 39: extern char *malloc(); ! 40: extern int errno; ! 41: ! 42: static init = 0; ! 43: static char default_db_name[] = DBM_FILE; ! 44: static char *current_db_name = default_db_name; ! 45: static void encode_princ_key(), decode_princ_key(); ! 46: static void encode_princ_contents(), decode_princ_contents(); ! 47: static void kerb_dbl_fini(); ! 48: static int kerb_dbl_lock(); ! 49: static void kerb_dbl_unlock(); ! 50: ! 51: static struct timeval timestamp;/* current time of request */ ! 52: static int non_blocking = 0; ! 53: ! 54: /* ! 55: * This module contains all of the code which directly interfaces to ! 56: * the underlying representation of the Kerberos database; this ! 57: * implementation uses a DBM or NDBM indexed "file" (actually ! 58: * implemented as two separate files) to store the relations, plus a ! 59: * third file as a semaphore to allow the database to be replaced out ! 60: * from underneath the KDC server. ! 61: */ ! 62: ! 63: /* ! 64: * Locking: ! 65: * ! 66: * There are two distinct locking protocols used. One is designed to ! 67: * lock against processes (the admin_server, for one) which make ! 68: * incremental changes to the database; the other is designed to lock ! 69: * against utilities (kdb_util, kpropd) which replace the entire ! 70: * database in one fell swoop. ! 71: * ! 72: * The first locking protocol is implemented using flock() in the ! 73: * krb_dbl_lock() and krb_dbl_unlock routines. ! 74: * ! 75: * The second locking protocol is necessary because DBM "files" are ! 76: * actually implemented as two separate files, and it is impossible to ! 77: * atomically rename two files simultaneously. It assumes that the ! 78: * database is replaced only very infrequently in comparison to the time ! 79: * needed to do a database read operation. ! 80: * ! 81: * A third file is used as a "version" semaphore; the modification ! 82: * time of this file is the "version number" of the database. ! 83: * At the start of a read operation, the reader checks the version ! 84: * number; at the end of the read operation, it checks again. If the ! 85: * version number changed, or if the semaphore was nonexistant at ! 86: * either time, the reader sleeps for a second to let things ! 87: * stabilize, and then tries again; if it does not succeed after ! 88: * KERB_DB_MAX_RETRY attempts, it gives up. ! 89: * ! 90: * On update, the semaphore file is deleted (if it exists) before any ! 91: * update takes place; at the end of the update, it is replaced, with ! 92: * a version number strictly greater than the version number which ! 93: * existed at the start of the update. ! 94: * ! 95: * If the system crashes in the middle of an update, the semaphore ! 96: * file is not automatically created on reboot; this is a feature, not ! 97: * a bug, since the database may be inconsistant. Note that the ! 98: * absence of a semaphore file does not prevent another _update_ from ! 99: * taking place later. Database replacements take place automatically ! 100: * only on slave servers; a crash in the middle of an update will be ! 101: * fixed by the next slave propagation. A crash in the middle of an ! 102: * update on the master would be somewhat more serious, but this would ! 103: * likely be noticed by an administrator, who could fix the problem and ! 104: * retry the operation. ! 105: */ ! 106: ! 107: /* Macros to convert ndbm names to dbm names. ! 108: * Note that dbm_nextkey() cannot be simply converted using a macro, since ! 109: * it is invoked giving the database, and nextkey() needs the previous key. ! 110: * ! 111: * Instead, all routines call "dbm_next" instead. ! 112: */ ! 113: ! 114: #define dbm_next(db,key) dbm_nextkey(db) ! 115: ! 116: /* ! 117: * Utility routine: generate name of database file. ! 118: */ ! 119: ! 120: static char *gen_dbsuffix(db_name, sfx) ! 121: char *db_name; ! 122: char *sfx; ! 123: { ! 124: char *dbsuffix; ! 125: ! 126: if (sfx == NULL) ! 127: sfx = ".ok"; ! 128: ! 129: dbsuffix = malloc (strlen(db_name) + strlen(sfx) + 1); ! 130: strcpy(dbsuffix, db_name); ! 131: strcat(dbsuffix, sfx); ! 132: return dbsuffix; ! 133: } ! 134: ! 135: /* ! 136: * initialization for data base routines. ! 137: */ ! 138: ! 139: kerb_db_init() ! 140: { ! 141: init = 1; ! 142: return (0); ! 143: } ! 144: ! 145: /* ! 146: * gracefully shut down database--must be called by ANY program that does ! 147: * a kerb_db_init ! 148: */ ! 149: ! 150: kerb_db_fini() ! 151: { ! 152: } ! 153: ! 154: /* ! 155: * Set the "name" of the current database to some alternate value. ! 156: * ! 157: * Passing a null pointer as "name" will set back to the default. ! 158: * If the alternate database doesn't exist, nothing is changed. ! 159: */ ! 160: ! 161: kerb_db_set_name(name) ! 162: char *name; ! 163: { ! 164: DBM *db; ! 165: ! 166: if (name == NULL) ! 167: name = default_db_name; ! 168: db = dbm_open(name, 0, 0); ! 169: if (db == NULL) ! 170: return errno; ! 171: dbm_close(db); ! 172: kerb_dbl_fini(); ! 173: current_db_name = name; ! 174: return 0; ! 175: } ! 176: ! 177: /* ! 178: * Return the last modification time of the database. ! 179: */ ! 180: ! 181: long kerb_get_db_age() ! 182: { ! 183: struct stat st; ! 184: char *okname; ! 185: long age; ! 186: ! 187: okname = gen_dbsuffix(current_db_name, ".ok"); ! 188: ! 189: if (stat (okname, &st) < 0) ! 190: age = 0; ! 191: else ! 192: age = st.st_mtime; ! 193: ! 194: free (okname); ! 195: return age; ! 196: } ! 197: ! 198: /* ! 199: * Remove the semaphore file; indicates that database is currently ! 200: * under renovation. ! 201: * ! 202: * This is only for use when moving the database out from underneath ! 203: * the server (for example, during slave updates). ! 204: */ ! 205: ! 206: static long kerb_start_update(db_name) ! 207: char *db_name; ! 208: { ! 209: char *okname = gen_dbsuffix(db_name, ".ok"); ! 210: long age = kerb_get_db_age(); ! 211: ! 212: if (unlink(okname) < 0 ! 213: && errno != ENOENT) { ! 214: age = -1; ! 215: } ! 216: free (okname); ! 217: return age; ! 218: } ! 219: ! 220: static long kerb_end_update(db_name, age) ! 221: char *db_name; ! 222: long age; ! 223: { ! 224: int fd; ! 225: int retval = 0; ! 226: char *new_okname = gen_dbsuffix(db_name, ".ok#"); ! 227: char *okname = gen_dbsuffix(db_name, ".ok"); ! 228: ! 229: fd = open (new_okname, O_CREAT|O_RDWR|O_TRUNC, 0600); ! 230: if (fd < 0) ! 231: retval = errno; ! 232: else { ! 233: struct stat st; ! 234: struct timeval tv[2]; ! 235: /* make sure that semaphore is "after" previous value. */ ! 236: if (fstat (fd, &st) == 0 ! 237: && st.st_mtime <= age) { ! 238: tv[0].tv_sec = st.st_atime; ! 239: tv[0].tv_usec = 0; ! 240: tv[1].tv_sec = age; ! 241: tv[1].tv_usec = 0; ! 242: /* set times.. */ ! 243: utimes (new_okname, tv); ! 244: fsync(fd); ! 245: } ! 246: close(fd); ! 247: if (rename (new_okname, okname) < 0) ! 248: retval = errno; ! 249: } ! 250: ! 251: free (new_okname); ! 252: free (okname); ! 253: ! 254: return retval; ! 255: } ! 256: ! 257: static long kerb_start_read() ! 258: { ! 259: return kerb_get_db_age(); ! 260: } ! 261: ! 262: static long kerb_end_read(age) ! 263: u_long age; ! 264: { ! 265: if (kerb_get_db_age() != age || age == -1) { ! 266: return -1; ! 267: } ! 268: return 0; ! 269: } ! 270: ! 271: /* ! 272: * Create the database, assuming it's not there. ! 273: */ ! 274: ! 275: kerb_db_create(db_name) ! 276: char *db_name; ! 277: { ! 278: char *okname = gen_dbsuffix(db_name, ".ok"); ! 279: int fd; ! 280: register int ret = 0; ! 281: DBM *db; ! 282: ! 283: db = dbm_open(db_name, O_RDWR|O_CREAT|O_EXCL, 0600); ! 284: if (db == NULL) ! 285: ret = errno; ! 286: else ! 287: dbm_close(db); ! 288: ! 289: if (ret == 0) { ! 290: fd = open (okname, O_CREAT|O_RDWR|O_TRUNC, 0600); ! 291: if (fd < 0) ! 292: ret = errno; ! 293: close(fd); ! 294: } ! 295: return ret; ! 296: } ! 297: ! 298: /* ! 299: * "Atomically" rename the database in a way that locks out read ! 300: * access in the middle of the rename. ! 301: * ! 302: * Not perfect; if we crash in the middle of an update, we don't ! 303: * necessarily know to complete the transaction the rename, but... ! 304: */ ! 305: ! 306: kerb_db_rename(from, to) ! 307: char *from; ! 308: char *to; ! 309: { ! 310: char *fromdir = gen_dbsuffix (from, ".dir"); ! 311: char *todir = gen_dbsuffix (to, ".dir"); ! 312: char *frompag = gen_dbsuffix (from , ".pag"); ! 313: char *topag = gen_dbsuffix (to, ".pag"); ! 314: char *fromok = gen_dbsuffix(from, ".ok"); ! 315: long trans = kerb_start_update(to); ! 316: int ok; ! 317: ! 318: if ((rename (fromdir, todir) == 0) ! 319: && (rename (frompag, topag) == 0)) { ! 320: (void) unlink (fromok); ! 321: ok = 1; ! 322: } ! 323: ! 324: free (fromok); ! 325: free (fromdir); ! 326: free (todir); ! 327: free (frompag); ! 328: free (topag); ! 329: if (ok) ! 330: return kerb_end_update(to, trans); ! 331: else ! 332: return -1; ! 333: } ! 334: ! 335: /* ! 336: * look up a principal in the data base returns number of principals ! 337: * found , and whether there were more than requested. ! 338: */ ! 339: ! 340: kerb_db_get_principal(name, inst, principal, max, more) ! 341: char *name; /* could have wild card */ ! 342: char *inst; /* could have wild card */ ! 343: Principal *principal; ! 344: unsigned int max; /* max number of name structs to return */ ! 345: int *more; /* where there more than 'max' tuples? */ ! 346: ! 347: { ! 348: int found = 0, code; ! 349: extern int errorproc(); ! 350: int wildp, wildi; ! 351: datum key, contents; ! 352: char testname[ANAME_SZ], testinst[INST_SZ]; ! 353: u_long trans; ! 354: int try; ! 355: DBM *db; ! 356: ! 357: if (!init) ! 358: kerb_db_init(); /* initialize database routines */ ! 359: ! 360: for (try = 0; try < KERB_DB_MAX_RETRY; try++) { ! 361: trans = kerb_start_read(); ! 362: ! 363: if ((code = kerb_dbl_lock(KERB_DBL_SHARED)) != 0) ! 364: return -1; ! 365: ! 366: db = dbm_open(current_db_name, O_RDONLY, 0600); ! 367: ! 368: *more = 0; ! 369: ! 370: #ifdef DEBUG ! 371: if (kerb_debug & 2) ! 372: fprintf(stderr, ! 373: "%s: db_get_principal for %s %s max = %d", ! 374: progname, name, inst, max); ! 375: #endif ! 376: ! 377: wildp = !strcmp(name, "*"); ! 378: wildi = !strcmp(inst, "*"); ! 379: ! 380: if (!wildi && !wildp) { /* nothing's wild */ ! 381: encode_princ_key(&key, name, inst); ! 382: contents = dbm_fetch(db, key); ! 383: if (contents.dptr == NULL) { ! 384: found = 0; ! 385: goto done; ! 386: } ! 387: decode_princ_contents(&contents, principal); ! 388: #ifdef DEBUG ! 389: if (kerb_debug & 1) { ! 390: fprintf(stderr, "\t found %s %s p_n length %d t_n length %d\n", ! 391: principal->name, principal->instance, ! 392: strlen(principal->name), ! 393: strlen(principal->instance)); ! 394: } ! 395: #endif ! 396: found = 1; ! 397: goto done; ! 398: } ! 399: /* process wild cards by looping through entire database */ ! 400: ! 401: for (key = dbm_firstkey(db); key.dptr != NULL; ! 402: key = dbm_next(db, key)) { ! 403: decode_princ_key(&key, testname, testinst); ! 404: if ((wildp || !strcmp(testname, name)) && ! 405: (wildi || !strcmp(testinst, inst))) { /* have a match */ ! 406: if (found >= max) { ! 407: *more = 1; ! 408: goto done; ! 409: } else { ! 410: found++; ! 411: contents = dbm_fetch(db, key); ! 412: decode_princ_contents(&contents, principal); ! 413: #ifdef DEBUG ! 414: if (kerb_debug & 1) { ! 415: fprintf(stderr, ! 416: "\tfound %s %s p_n length %d t_n length %d\n", ! 417: principal->name, principal->instance, ! 418: strlen(principal->name), ! 419: strlen(principal->instance)); ! 420: } ! 421: #endif ! 422: principal++; /* point to next */ ! 423: } ! 424: } ! 425: } ! 426: ! 427: done: ! 428: kerb_dbl_unlock(); /* unlock read lock */ ! 429: dbm_close(db); ! 430: if (kerb_end_read(trans) == 0) ! 431: break; ! 432: found = -1; ! 433: if (!non_blocking) ! 434: sleep(1); ! 435: } ! 436: return (found); ! 437: } ! 438: ! 439: /* ! 440: * Update a name in the data base. Returns number of names ! 441: * successfully updated. ! 442: */ ! 443: ! 444: kerb_db_put_principal(principal, max) ! 445: Principal *principal; ! 446: unsigned int max; /* number of principal structs to ! 447: * update */ ! 448: ! 449: { ! 450: int found = 0, code; ! 451: u_long i; ! 452: extern int errorproc(); ! 453: datum key, contents; ! 454: DBM *db; ! 455: ! 456: gettimeofday(×tamp, NULL); ! 457: ! 458: if (!init) ! 459: kerb_db_init(); ! 460: ! 461: if ((code = kerb_dbl_lock(KERB_DBL_EXCLUSIVE)) != 0) ! 462: return -1; ! 463: ! 464: db = dbm_open(current_db_name, O_RDWR, 0600); ! 465: ! 466: #ifdef DEBUG ! 467: if (kerb_debug & 2) ! 468: fprintf(stderr, "%s: kerb_db_put_principal max = %d", ! 469: progname, max); ! 470: #endif ! 471: ! 472: /* for each one, stuff temps, and do replace/append */ ! 473: for (i = 0; i < max; i++) { ! 474: encode_princ_contents(&contents, principal); ! 475: encode_princ_key(&key, principal->name, principal->instance); ! 476: dbm_store(db, key, contents, DBM_REPLACE); ! 477: #ifdef DEBUG ! 478: if (kerb_debug & 1) { ! 479: fprintf(stderr, "\n put %s %s\n", ! 480: principal->name, principal->instance); ! 481: } ! 482: #endif ! 483: found++; ! 484: principal++; /* bump to next struct */ ! 485: } ! 486: ! 487: dbm_close(db); ! 488: kerb_dbl_unlock(); /* unlock database */ ! 489: return (found); ! 490: } ! 491: ! 492: static void ! 493: encode_princ_key(key, name, instance) ! 494: datum *key; ! 495: char *name, *instance; ! 496: { ! 497: static char keystring[ANAME_SZ + INST_SZ]; ! 498: ! 499: bzero(keystring, ANAME_SZ + INST_SZ); ! 500: strncpy(keystring, name, ANAME_SZ); ! 501: strncpy(&keystring[ANAME_SZ], instance, INST_SZ); ! 502: key->dptr = keystring; ! 503: key->dsize = ANAME_SZ + INST_SZ; ! 504: } ! 505: ! 506: static void ! 507: decode_princ_key(key, name, instance) ! 508: datum *key; ! 509: char *name, *instance; ! 510: { ! 511: strncpy(name, key->dptr, ANAME_SZ); ! 512: strncpy(instance, key->dptr + ANAME_SZ, INST_SZ); ! 513: name[ANAME_SZ - 1] = '\0'; ! 514: instance[INST_SZ - 1] = '\0'; ! 515: } ! 516: ! 517: static void ! 518: encode_princ_contents(contents, principal) ! 519: datum *contents; ! 520: Principal *principal; ! 521: { ! 522: contents->dsize = sizeof(*principal); ! 523: contents->dptr = (char *) principal; ! 524: } ! 525: ! 526: static void ! 527: decode_princ_contents(contents, principal) ! 528: datum *contents; ! 529: Principal *principal; ! 530: { ! 531: bcopy(contents->dptr, (char *) principal, sizeof(*principal)); ! 532: } ! 533: ! 534: kerb_db_get_stat(s) ! 535: DB_stat *s; ! 536: { ! 537: gettimeofday(×tamp, NULL); ! 538: ! 539: ! 540: s->cpu = 0; ! 541: s->elapsed = 0; ! 542: s->dio = 0; ! 543: s->pfault = 0; ! 544: s->t_stamp = timestamp.tv_sec; ! 545: s->n_retrieve = 0; ! 546: s->n_replace = 0; ! 547: s->n_append = 0; ! 548: s->n_get_stat = 0; ! 549: s->n_put_stat = 0; ! 550: /* update local copy too */ ! 551: } ! 552: ! 553: kerb_db_put_stat(s) ! 554: DB_stat *s; ! 555: { ! 556: } ! 557: ! 558: delta_stat(a, b, c) ! 559: DB_stat *a, *b, *c; ! 560: { ! 561: /* c = a - b then b = a for the next time */ ! 562: ! 563: c->cpu = a->cpu - b->cpu; ! 564: c->elapsed = a->elapsed - b->elapsed; ! 565: c->dio = a->dio - b->dio; ! 566: c->pfault = a->pfault - b->pfault; ! 567: c->t_stamp = a->t_stamp - b->t_stamp; ! 568: c->n_retrieve = a->n_retrieve - b->n_retrieve; ! 569: c->n_replace = a->n_replace - b->n_replace; ! 570: c->n_append = a->n_append - b->n_append; ! 571: c->n_get_stat = a->n_get_stat - b->n_get_stat; ! 572: c->n_put_stat = a->n_put_stat - b->n_put_stat; ! 573: ! 574: bcopy(a, b, sizeof(DB_stat)); ! 575: return; ! 576: } ! 577: ! 578: /* ! 579: * look up a dba in the data base returns number of dbas found , and ! 580: * whether there were more than requested. ! 581: */ ! 582: ! 583: kerb_db_get_dba(dba_name, dba_inst, dba, max, more) ! 584: char *dba_name; /* could have wild card */ ! 585: char *dba_inst; /* could have wild card */ ! 586: Dba *dba; ! 587: unsigned int max; /* max number of name structs to return */ ! 588: int *more; /* where there more than 'max' tuples? */ ! 589: ! 590: { ! 591: *more = 0; ! 592: return (0); ! 593: } ! 594: ! 595: kerb_db_iterate (func, arg) ! 596: int (*func)(); ! 597: char *arg; /* void *, really */ ! 598: { ! 599: datum key, contents; ! 600: Principal *principal; ! 601: int code; ! 602: DBM *db; ! 603: ! 604: kerb_db_init(); /* initialize and open the database */ ! 605: if ((code = kerb_dbl_lock(KERB_DBL_SHARED)) != 0) ! 606: return code; ! 607: ! 608: db = dbm_open(current_db_name, O_RDONLY, 0600); ! 609: ! 610: for (key = dbm_firstkey (db); key.dptr != NULL; key = dbm_next(db, key)) { ! 611: contents = dbm_fetch (db, key); ! 612: /* XXX may not be properly aligned */ ! 613: principal = (Principal *) contents.dptr; ! 614: if ((code = (*func)(arg, principal)) != 0) ! 615: return code; ! 616: } ! 617: dbm_close(db); ! 618: kerb_dbl_unlock(); ! 619: return 0; ! 620: } ! 621: ! 622: static int dblfd = -1; ! 623: static int mylock = 0; ! 624: static int inited = 0; ! 625: ! 626: static kerb_dbl_init() ! 627: { ! 628: if (!inited) { ! 629: char *filename = gen_dbsuffix (current_db_name, ".ok"); ! 630: if ((dblfd = open(filename, 0)) < 0) { ! 631: fprintf(stderr, "kerb_dbl_init: couldn't open %s\n", filename); ! 632: fflush(stderr); ! 633: perror("open"); ! 634: exit(1); ! 635: } ! 636: free(filename); ! 637: inited++; ! 638: } ! 639: return (0); ! 640: } ! 641: ! 642: static void kerb_dbl_fini() ! 643: { ! 644: close(dblfd); ! 645: dblfd = -1; ! 646: inited = 0; ! 647: mylock = 0; ! 648: } ! 649: ! 650: static int kerb_dbl_lock(mode) ! 651: int mode; ! 652: { ! 653: int flock_mode; ! 654: ! 655: if (!inited) ! 656: kerb_dbl_init(); ! 657: if (mylock) { /* Detect lock call when lock already ! 658: * locked */ ! 659: fprintf(stderr, "Kerberos locking error (mylock)\n"); ! 660: fflush(stderr); ! 661: exit(1); ! 662: } ! 663: switch (mode) { ! 664: case KERB_DBL_EXCLUSIVE: ! 665: flock_mode = LOCK_EX; ! 666: break; ! 667: case KERB_DBL_SHARED: ! 668: flock_mode = LOCK_SH; ! 669: break; ! 670: default: ! 671: fprintf(stderr, "invalid lock mode %d\n", mode); ! 672: abort(); ! 673: } ! 674: if (non_blocking) ! 675: flock_mode |= LOCK_NB; ! 676: ! 677: if (flock(dblfd, flock_mode) < 0) ! 678: return errno; ! 679: mylock++; ! 680: return 0; ! 681: } ! 682: ! 683: static void kerb_dbl_unlock() ! 684: { ! 685: if (!mylock) { /* lock already unlocked */ ! 686: fprintf(stderr, "Kerberos database lock not locked when unlocking.\n"); ! 687: fflush(stderr); ! 688: exit(1); ! 689: } ! 690: if (flock(dblfd, LOCK_UN) < 0) { ! 691: fprintf(stderr, "Kerberos database lock error. (unlocking)\n"); ! 692: fflush(stderr); ! 693: perror("flock"); ! 694: exit(1); ! 695: } ! 696: mylock = 0; ! 697: } ! 698: ! 699: int kerb_db_set_lockmode(mode) ! 700: int mode; ! 701: { ! 702: int old = non_blocking; ! 703: non_blocking = mode; ! 704: return old; ! 705: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.