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
osa_adb_create_db(char * filename,char * lockfilename,int magic)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
osa_adb_destroy_db(char * filename,char * lockfilename,int magic)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
osa_adb_rename_db(char * filefrom,char * lockfrom,char * fileto,char * lockto,int magic)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
osa_adb_init_db(osa_adb_db_t * dbp,char * filename,char * lockfilename,int magic)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
osa_adb_fini_db(osa_adb_db_t db,int magic)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
osa_adb_get_lock(osa_adb_db_t db,int mode)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
osa_adb_release_lock(osa_adb_db_t db)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
osa_adb_open_and_lock(osa_adb_princ_t db,int locktype)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
osa_adb_close_and_unlock(osa_adb_princ_t db)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