/* db_ndbm.c--SASL ndbm interface
* Rob Siemborski
* Rob Earhart
* $Id: db_ndbm.c,v 1.5 2003/02/13 19:56:14 rjs3 Exp $
*/
/*
* Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The name "Carnegie Mellon University" must not be used to
* endorse or promote products derived from this software without
* prior written permission. For permission or any other legal
* details, please contact
* Office of Technology Transfer
* Carnegie Mellon University
* 5000 Forbes Avenue
* Pittsburgh, PA 15213-3890
* (412) 268-4387, fax: (412) 268-7395
* tech-transfer@andrew.cmu.edu
*
* 4. Redistributions of any form whatsoever must retain the following
* acknowledgment:
* "This product includes software developed by Computing Services
* at Carnegie Mellon University (http://www.cmu.edu/computing/)."
*
* CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
* THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <stdio.h>
#include <ndbm.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <assert.h>
#include "sasldb.h"
static int db_ok = 0;
/* This provides a version of _sasl_db_getsecret and
* _sasl_db_putsecret which work with ndbm. */
int _sasldb_getdata(const sasl_utils_t *utils,
sasl_conn_t *conn,
const char *authid,
const char *realm,
const char *propName,
char *out, const size_t max_out, size_t *out_len)
{
int result = SASL_OK;
char *key;
size_t key_len;
DBM *db;
datum dkey, dvalue;
void *cntxt;
sasl_getopt_t *getopt;
const char *path = SASL_DB_PATH;
if (!utils) return SASL_BADPARAM;
if (!authid || !propName || !realm || !out || !max_out) {
utils->seterror(conn, 0,
"Bad parameter in db_ndbm.c: _sasldb_getdata");
return SASL_BADPARAM;
}
if (!db_ok) {
utils->seterror(conn, 0, "Database not checked");
return SASL_FAIL;
}
result = _sasldb_alloc_key(utils, authid, realm, propName,
&key, &key_len);
if (result != SASL_OK) {
utils->seterror(conn, 0,
"Could not allocate key in _sasldb_getdata");
return result;
}
if (utils->getcallback(conn, SASL_CB_GETOPT,
&getopt, &cntxt) == SASL_OK) {
const char *p;
if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
&& p != NULL && *p != 0) {
path = p;
}
}
db = dbm_open(path, O_RDONLY, S_IRUSR | S_IWUSR);
if (! db) {
utils->seterror(cntxt, 0, "Could not open db");
result = SASL_FAIL;
goto cleanup;
}
dkey.dptr = key;
dkey.dsize = key_len;
dvalue = dbm_fetch(db, dkey);
if (! dvalue.dptr) {
utils->seterror(cntxt, 0, "no user in db");
result = SASL_NOUSER;
goto cleanup;
}
if((size_t)dvalue.dsize > max_out + 1) {
utils->seterror(cntxt, 0, "buffer overflow");
return SASL_BUFOVER;
}
if(out_len) *out_len = dvalue.dsize;
memcpy(out, dvalue.dptr, dvalue.dsize);
out[dvalue.dsize] = '\0';
#if NDBM_FREE
/* Note: not sasl_FREE! This is memory allocated by ndbm,
* which is using libc malloc/free. */
free(dvalue.dptr);
#endif
cleanup:
utils->free(key);
if(db)
dbm_close(db);
return result;
}
int _sasldb_putdata(const sasl_utils_t *utils,
sasl_conn_t *conn,
const char *authid,
const char *realm,
const char *propName,
const char *data, size_t data_len)
{
int result = SASL_OK;
char *key;
size_t key_len;
DBM *db;
datum dkey;
void *cntxt;
sasl_getopt_t *getopt;
const char *path = SASL_DB_PATH;
if (!utils) return SASL_BADPARAM;
if (!authid || !realm || !propName) {
utils->seterror(conn, 0,
"Bad parameter in db_ndbm.c: _sasldb_putdata");
return SASL_BADPARAM;
}
result = _sasldb_alloc_key(utils, authid, realm, propName,
&key, &key_len);
if (result != SASL_OK) {
utils->seterror(conn, 0,
"Could not allocate key in _sasldb_putdata");
return result;
}
if (utils->getcallback(conn, SASL_CB_GETOPT,
&getopt, &cntxt) == SASL_OK) {
const char *p;
if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
&& p != NULL && *p != 0) {
path = p;
}
}
db = dbm_open(path,
O_RDWR | O_CREAT,
S_IRUSR | S_IWUSR);
if (! db) {
utils->log(conn, SASL_LOG_ERR,
"SASL error opening password file. "
"Do you have write permissions?\n");
utils->seterror(conn, 0, "Could not open db for write");
goto cleanup;
}
dkey.dptr = key;
dkey.dsize = key_len;
if (data) {
datum dvalue;
dvalue.dptr = (void *)data;
if(!data_len) data_len = strlen(data);
dvalue.dsize = data_len;
if (dbm_store(db, dkey, dvalue, DBM_REPLACE)) {
utils->seterror(conn, 0,
"Couldn't update db");
result = SASL_FAIL;
}
} else {
if (dbm_delete(db, dkey)) {
utils->seterror(conn, 0,
"Couldn't update db");
result = SASL_NOUSER;
}
}
dbm_close(db);
cleanup:
utils->free(key);
return result;
}
#ifdef DBM_SUFFIX
#define SUFLEN (strlen(DBM_SUFFIX) + 1)
#else
#define SUFLEN 5
#endif
int _sasl_check_db(const sasl_utils_t *utils,
sasl_conn_t *conn)
{
const char *path = SASL_DB_PATH;
void *cntxt;
sasl_getopt_t *getopt;
sasl_verifyfile_t *vf;
int ret = SASL_OK;
char *db;
if(!utils) return SASL_BADPARAM;
if (utils->getcallback(conn, SASL_CB_GETOPT,
&getopt, &cntxt) == SASL_OK) {
const char *p;
if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
&& p != NULL && *p != 0) {
path = p;
}
}
db = utils->malloc(strlen(path) + SUFLEN);
if (db == NULL) {
ret = SASL_NOMEM;
}
ret = utils->getcallback(NULL, SASL_CB_VERIFYFILE,
&vf, &cntxt);
if(ret != SASL_OK) {
utils->seterror(conn, 0,
"No verifyfile callback");
return ret;
}
#ifdef DBM_SUFFIX
if (ret == SASL_OK) {
sprintf(db, "%s%s", path, DBM_SUFFIX);
ret = vf(cntxt, db, SASL_VRFY_PASSWD);
}
#else
if (ret == SASL_OK) {
sprintf(db, "%s.dir", path);
ret = vf(cntxt, db, SASL_VRFY_PASSWD);
}
if (ret == SASL_OK) {
sprintf(db, "%s.pag", path);
ret = vf(cntxt, db, SASL_VRFY_PASSWD);
}
#endif
if (db) {
utils->free(db);
}
if (ret == SASL_OK) {
db_ok = 1;
}
if (ret == SASL_OK || ret == SASL_CONTINUE) {
return SASL_OK;
} else {
utils->seterror(conn, 0,
"Verifyfile failed");
return ret;
}
}
typedef struct ndbm_handle
{
DBM *db;
datum dkey;
int first;
} handle_t;
sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils,
sasl_conn_t *conn)
{
const char *path = SASL_DB_PATH;
sasl_getopt_t *getopt;
void *cntxt;
DBM *db;
handle_t *handle;
if(!utils || !conn) return NULL;
if(!db_ok) {
utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle");
return NULL;
}
if (utils->getcallback(conn, SASL_CB_GETOPT,
&getopt, &cntxt) == SASL_OK) {
const char *p;
if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK
&& p != NULL && *p != 0) {
path = p;
}
}
db = dbm_open(path, O_RDONLY, S_IRUSR | S_IWUSR);
if(!db) {
utils->seterror(conn, 0, "Could not open db");
return NULL;
}
handle = utils->malloc(sizeof(handle_t));
if(!handle) {
utils->seterror(conn, 0, "no memory in _sasldb_getkeyhandle");
dbm_close(db);
return NULL;
}
handle->db = db;
handle->first = 1;
return (sasldb_handle)handle;
}
int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)),
sasldb_handle handle, char *out,
const size_t max_out, size_t *out_len)
{
handle_t *dbh = (handle_t *)handle;
datum nextkey;
if(!utils || !handle || !out || !max_out)
return SASL_BADPARAM;
if(dbh->first) {
dbh->dkey = dbm_firstkey(dbh->db);
dbh->first = 0;
} else {
nextkey = dbm_nextkey(dbh->db);
dbh->dkey = nextkey;
}
if(dbh->dkey.dptr == NULL)
return SASL_OK;
if((unsigned)dbh->dkey.dsize > max_out)
return SASL_BUFOVER;
memcpy(out, dbh->dkey.dptr, dbh->dkey.dsize);
if(out_len) *out_len = dbh->dkey.dsize;
return SASL_CONTINUE;
}
int _sasldb_releasekeyhandle(const sasl_utils_t *utils,
sasldb_handle handle)
{
handle_t *dbh = (handle_t *)handle;
if(!utils || !dbh) return SASL_BADPARAM;
if(dbh->db) dbm_close(dbh->db);
utils->free(dbh);
return SASL_OK;
}