#include "k5-int.h"
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <db.h>
#include <stdio.h>
#include <errno.h>
#include <utime.h>
#include "kdb5.h"
#include "kdb_db2.h"
#include "kdb_xdr.h"
#include "policy_db.h"
#define KDB_DB2_DATABASE_NAME "database_name"
#include "kdb_db2.h"
static char *gen_dbsuffix(char *, char *);
static krb5_error_code krb5_db2_db_start_update(krb5_context);
static krb5_error_code krb5_db2_db_end_update(krb5_context);
static krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int);
krb5_error_code krb5_db2_db_lock(krb5_context, int);
static krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int);
static char default_db_name[] = DEFAULT_KDB_FILE;
#define free_dbsuffix(name) free(name)
#define k5db2_inited(c) (c && c->dal_handle \
&& c->dal_handle->db_context \
&& ((krb5_db2_context *) c->dal_handle->db_context)->db_inited)
static krb5_error_code
krb5_db2_get_db_opt(char *input, char **opt, char **val)
{
char *pos = strchr(input, '=');
if (pos == NULL) {
*opt = NULL;
*val = strdup(input);
if (*val == NULL) {
return ENOMEM;
}
} else {
*opt = malloc((pos - input) + 1);
*val = strdup(pos + 1);
if (!*opt || !*val) {
free(*opt);
*opt = NULL;
free(*val);
*val = NULL;
return ENOMEM;
}
memcpy(*opt, input, pos - input);
(*opt)[pos - input] = '\0';
}
return (0);
}
static void
k5db2_clear_context(krb5_db2_context *dbctx)
{
if (dbctx->db_lf_name)
free(dbctx->db_lf_name);
if (dbctx->db_name && (dbctx->db_name != default_db_name))
free(dbctx->db_name);
memset((char *) dbctx, 0, sizeof(krb5_db2_context));
dbctx->db_name = default_db_name;
dbctx->db_nb_locks = FALSE;
dbctx->tempdb = FALSE;
}
static krb5_error_code
k5db2_init_context(krb5_context context)
{
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
dal_handle = context->dal_handle;
if (dal_handle->db_context == NULL) {
db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
if (db_ctx == NULL)
return ENOMEM;
else {
memset((char *) db_ctx, 0, sizeof(krb5_db2_context));
k5db2_clear_context((krb5_db2_context *) db_ctx);
dal_handle->db_context = (void *) db_ctx;
}
}
return (0);
}
static char *
gen_dbsuffix(char *db_name, char *sfx)
{
char *dbsuffix;
if (sfx == NULL)
return ((char *) NULL);
if (asprintf(&dbsuffix, "%s%s", db_name, sfx) < 0)
return (0);
return dbsuffix;
}
static DB *
k5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb)
{
DB *db;
BTREEINFO bti;
HASHINFO hashi;
bti.flags = 0;
bti.cachesize = 0;
bti.psize = 4096;
bti.lorder = 0;
bti.minkeypage = 0;
bti.compare = NULL;
bti.prefix = NULL;
if (tempdb) {
fname = gen_dbsuffix(fname, "~");
} else {
fname = strdup(fname);
}
if (fname == NULL)
{
errno = ENOMEM;
return NULL;
}
hashi.bsize = 4096;
hashi.cachesize = 0;
hashi.ffactor = 40;
hashi.hash = NULL;
hashi.lorder = 0;
hashi.nelem = 1;
db = dbopen(fname, flags, mode,
dbc->hashfirst ? DB_HASH : DB_BTREE,
dbc->hashfirst ? (void *) &hashi : (void *) &bti);
if (db != NULL) {
free(fname);
return db;
}
switch (errno) {
#ifdef EFTYPE
case EFTYPE:
#endif
case EINVAL:
db = dbopen(fname, flags, mode,
dbc->hashfirst ? DB_BTREE : DB_HASH,
dbc->hashfirst ? (void *) &bti : (void *) &hashi);
if (db != NULL)
dbc->hashfirst = !dbc->hashfirst;
default:
free(fname);
return db;
}
}
static krb5_error_code
krb5_db2_db_set_hashfirst(krb5_context context, int hashfirst)
{
krb5_db2_context *dbc;
kdb5_dal_handle *dal_handle;
if (k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
dbc->hashfirst = hashfirst;
return 0;
}
krb5_error_code
krb5_db2_db_init(krb5_context context)
{
char *filename = NULL;
krb5_db2_context *db_ctx;
krb5_error_code retval;
kdb5_dal_handle *dal_handle;
char policy_db_name[1024], policy_lock_name[1024];
if (k5db2_inited(context))
return 0;
if ((retval = k5db2_init_context(context)))
return (retval);
dal_handle = context->dal_handle;
db_ctx = dal_handle->db_context;
db_ctx->db = NULL;
if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb
?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT)))
return ENOMEM;
db_ctx->db_lf_name = filename;
if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) {
if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) {
retval = errno;
goto err_out;
}
}
set_cloexec_fd(db_ctx->db_lf_file);
db_ctx->db_inited++;
if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time)))
goto err_out;
snprintf(policy_db_name, sizeof(policy_db_name),
db_ctx->tempdb ? "%s~.kadm5" : "%s.kadm5",
db_ctx->db_name);
snprintf(policy_lock_name, sizeof(policy_lock_name),
"%s.lock", policy_db_name);
if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name,
policy_lock_name, OSA_ADB_POLICY_DB_MAGIC)))
{
goto err_out;
}
return 0;
err_out:
db_ctx->db = NULL;
k5db2_clear_context(db_ctx);
return (retval);
}
krb5_error_code
krb5_db2_db_fini(krb5_context context)
{
krb5_error_code retval = 0;
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
dal_handle = context->dal_handle;
if (dal_handle == NULL) {
return 0;
}
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if (k5db2_inited(context)) {
if (close(db_ctx->db_lf_file))
retval = errno;
else
retval = 0;
}
if (db_ctx) {
if (db_ctx->policy_db) {
retval =
osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC);
if (retval)
return retval;
}
k5db2_clear_context(db_ctx);
dal_handle->db_context = NULL;
}
return retval;
}
krb5_error_code
krb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key)
{
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
dal_handle = context->dal_handle;
db_ctx = dal_handle->db_context;
db_ctx->db_master_key = key;
return 0;
}
krb5_error_code
krb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key)
{
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
dal_handle = context->dal_handle;
db_ctx = dal_handle->db_context;
*key = db_ctx->db_master_key;
return 0;
}
static krb5_error_code
krb5_db2_db_set_name(krb5_context context, char *name, int tempdb)
{
DB *db;
krb5_db2_context *db_ctx;
krb5_error_code kret;
kdb5_dal_handle *dal_handle;
if (k5db2_inited(context))
return KRB5_KDB_DBINITED;
if ((kret = k5db2_init_context(context)))
return (kret);
if (name == NULL)
name = default_db_name;
dal_handle = context->dal_handle;
db_ctx = dal_handle->db_context;
db_ctx->tempdb = tempdb;
db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb);
if (db == NULL)
return errno;
db_ctx->db_name = strdup(name);
(*db->close) (db);
return 0;
}
krb5_error_code
krb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age)
{
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
struct stat st;
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if (fstat(db_ctx->db_lf_file, &st) < 0)
*age = -1;
else
*age = st.st_mtime;
return 0;
}
static krb5_error_code
krb5_db2_db_start_update(krb5_context context)
{
return 0;
}
static krb5_error_code
krb5_db2_db_end_update(krb5_context context)
{
krb5_error_code retval;
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
struct stat st;
time_t now;
struct utimbuf utbuf;
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
retval = 0;
dal_handle = context->dal_handle;
db_ctx = dal_handle->db_context;
now = time((time_t *) NULL);
if (fstat(db_ctx->db_lf_file, &st) == 0) {
if (st.st_mtime >= now) {
utbuf.actime = st.st_mtime + 1;
utbuf.modtime = st.st_mtime + 1;
if (utime(db_ctx->db_lf_name, &utbuf))
retval = errno;
} else {
if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL))
retval = errno;
}
} else
retval = errno;
if (!retval) {
if (fstat(db_ctx->db_lf_file, &st) == 0)
db_ctx->db_lf_time = st.st_mtime;
else
retval = errno;
}
return (retval);
}
#define MAX_LOCK_TRIES 5
krb5_error_code
krb5_db2_db_lock(krb5_context context, int in_mode)
{
krb5_db2_context *db_ctx;
int krb5_lock_mode;
DB *db;
krb5_error_code retval;
time_t mod_time;
kdb5_dal_handle *dal_handle;
int mode, gotlock, tries;
switch (in_mode) {
case KRB5_DB_LOCKMODE_PERMANENT:
mode = KRB5_DB_LOCKMODE_EXCLUSIVE;
break;
case KRB5_DB_LOCKMODE_EXCLUSIVE:
mode = KRB5_LOCKMODE_EXCLUSIVE;
break;
case KRB5_DB_LOCKMODE_SHARED:
mode = KRB5_LOCKMODE_SHARED;
break;
default:
return EINVAL;
}
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) {
db_ctx->db_locks_held++;
goto policy_lock;
}
if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE))
return KRB5_KDB_BADLOCKMODE;
krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK;
for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode);
if (retval == 0) {
gotlock++;
break;
} else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
return KRB5_KDB_CANTLOCK_DB;
sleep(1);
}
if (retval == EACCES)
return KRB5_KDB_CANTLOCK_DB;
else if (retval == EAGAIN || retval == EWOULDBLOCK)
return OSA_ADB_CANTLOCK_DB;
else if (retval != 0)
return retval;
if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time)))
goto lock_error;
db = k5db2_dbopen(db_ctx, db_ctx->db_name,
mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb);
if (db) {
db_ctx->db_lf_time = mod_time;
db_ctx->db = db;
} else {
retval = errno;
db_ctx->db = NULL;
goto lock_error;
}
db_ctx->db_lock_mode = mode;
db_ctx->db_locks_held++;
policy_lock:
if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) {
krb5_db2_db_unlock(context);
}
return retval;
lock_error:;
db_ctx->db_lock_mode = 0;
db_ctx->db_locks_held = 0;
krb5_db2_db_unlock(context);
return retval;
}
krb5_error_code
krb5_db2_db_unlock(krb5_context context)
{
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
DB *db;
krb5_error_code retval;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if ((retval = osa_adb_release_lock(db_ctx->policy_db))) {
return retval;
}
if (!db_ctx->db_locks_held)
return KRB5_KDB_NOTLOCKED;
db = db_ctx->db;
if (--(db_ctx->db_locks_held) == 0) {
(*db->close) (db);
db_ctx->db = NULL;
retval = krb5_lock_file(context, db_ctx->db_lf_file,
KRB5_LOCKMODE_UNLOCK);
db_ctx->db_lock_mode = 0;
return (retval);
}
return 0;
}
krb5_error_code
krb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags)
{
register krb5_error_code retval = 0;
kdb5_dal_handle *dal_handle;
char *okname;
char *db_name2 = NULL;
int fd;
krb5_db2_context *db_ctx;
DB *db;
char policy_db_name[1024], policy_lock_name[1024];
if ((retval = k5db2_init_context(context)))
return (retval);
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
switch (flags) {
case KRB5_KDB_CREATE_HASH:
if ((retval = krb5_db2_db_set_hashfirst(context, TRUE)))
return retval;
break;
case KRB5_KDB_CREATE_BTREE:
case 0:
if ((retval = krb5_db2_db_set_hashfirst(context, FALSE)))
return retval;
break;
default:
return KRB5_KDB_BAD_CREATEFLAGS;
}
db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb);
if (db == NULL)
return errno;
(*db->close) (db);
db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name);
if (db_name2 == NULL)
return ENOMEM;
okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT);
if (!okname)
retval = ENOMEM;
else {
fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600);
if (fd < 0)
retval = errno;
else
close(fd);
free_dbsuffix(okname);
}
snprintf(policy_db_name, sizeof(policy_db_name), "%s.kadm5", db_name2);
snprintf(policy_lock_name, sizeof(policy_lock_name),
"%s.lock", policy_db_name);
retval = osa_adb_create_db(policy_db_name,
policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
free(db_name2);
return retval;
}
static krb5_error_code
destroy_file_suffix(char *dbname, char *suffix)
{
char *filename;
struct stat statb;
int nb, fd;
unsigned int j;
off_t pos;
char buf[BUFSIZ];
char zbuf[BUFSIZ];
int dowrite;
filename = gen_dbsuffix(dbname, suffix);
if (filename == 0)
return ENOMEM;
if ((fd = open(filename, O_RDWR, 0)) < 0) {
free(filename);
return errno;
}
set_cloexec_fd(fd);
if (fstat(fd, &statb) == -1) {
int retval = errno;
free(filename);
return retval;
}
memset(zbuf, 0, BUFSIZ);
pos = 0;
while (pos < statb.st_size) {
dowrite = 0;
nb = read(fd, buf, BUFSIZ);
if (nb < 0) {
int retval = errno;
free(filename);
return retval;
}
for (j = 0; j < nb; j++) {
if (buf[j] != '\0') {
dowrite = 1;
break;
}
}
j = nb;
if (dowrite) {
lseek(fd, pos, SEEK_SET);
nb = write(fd, zbuf, j);
if (nb < 0) {
int retval = errno;
free(filename);
return retval;
}
}
pos += nb;
}
#ifndef NOFSYNC
fsync(fd);
#endif
close(fd);
if (unlink(filename)) {
free(filename);
return (errno);
}
free(filename);
return (0);
}
krb5_error_code
krb5_db2_db_destroy(krb5_context context, char *dbname)
{
krb5_error_code retval1, retval2;
krb5_boolean tmpcontext;
char policy_db_name[1024], policy_lock_name[1024];
tmpcontext = 0;
if (!context->dal_handle
|| !context->dal_handle->db_context) {
tmpcontext = 1;
if ((retval1 = k5db2_init_context(context)))
return (retval1);
}
retval1 = retval2 = 0;
retval1 = destroy_file_suffix(dbname, "");
retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT);
if (tmpcontext) {
k5db2_clear_context((krb5_db2_context *) context->dal_handle->db_context);
free(context->dal_handle->db_context);
context->dal_handle->db_context = NULL;
}
if (retval1 || retval2)
return (retval1 ? retval1 : retval2);
snprintf(policy_db_name, sizeof(policy_db_name), "%s.kadm5", dbname);
snprintf(policy_lock_name, sizeof(policy_lock_name),
"%s.lock", policy_db_name);
retval1 = osa_adb_destroy_db(policy_db_name,
policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
return retval1;
}
krb5_error_code
krb5_db2_db_get_principal(krb5_context context,
krb5_const_principal searchfor,
krb5_db_entry *entries,
int *nentries,
krb5_boolean *more)
{
krb5_db2_context *db_ctx;
krb5_error_code retval;
DB *db;
DBT key, contents;
krb5_data keydata, contdata;
int trynum, dbret;
kdb5_dal_handle *dal_handle;
*more = FALSE;
*nentries = 0;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) {
if (db_ctx->db_nb_locks)
return (retval);
sleep(1);
continue;
}
break;
}
if (trynum == KRB5_DB2_MAX_RETRY)
return KRB5_KDB_DB_INUSE;
retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
if (retval)
goto cleanup;
key.data = keydata.data;
key.size = keydata.length;
db = db_ctx->db;
dbret = (*db->get) (db, &key, &contents, 0);
retval = errno;
krb5_free_data_contents(context, &keydata);
switch (dbret) {
case 1:
retval = 0;
case -1:
default:
*nentries = 0;
goto cleanup;
case 0:
contdata.data = contents.data;
contdata.length = contents.size;
retval = krb5_decode_princ_contents(context, &contdata, entries);
if (!retval)
*nentries = 1;
break;
}
cleanup:
(void) krb5_db2_db_unlock(context);
return retval;
}
krb5_error_code
krb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries,
int nentries)
{
register int i;
for (i = 0; i < nentries; i++)
krb5_dbe_free_contents(context, &entries[i]);
return 0;
}
krb5_error_code
krb5_db2_db_put_principal(krb5_context context,
krb5_db_entry *entries,
int *nentries,
char **db_args)
{
int i, n, dbret;
DB *db;
DBT key, contents;
krb5_data contdata, keydata;
krb5_error_code retval;
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
krb5_clear_error_message (context);
if (db_args) {
krb5_set_error_message(context, EINVAL,
"Unsupported argument \"%s\" for db2",
db_args[0]);
return EINVAL;
}
n = *nentries;
*nentries = 0;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
return retval;
db = db_ctx->db;
if ((retval = krb5_db2_db_start_update(context))) {
(void) krb5_db2_db_unlock(context);
return retval;
}
for (i = 0; i < n; i++) {
retval = krb5_encode_princ_contents(context, &contdata, entries);
if (retval)
break;
contents.data = contdata.data;
contents.size = contdata.length;
retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ);
if (retval) {
krb5_free_data_contents(context, &contdata);
break;
}
key.data = keydata.data;
key.size = keydata.length;
dbret = (*db->put) (db, &key, &contents, 0);
retval = dbret ? errno : 0;
krb5_free_data_contents(context, &keydata);
krb5_free_data_contents(context, &contdata);
if (retval)
break;
entries++;
}
(void) krb5_db2_db_end_update(context);
(void) krb5_db2_db_unlock(context);
*nentries = i;
return (retval);
}
krb5_error_code
krb5_db2_db_delete_principal(krb5_context context,
krb5_const_principal searchfor,
int *nentries)
{
krb5_error_code retval;
krb5_db_entry entry;
krb5_db2_context *db_ctx;
DB *db;
DBT key, contents;
krb5_data keydata, contdata;
int i, dbret;
kdb5_dal_handle *dal_handle;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
return (retval);
if ((retval = krb5_db2_db_start_update(context))) {
(void) krb5_db2_db_unlock(context);
return (retval);
}
if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
goto cleanup;
key.data = keydata.data;
key.size = keydata.length;
db = db_ctx->db;
dbret = (*db->get) (db, &key, &contents, 0);
retval = errno;
switch (dbret) {
case 1:
retval = KRB5_KDB_NOENTRY;
case -1:
default:
*nentries = 0;
goto cleankey;
case 0:
;
}
memset((char *) &entry, 0, sizeof(entry));
contdata.data = contents.data;
contdata.length = contents.size;
retval = krb5_decode_princ_contents(context, &contdata, &entry);
if (retval)
goto cleankey;
*nentries = 1;
for (i = 0; i < entry.n_key_data; i++) {
if (entry.key_data[i].key_data_length[0]) {
memset((char *) entry.key_data[i].key_data_contents[0], 0,
(unsigned) entry.key_data[i].key_data_length[0]);
}
}
retval = krb5_encode_princ_contents(context, &contdata, &entry);
krb5_dbe_free_contents(context, &entry);
if (retval)
goto cleankey;
contents.data = contdata.data;
contents.size = contdata.length;
dbret = (*db->put) (db, &key, &contents, 0);
retval = dbret ? errno : 0;
krb5_free_data_contents(context, &contdata);
if (retval)
goto cleankey;
dbret = (*db->del) (db, &key, 0);
retval = dbret ? errno : 0;
cleankey:
krb5_free_data_contents(context, &keydata);
cleanup:
(void) krb5_db2_db_end_update(context);
(void) krb5_db2_db_unlock(context);
return retval;
}
krb5_error_code
krb5_db2_db_iterate_ext(krb5_context context,
krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg,
int backwards, int recursive)
{
krb5_db2_context *db_ctx;
DB *db;
DBT key, contents;
krb5_data contdata;
krb5_db_entry entries;
krb5_error_code retval;
kdb5_dal_handle *dal_handle;
int dbret;
void *cookie;
cookie = NULL;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
dal_handle = context->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED);
if (retval)
return retval;
db = db_ctx->db;
if (recursive && db->type != DB_BTREE) {
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR;
}
if (!recursive) {
dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST);
} else {
#ifdef HAVE_BT_RSEQ
dbret = bt_rseq(db, &key, &contents, &cookie,
backwards ? R_LAST : R_FIRST);
#else
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR;
#endif
}
while (dbret == 0) {
contdata.data = contents.data;
contdata.length = contents.size;
retval = krb5_decode_princ_contents(context, &contdata, &entries);
if (retval)
break;
retval = (*func) (func_arg, &entries);
krb5_dbe_free_contents(context, &entries);
if (retval)
break;
if (!recursive) {
dbret = (*db->seq) (db, &key, &contents,
backwards ? R_PREV : R_NEXT);
} else {
#ifdef HAVE_BT_RSEQ
dbret = bt_rseq(db, &key, &contents, &cookie,
backwards ? R_PREV : R_NEXT);
#else
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR;
#endif
}
}
switch (dbret) {
case 1:
case 0:
break;
case -1:
default:
retval = errno;
}
(void) krb5_db2_db_unlock(context);
return retval;
}
krb5_error_code
krb5_db2_db_iterate(krb5_context context,
char *match_expr,
krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
krb5_pointer func_arg)
{
return krb5_db2_db_iterate_ext(context, func, func_arg, 0, 0);
}
krb5_boolean
krb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode)
{
krb5_boolean old;
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
dal_handle = context->dal_handle;
old = mode;
if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) {
old = db_ctx->db_nb_locks;
db_ctx->db_nb_locks = mode;
}
return old;
}
krb5_error_code
krb5_db2_lib_init()
{
return 0;
}
krb5_error_code
krb5_db2_lib_cleanup()
{
return 0;
}
krb5_error_code
krb5_db2_open(krb5_context kcontext,
char *conf_section, char **db_args, int mode)
{
krb5_error_code status = 0;
char **t_ptr = db_args;
int db_name_set = 0, tempdb=0;
char *dbname = NULL;
krb5_clear_error_message (kcontext);
if (k5db2_inited(kcontext))
return 0;
while (t_ptr && *t_ptr) {
char *opt = NULL, *val = NULL;
krb5_db2_get_db_opt(*t_ptr, &opt, &val);
if (opt && !strcmp(opt, "dbname")) {
if (dbname) free(dbname);
dbname = strdup(val);
if (dbname == NULL) {
free(opt);
free(val);
return ENOMEM;
}
}
else if (!opt && !strcmp(val, "temporary") ) {
tempdb = 1;
}
else if (!opt || strcmp(opt, "hash")) {
krb5_set_error_message(kcontext, EINVAL,
"Unsupported argument \"%s\" for db2",
opt ? opt : val);
free(opt);
free(val);
return EINVAL;
}
free(opt);
free(val);
t_ptr++;
}
if(dbname) {
status = krb5_db2_db_set_name(kcontext, dbname, tempdb);
free(dbname);
if (status) {
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
char *value = NULL;
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,
NULL, &value);
if (value == NULL) {
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,
default_db_name, &value);
if (status) {
goto clean_n_exit;
}
}
status = krb5_db2_db_set_name(kcontext, value, tempdb);
profile_release_string(value);
if (status) {
goto clean_n_exit;
}
}
status = krb5_db2_db_init(kcontext);
clean_n_exit:
return status;
}
krb5_error_code
krb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
{
krb5_error_code status = 0;
char **t_ptr = db_args;
int db_name_set = 0, tempdb=0;
krb5_int32 flags = KRB5_KDB_CREATE_BTREE;
char *db_name = NULL;
krb5_clear_error_message (kcontext);
if (k5db2_inited(kcontext))
return 0;
while (t_ptr && *t_ptr) {
char *opt = NULL, *val = NULL;
krb5_db2_get_db_opt(*t_ptr, &opt, &val);
if (opt && !strcmp(opt, "dbname")) {
db_name = strdup(val);
if (db_name == NULL) {
free(opt);
free(val);
return ENOMEM;
}
}
else if (!opt && !strcmp(val, "temporary")) {
tempdb = 1;
}
else if (opt && !strcmp(opt, "hash")) {
flags = KRB5_KDB_CREATE_HASH;
} else {
krb5_set_error_message(kcontext, EINVAL,
"Unsupported argument \"%s\" for db2",
opt ? opt : val);
free(opt);
free(val);
return EINVAL;
}
free(opt);
free(val);
t_ptr++;
}
if (db_name) {
status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
if (!status) {
status = EEXIST;
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
char *value = NULL;
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
KDB_MODULE_SECTION, conf_section,
KDB_DB2_DATABASE_NAME, NULL, &value);
if (value == NULL) {
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
KDB_REALM_SECTION,
KRB5_DB_GET_REALM(kcontext),
KDB_DB2_DATABASE_NAME,
default_db_name, &value);
if (status) {
goto clean_n_exit;
}
}
db_name = strdup(value);
if (db_name == NULL) {
status = ENOMEM;
profile_release_string(value);
goto clean_n_exit;
}
status = krb5_db2_db_set_name(kcontext, value, tempdb);
profile_release_string(value);
if (!status) {
status = EEXIST;
goto clean_n_exit;
}
}
status = krb5_db2_db_create(kcontext, db_name, flags);
if (status)
goto clean_n_exit;
status = krb5_db2_db_fini(kcontext);
if (status)
goto clean_n_exit;
status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW);
clean_n_exit:
if (db_name)
free(db_name);
return status;
}
krb5_error_code
krb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args)
{
krb5_error_code status = 0;
char **t_ptr = db_args;
int db_name_set = 0, tempdb=0;
char *db_name = NULL;
while (t_ptr && *t_ptr) {
char *opt = NULL, *val = NULL;
krb5_db2_get_db_opt(*t_ptr, &opt, &val);
if (opt && !strcmp(opt, "dbname")) {
db_name = strdup(val);
if (db_name == NULL) {
free(opt);
free(val);
return ENOMEM;
}
}
else if (!opt && !strcmp(val, "temporary")) {
tempdb = 1;
}
else if (!opt || strcmp(opt, "hash")) {
free(opt);
free(val);
return EINVAL;
}
free(opt);
free(val);
t_ptr++;
}
if (db_name) {
status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
if (status) {
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
char *value = NULL;
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME,
NULL, &value);
if (value == NULL) {
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME,
default_db_name, &value);
if (status) {
goto clean_n_exit;
}
}
db_name = strdup(value);
if (db_name == NULL) {
status = ENOMEM;
goto clean_n_exit;
}
status = krb5_db2_db_set_name(kcontext, value, tempdb);
profile_release_string(value);
if (status) {
goto clean_n_exit;
}
}
status = krb5_db2_db_destroy(kcontext, db_name);
clean_n_exit:
if (db_name)
free(db_name);
return status;
}
krb5_error_code
krb5_db2_set_master_key_ext(krb5_context kcontext,
char *pwd, krb5_keyblock * key)
{
return krb5_db2_db_set_mkey(kcontext, key);
}
krb5_error_code
krb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
{
krb5_error_code status = 0;
krb5_boolean oldval;
krb5_db2_context *db_ctx;
kdb5_dal_handle *dal_handle;
if (!k5db2_inited(kcontext))
return KRB5_KDB_DBNOTINITED;
dal_handle = kcontext->dal_handle;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
switch (option) {
case KRB5_KDB_OPT_SET_DB_NAME:
status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb);
break;
case KRB5_KDB_OPT_SET_LOCK_MODE:
oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value));
*((krb5_boolean *) value) = oldval;
break;
default:
status = -1;
break;
}
return status;
}
void *
krb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
{
return realloc(ptr, size);
}
void
krb5_db2_free(krb5_context kcontext, void *ptr)
{
free(ptr);
}
krb5_error_code
krb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
{
kdb5_dal_handle *dal_handle;
krb5_db2_context *dbc;
dal_handle = kcontext->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
return osa_adb_create_policy(dbc->policy_db, policy);
}
krb5_error_code
krb5_db2_get_policy(krb5_context kcontext,
char *name, osa_policy_ent_t * policy, int *cnt)
{
kdb5_dal_handle *dal_handle;
krb5_db2_context *dbc;
dal_handle = kcontext->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
return osa_adb_get_policy(dbc->policy_db, name, policy, cnt);
}
krb5_error_code
krb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
{
kdb5_dal_handle *dal_handle;
krb5_db2_context *dbc;
dal_handle = kcontext->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
return osa_adb_put_policy(dbc->policy_db, policy);
}
krb5_error_code
krb5_db2_iter_policy(krb5_context kcontext,
char *match_entry,
osa_adb_iter_policy_func func, void *data)
{
kdb5_dal_handle *dal_handle;
krb5_db2_context *dbc;
dal_handle = kcontext->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
return osa_adb_iter_policy(dbc->policy_db, func, data);
}
krb5_error_code
krb5_db2_delete_policy(krb5_context kcontext, char *policy)
{
kdb5_dal_handle *dal_handle;
krb5_db2_context *dbc;
dal_handle = kcontext->dal_handle;
dbc = (krb5_db2_context *) dal_handle->db_context;
return osa_adb_destroy_policy(dbc->policy_db, policy);
}
void
krb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
{
osa_free_policy_ent(entry);
}
krb5_error_code
krb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
{
krb5_error_code status = 0;
char *db_name = NULL;
char *temp_db_name = NULL;
krb5_clear_error_message (kcontext);
{
kdb5_dal_handle *dal_handle = kcontext->dal_handle;
krb5_db2_context *db_ctx = dal_handle->db_context;
db_name = strdup(db_ctx->db_name);
if (db_name == NULL) {
status = ENOMEM;
goto clean_n_exit;
}
}
assert(kcontext->dal_handle != NULL);
temp_db_name = gen_dbsuffix(db_name, "~");
if (temp_db_name == NULL) {
status = ENOMEM;
goto clean_n_exit;
}
status = krb5_db2_db_rename (kcontext, temp_db_name, db_name);
if (status)
goto clean_n_exit;
clean_n_exit:
if (db_name)
free(db_name);
if (temp_db_name)
free(temp_db_name);
return status;
}
krb5_error_code
krb5_db2_db_rename(context, from, to)
krb5_context context;
char *from;
char *to;
{
DB *db;
char *fromok;
krb5_error_code retval;
krb5_db2_context *s_context, *db_ctx;
kdb5_dal_handle *dal_handle = context->dal_handle;
s_context = dal_handle->db_context;
dal_handle->db_context = NULL;
if ((retval = k5db2_init_context(context)))
return retval;
db_ctx = (krb5_db2_context *) dal_handle->db_context;
db = k5db2_dbopen(db_ctx, to, O_RDWR|O_CREAT, 0600, 0);
if (db == NULL) {
retval = errno;
goto errout;
}
else
(*db->close)(db);
retval = krb5_db2_db_set_name(context, to, 0);
if (retval)
goto errout;
retval = krb5_db2_db_init(context);
if (retval)
goto errout;
{
char policy[2048], new_policy[2048];
assert (strlen(db_ctx->db_name) < 2000);
snprintf(policy, sizeof(policy), "%s.kadm5", db_ctx->db_name);
snprintf(new_policy, sizeof(new_policy),
"%s~.kadm5", db_ctx->db_name);
if (0 != rename(new_policy, policy)) {
retval = errno;
goto errout;
}
strlcat(new_policy, ".lock",sizeof(new_policy));
(void) unlink(new_policy);
}
db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT);
if (db_ctx->db_lf_name == NULL) {
retval = ENOMEM;
goto errout;
}
db_ctx->db_lf_file = open(db_ctx->db_lf_name, O_RDWR|O_CREAT, 0600);
if (db_ctx->db_lf_file < 0) {
retval = errno;
goto errout;
}
set_cloexec_fd(db_ctx->db_lf_file);
db_ctx->db_inited = 1;
retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time);
if (retval)
goto errout;
fromok = gen_dbsuffix(from, KDB2_LOCK_EXT);
if (fromok == NULL) {
retval = ENOMEM;
goto errout;
}
if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
goto errfromok;
if ((retval = krb5_db2_db_start_update(context)))
goto errfromok;
if (rename(from, to)) {
retval = errno;
goto errfromok;
}
if (unlink(fromok)) {
retval = errno;
goto errfromok;
}
retval = krb5_db2_db_end_update(context);
errfromok:
free_dbsuffix(fromok);
errout:
if (dal_handle->db_context) {
if (db_ctx->db_lf_file >= 0) {
krb5_db2_db_unlock(context);
close(db_ctx->db_lf_file);
}
k5db2_clear_context((krb5_db2_context *) dal_handle->db_context);
free(dal_handle->db_context);
}
dal_handle->db_context = s_context;
(void) krb5_db2_db_unlock(context);
return retval;
}