2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * lib/kdb/kdb_db2.c
2N/A *
2N/A * Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * Copyright (C) 1998 by the FundsXpress, INC.
2N/A *
2N/A * All rights reserved.
2N/A *
2N/A * Export of this software from the United States of America may require
2N/A * a specific license from the United States Government. It is the
2N/A * responsibility of any person or organization contemplating export to
2N/A * obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of FundsXpress. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. FundsXpress makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
2N/A * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
2N/A * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include "k5-int.h"
2N/A
2N/A#if HAVE_UNISTD_H
2N/A#include <unistd.h>
2N/A#endif
2N/A
2N/A#include <db.h>
2N/A#include <stdio.h>
2N/A#include <errno.h>
2N/A#include <utime.h>
2N/A#include "kdb5.h"
2N/A#include "kdb_db2.h"
2N/A#include "kdb_xdr.h"
2N/A#include "policy_db.h"
2N/A#include <libintl.h> /* Solaris Kerberos */
2N/A
2N/A#define KDB_DB2_DATABASE_NAME "database_name"
2N/A
2N/Astatic char *gen_dbsuffix(char *, char *);
2N/A
2N/Astatic krb5_error_code krb5_db2_db_start_update(krb5_context);
2N/Astatic krb5_error_code krb5_db2_db_end_update(krb5_context);
2N/A
2N/Astatic krb5_error_code krb5_db2_db_set_name(krb5_context, char *, int);
2N/A
2N/Akrb5_error_code krb5_db2_db_lock(krb5_context, int);
2N/A
2N/Astatic krb5_error_code krb5_db2_db_set_hashfirst(krb5_context, int);
2N/A
2N/Astatic char default_db_name[] = DEFAULT_KDB_FILE;
2N/A
2N/A/*
2N/A * Solaris Kerberos
2N/A * Extra error handling
2N/A */
2N/Achar errbuf[1024];
2N/Astatic void krb5_db2_prepend_err_str(krb5_context , const char *,
2N/A krb5_error_code, krb5_error_code);
2N/A
2N/A/*
2N/A * Locking:
2N/A *
2N/A * There are two distinct locking protocols used. One is designed to
2N/A * lock against processes (the admin_server, for one) which make
2N/A * incremental changes to the database; the other is designed to lock
2N/A * against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
2N/A * entire database in one fell swoop.
2N/A *
2N/A * The first locking protocol is implemented using flock() in the
2N/A * krb_dbl_lock() and krb_dbl_unlock routines.
2N/A *
2N/A * The second locking protocol is necessary because DBM "files" are
2N/A * actually implemented as two separate files, and it is impossible to
2N/A * atomically rename two files simultaneously. It assumes that the
2N/A * database is replaced only very infrequently in comparison to the time
2N/A * needed to do a database read operation.
2N/A *
2N/A * A third file is used as a "version" semaphore; the modification
2N/A * time of this file is the "version number" of the database.
2N/A * At the start of a read operation, the reader checks the version
2N/A * number; at the end of the read operation, it checks again. If the
2N/A * version number changed, or if the semaphore was nonexistant at
2N/A * either time, the reader sleeps for a second to let things
2N/A * stabilize, and then tries again; if it does not succeed after
2N/A * KRB5_DBM_MAX_RETRY attempts, it gives up.
2N/A *
2N/A * On update, the semaphore file is deleted (if it exists) before any
2N/A * update takes place; at the end of the update, it is replaced, with
2N/A * a version number strictly greater than the version number which
2N/A * existed at the start of the update.
2N/A *
2N/A * If the system crashes in the middle of an update, the semaphore
2N/A * file is not automatically created on reboot; this is a feature, not
2N/A * a bug, since the database may be inconsistant. Note that the
2N/A * absence of a semaphore file does not prevent another _update_ from
2N/A * taking place later. Database replacements take place automatically
2N/A * only on slave servers; a crash in the middle of an update will be
2N/A * fixed by the next slave propagation. A crash in the middle of an
2N/A * update on the master would be somewhat more serious, but this would
2N/A * likely be noticed by an administrator, who could fix the problem and
2N/A * retry the operation.
2N/A */
2N/A
2N/A#define free_dbsuffix(name) free(name)
2N/A
2N/A/*
2N/A * Routines to deal with context.
2N/A */
2N/A#define k5db2_inited(c) (c && c->dal_handle \
2N/A && c->dal_handle->db_context \
2N/A && ((krb5_db2_context *) c->dal_handle->db_context)->db_inited)
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_db2_get_db_opt(char *input, char **opt, char **val)
2N/A{
2N/A char *pos = strchr(input, '=');
2N/A if (pos == NULL) {
2N/A *opt = NULL;
2N/A *val = strdup(input);
2N/A if (*val == NULL) {
2N/A return ENOMEM;
2N/A }
2N/A } else {
2N/A *opt = malloc((pos - input) + 1);
2N/A *val = strdup(pos + 1);
2N/A if (!*opt || !*val) {
2N/A free(*opt);
2N/A *opt = NULL;
2N/A free(*val);
2N/A *val = NULL;
2N/A return ENOMEM;
2N/A }
2N/A memcpy(*opt, input, pos - input);
2N/A (*opt)[pos - input] = '\0';
2N/A }
2N/A return (0);
2N/A
2N/A}
2N/A
2N/A/*
2N/A * Restore the default context.
2N/A */
2N/Astatic void
2N/Ak5db2_clear_context(krb5_db2_context *dbctx)
2N/A{
2N/A /*
2N/A * Free any dynamically allocated memory. File descriptors and locks
2N/A * are the caller's problem.
2N/A */
2N/A if (dbctx->db_lf_name)
2N/A free(dbctx->db_lf_name);
2N/A if (dbctx->db_name && (dbctx->db_name != default_db_name))
2N/A free(dbctx->db_name);
2N/A /*
2N/A * Clear the structure and reset the defaults.
2N/A */
2N/A memset(dbctx, 0, sizeof(krb5_db2_context));
2N/A dbctx->db_name = default_db_name;
2N/A dbctx->db_nb_locks = FALSE;
2N/A dbctx->tempdb = FALSE;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Ak5db2_init_context(krb5_context context)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A
2N/A if (dal_handle->db_context == NULL) {
2N/A db_ctx = (krb5_db2_context *) malloc(sizeof(krb5_db2_context));
2N/A if (db_ctx == NULL)
2N/A return ENOMEM;
2N/A else {
2N/A memset(db_ctx, 0, sizeof(krb5_db2_context));
2N/A k5db2_clear_context((krb5_db2_context *) db_ctx);
2N/A dal_handle->db_context = (void *) db_ctx;
2N/A }
2N/A }
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Utility routine: generate name of database file.
2N/A */
2N/A
2N/Astatic char *
2N/Agen_dbsuffix(char *db_name, char *sfx)
2N/A{
2N/A char *dbsuffix;
2N/A
2N/A if (sfx == NULL)
2N/A return ((char *) NULL);
2N/A
2N/A if (asprintf(&dbsuffix, "%s%s", db_name, sfx) < 0)
2N/A return (0);
2N/A return dbsuffix;
2N/A}
2N/A
2N/Astatic DB *
2N/Ak5db2_dbopen(krb5_db2_context *dbc, char *fname, int flags, int mode, int tempdb)
2N/A{
2N/A DB *db;
2N/A BTREEINFO bti;
2N/A HASHINFO hashi;
2N/A bti.flags = 0;
2N/A bti.cachesize = 0;
2N/A bti.psize = 4096;
2N/A bti.lorder = 0;
2N/A bti.minkeypage = 0;
2N/A bti.compare = NULL;
2N/A bti.prefix = NULL;
2N/A
2N/A if (tempdb) {
2N/A fname = gen_dbsuffix(fname, "~");
2N/A } else {
2N/A fname = strdup(fname);
2N/A }
2N/A if (fname == NULL)
2N/A {
2N/A errno = ENOMEM;
2N/A return NULL;
2N/A }
2N/A
2N/A
2N/A hashi.bsize = 4096;
2N/A hashi.cachesize = 0;
2N/A hashi.ffactor = 40;
2N/A hashi.hash = NULL;
2N/A hashi.lorder = 0;
2N/A hashi.nelem = 1;
2N/A
2N/A db = dbopen(fname, flags, mode,
2N/A dbc->hashfirst ? DB_HASH : DB_BTREE,
2N/A dbc->hashfirst ? (void *) &hashi : (void *) &bti);
2N/A if (db != NULL) {
2N/A free(fname);
2N/A return db;
2N/A }
2N/A switch (errno) {
2N/A#ifdef EFTYPE
2N/A case EFTYPE:
2N/A#endif
2N/A case EINVAL:
2N/A db = dbopen(fname, flags, mode,
2N/A dbc->hashfirst ? DB_BTREE : DB_HASH,
2N/A dbc->hashfirst ? (void *) &bti : (void *) &hashi);
2N/A if (db != NULL)
2N/A dbc->hashfirst = !dbc->hashfirst;
2N/A default:
2N/A free(fname);
2N/A return db;
2N/A }
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_db2_db_set_hashfirst(krb5_context context, int hashfirst)
2N/A{
2N/A krb5_db2_context *dbc;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A dal_handle = context->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A dbc->hashfirst = hashfirst;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * initialization for data base routines.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_init(krb5_context context)
2N/A{
2N/A char *filename = NULL;
2N/A krb5_db2_context *db_ctx;
2N/A krb5_error_code retval;
2N/A kdb5_dal_handle *dal_handle;
2N/A char policy_db_name[1024], policy_lock_name[1024];
2N/A
2N/A if (k5db2_inited(context))
2N/A return 0;
2N/A
2N/A /* Check for presence of our context, if not present, allocate one. */
2N/A if ((retval = k5db2_init_context(context)))
2N/A return (retval);
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = dal_handle->db_context;
2N/A db_ctx->db = NULL;
2N/A
2N/A if (!(filename = gen_dbsuffix(db_ctx->db_name, db_ctx->tempdb
2N/A ?KDB2_TEMP_LOCK_EXT:KDB2_LOCK_EXT)))
2N/A return ENOMEM;
2N/A db_ctx->db_lf_name = filename; /* so it gets freed by clear_context */
2N/A
2N/A /*
2N/A * should be opened read/write so that write locking can work with
2N/A * POSIX systems
2N/A */
2N/A if ((db_ctx->db_lf_file = open(filename, O_RDWR, 0666)) < 0) {
2N/A if ((db_ctx->db_lf_file = open(filename, O_RDONLY, 0666)) < 0) {
2N/A retval = errno;
2N/A
2N/A /* Solaris Kerberos: Better error logging */
2N/A (void) snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), filename);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A
2N/A goto err_out;
2N/A }
2N/A }
2N/A set_cloexec_fd(db_ctx->db_lf_file);
2N/A db_ctx->db_inited++;
2N/A
2N/A if ((retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time)))
2N/A goto err_out;
2N/A
2N/A snprintf(policy_db_name, sizeof(policy_db_name), "%s%s.kadm5",
2N/A db_ctx->db_name, db_ctx->tempdb ? "~" : "");
2N/A snprintf(policy_lock_name, sizeof(policy_lock_name),
2N/A "%s.lock", policy_db_name);
2N/A
2N/A if ((retval = osa_adb_init_db(&db_ctx->policy_db, policy_db_name,
2N/A policy_lock_name, OSA_ADB_POLICY_DB_MAGIC)))
2N/A {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf),
2N/A gettext("Failed to initialize db, \"%s\", lockfile, \"%s\" : "),
2N/A policy_db_name, policy_lock_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A
2N/A goto err_out;
2N/A }
2N/A return 0;
2N/A
2N/Aerr_out:
2N/A db_ctx->db = NULL;
2N/A k5db2_clear_context(db_ctx);
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * gracefully shut down database--must be called by ANY program that does
2N/A * a krb5_db2_db_init
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_db_fini(krb5_context context)
2N/A{
2N/A krb5_error_code retval = 0;
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A if (dal_handle == NULL) {
2N/A return 0;
2N/A }
2N/A
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A if (k5db2_inited(context)) {
2N/A if (close(db_ctx->db_lf_file))
2N/A retval = errno;
2N/A else
2N/A retval = 0;
2N/A }
2N/A if (db_ctx) {
2N/A if (db_ctx->policy_db) {
2N/A retval =
2N/A osa_adb_fini_db(db_ctx->policy_db, OSA_ADB_POLICY_DB_MAGIC);
2N/A if (retval)
2N/A return retval;
2N/A }
2N/A
2N/A k5db2_clear_context(db_ctx);
2N/A /* free(dal_handle->db_context); */
2N/A dal_handle->db_context = NULL;
2N/A }
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * Set/Get the master key associated with the database
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_db_set_mkey(krb5_context context, krb5_keyblock *key)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return (KRB5_KDB_DBNOTINITED);
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = dal_handle->db_context;
2N/A db_ctx->db_master_key = key;
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_get_mkey(krb5_context context, krb5_keyblock **key)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return (KRB5_KDB_DBNOTINITED);
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = dal_handle->db_context;
2N/A *key = db_ctx->db_master_key;
2N/A
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Set the "name" of the current database to some alternate value.
2N/A *
2N/A * Passing a null pointer as "name" will set back to the default.
2N/A * If the alternate database doesn't exist, nothing is changed.
2N/A *
2N/A * XXX rethink this
2N/A */
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_db2_db_set_name(krb5_context context, char *name, int tempdb)
2N/A{
2N/A DB *db;
2N/A krb5_db2_context *db_ctx;
2N/A krb5_error_code kret;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (k5db2_inited(context))
2N/A return KRB5_KDB_DBINITED;
2N/A
2N/A /* Check for presence of our context, if not present, allocate one. */
2N/A if ((kret = k5db2_init_context(context)))
2N/A return (kret);
2N/A
2N/A if (name == NULL)
2N/A name = default_db_name;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = dal_handle->db_context;
2N/A db_ctx->tempdb = tempdb;
2N/A db = k5db2_dbopen(db_ctx, name, O_RDONLY, 0, tempdb);
2N/A if (db == NULL)
2N/A return errno;
2N/A
2N/A db_ctx->db_name = strdup(name);
2N/A /* Solaris Kerberos */
2N/A if (db_ctx->db_name == NULL) {
2N/A (*db->close) (db);
2N/A return ENOMEM;
2N/A }
2N/A (*db->close) (db);
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Return the last modification time of the database.
2N/A *
2N/A * Think about using fstat.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_get_age(krb5_context context, char *db_name, time_t *age)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A struct stat st;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return (KRB5_KDB_DBNOTINITED);
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A if (fstat(db_ctx->db_lf_file, &st) < 0)
2N/A *age = -1;
2N/A else
2N/A *age = st.st_mtime;
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Remove the semaphore file; indicates that database is currently
2N/A * under renovation.
2N/A *
2N/A * This is only for use when moving the database out from underneath
2N/A * the server (for example, during slave updates).
2N/A */
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_db2_db_start_update(krb5_context context)
2N/A{
2N/A return 0;
2N/A}
2N/A
2N/Astatic krb5_error_code
2N/Akrb5_db2_db_end_update(krb5_context context)
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A struct stat st;
2N/A time_t now;
2N/A struct utimbuf utbuf;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return (KRB5_KDB_DBNOTINITED);
2N/A
2N/A retval = 0;
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = dal_handle->db_context;
2N/A now = time((time_t *) NULL);
2N/A if (fstat(db_ctx->db_lf_file, &st) == 0) {
2N/A if (st.st_mtime >= now) {
2N/A utbuf.actime = st.st_mtime + 1;
2N/A utbuf.modtime = st.st_mtime + 1;
2N/A if (utime(db_ctx->db_lf_name, &utbuf))
2N/A retval = errno;
2N/A } else {
2N/A if (utime(db_ctx->db_lf_name, (struct utimbuf *) NULL))
2N/A retval = errno;
2N/A }
2N/A if (retval) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to modify "
2N/A "access and modification times for \"%s\": "),
2N/A db_ctx->db_lf_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A }
2N/A } else
2N/A retval = errno;
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to stat \"%s\": "),
2N/A db_ctx->db_lf_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A if (!retval) {
2N/A if (fstat(db_ctx->db_lf_file, &st) == 0)
2N/A db_ctx->db_lf_time = st.st_mtime;
2N/A else
2N/A retval = errno;
2N/A }
2N/A return (retval);
2N/A}
2N/A
2N/A#define MAX_LOCK_TRIES 5
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_lock(krb5_context context, int in_mode)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A int krb5_lock_mode;
2N/A DB *db;
2N/A krb5_error_code retval;
2N/A time_t mod_time;
2N/A kdb5_dal_handle *dal_handle;
2N/A int mode, gotlock, tries;
2N/A
2N/A switch (in_mode) {
2N/A case KRB5_DB_LOCKMODE_PERMANENT:
2N/A mode = KRB5_DB_LOCKMODE_EXCLUSIVE;
2N/A break;
2N/A case KRB5_DB_LOCKMODE_EXCLUSIVE:
2N/A mode = KRB5_LOCKMODE_EXCLUSIVE;
2N/A break;
2N/A
2N/A case KRB5_DB_LOCKMODE_SHARED:
2N/A mode = KRB5_LOCKMODE_SHARED;
2N/A break;
2N/A default:
2N/A return EINVAL;
2N/A }
2N/A
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A if (db_ctx->db_locks_held && (db_ctx->db_lock_mode >= mode)) {
2N/A /* No need to upgrade lock, just return */
2N/A db_ctx->db_locks_held++;
2N/A goto policy_lock;
2N/A }
2N/A
2N/A if ((mode != KRB5_LOCKMODE_SHARED) && (mode != KRB5_LOCKMODE_EXCLUSIVE))
2N/A return KRB5_KDB_BADLOCKMODE;
2N/A
2N/A krb5_lock_mode = mode | KRB5_LOCKMODE_DONTBLOCK;
2N/A for (gotlock = tries = 0; tries < MAX_LOCK_TRIES; tries++) {
2N/A retval = krb5_lock_file(context, db_ctx->db_lf_file, krb5_lock_mode);
2N/A if (retval == 0) {
2N/A gotlock++;
2N/A break;
2N/A } else if (retval == EBADF && mode == KRB5_DB_LOCKMODE_EXCLUSIVE)
2N/A /* tried to exclusive-lock something we don't have */
2N/A /* write access to */
2N/A
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf),
2N/A gettext("Failed to exclusively lock \"%s\": "),
2N/A db_ctx->db_lf_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, EBADF, EBADF);
2N/A
2N/A return KRB5_KDB_CANTLOCK_DB;
2N/A sleep(1);
2N/A }
2N/A
2N/A if (retval) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf),
2N/A gettext("Failed to lock \"%s\": "),
2N/A db_ctx->db_lf_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A }
2N/A
2N/A if (retval == EACCES)
2N/A return KRB5_KDB_CANTLOCK_DB;
2N/A else if (retval == EAGAIN || retval == EWOULDBLOCK)
2N/A return OSA_ADB_CANTLOCK_DB;
2N/A else if (retval != 0)
2N/A return retval;
2N/A
2N/A if ((retval = krb5_db2_db_get_age(context, NULL, &mod_time)))
2N/A goto lock_error;
2N/A
2N/A db = k5db2_dbopen(db_ctx, db_ctx->db_name,
2N/A mode == KRB5_LOCKMODE_SHARED ? O_RDONLY : O_RDWR, 0600, db_ctx->tempdb);
2N/A if (db) {
2N/A db_ctx->db_lf_time = mod_time;
2N/A db_ctx->db = db;
2N/A } else {
2N/A retval = errno;
2N/A
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf),
2N/A gettext("Failed to open db \"%s\": "),
2N/A db_ctx->db_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A
2N/A db_ctx->db = NULL;
2N/A goto lock_error;
2N/A }
2N/A
2N/A db_ctx->db_lock_mode = mode;
2N/A db_ctx->db_locks_held++;
2N/A
2N/Apolicy_lock:
2N/A if ((retval = osa_adb_get_lock(db_ctx->policy_db, in_mode))) {
2N/A krb5_db2_db_unlock(context);
2N/A }
2N/A return retval;
2N/A
2N/Alock_error:;
2N/A db_ctx->db_lock_mode = 0;
2N/A db_ctx->db_locks_held = 0;
2N/A krb5_db2_db_unlock(context);
2N/A return retval;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_unlock(krb5_context context)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A DB *db;
2N/A krb5_error_code retval;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A if ((retval = osa_adb_release_lock(db_ctx->policy_db))) {
2N/A return retval;
2N/A }
2N/A
2N/A if (!db_ctx->db_locks_held) /* lock already unlocked */
2N/A return KRB5_KDB_NOTLOCKED;
2N/A db = db_ctx->db;
2N/A if (--(db_ctx->db_locks_held) == 0) {
2N/A (*db->close) (db);
2N/A db_ctx->db = NULL;
2N/A
2N/A retval = krb5_lock_file(context, db_ctx->db_lf_file,
2N/A KRB5_LOCKMODE_UNLOCK);
2N/A db_ctx->db_lock_mode = 0;
2N/A return (retval);
2N/A }
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * Create the database, assuming it's not there.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_db_create(krb5_context context, char *db_name, krb5_int32 flags)
2N/A{
2N/A register krb5_error_code retval = 0;
2N/A kdb5_dal_handle *dal_handle;
2N/A char *okname;
2N/A char *db_name2 = NULL;
2N/A int fd;
2N/A krb5_db2_context *db_ctx;
2N/A DB *db;
2N/A char policy_db_name[1024], policy_lock_name[1024];
2N/A
2N/A if ((retval = k5db2_init_context(context)))
2N/A return (retval);
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A switch (flags) {
2N/A case KRB5_KDB_CREATE_HASH:
2N/A if ((retval = krb5_db2_db_set_hashfirst(context, TRUE)))
2N/A return retval;
2N/A break;
2N/A case KRB5_KDB_CREATE_BTREE:
2N/A case 0:
2N/A if ((retval = krb5_db2_db_set_hashfirst(context, FALSE)))
2N/A return retval;
2N/A break;
2N/A default:
2N/A return KRB5_KDB_BAD_CREATEFLAGS;
2N/A }
2N/A db = k5db2_dbopen(db_ctx, db_name, O_RDWR | O_CREAT | O_EXCL, 0600, db_ctx->tempdb);
2N/A if (db == NULL) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), db_name);
2N/A krb5_db2_prepend_err_str(context, errbuf, errno, errno);
2N/A
2N/A return errno;
2N/A }
2N/A (*db->close) (db);
2N/A
2N/A db_name2 = db_ctx->tempdb ? gen_dbsuffix(db_name, "~") : strdup(db_name);
2N/A if (db_name2 == NULL)
2N/A return ENOMEM;
2N/A okname = gen_dbsuffix(db_name2, KDB2_LOCK_EXT);
2N/A if (!okname)
2N/A retval = ENOMEM;
2N/A else {
2N/A fd = open(okname, O_CREAT | O_RDWR | O_TRUNC, 0600);
2N/A if (fd < 0) {
2N/A retval = errno;
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to open \"%s\": "), okname);
2N/A krb5_db2_prepend_err_str(context, errbuf, retval, retval);
2N/A }
2N/A else
2N/A close(fd);
2N/A free_dbsuffix(okname);
2N/A }
2N/A
2N/A snprintf(policy_db_name, sizeof(policy_db_name), "%s.kadm5", db_name2);
2N/A snprintf(policy_lock_name, sizeof(policy_lock_name),
2N/A "%s.lock", policy_db_name);
2N/A
2N/A retval = osa_adb_create_db(policy_db_name,
2N/A policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
2N/A free(db_name2);
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * Destroy the database. Zero's out all of the files, just to be sure.
2N/A */
2N/Astatic krb5_error_code
2N/Adestroy_file_suffix(char *dbname, char *suffix)
2N/A{
2N/A char *filename;
2N/A struct stat statb;
2N/A int nb, fd;
2N/A int j;
2N/A off_t pos;
2N/A char buf[BUFSIZ];
2N/A char zbuf[BUFSIZ];
2N/A int dowrite;
2N/A
2N/A filename = gen_dbsuffix(dbname, suffix);
2N/A if (filename == 0)
2N/A return ENOMEM;
2N/A if ((fd = open(filename, O_RDWR, 0)) < 0) {
2N/A free(filename);
2N/A return errno;
2N/A }
2N/A set_cloexec_fd(fd);
2N/A /* fstat() will probably not fail unless using a remote filesystem
2N/A * (which is inappropriate for the kerberos database) so this check
2N/A * is mostly paranoia. */
2N/A if (fstat(fd, &statb) == -1) {
2N/A int retval = errno;
2N/A free(filename);
2N/A return retval;
2N/A }
2N/A /*
2N/A * Stroll through the file, reading in BUFSIZ chunks. If everything
2N/A * is zero, then we're done for that block, otherwise, zero the block.
2N/A * We would like to just blast through everything, but some DB
2N/A * implementations make holey files and writing data to the holes
2N/A * causes actual blocks to be allocated which is no good, since
2N/A * we're just about to unlink it anyways.
2N/A */
2N/A memset(zbuf, 0, BUFSIZ);
2N/A pos = 0;
2N/A while (pos < statb.st_size) {
2N/A dowrite = 0;
2N/A nb = read(fd, buf, BUFSIZ);
2N/A if (nb < 0) {
2N/A int retval = errno;
2N/A free(filename);
2N/A return retval;
2N/A }
2N/A for (j = 0; j < nb; j++) {
2N/A if (buf[j] != '\0') {
2N/A dowrite = 1;
2N/A break;
2N/A }
2N/A }
2N/A /* For signedness */
2N/A j = nb;
2N/A if (dowrite) {
2N/A lseek(fd, pos, SEEK_SET);
2N/A nb = write(fd, zbuf, j);
2N/A if (nb < 0) {
2N/A int retval = errno;
2N/A free(filename);
2N/A return retval;
2N/A }
2N/A }
2N/A pos += nb;
2N/A }
2N/A /* ??? Is fsync really needed? I don't know of any non-networked
2N/A * filesystem which will discard queued writes to disk if a file
2N/A * is deleted after it is closed. --jfc */
2N/A#ifndef NOFSYNC
2N/A fsync(fd);
2N/A#endif
2N/A close(fd);
2N/A
2N/A if (unlink(filename)) {
2N/A free(filename);
2N/A return (errno);
2N/A }
2N/A free(filename);
2N/A return (0);
2N/A}
2N/A
2N/A/*
2N/A * Since the destroy operation happens outside the init/fini bracket, we
2N/A * have some tomfoolery to undergo here. If we're operating under no
2N/A * database context, then we initialize with the default. If the caller
2N/A * wishes a different context (e.g. different dispatch table), it's their
2N/A * responsibility to call kdb5_db_set_dbops() before this call. That will
2N/A * set up the right dispatch table values (e.g. name extensions).
2N/A *
2N/A * Not quite valid due to ripping out of dbops...
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_db_destroy(krb5_context context, char *dbname)
2N/A{
2N/A krb5_error_code retval1, retval2;
2N/A krb5_boolean tmpcontext;
2N/A char policy_db_name[1024], policy_lock_name[1024];
2N/A
2N/A tmpcontext = 0;
2N/A if (!context->dal_handle
2N/A || !context->dal_handle->db_context) {
2N/A tmpcontext = 1;
2N/A if ((retval1 = k5db2_init_context(context)))
2N/A return (retval1);
2N/A }
2N/A
2N/A retval1 = retval2 = 0;
2N/A retval1 = destroy_file_suffix(dbname, "");
2N/A retval2 = destroy_file_suffix(dbname, KDB2_LOCK_EXT);
2N/A
2N/A if (tmpcontext) {
2N/A k5db2_clear_context((krb5_db2_context *) context->dal_handle->db_context);
2N/A free(context->dal_handle->db_context);
2N/A context->dal_handle->db_context = NULL;
2N/A }
2N/A
2N/A if (retval1 || retval2)
2N/A return (retval1 ? retval1 : retval2);
2N/A
2N/A snprintf(policy_db_name, sizeof(policy_db_name), "%s.kadm5", dbname);
2N/A snprintf(policy_lock_name, sizeof(policy_lock_name),
2N/A "%s.lock", policy_db_name);
2N/A
2N/A retval1 = osa_adb_destroy_db(policy_db_name,
2N/A policy_lock_name, OSA_ADB_POLICY_DB_MAGIC);
2N/A
2N/A return retval1;
2N/A}
2N/A
2N/A/*
2N/A * look up a principal in the data base.
2N/A * returns number of entries found, and whether there were
2N/A * more than requested.
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_get_principal(krb5_context context,
2N/A krb5_const_principal searchfor,
2N/A krb5_db_entry *entries, /* filled in */
2N/A int *nentries, /* how much room/how many found */
2N/A krb5_boolean *more) /* are there more? */
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A krb5_error_code retval;
2N/A DB *db;
2N/A DBT key, contents;
2N/A krb5_data keydata, contdata;
2N/A int trynum, dbret;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A *more = FALSE;
2N/A *nentries = 0;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A for (trynum = 0; trynum < KRB5_DB2_MAX_RETRY; trynum++) {
2N/A if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED))) {
2N/A if (db_ctx->db_nb_locks)
2N/A return (retval);
2N/A sleep(1);
2N/A continue;
2N/A }
2N/A break;
2N/A }
2N/A if (trynum == KRB5_DB2_MAX_RETRY)
2N/A return KRB5_KDB_DB_INUSE;
2N/A
2N/A /* XXX deal with wildcard lookups */
2N/A retval = krb5_encode_princ_dbkey(context, &keydata, searchfor);
2N/A if (retval)
2N/A goto cleanup;
2N/A key.data = keydata.data;
2N/A key.size = keydata.length;
2N/A
2N/A db = db_ctx->db;
2N/A dbret = (*db->get) (db, &key, &contents, 0);
2N/A retval = errno;
2N/A krb5_free_data_contents(context, &keydata);
2N/A switch (dbret) {
2N/A case 1:
2N/A retval = 0;
2N/A case -1:
2N/A default:
2N/A *nentries = 0;
2N/A goto cleanup;
2N/A case 0:
2N/A contdata.data = contents.data;
2N/A contdata.length = contents.size;
2N/A retval = krb5_decode_princ_contents(context, &contdata, entries);
2N/A if (!retval)
2N/A *nentries = 1;
2N/A break;
2N/A }
2N/A
2N/Acleanup:
2N/A (void) krb5_db2_db_unlock(context); /* unlock read lock */
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A Free stuff returned by krb5_db2_db_get_principal.
2N/A*/
2N/Akrb5_error_code
2N/Akrb5_db2_db_free_principal(krb5_context context, krb5_db_entry *entries,
2N/A int nentries)
2N/A{
2N/A register int i;
2N/A for (i = 0; i < nentries; i++)
2N/A krb5_dbe_free_contents(context, &entries[i]);
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A Stores the *"nentries" entry structures pointed to by "entries" in the
2N/A database.
2N/A
2N/A *"nentries" is updated upon return to reflect the number of records
2N/A acutally stored; the first *"nstored" records will have been stored in the
2N/A database (even if an error occurs).
2N/A
2N/A*/
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_put_principal(krb5_context context,
2N/A krb5_db_entry *entries,
2N/A int *nentries, /* number of entry structs to update */
2N/A char **db_args)
2N/A{
2N/A int i, n, dbret;
2N/A DB *db;
2N/A DBT key, contents;
2N/A krb5_data contdata, keydata;
2N/A krb5_error_code retval;
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A krb5_clear_error_message (context);
2N/A if (db_args) {
2N/A /* DB2 does not support db_args DB arguments for principal */
2N/A krb5_set_error_message(context, EINVAL,
2N/A gettext("Unsupported argument \"%s\" for db2"),
2N/A db_args[0]);
2N/A return EINVAL;
2N/A }
2N/A
2N/A n = *nentries;
2N/A *nentries = 0;
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
2N/A return retval;
2N/A
2N/A db = db_ctx->db;
2N/A if ((retval = krb5_db2_db_start_update(context))) {
2N/A (void) krb5_db2_db_unlock(context);
2N/A return retval;
2N/A }
2N/A
2N/A /* for each one, stuff temps, and do replace/append */
2N/A for (i = 0; i < n; i++) {
2N/A retval = krb5_encode_princ_contents(context, &contdata, entries);
2N/A if (retval)
2N/A break;
2N/A contents.data = contdata.data;
2N/A contents.size = contdata.length;
2N/A retval = krb5_encode_princ_dbkey(context, &keydata, entries->princ);
2N/A if (retval) {
2N/A krb5_free_data_contents(context, &contdata);
2N/A break;
2N/A }
2N/A
2N/A key.data = keydata.data;
2N/A key.size = keydata.length;
2N/A dbret = (*db->put) (db, &key, &contents, 0);
2N/A retval = dbret ? errno : 0;
2N/A krb5_free_data_contents(context, &keydata);
2N/A krb5_free_data_contents(context, &contdata);
2N/A if (retval)
2N/A break;
2N/A entries++; /* bump to next struct */
2N/A }
2N/A
2N/A (void) krb5_db2_db_end_update(context);
2N/A (void) krb5_db2_db_unlock(context); /* unlock database */
2N/A *nentries = i;
2N/A return (retval);
2N/A}
2N/A
2N/A/*
2N/A * delete a principal from the data base.
2N/A * returns number of entries removed
2N/A */
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_delete_principal(krb5_context context,
2N/A krb5_const_principal searchfor,
2N/A int *nentries) /* how many found & deleted */
2N/A{
2N/A krb5_error_code retval;
2N/A krb5_db_entry entry;
2N/A krb5_db2_context *db_ctx;
2N/A DB *db;
2N/A DBT key, contents;
2N/A krb5_data keydata, contdata;
2N/A int i, dbret;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
2N/A return (retval);
2N/A
2N/A if ((retval = krb5_db2_db_start_update(context))) {
2N/A (void) krb5_db2_db_unlock(context); /* unlock write lock */
2N/A return (retval);
2N/A }
2N/A
2N/A if ((retval = krb5_encode_princ_dbkey(context, &keydata, searchfor)))
2N/A goto cleanup;
2N/A key.data = keydata.data;
2N/A key.size = keydata.length;
2N/A
2N/A db = db_ctx->db;
2N/A dbret = (*db->get) (db, &key, &contents, 0);
2N/A retval = errno;
2N/A switch (dbret) {
2N/A case 1:
2N/A retval = KRB5_KDB_NOENTRY;
2N/A case -1:
2N/A default:
2N/A *nentries = 0;
2N/A goto cleankey;
2N/A case 0:
2N/A ;
2N/A }
2N/A memset(&entry, 0, sizeof(entry));
2N/A contdata.data = contents.data;
2N/A contdata.length = contents.size;
2N/A retval = krb5_decode_princ_contents(context, &contdata, &entry);
2N/A if (retval)
2N/A goto cleankey;
2N/A *nentries = 1;
2N/A
2N/A /* Clear encrypted key contents */
2N/A for (i = 0; i < entry.n_key_data; i++) {
2N/A if (entry.key_data[i].key_data_length[0]) {
2N/A memset(entry.key_data[i].key_data_contents[0], 0,
2N/A (unsigned) entry.key_data[i].key_data_length[0]);
2N/A }
2N/A }
2N/A
2N/A retval = krb5_encode_princ_contents(context, &contdata, &entry);
2N/A krb5_dbe_free_contents(context, &entry);
2N/A if (retval)
2N/A goto cleankey;
2N/A
2N/A contents.data = contdata.data;
2N/A contents.size = contdata.length;
2N/A dbret = (*db->put) (db, &key, &contents, 0);
2N/A retval = dbret ? errno : 0;
2N/A krb5_free_data_contents(context, &contdata);
2N/A if (retval)
2N/A goto cleankey;
2N/A dbret = (*db->del) (db, &key, 0);
2N/A retval = dbret ? errno : 0;
2N/Acleankey:
2N/A krb5_free_data_contents(context, &keydata);
2N/A
2N/Acleanup:
2N/A (void) krb5_db2_db_end_update(context);
2N/A (void) krb5_db2_db_unlock(context); /* unlock write lock */
2N/A return retval;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_iterate_ext(krb5_context context,
2N/A krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
2N/A krb5_pointer func_arg,
2N/A int backwards, int recursive)
2N/A{
2N/A krb5_db2_context *db_ctx;
2N/A DB *db;
2N/A DBT key, contents;
2N/A krb5_data contdata;
2N/A krb5_db_entry entries;
2N/A krb5_error_code retval;
2N/A kdb5_dal_handle *dal_handle;
2N/A int dbret;
2N/A void *cookie;
2N/A
2N/A cookie = NULL;
2N/A if (!k5db2_inited(context))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_SHARED);
2N/A
2N/A if (retval)
2N/A return retval;
2N/A
2N/A db = db_ctx->db;
2N/A if (recursive && db->type != DB_BTREE) {
2N/A (void) krb5_db2_db_unlock(context);
2N/A return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
2N/A }
2N/A
2N/A if (!recursive) {
2N/A dbret = (*db->seq) (db, &key, &contents, backwards ? R_LAST : R_FIRST);
2N/A } else {
2N/A#ifdef HAVE_BT_RSEQ
2N/A dbret = bt_rseq(db, &key, &contents, &cookie,
2N/A backwards ? R_LAST : R_FIRST);
2N/A#else
2N/A (void) krb5_db2_db_unlock(context);
2N/A return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
2N/A#endif
2N/A }
2N/A while (dbret == 0) {
2N/A krb5_error_code retval2;
2N/A
2N/A contdata.data = contents.data;
2N/A contdata.length = contents.size;
2N/A retval = krb5_decode_princ_contents(context, &contdata, &entries);
2N/A if (retval)
2N/A break;
2N/A retval = k5_mutex_unlock(krb5_db2_mutex);
2N/A if (retval)
2N/A break;
2N/A retval = (*func) (func_arg, &entries);
2N/A krb5_dbe_free_contents(context, &entries);
2N/A retval2 = k5_mutex_lock(krb5_db2_mutex);
2N/A /* Note: If re-locking fails, the wrapper in db2_exp.c will
2N/A still try to unlock it again. That would be a bug. Fix
2N/A when integrating the locking better. */
2N/A if (retval)
2N/A break;
2N/A if (retval2) {
2N/A retval = retval2;
2N/A break;
2N/A }
2N/A /*
2N/A * Solaris Kerberos: to be safe as the (*func) could close and reopen
2N/A * the DB. Note that when resyncing with MIT 1.10 or later the call to
2N/A * db->seq() should replaced globally with db_ctx->db->seq().
2N/A */
2N/A db = db_ctx->db;
2N/A if (!recursive) {
2N/A dbret = (*db->seq) (db, &key, &contents,
2N/A backwards ? R_PREV : R_NEXT);
2N/A } else {
2N/A#ifdef HAVE_BT_RSEQ
2N/A dbret = bt_rseq(db, &key, &contents, &cookie,
2N/A backwards ? R_PREV : R_NEXT);
2N/A#else
2N/A (void) krb5_db2_db_unlock(context);
2N/A return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
2N/A#endif
2N/A }
2N/A }
2N/A switch (dbret) {
2N/A case 1:
2N/A case 0:
2N/A break;
2N/A case -1:
2N/A default:
2N/A retval = errno;
2N/A }
2N/A (void) krb5_db2_db_unlock(context);
2N/A return retval;
2N/A}
2N/A
2N/A/* Solaris Kerberos - support for db_args / -rev / -recurse (not in 183) */
2N/Akrb5_error_code
2N/Akrb5_db2_db_iterate(krb5_context context,
2N/A char *match_expr,
2N/A krb5_error_code(*func) (krb5_pointer, krb5_db_entry *),
2N/A krb5_pointer func_arg, char **db_args)
2N/A{
2N/A char **t_ptr = db_args;
2N/A int backwards = 0, recursive = 0;
2N/A
2N/A while (t_ptr && *t_ptr) {
2N/A char *opt = NULL, *val = NULL;
2N/A
2N/A krb5_db2_get_db_opt(*t_ptr, &opt, &val);
2N/A
2N/A /* Solaris Kerberos: adding support for -rev/recurse flags */
2N/A if (val && !strcmp(val, "rev"))
2N/A backwards = 1;
2N/A else if (val && !strcmp(val, "recurse"))
2N/A recursive = 1;
2N/A else {
2N/A krb5_set_error_message(context, EINVAL,
2N/A gettext("Unsupported argument \"%s\" for db2"),
2N/A val);
2N/A free(opt);
2N/A free(val);
2N/A return EINVAL;
2N/A }
2N/A
2N/A free(opt);
2N/A free(val);
2N/A t_ptr++;
2N/A }
2N/A
2N/A /* Solaris Kerberos: adding support for -rev/recurse flags */
2N/A return krb5_db2_db_iterate_ext(context, func, func_arg, backwards, recursive);
2N/A}
2N/A
2N/Akrb5_boolean
2N/Akrb5_db2_db_set_lockmode(krb5_context context, krb5_boolean mode)
2N/A{
2N/A krb5_boolean old;
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A dal_handle = context->dal_handle;
2N/A old = mode;
2N/A if (dal_handle && (db_ctx = (krb5_db2_context *) dal_handle->db_context)) {
2N/A old = db_ctx->db_nb_locks;
2N/A db_ctx->db_nb_locks = mode;
2N/A }
2N/A return old;
2N/A}
2N/A
2N/A/*
2N/A * DAL API functions
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_lib_init()
2N/A{
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_lib_cleanup()
2N/A{
2N/A /* right now, no cleanup required */
2N/A return 0;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_open(krb5_context kcontext,
2N/A char *conf_section, char **db_args, int mode)
2N/A{
2N/A krb5_error_code status = 0;
2N/A char **t_ptr = db_args;
2N/A int db_name_set = 0, tempdb=0;
2N/A char *dbname = NULL;
2N/A
2N/A krb5_clear_error_message (kcontext);
2N/A
2N/A if (k5db2_inited(kcontext))
2N/A return 0;
2N/A
2N/A while (t_ptr && *t_ptr) {
2N/A char *opt = NULL, *val = NULL;
2N/A
2N/A krb5_db2_get_db_opt(*t_ptr, &opt, &val);
2N/A if (opt && !strcmp(opt, "dbname")) {
2N/A if (dbname) free(dbname);
2N/A dbname = strdup(val);
2N/A if (dbname == NULL) {
2N/A free(opt);
2N/A free(val);
2N/A return ENOMEM;
2N/A }
2N/A }
2N/A else if (!opt && !strcmp(val, "temporary") ) {
2N/A tempdb = 1;
2N/A }
2N/A else if (!opt && !strcmp(val, "merge_nra")) {
2N/A ;
2N/A }
2N/A /* ignore hash argument. Might have been passed from create */
2N/A else if (!opt || strcmp(opt, "hash")) {
2N/A krb5_set_error_message(kcontext, EINVAL,
2N/A gettext("Unsupported argument "
2N/A "\"%s\" for db2"),
2N/A opt ? opt : val);
2N/A free(opt);
2N/A free(val);
2N/A return EINVAL;
2N/A }
2N/A
2N/A free(opt);
2N/A free(val);
2N/A t_ptr++;
2N/A }
2N/A
2N/A if(dbname) {
2N/A status = krb5_db2_db_set_name(kcontext, dbname, tempdb);
2N/A if (status) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), dbname);
2N/A krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
2N/A
2N/A free(dbname);
2N/A goto clean_n_exit;
2N/A }
2N/A db_name_set = 1;
2N/A }
2N/A if (!db_name_set) {
2N/A char *value = NULL;
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
2N/A NULL, &value);
2N/A
2N/A if (value == NULL) {
2N/A /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
2N/A default_db_name, &value);
2N/A if (status) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed when searching for "
2N/A "\"%s\", \"%s\", \"%s\" in profile: "), KDB_REALM_SECTION,
2N/A KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME);
2N/A krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
2N/A goto clean_n_exit;
2N/A }
2N/A }
2N/A
2N/A status = krb5_db2_db_set_name(kcontext, value, tempdb);
2N/A if (status) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to set db2 name to \"%s\": "), value);
2N/A krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
2N/A profile_release_string(value);
2N/A
2N/A goto clean_n_exit;
2N/A }
2N/A profile_release_string(value);
2N/A
2N/A }
2N/A
2N/A status = krb5_db2_db_init(kcontext);
2N/A if (status) {
2N/A /* Solaris Kerberos: Better error logging */
2N/A snprintf(errbuf, sizeof(errbuf), gettext("Failed to initialize db2 db: "));
2N/A krb5_db2_prepend_err_str(kcontext, errbuf, status, status);
2N/A }
2N/A
2N/Aclean_n_exit:
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_create(krb5_context kcontext, char *conf_section, char **db_args)
2N/A{
2N/A krb5_error_code status = 0;
2N/A char **t_ptr = db_args;
2N/A int db_name_set = 0, tempdb=0;
2N/A krb5_int32 flags = KRB5_KDB_CREATE_BTREE;
2N/A char *db_name = NULL;
2N/A
2N/A krb5_clear_error_message (kcontext);
2N/A
2N/A if (k5db2_inited(kcontext))
2N/A return 0;
2N/A
2N/A while (t_ptr && *t_ptr) {
2N/A char *opt = NULL, *val = NULL;
2N/A
2N/A krb5_db2_get_db_opt(*t_ptr, &opt, &val);
2N/A if (opt && !strcmp(opt, "dbname")) {
2N/A db_name = strdup(val);
2N/A if (db_name == NULL) {
2N/A free(opt);
2N/A free(val);
2N/A return ENOMEM;
2N/A }
2N/A }
2N/A else if (!opt && !strcmp(val, "temporary")) {
2N/A tempdb = 1;
2N/A } else if (!opt && !strcmp(val, "merge_nra")) {
2N/A ;
2N/A } else if (opt && !strcmp(opt, "hash")) {
2N/A flags = KRB5_KDB_CREATE_HASH;
2N/A } else {
2N/A krb5_set_error_message(kcontext, EINVAL,
2N/A "Unsupported argument \"%s\" for db2",
2N/A opt ? opt : val);
2N/A free(opt);
2N/A free(val);
2N/A return EINVAL;
2N/A }
2N/A
2N/A free(opt);
2N/A free(val);
2N/A t_ptr++;
2N/A }
2N/A if (db_name) {
2N/A status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
2N/A if (!status) {
2N/A status = EEXIST;
2N/A
2N/A /* Solaris Kerberos: Better error logging */
2N/A krb5_set_error_message(kcontext, EEXIST,
2N/A gettext("Kerberos database lock file %s~ exists, "
2N/A "loading of database failed - error %d."),
2N/A db_name, status);
2N/A
2N/A goto clean_n_exit;
2N/A }
2N/A db_name_set = 1;
2N/A }
2N/A if (!db_name_set) {
2N/A char *value = NULL;
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
2N/A KDB_MODULE_SECTION, conf_section,
2N/A /* under given conf section */
2N/A KDB_DB2_DATABASE_NAME, NULL, &value);
2N/A
2N/A if (value == NULL) {
2N/A /* Special case for db2. We might actually be looking at
2N/A * old type config file where database is specified as
2N/A * part of realm. */
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext),
2N/A KDB_REALM_SECTION,
2N/A KRB5_DB_GET_REALM(kcontext),
2N/A /* under given realm */
2N/A KDB_DB2_DATABASE_NAME,
2N/A default_db_name, &value);
2N/A if (status) {
2N/A goto clean_n_exit;
2N/A }
2N/A }
2N/A
2N/A db_name = strdup(value);
2N/A if (db_name == NULL) {
2N/A status = ENOMEM;
2N/A profile_release_string(value);
2N/A goto clean_n_exit;
2N/A }
2N/A status = krb5_db2_db_set_name(kcontext, value, tempdb);
2N/A profile_release_string(value);
2N/A if (!status) {
2N/A status = EEXIST;
2N/A
2N/A /* Solaris Kerberos: Better error logging */
2N/A krb5_set_error_message(kcontext, EEXIST,
2N/A gettext("Kerberos database lock file %s~ exists, "
2N/A "loading of database failed - error %d."),
2N/A db_name, status);
2N/A
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A }
2N/A
2N/A status = krb5_db2_db_create(kcontext, db_name, flags);
2N/A if (status)
2N/A goto clean_n_exit;
2N/A /* db2 has a problem of needing to close and open the database again. This removes that need */
2N/A status = krb5_db2_db_fini(kcontext);
2N/A if (status)
2N/A goto clean_n_exit;
2N/A
2N/A status = krb5_db2_open(kcontext, conf_section, db_args, KRB5_KDB_OPEN_RW);
2N/A
2N/Aclean_n_exit:
2N/A if (db_name)
2N/A free(db_name);
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_destroy(krb5_context kcontext, char *conf_section, char **db_args)
2N/A{
2N/A krb5_error_code status = 0;
2N/A char **t_ptr = db_args;
2N/A int db_name_set = 0, tempdb=0;
2N/A char *db_name = NULL;
2N/A
2N/A while (t_ptr && *t_ptr) {
2N/A char *opt = NULL, *val = NULL;
2N/A
2N/A krb5_db2_get_db_opt(*t_ptr, &opt, &val);
2N/A if (opt && !strcmp(opt, "dbname")) {
2N/A db_name = strdup(val);
2N/A if (db_name == NULL) {
2N/A free(opt);
2N/A free(val);
2N/A return ENOMEM;
2N/A }
2N/A }
2N/A else if (!opt && !strcmp(val, "temporary")) {
2N/A tempdb = 1;
2N/A }
2N/A /* ignore hash argument. Might have been passed from create */
2N/A else if (!opt || strcmp(opt, "hash")) {
2N/A free(opt);
2N/A free(val);
2N/A return EINVAL;
2N/A }
2N/A
2N/A free(opt);
2N/A free(val);
2N/A t_ptr++;
2N/A }
2N/A
2N/A if (db_name) {
2N/A status = krb5_db2_db_set_name(kcontext, db_name, tempdb);
2N/A if (status) {
2N/A goto clean_n_exit;
2N/A }
2N/A db_name_set = 1;
2N/A }
2N/A if (!db_name_set) {
2N/A char *value = NULL;
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
2N/A NULL, &value);
2N/A
2N/A if (value == NULL) {
2N/A /* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
2N/A status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
2N/A default_db_name, &value);
2N/A if (status) {
2N/A goto clean_n_exit;
2N/A }
2N/A }
2N/A
2N/A db_name = strdup(value);
2N/A if (db_name == NULL) {
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A status = krb5_db2_db_set_name(kcontext, value, tempdb);
2N/A profile_release_string(value);
2N/A if (status) {
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A }
2N/A
2N/A status = krb5_db2_db_destroy(kcontext, db_name);
2N/A
2N/Aclean_n_exit:
2N/A if (db_name)
2N/A free(db_name);
2N/A return status;
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_set_master_key_ext(krb5_context kcontext,
2N/A char *pwd, krb5_keyblock * key)
2N/A{
2N/A return krb5_db2_db_set_mkey(kcontext, key);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_db_set_option(krb5_context kcontext, int option, void *value)
2N/A{
2N/A krb5_error_code status = 0;
2N/A krb5_boolean oldval;
2N/A krb5_db2_context *db_ctx;
2N/A kdb5_dal_handle *dal_handle;
2N/A
2N/A if (!k5db2_inited(kcontext))
2N/A return KRB5_KDB_DBNOTINITED;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A
2N/A switch (option) {
2N/A case KRB5_KDB_OPT_SET_DB_NAME:
2N/A status = krb5_db2_db_set_name(kcontext, (char *) value, db_ctx->tempdb);
2N/A break;
2N/A
2N/A case KRB5_KDB_OPT_SET_LOCK_MODE:
2N/A oldval = krb5_db2_db_set_lockmode(kcontext, *((krb5_boolean *) value));
2N/A *((krb5_boolean *) value) = oldval;
2N/A break;
2N/A
2N/A default:
2N/A status = -1; /* TBD */
2N/A break;
2N/A }
2N/A
2N/A return status;
2N/A}
2N/A
2N/Avoid *
2N/Akrb5_db2_alloc(krb5_context kcontext, void *ptr, size_t size)
2N/A{
2N/A return realloc(ptr, size);
2N/A}
2N/A
2N/Avoid
2N/Akrb5_db2_free(krb5_context kcontext, void *ptr)
2N/A{
2N/A free(ptr);
2N/A}
2N/A
2N/A/* policy functions */
2N/Akrb5_error_code
2N/Akrb5_db2_create_policy(krb5_context kcontext, osa_policy_ent_t policy)
2N/A{
2N/A kdb5_dal_handle *dal_handle;
2N/A krb5_db2_context *dbc;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A return osa_adb_create_policy(dbc->policy_db, policy);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_get_policy(krb5_context kcontext,
2N/A char *name, osa_policy_ent_t * policy, int *cnt)
2N/A{
2N/A kdb5_dal_handle *dal_handle;
2N/A krb5_db2_context *dbc;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A return osa_adb_get_policy(dbc->policy_db, name, policy, cnt);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_put_policy(krb5_context kcontext, osa_policy_ent_t policy)
2N/A{
2N/A kdb5_dal_handle *dal_handle;
2N/A krb5_db2_context *dbc;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A return osa_adb_put_policy(dbc->policy_db, policy);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_iter_policy(krb5_context kcontext,
2N/A char *match_entry,
2N/A osa_adb_iter_policy_func func, void *data)
2N/A{
2N/A kdb5_dal_handle *dal_handle;
2N/A krb5_db2_context *dbc;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A return osa_adb_iter_policy(dbc->policy_db, func, data);
2N/A}
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_delete_policy(krb5_context kcontext, char *policy)
2N/A{
2N/A kdb5_dal_handle *dal_handle;
2N/A krb5_db2_context *dbc;
2N/A
2N/A dal_handle = kcontext->dal_handle;
2N/A dbc = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A return osa_adb_destroy_policy(dbc->policy_db, policy);
2N/A}
2N/A
2N/Avoid
2N/Akrb5_db2_free_policy(krb5_context kcontext, osa_policy_ent_t entry)
2N/A{
2N/A osa_free_policy_ent(entry);
2N/A}
2N/A
2N/A
2N/A/* */
2N/A
2N/Akrb5_error_code
2N/Akrb5_db2_promote_db(krb5_context kcontext, char *conf_section, char **db_args)
2N/A{
2N/A krb5_error_code status = 0;
2N/A char *db_name = NULL;
2N/A char *temp_db_name = NULL;
2N/A char **db_argp;
2N/A int merge_nra = 0;
2N/A
2N/A krb5_clear_error_message (kcontext);
2N/A
2N/A {
2N/A kdb5_dal_handle *dal_handle = kcontext->dal_handle;
2N/A krb5_db2_context *db_ctx = dal_handle->db_context;
2N/A db_name = strdup(db_ctx->db_name);
2N/A if (db_name == NULL) {
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A }
2N/A
2N/A assert(kcontext->dal_handle != NULL);
2N/A temp_db_name = gen_dbsuffix(db_name, "~");
2N/A if (temp_db_name == NULL) {
2N/A status = ENOMEM;
2N/A goto clean_n_exit;
2N/A }
2N/A
2N/A for (db_argp = db_args; *db_argp; db_argp++) {
2N/A if (!strcmp(*db_argp, "merge_nra")) {
2N/A merge_nra++;
2N/A break;
2N/A }
2N/A }
2N/A
2N/A status = krb5_db2_db_rename (kcontext, temp_db_name, db_name, merge_nra);
2N/A if (status)
2N/A goto clean_n_exit;
2N/A
2N/Aclean_n_exit:
2N/A if (db_name)
2N/A free(db_name);
2N/A if (temp_db_name)
2N/A free(temp_db_name);
2N/A return status;
2N/A}
2N/A
2N/A/*
2N/A * Merge non-replicated attributes from src into dst, setting
2N/A * changed to non-zero if dst was changed.
2N/A *
2N/A * Non-replicated attributes are: last_success, last_failed,
2N/A * fail_auth_count, and any negative TL data values.
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_db2_merge_principal(krb5_context kcontext,
2N/A krb5_db_entry *src,
2N/A krb5_db_entry *dst,
2N/A int *changed)
2N/A{
2N/A *changed = 0;
2N/A
2N/A if (dst->last_success != src->last_success) {
2N/A dst->last_success = src->last_success;
2N/A (*changed)++;
2N/A }
2N/A
2N/A if (dst->last_failed != src->last_failed) {
2N/A dst->last_failed = src->last_failed;
2N/A (*changed)++;
2N/A }
2N/A
2N/A if (dst->fail_auth_count != src->fail_auth_count) {
2N/A dst->fail_auth_count = src->fail_auth_count;
2N/A (*changed)++;
2N/A }
2N/A
2N/A return 0;
2N/A}
2N/A
2N/Astruct nra_context {
2N/A krb5_context kcontext;
2N/A krb5_db2_context *db_context;
2N/A};
2N/A
2N/A/*
2N/A * Iteration callback merges non-replicated attributes from
2N/A * old database.
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_db2_merge_nra_iterator(krb5_pointer ptr, krb5_db_entry *entry)
2N/A{
2N/A struct nra_context *nra = (struct nra_context *)ptr;
2N/A kdb5_dal_handle *dal_handle = nra->kcontext->dal_handle;
2N/A krb5_error_code retval;
2N/A int n_entries = 0, changed;
2N/A krb5_db_entry s_entry;
2N/A krb5_boolean more;
2N/A krb5_db2_context *dst_db;
2N/A
2N/A memset(&s_entry, 0, sizeof(s_entry));
2N/A
2N/A dst_db = dal_handle->db_context;
2N/A dal_handle->db_context = nra->db_context;
2N/A
2N/A /* look up the new principal in the old DB */
2N/A retval = krb5_db2_db_get_principal(nra->kcontext,
2N/A entry->princ,
2N/A &s_entry,
2N/A &n_entries,
2N/A &more);
2N/A if (retval != 0 || n_entries == 0) {
2N/A /* principal may be newly created, so ignore */
2N/A dal_handle->db_context = dst_db;
2N/A return 0;
2N/A }
2N/A
2N/A /* merge non-replicated attributes from the old entry in */
2N/A krb5_db2_merge_principal(nra->kcontext, &s_entry, entry, &changed);
2N/A
2N/A dal_handle->db_context = dst_db;
2N/A
2N/A /* if necessary, commit the modified new entry to the new DB */
2N/A if (changed) {
2N/A retval = krb5_db2_db_put_principal(nra->kcontext,
2N/A entry,
2N/A &n_entries,
2N/A NULL);
2N/A } else {
2N/A retval = 0;
2N/A }
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * Merge non-replicated attributes (that is, lockout-related
2N/A * attributes and negative TL data types) from the old database
2N/A * into the new one.
2N/A *
2N/A * Note: src_db is locked on success.
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_db2_begin_nra_merge(krb5_context kcontext,
2N/A krb5_db2_context *src_db,
2N/A krb5_db2_context *dst_db)
2N/A{
2N/A krb5_error_code retval;
2N/A kdb5_dal_handle *dal_handle = kcontext->dal_handle;
2N/A struct nra_context nra;
2N/A
2N/A nra.kcontext = kcontext;
2N/A nra.db_context = dst_db;
2N/A
2N/A assert(dal_handle->db_context == dst_db);
2N/A dal_handle->db_context = src_db;
2N/A
2N/A retval = krb5_db2_db_lock(kcontext, KRB5_LOCKMODE_EXCLUSIVE);
2N/A if (retval) {
2N/A dal_handle->db_context = dst_db;
2N/A return retval;
2N/A }
2N/A
2N/A retval = krb5_db2_db_iterate_ext(kcontext,
2N/A krb5_db2_merge_nra_iterator,
2N/A &nra,
2N/A 0,
2N/A 0);
2N/A if (retval != 0)
2N/A (void) krb5_db2_db_unlock(kcontext);
2N/A
2N/A dal_handle->db_context = dst_db;
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * Finish merge of non-replicated attributes by unlocking
2N/A * src_db.
2N/A */
2N/Astatic krb5_error_code
2N/Akrb5_db2_end_nra_merge(krb5_context kcontext,
2N/A krb5_db2_context *src_db,
2N/A krb5_db2_context *dst_db)
2N/A{
2N/A krb5_error_code retval;
2N/A kdb5_dal_handle *dal_handle = kcontext->dal_handle;
2N/A
2N/A dal_handle->db_context = src_db;
2N/A retval = krb5_db2_db_unlock(kcontext);
2N/A dal_handle->db_context = dst_db;
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A/* Retrieved from pre-DAL code base. */
2N/A/*
2N/A * "Atomically" rename the database in a way that locks out read
2N/A * access in the middle of the rename.
2N/A *
2N/A * Not perfect; if we crash in the middle of an update, we don't
2N/A * necessarily know to complete the transaction the rename, but...
2N/A *
2N/A * Since the rename operation happens outside the init/fini bracket, we
2N/A * have to go through the same stuff that we went through up in db_destroy.
2N/A */
2N/Akrb5_error_code
2N/Akrb5_db2_db_rename(context, from, to, merge_nra)
2N/A krb5_context context;
2N/A char *from;
2N/A char *to;
2N/A int merge_nra;
2N/A{
2N/A char *fromok;
2N/A krb5_error_code retval;
2N/A krb5_db2_context *s_context, *db_ctx;
2N/A kdb5_dal_handle *dal_handle = context->dal_handle;
2N/A
2N/A s_context = dal_handle->db_context;
2N/A dal_handle->db_context = NULL;
2N/A if ((retval = k5db2_init_context(context)))
2N/A return retval;
2N/A db_ctx = (krb5_db2_context *) dal_handle->db_context;
2N/A
2N/A /*
2N/A * Create the database if it does not already exist; the
2N/A * files must exist because krb5_db2_db_lock, called below,
2N/A * will fail otherwise.
2N/A */
2N/A retval = krb5_db2_db_create(context, to, 0);
2N/A if (retval != 0 && retval != EEXIST)
2N/A goto errout;
2N/A
2N/A /*
2N/A * Set the database to the target, so that other processes sharing
2N/A * the target will stop their activity, and notice the new database.
2N/A */
2N/A retval = krb5_db2_db_set_name(context, to, 0);
2N/A if (retval)
2N/A goto errout;
2N/A
2N/A retval = krb5_db2_db_init(context);
2N/A if (retval)
2N/A goto errout;
2N/A
2N/A db_ctx->db_lf_name = gen_dbsuffix(db_ctx->db_name, KDB2_LOCK_EXT);
2N/A if (db_ctx->db_lf_name == NULL) {
2N/A retval = ENOMEM;
2N/A goto errout;
2N/A }
2N/A db_ctx->db_lf_file = open(db_ctx->db_lf_name, O_RDWR|O_CREAT, 0600);
2N/A if (db_ctx->db_lf_file < 0) {
2N/A retval = errno;
2N/A goto errout;
2N/A }
2N/A set_cloexec_fd(db_ctx->db_lf_file);
2N/A
2N/A db_ctx->db_inited = 1;
2N/A
2N/A retval = krb5_db2_db_get_age(context, NULL, &db_ctx->db_lf_time);
2N/A if (retval)
2N/A goto errout;
2N/A
2N/A fromok = gen_dbsuffix(from, KDB2_LOCK_EXT);
2N/A if (fromok == NULL) {
2N/A retval = ENOMEM;
2N/A goto errout;
2N/A }
2N/A
2N/A if ((retval = krb5_db2_db_lock(context, KRB5_LOCKMODE_EXCLUSIVE)))
2N/A goto errfromok;
2N/A
2N/A if ((retval = krb5_db2_db_start_update(context)))
2N/A goto errfromok;
2N/A
2N/A if (merge_nra) {
2N/A if ((retval = krb5_db2_begin_nra_merge(context, s_context, db_ctx)))
2N/A goto errfromok;
2N/A }
2N/A
2N/A if (rename(from, to)) {
2N/A retval = errno;
2N/A goto errfromok;
2N/A }
2N/A if (unlink(fromok)) {
2N/A retval = errno;
2N/A goto errfromok;
2N/A }
2N/A
2N/A if (merge_nra) {
2N/A krb5_db2_end_nra_merge(context, s_context, db_ctx);
2N/A }
2N/A
2N/A retval = krb5_db2_db_end_update(context);
2N/A if (retval)
2N/A goto errfromok;
2N/A
2N/A {
2N/A /* XXX moved so that NRA merge works */
2N/A /* Ugly brute force hack.
2N/A
2N/A Should be going through nice friendly helper routines for
2N/A this, but it's a mess of jumbled so-called interfaces right
2N/A now. */
2N/A char policy[2048], new_policy[2048];
2N/A assert (strlen(db_ctx->db_name) < 2000);
2N/A snprintf(policy, sizeof(policy), "%s.kadm5", db_ctx->db_name);
2N/A snprintf(new_policy, sizeof(new_policy),
2N/A "%s~.kadm5", db_ctx->db_name);
2N/A if (0 != rename(new_policy, policy)) {
2N/A retval = errno;
2N/A goto errfromok;
2N/A }
2N/A strlcat(new_policy, ".lock",sizeof(new_policy));
2N/A (void) unlink(new_policy);
2N/A }
2N/A
2N/Aerrfromok:
2N/A free_dbsuffix(fromok);
2N/Aerrout:
2N/A if (dal_handle->db_context) {
2N/A if (db_ctx->db_lf_file >= 0) {
2N/A krb5_db2_db_unlock(context);
2N/A close(db_ctx->db_lf_file);
2N/A }
2N/A k5db2_clear_context((krb5_db2_context *) dal_handle->db_context);
2N/A free(dal_handle->db_context);
2N/A }
2N/A
2N/A dal_handle->db_context = s_context;
2N/A (void) krb5_db2_db_unlock(context); /* unlock saved context db */
2N/A
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * Solaris Kerberos:
2N/A * Similar to the ldap plugin.
2N/A */
2N/Astatic void
2N/Akrb5_db2_prepend_err_str(krb5_context ctx, const char *str, krb5_error_code err,
2N/A krb5_error_code oerr) {
2N/A const char *omsg;
2N/A if (oerr == 0)
2N/A oerr = err;
2N/A omsg = krb5_get_error_message (ctx, err);
2N/A krb5_set_error_message (ctx, err, "%s %s", str, omsg);
2N/A krb5_free_error_message(ctx, omsg);
2N/A}