Path: blob/main/crypto/krb5/src/plugins/kdb/db2/adb_openclose.c
34914 views
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */1/*2* Copyright 1993 OpenVision Technologies, Inc., All Rights Reserved3*4* $Header$5*/67#include <k5-int.h>8#include <sys/file.h>9#include <fcntl.h>10#include <unistd.h>11#include "policy_db.h"12#include <stdlib.h>13#include <db.h>1415struct _locklist {16osa_adb_lock_ent lockinfo;17struct _locklist *next;18};1920krb5_error_code21osa_adb_create_db(char *filename, char *lockfilename, int magic)22{23int lf;24DB *db;25BTREEINFO btinfo;2627memset(&btinfo, 0, sizeof(btinfo));28btinfo.flags = 0;29btinfo.cachesize = 0;30btinfo.psize = 4096;31btinfo.lorder = 0;32btinfo.minkeypage = 0;33btinfo.compare = NULL;34btinfo.prefix = NULL;35db = dbopen(filename, O_RDWR | O_CREAT | O_EXCL, 0600, DB_BTREE, &btinfo);36if (db == NULL)37return errno;38if (db->close(db) < 0)39return errno;4041/* only create the lock file if we successfully created the db */42lf = THREEPARAMOPEN(lockfilename, O_RDWR | O_CREAT | O_EXCL, 0600);43if (lf == -1)44return errno;45(void) close(lf);4647return OSA_ADB_OK;48}4950krb5_error_code51osa_adb_destroy_db(char *filename, char *lockfilename, int magic)52{53/* the admin databases do not contain security-critical data */54if (unlink(filename) < 0 ||55unlink(lockfilename) < 0)56return errno;57return OSA_ADB_OK;58}5960krb5_error_code61osa_adb_init_db(osa_adb_db_t *dbp, char *filename, char *lockfilename,62int magic)63{64osa_adb_db_t db;65static struct _locklist *locklist = NULL;66struct _locklist *lockp;67krb5_error_code code;6869if (dbp == NULL || filename == NULL)70return EINVAL;7172db = (osa_adb_princ_t) malloc(sizeof(osa_adb_db_ent));73if (db == NULL)74return ENOMEM;7576memset(db, 0, sizeof(*db));77db->info.hash = NULL;78db->info.bsize = 256;79db->info.ffactor = 8;80db->info.nelem = 25000;81db->info.lorder = 0;8283db->btinfo.flags = 0;84db->btinfo.cachesize = 0;85db->btinfo.psize = 4096;86db->btinfo.lorder = 0;87db->btinfo.minkeypage = 0;88db->btinfo.compare = NULL;89db->btinfo.prefix = NULL;90/*91* A process is allowed to open the same database multiple times92* and access it via different handles. If the handles use93* distinct lockinfo structures, things get confused: lock(A),94* lock(B), release(B) will result in the kernel unlocking the95* lock file but handle A will still think the file is locked.96* Therefore, all handles using the same lock file must share a97* single lockinfo structure.98*99* It is not sufficient to have a single lockinfo structure,100* however, because a single process may also wish to open101* multiple different databases simultaneously, with different102* lock files. This code used to use a single static lockinfo103* structure, which means that the second database opened used104* the first database's lock file. This was Bad.105*106* We now maintain a linked list of lockinfo structures, keyed by107* lockfilename. An entry is added when this function is called108* with a new lockfilename, and all subsequent calls with that109* lockfilename use the existing entry, updating the refcnt.110* When the database is closed with fini_db(), the refcnt is111* decremented, and when it is zero the lockinfo structure is112* freed and reset. The entry in the linked list, however, is113* never removed; it will just be reinitialized the next time114* init_db is called with the right lockfilename.115*/116117/* find or create the lockinfo structure for lockfilename */118lockp = locklist;119while (lockp) {120if (strcmp(lockp->lockinfo.filename, lockfilename) == 0)121break;122else123lockp = lockp->next;124}125if (lockp == NULL) {126/* doesn't exist, create it, add to list */127lockp = (struct _locklist *) malloc(sizeof(*lockp));128if (lockp == NULL) {129free(db);130return ENOMEM;131}132memset(lockp, 0, sizeof(*lockp));133lockp->lockinfo.filename = strdup(lockfilename);134if (lockp->lockinfo.filename == NULL) {135free(lockp);136free(db);137return ENOMEM;138}139lockp->next = locklist;140locklist = lockp;141}142143/* now initialize lockp->lockinfo if necessary */144if (lockp->lockinfo.lockfile == NULL) {145if ((code = krb5int_init_context_kdc(&lockp->lockinfo.context))) {146free(db);147return((krb5_error_code) code);148}149150/*151* needs be open read/write so that write locking can work with152* POSIX systems153*/154if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r+")) == NULL) {155/*156* maybe someone took away write permission so we could only157* get shared locks?158*/159if ((lockp->lockinfo.lockfile = fopen(lockfilename, "r"))160== NULL) {161free(db);162return OSA_ADB_NOLOCKFILE;163}164}165set_cloexec_file(lockp->lockinfo.lockfile);166lockp->lockinfo.lockmode = lockp->lockinfo.lockcnt = 0;167}168169/* lockp is set, lockinfo is initialized, update the reference count */170db->lock = &lockp->lockinfo;171db->lock->refcnt++;172173db->opencnt = 0;174db->filename = strdup(filename);175db->magic = magic;176177*dbp = db;178179return OSA_ADB_OK;180}181182krb5_error_code183osa_adb_fini_db(osa_adb_db_t db, int magic)184{185if (db->magic != magic)186return EINVAL;187if (db->lock->refcnt == 0) {188/* barry says this can't happen */189return OSA_ADB_FAILURE;190} else {191db->lock->refcnt--;192}193194if (db->lock->refcnt == 0) {195/*196* Don't free db->lock->filename, it is used as a key to197* find the lockinfo entry in the linked list. If the198* lockfile doesn't exist, we must be closing the database199* after trashing it. This has to be allowed, so don't200* generate an error.201*/202if (db->lock->lockmode != KRB5_DB_LOCKMODE_PERMANENT)203(void) fclose(db->lock->lockfile);204db->lock->lockfile = NULL;205krb5_free_context(db->lock->context);206}207208db->magic = 0;209free(db->filename);210free(db);211return OSA_ADB_OK;212}213214krb5_error_code215osa_adb_get_lock(osa_adb_db_t db, int mode)216{217int perm, krb5_mode, ret = 0;218219if (db->lock->lockmode >= mode) {220/* No need to upgrade lock, just incr refcnt and return */221db->lock->lockcnt++;222return(OSA_ADB_OK);223}224225perm = 0;226switch (mode) {227case KRB5_DB_LOCKMODE_PERMANENT:228perm = 1;229case KRB5_DB_LOCKMODE_EXCLUSIVE:230krb5_mode = KRB5_LOCKMODE_EXCLUSIVE;231break;232case KRB5_DB_LOCKMODE_SHARED:233krb5_mode = KRB5_LOCKMODE_SHARED;234break;235default:236return(EINVAL);237}238239ret = krb5_lock_file(db->lock->context, fileno(db->lock->lockfile),240krb5_mode);241if (ret == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)242return OSA_ADB_NOEXCL_PERM;243else if (ret == EACCES || ret == EAGAIN || ret == EWOULDBLOCK)244return OSA_ADB_CANTLOCK_DB;245else if (ret != 0)246return ret;247248/*249* If the file no longer exists, someone acquired a permanent250* lock. If that process terminates its exclusive lock is lost,251* but if we already had the file open we can (probably) lock it252* even though it has been unlinked. So we need to insist that253* it exist.254*/255if (access(db->lock->filename, F_OK) < 0) {256(void) krb5_lock_file(db->lock->context,257fileno(db->lock->lockfile),258KRB5_LOCKMODE_UNLOCK);259return OSA_ADB_NOLOCKFILE;260}261262/* we have the shared/exclusive lock */263264if (perm) {265if (unlink(db->lock->filename) < 0) {266/* somehow we can't delete the file, but we already */267/* have the lock, so release it and return */268269ret = errno;270(void) krb5_lock_file(db->lock->context,271fileno(db->lock->lockfile),272KRB5_LOCKMODE_UNLOCK);273274/* maybe we should return CANTLOCK_DB.. but that would */275/* look just like the db was already locked */276return ret;277}278279/* this releases our exclusive lock.. which is okay because */280/* now no one else can get one either */281(void) fclose(db->lock->lockfile);282}283284db->lock->lockmode = mode;285db->lock->lockcnt++;286return OSA_ADB_OK;287}288289krb5_error_code290osa_adb_release_lock(osa_adb_db_t db)291{292int ret, fd;293294if (!db->lock->lockcnt) /* lock already unlocked */295return OSA_ADB_NOTLOCKED;296297if (--db->lock->lockcnt == 0) {298if (db->lock->lockmode == KRB5_DB_LOCKMODE_PERMANENT) {299/* now we need to create the file since it does not exist */300fd = THREEPARAMOPEN(db->lock->filename,O_RDWR | O_CREAT | O_EXCL,3010600);302if (fd < 0)303return OSA_ADB_NOLOCKFILE;304set_cloexec_fd(fd);305if ((db->lock->lockfile = fdopen(fd, "w+")) == NULL)306return OSA_ADB_NOLOCKFILE;307} else if ((ret = krb5_lock_file(db->lock->context,308fileno(db->lock->lockfile),309KRB5_LOCKMODE_UNLOCK)))310return ret;311312db->lock->lockmode = 0;313}314return OSA_ADB_OK;315}316317krb5_error_code318osa_adb_open_and_lock(osa_adb_princ_t db, int locktype)319{320int ret;321322ret = osa_adb_get_lock(db, locktype);323if (ret != OSA_ADB_OK)324return ret;325if (db->opencnt)326goto open_ok;327328db->db = dbopen(db->filename, O_RDWR, 0600, DB_BTREE, &db->btinfo);329if (db->db == NULL && IS_EFTYPE(errno))330db->db = dbopen(db->filename, O_RDWR, 0600, DB_HASH, &db->info);331if (db->db == NULL) {332(void)osa_adb_release_lock(db);333return (errno == EINVAL) ? OSA_ADB_BAD_DB : errno;334}335336open_ok:337db->opencnt++;338return OSA_ADB_OK;339}340341krb5_error_code342osa_adb_close_and_unlock(osa_adb_princ_t db)343{344if (--db->opencnt)345return osa_adb_release_lock(db);346if(db->db != NULL && db->db->close(db->db) == -1) {347(void) osa_adb_release_lock(db);348return OSA_ADB_FAILURE;349}350351db->db = NULL;352353return(osa_adb_release_lock(db));354}355356357