154925bf6Swillf /* 254925bf6Swillf * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 354925bf6Swillf * Use is subject to license terms. 454925bf6Swillf */ 554925bf6Swillf 654925bf6Swillf /* 754925bf6Swillf * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 854925bf6Swillf * 954925bf6Swillf * Openvision retains the copyright to derivative works of 1054925bf6Swillf * this source code. Do *NOT* create a derivative of this 1154925bf6Swillf * source code before consulting with your legal department. 1254925bf6Swillf * Do *NOT* integrate *ANY* of this source code into another 1354925bf6Swillf * product before consulting with your legal department. 1454925bf6Swillf * 1554925bf6Swillf * For further information, read the top-level Openvision 1654925bf6Swillf * copyright which is contained in the top-level MIT Kerberos 1754925bf6Swillf * copyright. 1854925bf6Swillf * 1954925bf6Swillf * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING 2054925bf6Swillf * 2154925bf6Swillf */ 2254925bf6Swillf 2354925bf6Swillf 2454925bf6Swillf /* 2554925bf6Swillf * Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved 2654925bf6Swillf */ 2754925bf6Swillf 2854925bf6Swillf #include <sys/file.h> 2954925bf6Swillf #include <fcntl.h> 3054925bf6Swillf #include <unistd.h> 3154925bf6Swillf #include <k5-int.h> 3254925bf6Swillf #include "policy_db.h" 3354925bf6Swillf #include <stdlib.h> 3454925bf6Swillf #include <db.h> 3554925bf6Swillf 3654925bf6Swillf #define MAX_LOCK_TRIES 5 3754925bf6Swillf 3854925bf6Swillf struct _locklist { 3954925bf6Swillf osa_adb_lock_ent lockinfo; 4054925bf6Swillf struct _locklist *next; 4154925bf6Swillf }; 4254925bf6Swillf 4354925bf6Swillf krb5_error_code osa_adb_create_db(char *filename, char *lockfilename, 4454925bf6Swillf int magic) 4554925bf6Swillf { 4654925bf6Swillf int lf; 4754925bf6Swillf DB *db; 4854925bf6Swillf BTREEINFO btinfo; 4954925bf6Swillf 5054925bf6Swillf memset(&btinfo, 0, sizeof(btinfo)); 5154925bf6Swillf btinfo.flags = 0; 5254925bf6Swillf btinfo.cachesize = 0; 5354925bf6Swillf btinfo.psize = 4096; 5454925bf6Swillf btinfo.lorder = 0; 5554925bf6Swillf btinfo.minkeypage = 0; 5654925bf6Swillf btinfo.compare = NULL; 5754925bf6Swillf btinfo.prefix = NULL; 5854925bf6Swillf db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo); 5954925bf6Swillf if (db == NULL) 6054925bf6Swillf return errno; 6154925bf6Swillf if (db->close(db) < 0) 6254925bf6Swillf return errno; 6354925bf6Swillf 6454925bf6Swillf /* only create the lock file if we successfully created the db */ 6554925bf6Swillf lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600); 6654925bf6Swillf if (lf == -1) 6754925bf6Swillf return errno; 6854925bf6Swillf (void) close(lf); 6954925bf6Swillf 7054925bf6Swillf return OSA_ADB_OK; 7154925bf6Swillf } 7254925bf6Swillf 7354925bf6Swillf krb5_error_code osa_adb_destroy_db(char *filename, char *lockfilename, 7454925bf6Swillf int magic) 7554925bf6Swillf { 7654925bf6Swillf /* the admin databases do not contain security-critical data */ 7754925bf6Swillf if (unlink(filename) < 0 || 7854925bf6Swillf unlink(lockfilename) < 0) 7954925bf6Swillf return errno; 8054925bf6Swillf return OSA_ADB_OK; 8154925bf6Swillf } 8254925bf6Swillf 8354925bf6Swillf krb5_error_code osa_adb_rename_db(char *filefrom, char *lockfrom, 8454925bf6Swillf char *fileto, char *lockto, int magic) 8554925bf6Swillf { 8654925bf6Swillf osa_adb_db_t fromdb, todb; 8754925bf6Swillf krb5_error_code ret; 8854925bf6Swillf 8954925bf6Swillf /* make sure todb exists */ 9054925bf6Swillf /*LINTED*/ 9154925bf6Swillf if ((ret = osa_adb_create_db(fileto, lockto, magic)) && 9254925bf6Swillf ret != EEXIST) 9354925bf6Swillf return ret; 9454925bf6Swillf 9554925bf6Swillf if ((ret = osa_adb_init_db(&fromdb, filefrom, lockfrom, magic))) 9654925bf6Swillf return ret; 9754925bf6Swillf if ((ret = osa_adb_init_db(&todb, fileto, lockto, magic))) { 9854925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 9954925bf6Swillf return ret; 10054925bf6Swillf } 10154925bf6Swillf if ((ret = osa_adb_get_lock(fromdb, KRB5_DB_LOCKMODE_PERMANENT))) { 10254925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 10354925bf6Swillf (void) osa_adb_fini_db(todb, magic); 10454925bf6Swillf return ret; 10554925bf6Swillf } 10654925bf6Swillf if ((ret = osa_adb_get_lock(todb, KRB5_DB_LOCKMODE_PERMANENT))) { 10754925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 10854925bf6Swillf (void) osa_adb_fini_db(todb, magic); 10954925bf6Swillf return ret; 11054925bf6Swillf } 11154925bf6Swillf if ((rename(filefrom, fileto) < 0)) { 11254925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 11354925bf6Swillf (void) osa_adb_fini_db(todb, magic); 11454925bf6Swillf return errno; 11554925bf6Swillf } 11654925bf6Swillf /* 11754925bf6Swillf * Do not release the lock on fromdb because it is being renamed 11854925bf6Swillf * out of existence; no one can ever use it again. 11954925bf6Swillf */ 12054925bf6Swillf if ((ret = osa_adb_release_lock(todb))) { 12154925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 12254925bf6Swillf (void) osa_adb_fini_db(todb, magic); 12354925bf6Swillf return ret; 12454925bf6Swillf } 12554925bf6Swillf 12654925bf6Swillf (void) osa_adb_fini_db(fromdb, magic); 12754925bf6Swillf (void) osa_adb_fini_db(todb, magic); 12854925bf6Swillf return 0; 12954925bf6Swillf } 13054925bf6Swillf 13154925bf6Swillf krb5_error_code osa_adb_init_db(osa_adb_db_t *dbp, char *filename, 13254925bf6Swillf char *lockfilename, int magic) 13354925bf6Swillf { 13454925bf6Swillf osa_adb_db_t db; 13554925bf6Swillf static struct _locklist *locklist = NULL; 13654925bf6Swillf struct _locklist *lockp; 13754925bf6Swillf krb5_error_code code; 13854925bf6Swillf 13954925bf6Swillf if (dbp == NULL || filename == NULL) 14054925bf6Swillf return EINVAL; 14154925bf6Swillf 14254925bf6Swillf db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent)); 14354925bf6Swillf if (db == NULL) 14454925bf6Swillf return ENOMEM; 14554925bf6Swillf 14654925bf6Swillf memset(db, 0, sizeof(*db)); 14754925bf6Swillf db->info.hash = NULL; 14854925bf6Swillf db->info.bsize = 256; 14954925bf6Swillf db->info.ffactor = 8; 15054925bf6Swillf db->info.nelem = 25000; 15154925bf6Swillf db->info.lorder = 0; 15254925bf6Swillf 15354925bf6Swillf db->btinfo.flags = 0; 15454925bf6Swillf db->btinfo.cachesize = 0; 15554925bf6Swillf db->btinfo.psize = 4096; 15654925bf6Swillf db->btinfo.lorder = 0; 15754925bf6Swillf db->btinfo.minkeypage = 0; 15854925bf6Swillf db->btinfo.compare = NULL; 15954925bf6Swillf db->btinfo.prefix = NULL; 16054925bf6Swillf /* 16154925bf6Swillf * A process is allowed to open the same database multiple times 16254925bf6Swillf * and access it via different handles. If the handles use 16354925bf6Swillf * distinct lockinfo structures, things get confused: lock(A), 16454925bf6Swillf * lock(B), release(B) will result in the kernel unlocking the 16554925bf6Swillf * lock file but handle A will still think the file is locked. 16654925bf6Swillf * Therefore, all handles using the same lock file must share a 16754925bf6Swillf * single lockinfo structure. 16854925bf6Swillf * 16954925bf6Swillf * It is not sufficient to have a single lockinfo structure, 17054925bf6Swillf * however, because a single process may also wish to open 17154925bf6Swillf * multiple different databases simultaneously, with different 17254925bf6Swillf * lock files. This code used to use a single static lockinfo 17354925bf6Swillf * structure, which means that the second database opened used 17454925bf6Swillf * the first database's lock file. This was Bad. 17554925bf6Swillf * 17654925bf6Swillf * We now maintain a linked list of lockinfo structures, keyed by 17754925bf6Swillf * lockfilename. An entry is added when this function is called 17854925bf6Swillf * with a new lockfilename, and all subsequent calls with that 17954925bf6Swillf * lockfilename use the existing entry, updating the refcnt. 18054925bf6Swillf * When the database is closed with fini_db(), the refcnt is 18154925bf6Swillf * decremented, and when it is zero the lockinfo structure is 18254925bf6Swillf * freed and reset. The entry in the linked list, however, is 18354925bf6Swillf * never removed; it will just be reinitialized the next time 18454925bf6Swillf * init_db is called with the right lockfilename. 18554925bf6Swillf */ 18654925bf6Swillf 18754925bf6Swillf /* find or create the lockinfo structure for lockfilename */ 18854925bf6Swillf lockp = locklist; 18954925bf6Swillf while (lockp) { 19054925bf6Swillf if (strcmp(lockp->lockinfo.filename, lockfilename) == 0) 19154925bf6Swillf break; 19254925bf6Swillf else 19354925bf6Swillf lockp = lockp->next; 19454925bf6Swillf } 19554925bf6Swillf if (lockp == NULL) { 19654925bf6Swillf /* doesn't exist, create it, add to list */ 19754925bf6Swillf lockp = (struct _locklist *) malloc(sizeof(*lockp)); 19854925bf6Swillf if (lockp == NULL) { 19954925bf6Swillf free(db); 20054925bf6Swillf return ENOMEM; 20154925bf6Swillf } 20254925bf6Swillf memset(lockp, 0, sizeof(*lockp)); 20354925bf6Swillf lockp->next = locklist; 20454925bf6Swillf locklist = lockp; 20554925bf6Swillf } 20654925bf6Swillf 20754925bf6Swillf /* now initialize lockp->lockinfo if necessary */ 20854925bf6Swillf if (lockp->lockinfo.lockfile == NULL) { 20954925bf6Swillf if ((code = krb5int_init_context_kdc(&lockp->lockinfo.context))) { 21054925bf6Swillf free(db); 21154925bf6Swillf return((krb5_error_code) code); 21254925bf6Swillf } 21354925bf6Swillf 21454925bf6Swillf /* 21554925bf6Swillf * needs be open read/write so that write locking can work with 21654925bf6Swillf * POSIX systems 21754925bf6Swillf */ 21854925bf6Swillf lockp->lockinfo.filename = strdup(lockfilename); 21954925bf6Swillf if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+F")) == NULL) { 22054925bf6Swillf /* 22154925bf6Swillf * maybe someone took away write permission so we could only 22254925bf6Swillf * get shared locks? 22354925bf6Swillf */ 22454925bf6Swillf if ((lockp->lockinfo.lockfile = fopen(lockfilename, "rF")) 22554925bf6Swillf == NULL) { 22654925bf6Swillf free(db); 22754925bf6Swillf return OSA_ADB_NOLOCKFILE; 22854925bf6Swillf } 22954925bf6Swillf } 23054925bf6Swillf lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0; 23154925bf6Swillf } 23254925bf6Swillf 23354925bf6Swillf /* lockp is set, lockinfo is initialized, update the reference count */ 23454925bf6Swillf db->lock = &lockp->lockinfo; 23554925bf6Swillf db->lock->refcnt++; 23654925bf6Swillf 23754925bf6Swillf db->opencnt = 0; 23854925bf6Swillf db->filename = strdup(filename); 23954925bf6Swillf db->magic = magic; 24054925bf6Swillf 24154925bf6Swillf *dbp = db; 24254925bf6Swillf 24354925bf6Swillf return OSA_ADB_OK; 24454925bf6Swillf } 24554925bf6Swillf 24654925bf6Swillf krb5_error_code osa_adb_fini_db(osa_adb_db_t db, int magic) 24754925bf6Swillf { 24854925bf6Swillf if (db->magic != magic) 24954925bf6Swillf return EINVAL; 25054925bf6Swillf if (db->lock->refcnt == 0) { 25154925bf6Swillf /* barry says this can't happen */ 25254925bf6Swillf return OSA_ADB_FAILURE; 25354925bf6Swillf } else { 25454925bf6Swillf db->lock->refcnt--; 25554925bf6Swillf } 25654925bf6Swillf 25754925bf6Swillf if (db->lock->refcnt == 0) { 25854925bf6Swillf /* 25954925bf6Swillf * Don't free db->lock->filename, it is used as a key to 26054925bf6Swillf * find the lockinfo entry in the linked list. If the 26154925bf6Swillf * lockfile doesn't exist, we must be closing the database 26254925bf6Swillf * after trashing it. This has to be allowed, so don't 26354925bf6Swillf * generate an error. 26454925bf6Swillf */ 26554925bf6Swillf if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT) 26654925bf6Swillf (void) fclose(db->lock->lockfile); 26754925bf6Swillf db->lock->lockfile = NULL; 26854925bf6Swillf krb5_free_context(db->lock->context); 26954925bf6Swillf } 27054925bf6Swillf 27154925bf6Swillf db->magic = 0; 27254925bf6Swillf free(db->filename); 27354925bf6Swillf free(db); 27454925bf6Swillf return OSA_ADB_OK; 27554925bf6Swillf } 27654925bf6Swillf 27754925bf6Swillf krb5_error_code osa_adb_get_lock(osa_adb_db_t db, int mode) 27854925bf6Swillf { 27954925bf6Swillf int tries, gotlock, perm, krb5_mode, ret = 0; 28054925bf6Swillf 28154925bf6Swillf if (db->lock->lockmode >= mode) { 28254925bf6Swillf /* No need to upgrade lock, just incr refcnt and return */ 28354925bf6Swillf db->lock->lockcnt++; 28454925bf6Swillf return(OSA_ADB_OK); 28554925bf6Swillf } 28654925bf6Swillf 28754925bf6Swillf perm = 0; 28854925bf6Swillf switch (mode) { 28954925bf6Swillf case KRB5_DB_LOCKMODE_PERMANENT: 29054925bf6Swillf perm = 1; 291*0b16192fSToomas Soome /* FALLTHROUGH */ 29254925bf6Swillf case KRB5_DB_LOCKMODE_EXCLUSIVE: 29354925bf6Swillf krb5_mode = KRB5_LOCKMODE_EXCLUSIVE; 29454925bf6Swillf break; 29554925bf6Swillf case KRB5_DB_LOCKMODE_SHARED: 29654925bf6Swillf krb5_mode = KRB5_LOCKMODE_SHARED; 29754925bf6Swillf break; 29854925bf6Swillf default: 29954925bf6Swillf return(EINVAL); 30054925bf6Swillf } 30154925bf6Swillf 30254925bf6Swillf for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) { 30354925bf6Swillf if ((ret = krb5_lock_file(db->lock->context, 30454925bf6Swillf fileno(db->lock->lockfile), 30554925bf6Swillf krb5_mode|KRB5_LOCKMODE_DONTBLOCK)) == 0) { 30654925bf6Swillf gotlock++; 30754925bf6Swillf break; 30854925bf6Swillf } else if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE) 30954925bf6Swillf /* tried to exclusive-lock something we don't have */ 31054925bf6Swillf /* write access to */ 31154925bf6Swillf return OSA_ADB_NOEXCL_PERM; 31254925bf6Swillf 31354925bf6Swillf sleep(1); 31454925bf6Swillf } 31554925bf6Swillf 31654925bf6Swillf /* test for all the likely "can't get lock" error codes */ 31754925bf6Swillf if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK) 31854925bf6Swillf return OSA_ADB_CANTLOCK_DB; 31954925bf6Swillf else if (ret != 0) 32054925bf6Swillf return ret; 32154925bf6Swillf 32254925bf6Swillf /* 32354925bf6Swillf * If the file no longer exists, someone acquired a permanent 32454925bf6Swillf * lock. If that process terminates its exclusive lock is lost, 32554925bf6Swillf * but if we already had the file open we can (probably) lock it 32654925bf6Swillf * even though it has been unlinked. So we need to insist that 32754925bf6Swillf * it exist. 32854925bf6Swillf */ 32954925bf6Swillf if (access(db->lock->filename, F_OK) < 0) { 33054925bf6Swillf (void) krb5_lock_file(db->lock->context, 33154925bf6Swillf fileno(db->lock->lockfile), 33254925bf6Swillf KRB5_LOCKMODE_UNLOCK); 33354925bf6Swillf return OSA_ADB_NOLOCKFILE; 33454925bf6Swillf } 33554925bf6Swillf 33654925bf6Swillf /* we have the shared/exclusive lock */ 33754925bf6Swillf 33854925bf6Swillf if (perm) { 33954925bf6Swillf if (unlink(db->lock->filename) < 0) { 34054925bf6Swillf /* somehow we can't delete the file, but we already */ 34154925bf6Swillf /* have the lock, so release it and return */ 34254925bf6Swillf 34354925bf6Swillf ret = errno; 34454925bf6Swillf (void) krb5_lock_file(db->lock->context, 34554925bf6Swillf fileno(db->lock->lockfile), 34654925bf6Swillf KRB5_LOCKMODE_UNLOCK); 34754925bf6Swillf 34854925bf6Swillf /* maybe we should return CANTLOCK_DB.. but that would */ 34954925bf6Swillf /* look just like the db was already locked */ 35054925bf6Swillf return ret; 35154925bf6Swillf } 35254925bf6Swillf 35354925bf6Swillf /* this releases our exclusive lock.. which is okay because */ 35454925bf6Swillf /* now no one else can get one either */ 35554925bf6Swillf (void) fclose(db->lock->lockfile); 35654925bf6Swillf } 35754925bf6Swillf 35854925bf6Swillf db->lock->lockmode = mode; 35954925bf6Swillf db->lock->lockcnt++; 36054925bf6Swillf return OSA_ADB_OK; 36154925bf6Swillf } 36254925bf6Swillf 36354925bf6Swillf krb5_error_code osa_adb_release_lock(osa_adb_db_t db) 36454925bf6Swillf { 36554925bf6Swillf int ret, fd; 36654925bf6Swillf 36754925bf6Swillf if (!db->lock->lockcnt) /* lock already unlocked */ 36854925bf6Swillf return OSA_ADB_NOTLOCKED; 36954925bf6Swillf 37054925bf6Swillf if (--db->lock->lockcnt == 0) { 37154925bf6Swillf if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) { 37254925bf6Swillf /* now we need to create the file since it does not exist */ 37354925bf6Swillf fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL, 37454925bf6Swillf 0600); 37554925bf6Swillf if ((db->lock->lockfile = fdopen(fd, "w+F")) == NULL) 37654925bf6Swillf return OSA_ADB_NOLOCKFILE; 37754925bf6Swillf } else if ((ret = krb5_lock_file(db->lock->context, 37854925bf6Swillf fileno(db->lock->lockfile), 37954925bf6Swillf KRB5_LOCKMODE_UNLOCK))) 38054925bf6Swillf return ret; 38154925bf6Swillf 38254925bf6Swillf db->lock->lockmode = 0; 38354925bf6Swillf } 38454925bf6Swillf return OSA_ADB_OK; 38554925bf6Swillf } 38654925bf6Swillf 38754925bf6Swillf krb5_error_code osa_adb_open_and_lock(osa_adb_princ_t db, int locktype) 38854925bf6Swillf { 38954925bf6Swillf int ret; 39054925bf6Swillf 39154925bf6Swillf ret = osa_adb_get_lock(db, locktype); 39254925bf6Swillf if (ret != OSA_ADB_OK) 39354925bf6Swillf return ret; 39454925bf6Swillf if (db->opencnt) 39554925bf6Swillf goto open_ok; 39654925bf6Swillf 39754925bf6Swillf db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo); 39854925bf6Swillf if (db->db != NULL) 39954925bf6Swillf goto open_ok; 40054925bf6Swillf switch (errno) { 40154925bf6Swillf #ifdef EFTYPE 40254925bf6Swillf case EFTYPE: 40354925bf6Swillf #endif 40454925bf6Swillf case EINVAL: 40554925bf6Swillf db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info); 40654925bf6Swillf if (db->db != NULL) 40754925bf6Swillf goto open_ok; 408*0b16192fSToomas Soome /* FALLTHROUGH */ 40954925bf6Swillf default: 41054925bf6Swillf (void) osa_adb_release_lock(db); 41154925bf6Swillf if (errno == EINVAL) 41254925bf6Swillf return OSA_ADB_BAD_DB; 41354925bf6Swillf return errno; 41454925bf6Swillf } 41554925bf6Swillf open_ok: 41654925bf6Swillf db->opencnt++; 41754925bf6Swillf return OSA_ADB_OK; 41854925bf6Swillf } 41954925bf6Swillf 42054925bf6Swillf krb5_error_code osa_adb_close_and_unlock(osa_adb_princ_t db) 42154925bf6Swillf { 42254925bf6Swillf if (--db->opencnt) 42354925bf6Swillf return osa_adb_release_lock(db); 42454925bf6Swillf if(db->db != NULL && db->db->close(db->db) == -1) { 42554925bf6Swillf (void) osa_adb_release_lock(db); 42654925bf6Swillf return OSA_ADB_FAILURE; 42754925bf6Swillf } 42854925bf6Swillf 42954925bf6Swillf db->db = NULL; 43054925bf6Swillf 43154925bf6Swillf return(osa_adb_release_lock(db)); 43254925bf6Swillf } 433