/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/*
*
* Copyright 1997,2006,2007-2009 by the Massachusetts Institute of Technology.
* All Rights Reserved.
*
* Export of this software from the United States of America may
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of M.I.T. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. Furthermore if you modify this software you must label
* your software as modified software and not distribute it in such a
* fashion that it might be confused with the original M.I.T. software.
* M.I.T. makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
*/
/*
* Copyright (C) 1998 by the FundsXpress, INC.
*
* All rights reserved.
*
* Export of this software from the United States of America may require
* a specific license from the United States Government. It is the
* responsibility of any person or organization contemplating export to
* obtain such a license before exporting.
*
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
* notice appear in all copies and that both that copyright notice and
* this permission notice appear in supporting documentation, and that
* the name of FundsXpress. not be used in advertising or publicity pertaining
* to distribution of the software without specific, written prior
* permission. FundsXpress makes no representations about the suitability of
* this software for any purpose. It is provided "as is" without express
* or implied warranty.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
*/
#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"
#include <libintl.h> /* Solaris Kerberos */
static char *gen_dbsuffix(char *, char *);
/*
* Solaris Kerberos
* Extra error handling
*/
static void krb5_db2_prepend_err_str(krb5_context , const char *,
/*
* Locking:
*
* There are two distinct locking protocols used. One is designed to
* lock against processes (the admin_server, for one) which make
* incremental changes to the database; the other is designed to lock
* against utilities (kdb5_edit, kpropd, kdb5_convert) which replace the
* entire database in one fell swoop.
*
* The first locking protocol is implemented using flock() in the
* krb_dbl_lock() and krb_dbl_unlock routines.
*
* The second locking protocol is necessary because DBM "files" are
* actually implemented as two separate files, and it is impossible to
* atomically rename two files simultaneously. It assumes that the
* database is replaced only very infrequently in comparison to the time
* needed to do a database read operation.
*
* A third file is used as a "version" semaphore; the modification
* time of this file is the "version number" of the database.
* At the start of a read operation, the reader checks the version
* number; at the end of the read operation, it checks again. If the
* version number changed, or if the semaphore was nonexistant at
* either time, the reader sleeps for a second to let things
* stabilize, and then tries again; if it does not succeed after
* KRB5_DBM_MAX_RETRY attempts, it gives up.
*
* On update, the semaphore file is deleted (if it exists) before any
* update takes place; at the end of the update, it is replaced, with
* a version number strictly greater than the version number which
* existed at the start of the update.
*
* If the system crashes in the middle of an update, the semaphore
* file is not automatically created on reboot; this is a feature, not
* a bug, since the database may be inconsistant. Note that the
* absence of a semaphore file does not prevent another _update_ from
* taking place later. Database replacements take place automatically
* only on slave servers; a crash in the middle of an update will be
* fixed by the next slave propagation. A crash in the middle of an
* update on the master would be somewhat more serious, but this would
* likely be noticed by an administrator, who could fix the problem and
* retry the operation.
*/
/*
* Routines to deal with context.
*/
&& c->dal_handle->db_context \
static krb5_error_code
{
return ENOMEM;
}
} else {
return ENOMEM;
}
}
return (0);
}
/*
* Restore the default context.
*/
static void
{
/*
* Free any dynamically allocated memory. File descriptors and locks
* are the caller's problem.
*/
if (dbctx->db_lf_name)
/*
* Clear the structure and reset the defaults.
*/
}
static krb5_error_code
{
return ENOMEM;
else {
}
}
return (0);
}
/*
* Utility routine: generate name of database file.
*/
static char *
{
char *dbsuffix;
return ((char *) NULL);
return (0);
return dbsuffix;
}
static DB *
{
bti.minkeypage = 0;
if (tempdb) {
} else {
}
{
return NULL;
}
return db;
}
switch (errno) {
#ifdef EFTYPE
case EFTYPE:
#endif
case EINVAL:
default:
return db;
}
}
static krb5_error_code
{
if (k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
return 0;
}
/*
* initialization for data base routines.
*/
{
if (k5db2_inited(context))
return 0;
/* Check for presence of our context, if not present, allocate one. */
return (retval);
return ENOMEM;
/*
* POSIX systems
*/
/* Solaris Kerberos: Better error logging */
goto err_out;
}
}
goto err_out;
"%s.lock", policy_db_name);
{
/* Solaris Kerberos: Better error logging */
gettext("Failed to initialize db, \"%s\", lockfile, \"%s\" : "),
goto err_out;
}
return 0;
return (retval);
}
/*
* gracefully shut down database--must be called by ANY program that does
* a krb5_db2_db_init
*/
{
if (dal_handle == NULL) {
return 0;
}
if (k5db2_inited(context)) {
else
retval = 0;
}
if (db_ctx) {
retval =
if (retval)
return retval;
}
/* free(dal_handle->db_context); */
}
return retval;
}
/*
*/
{
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
return 0;
}
{
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
return 0;
}
/*
* Set the "name" of the current database to some alternate value.
*
* Passing a null pointer as "name" will set back to the default.
* If the alternate database doesn't exist, nothing is changed.
*
* XXX rethink this
*/
static krb5_error_code
{
if (k5db2_inited(context))
return KRB5_KDB_DBINITED;
/* Check for presence of our context, if not present, allocate one. */
return (kret);
return errno;
/* Solaris Kerberos */
return ENOMEM;
}
return 0;
}
/*
* Return the last modification time of the database.
*
* Think about using fstat.
*/
{
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
*age = -1;
else
return 0;
}
/*
* Remove the semaphore file; indicates that database is currently
* under renovation.
*
* This is only for use when moving the database out from underneath
* the server (for example, during slave updates).
*/
static krb5_error_code
{
return 0;
}
static krb5_error_code
{
if (!k5db2_inited(context))
return (KRB5_KDB_DBNOTINITED);
retval = 0;
} else {
}
if (retval) {
/* Solaris Kerberos: Better error logging */
"access and modification times for \"%s\": "),
db_ctx->db_lf_name);
}
} else
/* Solaris Kerberos: Better error logging */
db_ctx->db_lf_name);
if (!retval) {
else
}
return (retval);
}
{
int krb5_lock_mode;
switch (in_mode) {
break;
break;
case KRB5_DB_LOCKMODE_SHARED:
break;
default:
return EINVAL;
}
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
/* No need to upgrade lock, just return */
db_ctx->db_locks_held++;
goto policy_lock;
}
return KRB5_KDB_BADLOCKMODE;
if (retval == 0) {
gotlock++;
break;
/* tried to exclusive-lock something we don't have */
/* write access to */
/* Solaris Kerberos: Better error logging */
gettext("Failed to exclusively lock \"%s\": "),
db_ctx->db_lf_name);
return KRB5_KDB_CANTLOCK_DB;
sleep(1);
}
if (retval) {
/* Solaris Kerberos: Better error logging */
gettext("Failed to lock \"%s\": "),
db_ctx->db_lf_name);
}
return KRB5_KDB_CANTLOCK_DB;
return OSA_ADB_CANTLOCK_DB;
else if (retval != 0)
return retval;
goto lock_error;
if (db) {
} else {
/* Solaris Kerberos: Better error logging */
gettext("Failed to open db \"%s\": "),
goto lock_error;
}
db_ctx->db_locks_held++;
}
return retval;
db_ctx->db_lock_mode = 0;
db_ctx->db_locks_held = 0;
return retval;
}
{
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
return retval;
}
return KRB5_KDB_NOTLOCKED;
if (--(db_ctx->db_locks_held) == 0) {
db_ctx->db_lock_mode = 0;
return (retval);
}
return 0;
}
/*
* Create the database, assuming it's not there.
*/
{
char *okname;
int fd;
return (retval);
switch (flags) {
case KRB5_KDB_CREATE_HASH:
return retval;
break;
case KRB5_KDB_CREATE_BTREE:
case 0:
return retval;
break;
default:
return KRB5_KDB_BAD_CREATEFLAGS;
}
/* Solaris Kerberos: Better error logging */
return errno;
}
return ENOMEM;
if (!okname)
else {
if (fd < 0) {
/* Solaris Kerberos: Better error logging */
}
else
}
"%s.lock", policy_db_name);
return retval;
}
/*
* Destroy the database. Zero's out all of the files, just to be sure.
*/
static krb5_error_code
{
char *filename;
int j;
int dowrite;
if (filename == 0)
return ENOMEM;
return errno;
}
/* fstat() will probably not fail unless using a remote filesystem
* (which is inappropriate for the kerberos database) so this check
* is mostly paranoia. */
return retval;
}
/*
* Stroll through the file, reading in BUFSIZ chunks. If everything
* is zero, then we're done for that block, otherwise, zero the block.
* We would like to just blast through everything, but some DB
* implementations make holey files and writing data to the holes
* causes actual blocks to be allocated which is no good, since
* we're just about to unlink it anyways.
*/
pos = 0;
dowrite = 0;
if (nb < 0) {
return retval;
}
for (j = 0; j < nb; j++) {
if (buf[j] != '\0') {
dowrite = 1;
break;
}
}
/* For signedness */
j = nb;
if (dowrite) {
if (nb < 0) {
return retval;
}
}
}
/* ??? Is fsync really needed? I don't know of any non-networked
* filesystem which will discard queued writes to disk if a file
* is deleted after it is closed. --jfc */
#ifndef NOFSYNC
#endif
return (errno);
}
return (0);
}
/*
* have some tomfoolery to undergo here. If we're operating under no
* database context, then we initialize with the default. If the caller
* wishes a different context (e.g. different dispatch table), it's their
* responsibility to call kdb5_db_set_dbops() before this call. That will
* set up the right dispatch table values (e.g. name extensions).
*
* Not quite valid due to ripping out of dbops...
*/
{
tmpcontext = 0;
if (!context->dal_handle
tmpcontext = 1;
return (retval1);
}
if (tmpcontext) {
}
"%s.lock", policy_db_name);
return retval1;
}
/*
* look up a principal in the data base.
* returns number of entries found, and whether there were
* more than requested.
*/
{
*nentries = 0;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
if (db_ctx->db_nb_locks)
return (retval);
sleep(1);
continue;
}
break;
}
if (trynum == KRB5_DB2_MAX_RETRY)
return KRB5_KDB_DB_INUSE;
/* XXX deal with wildcard lookups */
if (retval)
goto cleanup;
switch (dbret) {
case 1:
retval = 0;
case -1:
default:
*nentries = 0;
goto cleanup;
case 0:
if (!retval)
*nentries = 1;
break;
}
return retval;
}
/*
Free stuff returned by krb5_db2_db_get_principal.
*/
int nentries)
{
register int i;
for (i = 0; i < nentries; i++)
return 0;
}
/*
Stores the *"nentries" entry structures pointed to by "entries" in the
database.
*"nentries" is updated upon return to reflect the number of records
acutally stored; the first *"nstored" records will have been stored in the
database (even if an error occurs).
*/
int *nentries, /* number of entry structs to update */
char **db_args)
{
int i, n, dbret;
if (db_args) {
/* DB2 does not support db_args DB arguments for principal */
gettext("Unsupported argument \"%s\" for db2"),
db_args[0]);
return EINVAL;
}
n = *nentries;
*nentries = 0;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
return retval;
(void) krb5_db2_db_unlock(context);
return retval;
}
for (i = 0; i < n; i++) {
if (retval)
break;
if (retval) {
break;
}
if (retval)
break;
entries++; /* bump to next struct */
}
(void) krb5_db2_db_end_update(context);
*nentries = i;
return (retval);
}
/*
* delete a principal from the data base.
* returns number of entries removed
*/
int *nentries) /* how many found & deleted */
{
int i, dbret;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
return (retval);
return (retval);
}
goto cleanup;
switch (dbret) {
case 1:
case -1:
default:
*nentries = 0;
goto cleankey;
case 0:
;
}
if (retval)
goto cleankey;
*nentries = 1;
/* Clear encrypted key contents */
for (i = 0; i < entry.n_key_data; i++) {
}
}
if (retval)
goto cleankey;
if (retval)
goto cleankey;
(void) krb5_db2_db_end_update(context);
return retval;
}
{
int dbret;
void *cookie;
if (!k5db2_inited(context))
return KRB5_KDB_DBNOTINITED;
if (retval)
return retval;
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
}
if (!recursive) {
} else {
#ifdef HAVE_BT_RSEQ
#else
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
#endif
}
while (dbret == 0) {
if (retval)
break;
if (retval)
break;
/* 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. */
if (retval)
break;
if (retval2) {
break;
}
/*
* Solaris Kerberos: to be safe as the (*func) could close and reopen
* the DB. Note that when resyncing with MIT 1.10 or later the call to
* db->seq() should replaced globally with db_ctx->db->seq().
*/
if (!recursive) {
} else {
#ifdef HAVE_BT_RSEQ
#else
(void) krb5_db2_db_unlock(context);
return KRB5_KDB_UK_RERROR; /* Not optimal, but close enough. */
#endif
}
}
switch (dbret) {
case 1:
case 0:
break;
case -1:
default:
}
(void) krb5_db2_db_unlock(context);
return retval;
}
/* Solaris Kerberos - support for db_args / -rev / -recurse (not in 183) */
char *match_expr,
{
backwards = 1;
recursive = 1;
else {
gettext("Unsupported argument \"%s\" for db2"),
val);
return EINVAL;
}
t_ptr++;
}
}
{
}
return old;
}
/*
* DAL API functions
*/
{
return 0;
}
{
/* right now, no cleanup required */
return 0;
}
{
if (k5db2_inited(kcontext))
return 0;
return ENOMEM;
}
}
tempdb = 1;
}
;
}
/* ignore hash argument. Might have been passed from create */
gettext("Unsupported argument "
"\"%s\" for db2"),
return EINVAL;
}
t_ptr++;
}
if(dbname) {
if (status) {
/* Solaris Kerberos: Better error logging */
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
/* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
default_db_name, &value);
if (status) {
/* Solaris Kerberos: Better error logging */
"\"%s\", \"%s\", \"%s\" in profile: "), KDB_REALM_SECTION,
goto clean_n_exit;
}
}
if (status) {
/* Solaris Kerberos: Better error logging */
goto clean_n_exit;
}
}
if (status) {
/* Solaris Kerberos: Better error logging */
}
return status;
}
{
if (k5db2_inited(kcontext))
return 0;
return ENOMEM;
}
}
tempdb = 1;
;
} else {
"Unsupported argument \"%s\" for db2",
return EINVAL;
}
t_ptr++;
}
if (db_name) {
if (!status) {
/* Solaris Kerberos: Better error logging */
gettext("Kerberos database lock file %s~ exists, "
"loading of database failed - error %d."),
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
/* under given conf section */
/* Special case for db2. We might actually be looking at
* old type config file where database is specified as
* part of realm. */
/* under given realm */
default_db_name, &value);
if (status) {
goto clean_n_exit;
}
}
goto clean_n_exit;
}
if (!status) {
/* Solaris Kerberos: Better error logging */
gettext("Kerberos database lock file %s~ exists, "
"loading of database failed - error %d."),
goto clean_n_exit;
}
}
if (status)
goto clean_n_exit;
/* db2 has a problem of needing to close and open the database again. This removes that need */
if (status)
goto clean_n_exit;
if (db_name)
return status;
}
{
return ENOMEM;
}
}
tempdb = 1;
}
/* ignore hash argument. Might have been passed from create */
return EINVAL;
}
t_ptr++;
}
if (db_name) {
if (status) {
goto clean_n_exit;
}
db_name_set = 1;
}
if (!db_name_set) {
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_MODULE_SECTION, conf_section, KDB_DB2_DATABASE_NAME, /* under given conf section */
/* special case for db2. We might actually be looking at old type config file where database is specified as part of realm */
status = profile_get_string(KRB5_DB_GET_PROFILE(kcontext), KDB_REALM_SECTION, KRB5_DB_GET_REALM(kcontext), KDB_DB2_DATABASE_NAME, /* under given realm */
default_db_name, &value);
if (status) {
goto clean_n_exit;
}
}
goto clean_n_exit;
}
if (status) {
goto clean_n_exit;
}
}
if (db_name)
return status;
}
{
}
{
if (!k5db2_inited(kcontext))
return KRB5_KDB_DBNOTINITED;
switch (option) {
case KRB5_KDB_OPT_SET_DB_NAME:
break;
break;
default:
break;
}
return status;
}
void *
{
}
void
{
}
/* policy functions */
{
}
{
}
{
}
char *match_entry,
{
}
{
}
void
{
}
/* */
{
char **db_argp;
int merge_nra = 0;
{
goto clean_n_exit;
}
}
if (temp_db_name == NULL) {
goto clean_n_exit;
}
merge_nra++;
break;
}
}
if (status)
goto clean_n_exit;
if (db_name)
if (temp_db_name)
return status;
}
/*
* Merge non-replicated attributes from src into dst, setting
* changed to non-zero if dst was changed.
*
* Non-replicated attributes are: last_success, last_failed,
* fail_auth_count, and any negative TL data values.
*/
static krb5_error_code
int *changed)
{
*changed = 0;
(*changed)++;
}
(*changed)++;
}
(*changed)++;
}
return 0;
}
struct nra_context {
};
/*
* Iteration callback merges non-replicated attributes from
* old database.
*/
static krb5_error_code
{
/* look up the new principal in the old DB */
&s_entry,
&more);
/* principal may be newly created, so ignore */
return 0;
}
/* merge non-replicated attributes from the old entry in */
/* if necessary, commit the modified new entry to the new DB */
if (changed) {
NULL);
} else {
retval = 0;
}
return retval;
}
/*
* Merge non-replicated attributes (that is, lockout-related
* attributes and negative TL data types) from the old database
* into the new one.
*
* Note: src_db is locked on success.
*/
static krb5_error_code
{
if (retval) {
return retval;
}
&nra,
0,
0);
if (retval != 0)
(void) krb5_db2_db_unlock(kcontext);
return retval;
}
/*
* Finish merge of non-replicated attributes by unlocking
* src_db.
*/
static krb5_error_code
{
return retval;
}
/* Retrieved from pre-DAL code base. */
/*
* "Atomically" rename the database in a way that locks out read
* access in the middle of the rename.
*
* Not perfect; if we crash in the middle of an update, we don't
* necessarily know to complete the transaction the rename, but...
*
* have to go through the same stuff that we went through up in db_destroy.
*/
char *from;
char *to;
int merge_nra;
{
char *fromok;
return retval;
/*
* Create the database if it does not already exist; the
* files must exist because krb5_db2_db_lock, called below,
* will fail otherwise.
*/
goto errout;
/*
* Set the database to the target, so that other processes sharing
* the target will stop their activity, and notice the new database.
*/
if (retval)
goto errout;
if (retval)
goto errout;
goto errout;
}
if (db_ctx->db_lf_file < 0) {
goto errout;
}
if (retval)
goto errout;
goto errout;
}
goto errfromok;
goto errfromok;
if (merge_nra) {
goto errfromok;
}
goto errfromok;
}
goto errfromok;
}
if (merge_nra) {
}
if (retval)
goto errfromok;
{
/* XXX moved so that NRA merge works */
/* Ugly brute force hack.
Should be going through nice friendly helper routines for
this, but it's a mess of jumbled so-called interfaces right
now. */
goto errfromok;
}
(void) unlink(new_policy);
}
if (dal_handle->db_context) {
if (db_ctx->db_lf_file >= 0) {
}
}
return retval;
}
/*
* Solaris Kerberos:
* Similar to the ldap plugin.
*/
static void
const char *omsg;
if (oerr == 0)
}