Re: Patch status

Howard Chu <[email protected]>
Newsgroups gmane.comp.security.cyrus.sasl
Message-ID <[email protected]>
Howard Chu wrote:
> Alexey Melnikov wrote:
>> On 09/10/2012 23:10, Howard Chu wrote:
>>> Speaking of new releases, I'd like to see some feedback/movement on
>>> these patches...
>>>
>>> http://lists.andrew.cmu.edu/pipermail/cyrus-sasl/2012-March/002479.html
>> If you add/update makefiles, the process would be much quicker. (And if
>> you are not sure, ask).
>>> http://lists.andrew.cmu.edu/pipermail/cyrus-sasl/2012-May/002490.html
>> This one is in GIT already.
>
> Ah, I wasn't aware there was a git repo, I was still looking at CVS.
>
> I'll sync up with git and provide Makefiles/etc for the MDB patch shortly.
>
Updated patch...

-- 
   -- Howard Chu
   CTO, Symas Corp.           http://www.symas.com
   Director, Highland Sun     http://highlandsun.com/hyc/
   Chief Architect, OpenLDAP  http://www.openldap.org/project/
0001-Add-support-for-OpenLDAP-MDB.patch (text/x-diff, 16.3 KB)
>From b106449b18200d3b176286575f76cbe5952e4f2a Mon Sep 17 00:00:00 2001
From: Howard Chu <[email protected]>
Date: Fri, 12 Oct 2012 11:11:31 -0700
Subject: [PATCH] Add support for OpenLDAP MDB

---
 config/sasldb.m4   |   21 ++-
 configure.in       |   10 +-
 doc/options.html   |   26 +++
 sasldb/Makefile.am |    2 +-
 sasldb/db_mdb.c    |  468 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 519 insertions(+), 8 deletions(-)
 create mode 100644 sasldb/db_mdb.c

diff --git a/config/sasldb.m4 b/config/sasldb.m4
index b28b087..e3c9016 100644
--- a/config/sasldb.m4
+++ b/config/sasldb.m4
@@ -5,7 +5,9 @@ dnl Berkeley DB specific checks first..
 dnl Figure out what database type we're using
 AC_DEFUN([SASL_DB_CHECK], [
 cmu_save_LIBS="$LIBS"
-AC_ARG_WITH(dblib, [  --with-dblib=DBLIB      set the DB library to use [berkeley] ],
+AC_ARG_WITH(dblib,
+  [AC_HELP_STRING([--with-dblib={berkeley|gdbm|mdb|ndbm|none|auto_detect}],
+  [set the DB library to use [[berkeley]]])],
   dblib=$withval,
   dblib=auto_detect)
 
@@ -43,6 +45,11 @@ dnl named.  arg.
                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 +62,12 @@ dnl named.  arg.
         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 +99,7 @@ dnl named.  arg.
 	;;
   *)
 	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 +119,10 @@ case "$dblib" in
     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])
diff --git a/configure.in b/configure.in
index 465a362..5f75064 100644
--- a/configure.in
+++ b/configure.in
@@ -200,19 +200,19 @@ SASL_DB_CHECK()
 # Do we not install the SASL DB man pages?
 AM_CONDITIONAL(NO_SASL_DB_MANS, test "x$SASL_DB_MANS" = "x")
 
-AC_ARG_ENABLE(keep_db_open, [  --enable-keep-db-open   keep handle to Berkeley DB open for improved performance [[no]] ],
+AC_ARG_ENABLE(keep_db_open, [  --enable-keep-db-open   keep handle to DB open for improved performance [[no]] ],
                 keep_db_open=$enableval,
                 keep_db_open=no)
 
-# Disable if Berkeley DB is not used
-if test "$dblib" != berkeley; then
+# Disable if Berkeley DB and MDB are not used
+if test "$dblib" != berkeley -a "$dblib" != mdb; then
   keep_db_open=no
 fi
 
 if test "$keep_db_open" = yes; then
-  AC_DEFINE(KEEP_DB_OPEN,[],[Should we keep handle to Berkeley DB open in SASLDB plugin?])
+  AC_DEFINE(KEEP_DB_OPEN,[],[Should we keep handle to DB open in SASLDB plugin?])
 fi
-AC_MSG_CHECKING(if Berkeley DB handle is kept open in SASLDB)
+AC_MSG_CHECKING(if DB handle is kept open in SASLDB)
 AC_MSG_RESULT($keep_db_open)
 
 AC_CHECK_LIB(dl, dlopen, SASL_DL_LIB="-ldl", SASL_DL_LIB="")
diff --git a/doc/options.html b/doc/options.html
index 4f195f7..66db5f6 100644
--- a/doc/options.html
+++ b/doc/options.html
@@ -148,6 +148,20 @@ cached for a fast reauth.  A value of 0 will disable reauth.</TD>
 <TR>
 <TD>sasldb_path</TD><TD>sasldb plugin</TD>
 <TD>Path to sasldb file</TD><TD><tt>/etc/sasldb2</tt> (system dependant)</TD>
+</TR>
+<TR>
+<TD>sasldb_mapsize</TD><TD>sasldb with MDB</TD>
+<TD>Size of the memory map used by the DB. This is also the maximum possible
+size of the database, so it must be set to a value large enough to contain
+all the desired user records.</TD>
+<TD>1048576 bytes</TD>
+</TR>
+<TR>
+<TD>sasldb_maxreaders</TD><TD>sasldb with MDB</TD>
+<TD>Maximum number of threads (or processes) that may concurrently read the
+database.</TD>
+<TD>126</TD>
+</TR>
 <TR>
 <TD>sql_engine</TD><TD>SQL plugin</TD>
 <TD>Name of SQL engine to use (possible values: 'mysql', 'pgsql', 'sqlite', 'sqlite3').</TD>
@@ -342,6 +356,18 @@ sasl-regexp uid=(.*),cn=external,cn=auth
 
 </p>
 
+<h2>Notes on sasldb with MDB</h2>
+
+<p>
+</p>
+
+<p>The OpenLDAP MDB library is an extremely compact, extremely high performance
+B+tree database. The code for it is available in the regular OpenLDAP source
+distributions and it is distributed under the terms of the OpenLDAP Public License.</p>
+
+<p>Full documentation, plus papers and presentations are available on
+<a href="http://highlandsun.com/hyc/mdb/">the MDB page</a>.</p>
+
 <hr>
 Back to the <A href=index.html>index</a>
 
diff --git a/sasldb/Makefile.am b/sasldb/Makefile.am
index 067477c..e1e3f56 100644
--- a/sasldb/Makefile.am
+++ b/sasldb/Makefile.am
@@ -46,7 +46,7 @@ sasl_version = 1:25:0
 
 INCLUDES=-I$(top_srcdir)/include -I$(top_builddir)/include @SASL_DB_INC@
 
-extra_common_sources = db_none.c db_ndbm.c db_gdbm.c db_berkeley.c
+extra_common_sources = db_none.c db_mdb.c db_ndbm.c db_gdbm.c db_berkeley.c
 
 EXTRA_DIST = NTMakefile
 
diff --git a/sasldb/db_mdb.c b/sasldb/db_mdb.c
new file mode 100644
index 0000000..c8e62c0
--- /dev/null
+++ b/sasldb/db_mdb.c
@@ -0,0 +1,468 @@
+/* db_mdb.c--SASL OpenLDAP MDB interface
+ * Howard Chu
+ * $Id$
+ */
+/*
+ * Copyright (C) 2011 Howard Chu, All rights reserved. <[email protected]>
+ *
+ * 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
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+
+#include <config.h>
+
+#include <mdb.h>
+
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include "sasldb.h"
+
+static int db_ok = 0;
+static MDB_env *db_env;
+static MDB_dbi db_dbi;
+
+#define KILO 1024
+
+/*
+ * 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;
+    void *cntxt;
+    MDB_env *env;
+    MDB_txn *txn;
+    sasl_getopt_t *getopt;
+    size_t mapsize = 0;
+    int readers = 0;
+    int ret;
+    int flags;
+
+    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;
+	    }
+	    if (getopt(cntxt, NULL, "sasldb_maxreaders", &p, NULL) == SASL_OK
+		&& p != NULL && *p != 0) {
+		readers = atoi(p);
+	    }
+	    if (getopt(cntxt, NULL, "sasldb_mapsize", &p, NULL) == SASL_OK
+		&& p != NULL && *p != 0) {
+		mapsize = atoi(p);
+		mapsize *= KILO;
+	    }
+	}
+
+	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;
+	}
+
+	if (readers) {
+	    ret = mdb_env_set_maxreaders(env, readers);
+	    if (ret) {
+		utils->log(conn, SASL_LOG_ERR,
+		       "unable to set MDB maxreaders: %s",
+		       mdb_strerror(ret));
+		utils->seterror(conn, SASL_NOLOG, "Unable to set MDB maxreaders");
+		return SASL_FAIL;
+	    }
+	}
+
+	if (mapsize) {
+	    ret = mdb_env_set_mapsize(env, mapsize);
+	    if (ret) {
+		utils->log(conn, SASL_LOG_ERR,
+		       "unable to set MDB mapsize: %s",
+		       mdb_strerror(ret));
+		utils->seterror(conn, SASL_NOLOG, "Unable to set MDB mapsize");
+		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;
+}
-- 
1.7.9.5
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.