Index: configure.in =================================================================== RCS file: /cvs/src/sasl/configure.in,v retrieving revision 1.224 diff -u -r1.224 configure.in --- configure.in 22 Sep 2011 14:44:15 -0000 1.224 +++ configure.in 18 Nov 2011 23:55:46 -0000 @@ -205,7 +205,7 @@ keep_db_open=no) # Disable if Berkeley DB is not used -if test "$dblib" != berkeley; then +if test "$dblib" != berkeley -a "$dblib" != mdb; then keep_db_open=no fi Index: config/sasldb.m4 =================================================================== RCS file: /cvs/src/sasl/config/sasldb.m4,v retrieving revision 1.26 diff -u -r1.26 sasldb.m4 --- config/sasldb.m4 20 May 2009 12:24:48 -0000 1.26 +++ config/sasldb.m4 18 Nov 2011 23:55:46 -0000 @@ -43,6 +43,11 @@ fi esac ;; + mdb) + AC_CHECK_HEADER(mdb.h, [ + AC_CHECK_LIB(mdb, mdb_env_create, SASL_DB_LIB="-lmdb"; enable_keep_db_open=yes, dblib="no")], + dblib="no") + ;; ndbm) dnl We want to attempt to use -lndbm if we can, just in case dnl there's some version of it installed and overriding libc @@ -55,6 +60,12 @@ dnl How about berkeley db? CYRUS_BERKELEY_DB_CHK() if test "$dblib" = no; then + dnl How about OpenLDAP's mdb? + AC_CHECK_HEADER(mdb.h, [ + AC_CHECK_LIB(mdb, mdb_env_create, SASL_DB_LIB="-lmdb"; enable_keep_db_open=yes, dblib="no")], + dblib="no") + fi + if test "$dblib" = no; then dnl How about ndbm? AC_CHECK_HEADER(ndbm.h, [ AC_CHECK_LIB(ndbm, dbm_open, @@ -86,7 +97,7 @@ ;; *) AC_MSG_WARN([Bad DB library implementation specified;]) - AC_ERROR([Use either \"berkeley\", \"gdbm\", \"ndbm\" or \"none\"]) + AC_ERROR([Use either \"berkeley\", \"gdbm\", \"mdb\", \"ndbm\" or \"none\"]) dblib=no ;; esac @@ -106,6 +117,10 @@ SASL_MECHS="$SASL_MECHS libsasldb.la" AC_DEFINE(SASL_GDBM,[],[Use GDBM for SASLdb]) ;; + mdb) + SASL_MECHS="$SASL_MECHS libsasldb.la" + AC_DEFINE(SASL_MDB,[],[Use MDB for SASLdb]) + ;; ndbm) SASL_MECHS="$SASL_MECHS libsasldb.la" AC_DEFINE(SASL_NDBM,[],[Use NDBM for SASLdb]) --- /dev/null 2011-10-11 06:44:09.762884985 -0700 +++ sasldb/db_mdb.c 2011-11-18 15:57:39.000000000 -0800 @@ -0,0 +1,433 @@ +/* db_mdb.c--SASL OpenLDAP MDB interface + * Howard Chu + * $Id$ + */ +/* + * Copyright (C) 2011 Howard Chu, All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted only as authorized by the OpenLDAP + * Public License. + * + * A copy of this license is available in the file LICENSE in the + * top-level directory of the distribution or, alternatively, at + * . + */ + +#include + +#include + +#include +#include +#include +#include +#include "sasldb.h" + +static int db_ok = 0; +static MDB_env *db_env; +static MDB_dbi db_dbi; + +/* + * Open the environment + */ +static int do_open(const sasl_utils_t *utils, + sasl_conn_t *conn, + int rdwr, MDB_txn **mtxn) +{ + const char *path = SASL_DB_PATH; + int ret; + int flags; + void *cntxt; + MDB_env *env; + MDB_txn *txn; + sasl_getopt_t *getopt; + + if (!db_env) { + + if (utils->getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { + const char *p; + if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK + && p != NULL && *p != 0) { + path = p; + } + } + + ret = mdb_env_create(&env); + if (ret) { + utils->log(conn, SASL_LOG_ERR, + "unable to create MDB environment: %s", + mdb_strerror(ret)); + utils->seterror(conn, SASL_NOLOG, "Unable to create MDB environment"); + return SASL_FAIL; + } + + flags = MDB_NOSUBDIR; + if (!rdwr) flags |= MDB_RDONLY; + ret = mdb_env_open(env, path, flags, 0660); + if (ret) { + mdb_env_close(env); + if (!rdwr && ret == ENOENT) { + /* File not found and we are only reading the data. + Treat as SASL_NOUSER. */ + return SASL_NOUSER; + } + utils->log(conn, SASL_LOG_ERR, + "unable to open MDB environment %s: %s", + path, mdb_strerror(ret)); + utils->seterror(conn, SASL_NOLOG, "Unable to open MDB environment"); + return SASL_FAIL; + } + } else { + env = db_env; + } + + ret = mdb_txn_begin(env, NULL, rdwr ? 0 : MDB_RDONLY, &txn); + if (ret) { + mdb_env_close(env); + utils->log(conn, SASL_LOG_ERR, + "unable to open MDB transaction: %s", + mdb_strerror(ret)); + utils->seterror(conn, SASL_NOLOG, "Unable to open MDB transaction"); + return SASL_FAIL; + } + + if (!db_dbi) { + ret = mdb_open(txn, NULL, 0, &db_dbi); + if (ret) { + mdb_txn_abort(txn); + mdb_env_close(env); + utils->log(conn, SASL_LOG_ERR, + "unable to open MDB database: %s", + mdb_strerror(ret)); + utils->seterror(conn, SASL_NOLOG, "Unable to open MDB database"); + return SASL_FAIL; + } + } + + if (!db_env) + db_env = env; + *mtxn = txn; + + return SASL_OK; +} + +/* + * Close the environment + */ +static void do_close() +{ + mdb_env_close(db_env); + db_env = NULL; +} + + +/* + * Retrieve the secret from the database. + * + * Return SASL_NOUSER if the entry doesn't exist, + * SASL_OK on success. + * + */ +int _sasldb_getdata(const sasl_utils_t *utils, + sasl_conn_t *context, + const char *auth_identity, + 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; + MDB_val dbkey, data; + MDB_txn *txn = NULL; + + if(!utils) return SASL_BADPARAM; + + /* check parameters */ + if (!auth_identity || !realm || !propName || !out || !max_out) { + utils->seterror(context, 0, + "Bad parameter in db_berkeley.c: _sasldb_getdata"); + return SASL_BADPARAM; + } + + if (!db_ok) { + utils->seterror(context, 0, + "Database not checked"); + return SASL_FAIL; + } + + /* allocate a key */ + result = _sasldb_alloc_key(utils, auth_identity, realm, propName, + &key, &key_len); + if (result != SASL_OK) { + utils->seterror(context, 0, + "Could not allocate key in _sasldb_getdata"); + return result; + } + + /* open the db */ + result = do_open(utils, context, 0, &txn); + if (result != SASL_OK) goto cleanup; + + /* create the key to search for */ + dbkey.mv_data = key; + dbkey.mv_size = key_len; + + /* ask MDB for the entry */ + result = mdb_get(txn, db_dbi, &dbkey, &data); + + switch (result) { + case 0: + /* success */ + break; + + case MDB_NOTFOUND: + result = SASL_NOUSER; + utils->seterror(context, SASL_NOLOG, + "user: %s@%s property: %s not found in sasldb", + auth_identity,realm,propName); + goto cleanup; + break; + default: + utils->seterror(context, 0, + "error fetching from sasldb: %s", + mdb_strerror(result)); + result = SASL_FAIL; + goto cleanup; + break; + } + + if(data.mv_size > max_out + 1) + return SASL_BUFOVER; + + if(out_len) *out_len = data.mv_size; + memcpy(out, data.mv_data, data.mv_size); + out[data.mv_size] = '\0'; + + cleanup: + + mdb_txn_abort(txn); + utils->free(key); + + return result; +} + +/* + * Put or delete an entry + * + * + */ + +int _sasldb_putdata(const sasl_utils_t *utils, + sasl_conn_t *context, + const char *authid, + const char *realm, + const char *propName, + const char *data_in, size_t data_len) +{ + int result = SASL_OK; + char *key; + size_t key_len; + MDB_val dbkey; + MDB_txn *txn = NULL; + + if (!utils) return SASL_BADPARAM; + + if (!authid || !realm || !propName) { + utils->seterror(context, 0, + "Bad parameter in db_berkeley.c: _sasldb_putdata"); + return SASL_BADPARAM; + } + + if (!db_ok) { + utils->seterror(context, 0, + "Database not checked"); + return SASL_FAIL; + } + + result = _sasldb_alloc_key(utils, authid, realm, propName, + &key, &key_len); + if (result != SASL_OK) { + utils->seterror(context, 0, + "Could not allocate key in _sasldb_putdata"); + return result; + } + + /* open the db */ + result=do_open(utils, context, 1, &txn); + if (result!=SASL_OK) goto cleanup; + + /* create the db key */ + dbkey.mv_data = key; + dbkey.mv_size = key_len; + + if (data_in) { /* putting secret */ + MDB_val data; + + data.mv_data = (char *)data_in; + if(!data_len) data_len = strlen(data_in); + data.mv_size = data_len; + + result = mdb_put(txn, db_dbi, &dbkey, &data, 0); + + if (result != 0) + { + utils->log(NULL, SASL_LOG_ERR, + "error updating sasldb: %s", mdb_strerror(result)); + utils->seterror(context, SASL_NOLOG, + "Couldn't update db"); + result = SASL_FAIL; + goto cleanup; + } + } else { /* removing secret */ + result=mdb_del(txn, db_dbi, &dbkey, NULL); + + if (result != 0) + { + utils->log(NULL, SASL_LOG_ERR, + "error deleting entry from sasldb: %s", mdb_strerror(result)); + utils->seterror(context, SASL_NOLOG, + "Couldn't update db"); + if (result == MDB_NOTFOUND) + result = SASL_NOUSER; + else + result = SASL_FAIL; + goto cleanup; + } + } + result = mdb_txn_commit(txn); + if (result) { + utils->log(NULL, SASL_LOG_ERR, + "error committing to sasldb: %s", mdb_strerror(result)); + utils->seterror(context, SASL_NOLOG, + "Couldn't update db"); + result = SASL_FAIL; + } + txn = NULL; + + cleanup: + + mdb_txn_abort(txn); + utils->free(key); + + return result; +} + +int _sasl_check_db(const sasl_utils_t *utils, + sasl_conn_t *conn) +{ + const char *path = SASL_DB_PATH; + int ret; + void *cntxt; + sasl_getopt_t *getopt; + sasl_verifyfile_t *vf; + + if (!utils) return SASL_BADPARAM; + + if (utils->getcallback(conn, SASL_CB_GETOPT, + (sasl_callback_ft *)&getopt, &cntxt) == SASL_OK) { + const char *p; + if (getopt(cntxt, NULL, "sasldb_path", &p, NULL) == SASL_OK + && p != NULL && *p != 0) { + path = p; + } + } + + ret = utils->getcallback(conn, SASL_CB_VERIFYFILE, + (sasl_callback_ft *)&vf, &cntxt); + if (ret != SASL_OK) { + utils->seterror(conn, 0, "verifyfile failed"); + return ret; + } + + ret = vf(cntxt, path, SASL_VRFY_PASSWD); + + if (ret == SASL_OK) { + db_ok = 1; + } + + if (ret == SASL_OK || ret == SASL_CONTINUE) { + return SASL_OK; + } else { + return ret; + } +} + +void sasldb_auxprop_free (void *glob_context, + const sasl_utils_t *utils) +{ + do_close(); +} + +sasldb_handle _sasldb_getkeyhandle(const sasl_utils_t *utils, + sasl_conn_t *conn) +{ + int ret; + MDB_txn *txn; + MDB_cursor *mc; + + if(!utils || !conn) return NULL; + + if(!db_ok) { + utils->seterror(conn, 0, "Database not OK in _sasldb_getkeyhandle"); + return NULL; + } + + ret = do_open(utils, conn, 0, &txn); + + if (ret != SASL_OK) { + return NULL; + } + + ret = mdb_cursor_open(txn, db_dbi, &mc); + if (ret) { + utils->seterror(conn, 0, "cursor_open failed in _sasldb_gekeythandle"); + return NULL; + } + + return (sasldb_handle)mc; +} + +int _sasldb_getnextkey(const sasl_utils_t *utils __attribute__((unused)), + sasldb_handle handle, char *out, + const size_t max_out, size_t *out_len) +{ + int result; + MDB_cursor *mc = (MDB_cursor *)handle; + MDB_val key; + + if(!utils || !handle || !out || !max_out) + return SASL_BADPARAM; + + result = mdb_cursor_get(mc, &key, NULL, MDB_NEXT); + + if (result == MDB_NOTFOUND) return SASL_OK; + + if (result != 0) { + return SASL_FAIL; + } + + if (key.mv_size > max_out) { + return SASL_BUFOVER; + } + + memcpy(out, key.mv_data, key.mv_size); + if (out_len) *out_len = key.mv_size; + + return SASL_CONTINUE; +} + + +int _sasldb_releasekeyhandle(const sasl_utils_t *utils, + sasldb_handle handle) +{ + MDB_cursor *mc = (MDB_cursor *)handle; + + if (!utils || !handle) return SASL_BADPARAM; + + mdb_cursor_close(mc); + + return SASL_OK; +}