if (status != 0)
return status;
+ /* Allow unlockiter to be overridden by command line db_args. */
+ status = profile_get_boolean(profile, KDB_MODULE_SECTION, conf_section,
+ KRB5_CONF_UNLOCKITER, FALSE, &bval);
+ if (status != 0)
+ goto cleanup;
+ dbc->unlockiter = bval;
+
for (t_ptr = db_args; t_ptr && *t_ptr; t_ptr++) {
free(opt);
free(val);
;
} else if (opt && !strcmp(opt, "hash")) {
dbc->hashfirst = TRUE;
+ } else if (!opt && !strcmp(val, "unlockiter")) {
+ dbc->unlockiter = TRUE;
+ } else if (!opt && !strcmp(val, "lockiter")) {
+ dbc->unlockiter = FALSE;
} else {
status = EINVAL;
- krb5_set_error_message(context, status,
- _("Unsupported argument \"%s\" for db2"),
- opt ? opt : val);
+ k5_setmsg(context, status,
+ _("Unsupported argument \"%s\" for db2"),
+ opt ? opt : val);
goto cleanup;
}
}
* dbc->hashfirst determines which is attempted first. If dbc->hashfirst
* indicated the wrong type, update it to indicate the correct type.
*/
-static DB *
-open_db(krb5_db2_context *dbc, int flags, int mode)
+static krb5_error_code
+open_db(krb5_context context, krb5_db2_context *dbc, int flags, int mode,
+ DB **db_out)
{
char *fname = NULL;
DB *db;
bti.compare = NULL;
bti.prefix = NULL;
- if (ctx_dbsuffix(dbc, SUFFIX_DB, &fname) != 0) {
- errno = ENOMEM;
- return NULL;
- }
+ *db_out = NULL;
+
+ if (ctx_dbsuffix(dbc, SUFFIX_DB, &fname) != 0)
+ return ENOMEM;
hashi.bsize = 4096;
hashi.cachesize = 0;
db = dbopen(fname, flags, mode,
dbc->hashfirst ? DB_HASH : DB_BTREE,
dbc->hashfirst ? (void *) &hashi : (void *) &bti);
- if (db != NULL)
- goto done;
- /* If that was wrong, retry with the other type. */
- switch (errno) {
-#ifdef EFTYPE
- case EFTYPE:
-#endif
- case EINVAL:
+ if (db == NULL && IS_EFTYPE(errno)) {
db = dbopen(fname, flags, mode,
dbc->hashfirst ? DB_BTREE : DB_HASH,
dbc->hashfirst ? (void *) &bti : (void *) &hashi);
/* If that worked, update our guess for next time. */
if (db != NULL)
dbc->hashfirst = !dbc->hashfirst;
- break;
}
-done:
+ /* Don't try unlocked iteration with a hash database. */
+ if (db != NULL && dbc->hashfirst)
+ dbc->unlockiter = FALSE;
+
+ if (db == NULL) {
+ k5_prependmsg(context, errno, _("Cannot open DB2 database '%s'"),
+ fname);
+ }
+
+ *db_out = db;
free(fname);
- return db;
+ return (db == NULL) ? errno : 0;
}
static krb5_error_code
ctx_unlock(krb5_context context, krb5_db2_context *dbc)
{
- krb5_error_code retval;
+ krb5_error_code retval, retval2;
DB *db;
retval = osa_adb_release_lock(dbc->policy_db);
- if (retval)
- return retval;
if (!dbc->db_locks_held) /* lock already unlocked */
return KRB5_KDB_NOTLOCKED;
dbc->db = NULL;
dbc->db_lock_mode = 0;
- retval = krb5_lock_file(context, dbc->db_lf_file,
+ retval2 = krb5_lock_file(context, dbc->db_lf_file,
KRB5_LOCKMODE_UNLOCK);
+ if (retval2)
+ return retval2;
}
+
+ /* We may be unlocking because osa_adb_get_lock() failed. */
+ if (retval == OSA_ADB_NOTLOCKED)
+ return 0;
return retval;
}
-#define MAX_LOCK_TRIES 5
-
static krb5_error_code
ctx_lock(krb5_context context, krb5_db2_context *dbc, int lockmode)
{
krb5_error_code retval;
- int kmode, tries;
+ int kmode;
if (lockmode == KRB5_DB_LOCKMODE_PERMANENT ||
lockmode == KRB5_DB_LOCKMODE_EXCLUSIVE)
if (dbc->db_locks_held == 0 || dbc->db_lock_mode < kmode) {
/* Acquire or upgrade the lock. */
- for (tries = 0; tries < MAX_LOCK_TRIES; tries++) {
- retval = krb5_lock_file(context, dbc->db_lf_file,
- kmode | KRB5_LOCKMODE_DONTBLOCK);
- if (retval == 0)
- break;
- if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
- /* Tried to lock something we don't have write access to. */
- return KRB5_KDB_CANTLOCK_DB;
- sleep(1);
- }
- if (retval == EACCES)
+ retval = krb5_lock_file(context, dbc->db_lf_file, kmode);
+ /* Check if we tried to lock something not open for write. */
+ if (retval == EBADF && kmode == KRB5_LOCKMODE_EXCLUSIVE)
+ return KRB5_KDB_CANTLOCK_DB;
+ else if (retval == EACCES || retval == EAGAIN || retval == EWOULDBLOCK)
return KRB5_KDB_CANTLOCK_DB;
- else if (retval == EAGAIN || retval == EWOULDBLOCK)
- return OSA_ADB_CANTLOCK_DB;
else if (retval)
return retval;
/* Open the DB (or re-open it for read/write). */
if (dbc->db != NULL)
dbc->db->close(dbc->db);
- dbc->db = open_db(dbc,
- kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
- 0600);
- if (dbc->db == NULL) {
- retval = errno;
+ retval = open_db(context, dbc,
+ kmode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR,
+ 0600, &dbc->db);
+ if (retval) {
dbc->db_locks_held = 0;
dbc->db_lock_mode = 0;
(void) osa_adb_release_lock(dbc->policy_db);
/* Acquire or upgrade the policy lock. */
retval = osa_adb_get_lock(dbc->policy_db, lockmode);
- if (retval)
+ if (retval) {
(void) ctx_unlock(context, dbc);
+ if (retval == OSA_ADB_NOEXCL_PERM || retval == OSA_ADB_CANTLOCK_DB ||
+ retval == OSA_ADB_NOLOCKFILE)
+ retval = KRB5_KDB_CANTLOCK_DB;
+ }
return retval;
}
static krb5_error_code
check_openable(krb5_context context)
{
+ krb5_error_code retval;
DB *db;
krb5_db2_context *dbc;
dbc = context->dal_handle->db_context;
- db = open_db(dbc, O_RDONLY, 0);
- if (db == NULL)
- return errno;
+ retval = open_db(context, dbc, O_RDONLY, 0, &db);
+ if (retval)
+ return retval;
db->close(db);
return 0;
}
retval = errno;
goto cleanup;
}
- retval = krb5_lock_file(context, dbc->db_lf_file,
- KRB5_LOCKMODE_EXCLUSIVE | KRB5_LOCKMODE_DONTBLOCK);
+ retval = krb5_lock_file(context, dbc->db_lf_file, KRB5_LOCKMODE_EXCLUSIVE);
if (retval != 0)
goto cleanup;
set_cloexec_fd(dbc->db_lf_file);
(void) unlink(plockname);
}
- dbc->db = open_db(dbc, O_RDWR | O_CREAT | O_EXCL, 0600);
- if (dbc->db == NULL) {
- retval = errno;
+ retval = open_db(context, dbc, O_RDWR | O_CREAT | O_EXCL, 0600, &dbc->db);
+ if (retval)
goto cleanup;
- }
/* Create the policy database, initialize a handle to it, and lock it. */
retval = osa_adb_create_db(polname, plockname, OSA_ADB_POLICY_DB_MAGIC);
DB *db;
DBT key, contents;
krb5_data keydata, contdata;
- int trynum, dbret;
+ int dbret;
*entry = NULL;
if (!inited(context))
dbc = context->dal_handle->db_context;
- for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
- if ((retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED))) {
- if (dbc->db_nb_locks)
- return (retval);
- sleep(1);
- continue;
- }
- break;
- }
- if (trynum == KRB5_DB2_MAX_RETRY)
- return KRB5_KDB_DB_INUSE;
+ retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED);
+ if (retval)
+ return retval;
/* XXX deal with wildcard lookups */
retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
return retval;
}
-/* Free an entry returned by krb5_db2_get_principal. */
-void
-krb5_db2_free_principal(krb5_context context, krb5_db_entry *entry)
-{
- krb5_dbe_free(context, entry);
-}
-
krb5_error_code
krb5_db2_put_principal(krb5_context context, krb5_db_entry *entry,
char **db_args)
krb5_clear_error_message (context);
if (db_args) {
/* DB2 does not support db_args DB arguments for principal */
- krb5_set_error_message(context, EINVAL,
- _("Unsupported argument \"%s\" for db2"),
- db_args[0]);
+ k5_setmsg(context, EINVAL, _("Unsupported argument \"%s\" for db2"),
+ db_args[0]);
return EINVAL;
}
}
retval = krb5_encode_princ_entry(context, &contdata, entry);
- krb5_dbe_free(context, entry);
+ krb5_db_free_principal(context, entry);
if (retval)
goto cleankey;
return retval;
}
+typedef krb5_error_code (*ctx_iterate_cb)(krb5_pointer, krb5_db_entry *);
+
+/* Cursor structure for ctx_iterate() */
+typedef struct iter_curs {
+ DBT key;
+ DBT data;
+ DBT keycopy;
+ unsigned int startflag;
+ unsigned int stepflag;
+ krb5_context ctx;
+ krb5_db2_context *dbc;
+ int lockmode;
+ krb5_boolean islocked;
+} iter_curs;
+
+/* Lock DB handle of curs, updating curs->islocked. */
static krb5_error_code
-ctx_iterate(krb5_context context, krb5_db2_context *dbc,
- krb5_error_code (*func)(krb5_pointer, krb5_db_entry *),
- krb5_pointer func_arg)
+curs_lock(iter_curs *curs)
+{
+ krb5_error_code retval;
+
+ retval = ctx_lock(curs->ctx, curs->dbc, curs->lockmode);
+ if (retval)
+ return retval;
+ curs->islocked = TRUE;
+ return 0;
+}
+
+/* Unlock DB handle of curs, updating curs->islocked. */
+static void
+curs_unlock(iter_curs *curs)
+{
+ ctx_unlock(curs->ctx, curs->dbc);
+ curs->islocked = FALSE;
+}
+
+/* Set up curs and lock DB. */
+static krb5_error_code
+curs_init(iter_curs *curs, krb5_context ctx, krb5_db2_context *dbc,
+ krb5_flags iterflags)
+{
+ int isrecurse = iterflags & KRB5_DB_ITER_RECURSE;
+ unsigned int prevflag = R_PREV;
+ unsigned int nextflag = R_NEXT;
+
+ curs->keycopy.size = 0;
+ curs->keycopy.data = NULL;
+ curs->islocked = FALSE;
+ curs->ctx = ctx;
+ curs->dbc = dbc;
+
+ if (iterflags & KRB5_DB_ITER_WRITE)
+ curs->lockmode = KRB5_LOCKMODE_EXCLUSIVE;
+ else
+ curs->lockmode = KRB5_LOCKMODE_SHARED;
+
+ if (isrecurse) {
+#ifdef R_RNEXT
+ if (dbc->hashfirst) {
+ k5_setmsg(ctx, EINVAL, _("Recursive iteration is not supported "
+ "for hash databases"));
+ return EINVAL;
+ }
+ prevflag = R_RPREV;
+ nextflag = R_RNEXT;
+#else
+ k5_setmsg(ctx, EINVAL, _("Recursive iteration not supported "
+ "in this version of libdb"));
+ return EINVAL;
+#endif
+ }
+ if (iterflags & KRB5_DB_ITER_REV) {
+ curs->startflag = R_LAST;
+ curs->stepflag = prevflag;
+ } else {
+ curs->startflag = R_FIRST;
+ curs->stepflag = nextflag;
+ }
+ return curs_lock(curs);
+}
+
+/* Get initial entry. */
+static int
+curs_start(iter_curs *curs)
+{
+ DB *db = curs->dbc->db;
+
+ return db->seq(db, &curs->key, &curs->data, curs->startflag);
+}
+
+/* Save iteration state so DB can be unlocked/closed. */
+static krb5_error_code
+curs_save(iter_curs *curs)
+{
+ if (!curs->dbc->unlockiter)
+ return 0;
+
+ curs->keycopy.data = malloc(curs->key.size);
+ if (curs->keycopy.data == NULL)
+ return ENOMEM;
+
+ curs->keycopy.size = curs->key.size;
+ memcpy(curs->keycopy.data, curs->key.data, curs->key.size);
+ return 0;
+}
+
+/* Free allocated cursor resources */
+static void
+curs_free(iter_curs *curs)
+{
+ free(curs->keycopy.data);
+ curs->keycopy.size = 0;
+ curs->keycopy.data = NULL;
+}
+
+/* Move one step of iteration (forwards or backwards as requested). Free
+ * curs->keycopy as a side effect, if needed. */
+static int
+curs_step(iter_curs *curs)
{
- DBT key, contents;
- krb5_data contdata;
- krb5_db_entry *entry;
- krb5_error_code retval, retval2;
int dbret;
+ krb5_db2_context *dbc = curs->dbc;
+
+ if (dbc->unlockiter) {
+ /* Reacquire libdb cursor using saved copy of key. */
+ curs->key = curs->keycopy;
+ dbret = dbc->db->seq(dbc->db, &curs->key, &curs->data, R_CURSOR);
+ curs_free(curs);
+ if (dbret)
+ return dbret;
+ }
+ return dbc->db->seq(dbc->db, &curs->key, &curs->data, curs->stepflag);
+}
- retval = ctx_lock(context, dbc, KRB5_LOCKMODE_SHARED);
+/* Run one invocation of the callback, unlocking the mutex and possibly the DB
+ * around the invocation. */
+static krb5_error_code
+curs_run_cb(iter_curs *curs, ctx_iterate_cb func, krb5_pointer func_arg)
+{
+ krb5_db2_context *dbc = curs->dbc;
+ krb5_error_code retval, lockerr;
+ krb5_db_entry *entry;
+ krb5_context ctx = curs->ctx;
+ krb5_data contdata;
+
+ contdata = make_data(curs->data.data, curs->data.size);
+ retval = krb5_decode_princ_entry(ctx, &contdata, &entry);
+ if (retval)
+ return retval;
+ /* Save libdb key across possible DB closure. */
+ retval = curs_save(curs);
if (retval)
return retval;
- dbret = dbc->db->seq(dbc->db, &key, &contents, R_FIRST);
+ if (dbc->unlockiter)
+ curs_unlock(curs);
+
+ k5_mutex_unlock(krb5_db2_mutex);
+ retval = (*func)(func_arg, entry);
+ krb5_db_free_principal(ctx, entry);
+ k5_mutex_lock(krb5_db2_mutex);
+ if (dbc->unlockiter) {
+ lockerr = curs_lock(curs);
+ if (lockerr)
+ return lockerr;
+ }
+ return retval;
+}
+
+/* Free cursor resources and unlock the DB if needed. */
+static void
+curs_fini(iter_curs *curs)
+{
+ curs_free(curs);
+ if (curs->islocked)
+ curs_unlock(curs);
+}
+
+static krb5_error_code
+ctx_iterate(krb5_context context, krb5_db2_context *dbc,
+ ctx_iterate_cb func, krb5_pointer func_arg, krb5_flags iterflags)
+{
+ krb5_error_code retval;
+ int dbret;
+ iter_curs curs;
+
+ retval = curs_init(&curs, context, dbc, iterflags);
+ if (retval)
+ return retval;
+ dbret = curs_start(&curs);
while (dbret == 0) {
- contdata.data = contents.data;
- contdata.length = contents.size;
- retval = krb5_decode_princ_entry(context, &contdata, &entry);
- if (retval)
- break;
- retval = k5_mutex_unlock(krb5_db2_mutex);
- if (retval)
- break;
- retval = (*func)(func_arg, entry);
- krb5_dbe_free(context, entry);
- retval2 = k5_mutex_lock(krb5_db2_mutex);
- /* Note: If re-locking fails, the wrapper in db2_exp.c will
- still try to unlock it again. That would be a bug. Fix
- when integrating the locking better. */
+ retval = curs_run_cb(&curs, func, func_arg);
if (retval)
- break;
- if (retval2) {
- retval = retval2;
- break;
- }
- dbret = dbc->db->seq(dbc->db, &key, &contents, R_NEXT);
+ goto cleanup;
+ dbret = curs_step(&curs);
}
switch (dbret) {
case 1:
default:
retval = errno;
}
- (void) ctx_unlock(context, dbc);
+cleanup:
+ curs_fini(&curs);
return retval;
}
krb5_error_code
-krb5_db2_iterate(krb5_context context, char *match_expr,
- krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
- krb5_pointer func_arg)
+krb5_db2_iterate(krb5_context context, char *match_expr, ctx_iterate_cb func,
+ krb5_pointer func_arg, krb5_flags iterflags)
{
if (!inited(context))
return KRB5_KDB_DBNOTINITED;
return ctx_iterate(context, context->dal_handle->db_context, func,
- func_arg);
+ func_arg, iterflags);
}
krb5_boolean
return status;
}
-void *
-krb5_db2_alloc(krb5_context context, void *ptr, size_t size)
-{
- return realloc(ptr, size);
-}
-
-void
-krb5_db2_free(krb5_context context, void *ptr)
-{
- free(ptr);
-}
-
/* policy functions */
krb5_error_code
krb5_db2_create_policy(krb5_context context, osa_policy_ent_t policy)
retval = 0;
}
+ krb5_db_free_principal(nra->kcontext, s_entry);
return retval;
}
nra.kcontext = context;
nra.db_context = dbc_real;
- return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra);
+ return ctx_iterate(context, dbc_temp, krb5_db2_merge_nra_iterator, &nra, 0);
}
/*