summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AUTHORS1
-rw-r--r--COPYING31
-rw-r--r--ChangeLog3
-rw-r--r--Makefile.am5
-rw-r--r--NEWS1
-rw-r--r--README2
-rwxr-xr-xautogen.sh11
-rw-r--r--configure.in53
-rw-r--r--plugin/Makefile.am10
-rw-r--r--plugin/delegateldap.c604
10 files changed, 721 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..0f9215f
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Stefan Walter <stef@memberwebs.com>
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..2632857
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,31 @@
+
+Copyright (c) 2008, Stefan Walter
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ * Redistributions of source code must retain the above
+ copyright notice, this list of conditions and the
+ following disclaimer.
+ * 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.
+ * The names of contributors to this software may not be
+ used to endorse or promote products derived from this
+ software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGE.
diff --git a/ChangeLog b/ChangeLog
new file mode 100644
index 0000000..1abe215
--- /dev/null
+++ b/ChangeLog
@@ -0,0 +1,3 @@
+0.1:
+ * Initial release
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..52abc56
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,5 @@
+SUBDIRS = plugin
+
+dist-hook:
+ rm -rf `find $(distdir)/ -name .svn`
+
diff --git a/NEWS b/NEWS
new file mode 100644
index 0000000..c7ab92a
--- /dev/null
+++ b/NEWS
@@ -0,0 +1 @@
+See ChangeLog \ No newline at end of file
diff --git a/README b/README
new file mode 100644
index 0000000..8ebfe92
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+This is a plugin module for Cyrus SASL.
+
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..100e70d
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,11 @@
+#!/bin/sh -ex
+
+set -ex
+
+aclocal
+autoheader
+libtoolize --force
+automake -a
+autoconf
+./configure --enable-maintainer-mode "$@"
+
diff --git a/configure.in b/configure.in
new file mode 100644
index 0000000..4301946
--- /dev/null
+++ b/configure.in
@@ -0,0 +1,53 @@
+# Process this file with autoconf to produce a configure script.
+AC_INIT(cyrus-distributed-sasl-plugin, 0.1, stef@memberwebs.com)
+AM_INIT_AUTOMAKE(cyrus-distributed-sasl-plugin, 0.1)
+
+AC_CONFIG_SRCDIR([plugin/delegateldap.c])
+AM_CONFIG_HEADER([config.h])
+
+LDFLAGS="$LDFLAGS -L/usr/local/lib"
+CFLAGS="$CFLAGS -I/usr/local/include"
+
+# Checks for programs.
+AC_PROG_CC
+AC_PROG_INSTALL
+AC_PROG_LIBTOOL
+
+# Debug mode
+AC_ARG_ENABLE(debug,
+ AC_HELP_STRING([--enable-debug],
+ [Compile binaries in debug mode]))
+
+if test "$enable_debug" = "yes"; then
+ CFLAGS="$CFLAGS -g -O0 -Wall"
+ AC_DEFINE_UNQUOTED(_DEBUG, 1, [In debug mode])
+ echo "enabling debug compile mode"
+fi
+
+# Checks for header files.
+AC_HEADER_STDC
+
+AC_CHECK_LIB(ldap, ldap_initialize, ,
+ [echo "Couldn't find the OpenLDAP library"; exit 1])
+AC_CHECK_LIB(lber, ber_bvfree, ,
+ [echo "Couldn't find the OpenLDAP ber library"; exit 1])
+
+AC_CHECK_LIB(sasl2, sasl_server_init, ,
+ [echo "Couldn't find the Cyrus SASL 2 library"; exit 1])
+AC_CHECK_HEADERS([sasl/sasl.h sasl/saslplug.h], ,
+ [echo "Couldn't find Cyrus SASL headers"; exit 1],
+ [#include <sasl/sasl.h>])
+
+# Checks for typedefs, structures, and compiler characteristics.
+AC_C_CONST
+AC_TYPE_SIZE_T
+
+# Checks for library functions.
+AC_FUNC_MALLOC
+AC_FUNC_MEMCMP
+
+AC_CONFIG_FILES([
+ Makefile
+ plugin/Makefile])
+AC_OUTPUT
+
diff --git a/plugin/Makefile.am b/plugin/Makefile.am
new file mode 100644
index 0000000..3431b09
--- /dev/null
+++ b/plugin/Makefile.am
@@ -0,0 +1,10 @@
+
+INCLUDES = -DCONF_PREFIX=\"$(sysconfdir)\"
+
+moduledir = $(prefix)/lib/sasl2/
+module_LTLIBRARIES = libdelegateldap.la
+
+libdelegateldap_la_LDFLAGS = -module -avoid-version
+libdelegateldap_la_SOURCES = delegateldap.c
+
+
diff --git a/plugin/delegateldap.c b/plugin/delegateldap.c
new file mode 100644
index 0000000..170c6a7
--- /dev/null
+++ b/plugin/delegateldap.c
@@ -0,0 +1,604 @@
+
+#include "config.h"
+
+#include <sasl/sasl.h>
+#include <sasl/saslplug.h>
+
+#define LDAP_DEPRECATED 1
+#include <ldap.h>
+#include <lber.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#define CANARY_VALUE "!DL!"
+
+#define OPTION_SERVER "delegateldap_uri"
+#define OPTION_TLS "delegateldap_tls"
+#define OPTION_LDAPRC "delegateldap_ldaprc"
+
+/*
+ * SETTINGS -------------------------------------------------------------------------
+ * TODO: Move these to a configuration file or some such
+ */
+
+#define SETTINGS_URL "ldap://209.66.100.155"
+
+typedef struct _supported_mechanisms {
+ void *mutex;
+ int loaded;
+ struct berval **mechs;
+} supported_mechanisms;
+
+static supported_mechanisms *the_supported;
+
+typedef struct _delegate_context {
+ char canary[4];
+ const char* mechanism;
+ LDAP *ldap;
+} delegate_context;
+
+extern sasl_server_plug_init_t sasl_server_plug_init;
+
+static void
+log_message (const sasl_utils_t *utils, int level, const char *format, ...)
+{
+ char buf[1024];
+ va_list va;
+
+ va_start (va, format);
+ vsnprintf (buf, sizeof (buf), format, va);
+ buf[sizeof (buf) - 1] = 0;
+ vfprintf (stderr, format, va);
+ fputc ('\n', stderr);
+ va_end (va);
+
+ (utils->log) (utils->conn, level, "%s", buf);
+}
+
+static int
+report_ldap_error (const sasl_utils_t *utils, int ldap, const char *format, ...)
+{
+ char buf[1024];
+ va_list va;
+
+ buf[0] = 0;
+
+ if (format) {
+ va_start (va, format);
+ vsnprintf (buf, sizeof (buf), format, va);
+ buf[sizeof (buf) - 1] = 0;
+ va_end (va);
+ }
+
+ (utils->seterror) (utils->conn, 0, "%s%s%s",
+ buf, buf[0] ? ":" : "", ldap_err2string (ldap));
+ fprintf (stderr, "%s%s%s", buf, buf[0] ? ": " : "", ldap_err2string (ldap));
+ fputc ('\n', stderr);
+
+ switch (ldap) {
+ case LDAP_MORE_RESULTS_TO_RETURN:
+ return SASL_CONTINUE;
+ case LDAP_SUCCESS:
+ return SASL_OK;
+ case LDAP_NO_MEMORY:
+ return SASL_NOMEM;
+ case LDAP_AUTH_UNKNOWN:
+ return SASL_NOMECH;
+ case LDAP_DECODING_ERROR:
+ return SASL_BADPROT;
+ }
+
+ return SASL_FAIL;
+}
+
+static void
+delegateldap_dispose (void *context, const sasl_utils_t *utils)
+{
+ delegate_context *ctx = (delegate_context*)context;
+
+ if (!ctx)
+ return;
+
+ if (memcmp (ctx->canary, CANARY_VALUE, sizeof (ctx->canary)) != 0) {
+ log_message (utils, SASL_LOG_ERR, "trying to dispose of invalid delegateldap mechanism");
+ return;
+ }
+
+ if (ctx->ldap) {
+ log_message (utils, SASL_LOG_NOTE, "closing ldap connection");
+ ldap_unbind_ext_s (ctx->ldap, NULL, NULL);
+ }
+ ctx->ldap = NULL;
+ memset (ctx, 0, sizeof (*ctx));
+
+ utils->free (ctx);
+}
+
+static int
+match_any_value (const char *match, ...)
+{
+ const char *value;
+ va_list va;
+
+ va_start (va, match);
+ for (;;) {
+ value = va_arg (va, const char*);
+ if (!value)
+ return 0;
+ if (strcasecmp (match, value) == 0)
+ return 1;
+ }
+}
+
+static int
+delegateldap_new (void *mech_context, sasl_server_params_t *sparams,
+ const char *challenge, unsigned challen, void **context)
+{
+ const char *mechanism;
+ const char *option;
+ delegate_context *ctx;
+ int res, protocol, ret;
+
+ *context = NULL;
+
+ /* Make sure we have appropriate options */
+ if ((sparams->utils->getopt) (sparams->utils->getopt_context, NULL,
+ OPTION_SERVER, &option, NULL) != SASL_OK) {
+ log_message (sparams->utils, SASL_LOG_ERR,
+ "missing '%s' option in config file", OPTION_SERVER);
+ return SASL_BADPARAM;
+ }
+
+ ctx = sparams->utils->malloc (sizeof (delegate_context));
+ if (!ctx)
+ return SASL_NOMEM;
+ memset (ctx, 0, sizeof (delegate_context));
+ memcpy (ctx->canary, CANARY_VALUE, sizeof (ctx->canary));
+
+ /* We use the mech_context to boot strap the mechanism */
+ mechanism = (const char*)mech_context;
+
+ log_message (sparams->utils, SASL_LOG_NOTE, "initializing ldap connection to: %s", option);
+
+ res = ldap_initialize (&ctx->ldap, option);
+ if (res != LDAP_SUCCESS) {
+ delegateldap_dispose (ctx, sparams->utils);
+ return report_ldap_error (sparams->utils, res, "couldn't initialize ldap connection: %s", option);
+ }
+
+ protocol = LDAP_VERSION3;
+ if (ldap_set_option (ctx->ldap, LDAP_OPT_PROTOCOL_VERSION, &protocol) != LDAP_OPT_SUCCESS) {
+ delegateldap_dispose (ctx, sparams->utils);
+ log_message (sparams->utils, SASL_LOG_ERR, "couldn't setup ldap connection to use version 3 protocol");
+ return SASL_FAIL;
+ }
+
+ /* See if we should connect with TLS */
+ (sparams->utils->getopt) (sparams->utils->getopt_context, NULL, OPTION_TLS, &option, NULL);
+ if (option && match_any_value (option, "yes", "y", "on", "try", "demand", "true", NULL)) {
+
+ /* Start the TLS thingy */
+ res = ldap_start_tls_s (ctx->ldap, NULL, NULL);
+ if (res != LDAP_SUCCESS) {
+ ret = report_ldap_error (sparams->utils, res, "couldn't initialize TLS on the ldap connection");
+
+ /* The configure requires that we have tls */
+ if (match_any_value (option, "demand", NULL)) {
+ delegateldap_dispose (ctx, sparams->utils);
+ return ret;
+ }
+ }
+ }
+
+ ctx->mechanism = mechanism;
+ *context = ctx;
+
+ return SASL_OK;
+}
+
+static int
+calculate_user (delegate_context *ctx, sasl_server_params_t *sparams,
+ sasl_out_params_t *oparams)
+{
+ struct berval *auth;
+ char *dn;
+ char **parts;
+ int res, ret;
+
+ res = ldap_whoami_s (ctx->ldap, &auth, NULL, NULL);
+ if (res != LDAP_SUCCESS)
+ return report_ldap_error (sparams->utils, res, "couldn't determine logged in user name");
+
+ /* Anonmyous authentication */
+ if (!auth) {
+ log_message (sparams->utils, SASL_LOG_NOTE, "anonymous user login");
+ ret = (sparams->canon_user) (sparams->utils->conn, "anonymous", 0,
+ SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
+
+ /* Use RDN as authenticator */
+ } else {
+ dn = strchr (auth->bv_val, ':');
+ if (dn) {
+ *dn = 0;
+ ++dn;
+ } else {
+ dn = auth->bv_val;
+ }
+
+ parts = ldap_explode_dn (dn, 1);
+ if (!parts || !parts[0]) {
+ log_message (sparams->utils, SASL_LOG_ERR,
+ "couldn't parse the user's dn: %s", auth->bv_val);
+ ret = SASL_FAIL;
+ } else {
+ log_message (sparams->utils, SASL_LOG_NOTE, "user login: %s", parts[0]);
+ ret = sparams->canon_user (sparams->utils->conn, parts[0], 0,
+ SASL_CU_AUTHID | SASL_CU_AUTHZID, oparams);
+ }
+
+ if (parts)
+ ldap_value_free (parts);
+
+ ber_bvfree (auth);
+ }
+
+ return ret;
+}
+
+static int
+delegateldap_step (void *context, sasl_server_params_t *sparams,
+ const char *clientin, unsigned clientinlen,
+ const char **serverout, unsigned *serveroutlen,
+ sasl_out_params_t *oparams)
+{
+ delegate_context *ctx = (delegate_context*)context;
+ struct berval *scred = NULL;
+ struct berval ccred;
+ char *buffer;
+ int ret;
+ int res;
+
+ *serverout = NULL;
+ *serveroutlen = 0;
+
+ ccred.bv_len = clientinlen;
+ ccred.bv_val = (char*)clientin;
+
+ log_message (sparams->utils, SASL_LOG_NOTE,
+ "%d bytes of client request", ccred.bv_len);
+
+ res = ldap_sasl_bind_s (ctx->ldap, NULL, ctx->mechanism, &ccred, NULL, NULL, &scred);
+
+ /* Authentication was successful! */
+ if (res == LDAP_SUCCESS) {
+ log_message (sparams->utils, SASL_LOG_NOTE, "successful authentication");
+ ret = calculate_user (ctx, sparams, oparams);
+
+ /* We need to keep shuttling information */
+ } else if (res == LDAP_SASL_BIND_IN_PROGRESS) {
+
+ ret = SASL_CONTINUE;
+
+ /* Copy the response from the server into SASL allocated memory */
+ if (scred && scred->bv_len) {
+ buffer = sparams->utils->malloc (scred->bv_len);
+ if (buffer) {
+ memcpy (buffer, scred->bv_val, scred->bv_len);
+ *serveroutlen = scred->bv_len;
+ *serverout = buffer;
+
+ log_message (sparams->utils, SASL_LOG_NOTE,
+ "%d bytes of server response", scred->bv_len);
+
+ } else {
+ log_message (sparams->utils, SASL_LOG_ERR,
+ "couldn't allocate memory for server response");
+ ret = SASL_NOMEM;
+ }
+
+ /* No response from the server, strange */
+ } else {
+ *serverout = NULL;
+ *serveroutlen = 0;
+
+ log_message (sparams->utils, SASL_LOG_WARN,
+ "no response from server during sasl step");
+ }
+
+ /* Bad login */
+ } else if (res == LDAP_INVALID_CREDENTIALS){
+ log_message (sparams->utils, SASL_LOG_FAIL, "user login failed: invalid credentials");
+ ret = SASL_FAIL;
+
+ /* An error condition */
+ } else {
+
+ ret = report_ldap_error (sparams->utils, res, "couldn't do sasl bind step");
+ }
+
+ if (scred)
+ ber_bvfree (scred);
+
+ if (ret == SASL_OK) {
+ oparams->doneflag = 1;
+ oparams->mech_ssf = 0;
+ oparams->maxoutbuf = 0;
+ oparams->encode_context = NULL;
+ oparams->encode = NULL;
+ oparams->decode_context = NULL;
+ oparams->decode = NULL;
+ oparams->param_version = 0;
+ }
+
+ return ret;
+}
+
+static const char*
+find_berval_value (const char *match, struct berval **values)
+{
+ struct berval **v;
+ size_t len;
+
+ if (!values)
+ return NULL;
+
+ len = strlen (match);
+ for (v = values; *v; ++v) {
+ if ((*v)->bv_len == len &&
+ strncasecmp (match, (*v)->bv_val, len) == 0) {
+ return match;
+ }
+ }
+
+ return NULL;
+}
+
+static int
+delegateldap_avail (void *mech_context, sasl_server_params_t *sparams, void **context)
+{
+ delegate_context *ctx = NULL;
+ LDAPMessage *message = NULL;
+ int ret = SASL_NOMECH;
+
+ char *attrs[] = { "supportedSASLMechanisms", NULL };
+ const char *mechanism;
+ LDAPMessage *entry;
+ struct berval **values;
+ int res, loaded;
+
+ mechanism = (const char*)mech_context;
+ *context = NULL;
+ ret = SASL_NOMECH;
+ loaded = 0;
+
+ /* Lock */
+ if (the_supported->mutex)
+ (sparams->utils->mutex_lock) (the_supported->mutex);
+
+ /* See if this mechanism is cached */
+ loaded = the_supported->loaded;
+ if (loaded && find_berval_value (mechanism, the_supported->mechs))
+ ret = SASL_OK;
+
+ /* Unlock the globals */
+ if (the_supported->mutex)
+ (sparams->utils->mutex_unlock) (the_supported->mutex);
+
+ /* We have a cache, no need to go further */
+ if (loaded)
+ goto cleanup;
+
+ log_message (sparams->utils, SASL_LOG_NOTE, "loading mechanism info from server");
+
+ /* Create a new context, connect to the LDAP server */
+ ret = delegateldap_new (mech_context, sparams, NULL, 0, context);
+ if (ret != SASL_OK)
+ goto cleanup;
+
+ ctx = (delegate_context*)*context;
+ values = NULL;
+
+ /* Query the server for supported mechanisms */
+ res = ldap_search_ext_s (ctx->ldap, "", LDAP_SCOPE_BASE, NULL, attrs, 0,
+ NULL, NULL, LDAP_NO_LIMIT, LDAP_NO_LIMIT, &message);
+
+ /* Couldn't find server info */
+ if (res == LDAP_NO_SUCH_OBJECT) {
+ log_message (sparams->utils, SASL_LOG_WARN, "no base object with LDAP server info");
+
+ /* Some other failure */
+ } else if (res != LDAP_SUCCESS) {
+ ret = report_ldap_error (sparams->utils, res, "couldn't search server for supported SASL mechanisms");
+ goto cleanup;
+
+ /* Found something */
+ } else {
+ entry = ldap_first_entry (ctx->ldap, message);
+ if (entry == NULL) {
+ log_message (sparams->utils, SASL_LOG_WARN, "no base object with LDAP server info");
+ } else {
+ values = ldap_get_values_len (ctx->ldap, entry, "supportedSASLMechanisms");
+ if (values == NULL)
+ log_message (sparams->utils, SASL_LOG_WARN, "no supportedSASLMechanisms attribute on LDAP server info");
+ }
+ }
+
+ /* Lock the globals */
+ if (the_supported->mutex)
+ (sparams->utils->mutex_lock) (the_supported->mutex);
+
+ /* Swap in the mechanisms we just loaded */
+ if (the_supported->mechs)
+ ldap_value_free_len (the_supported->mechs);
+ the_supported->mechs = values;
+ the_supported->loaded = 1;
+
+ /* And search through them */
+ if (find_berval_value (mechanism, the_supported->mechs))
+ ret = SASL_OK;
+
+ /* Unlock the globals */
+ if (the_supported->mutex)
+ (sparams->utils->mutex_unlock) (the_supported->mutex);
+
+cleanup:
+
+ if (message)
+ ldap_msgfree (message);
+ if (ret == SASL_OK) {
+ log_message (sparams->utils, SASL_LOG_DEBUG,
+ "server supports mechanism: %s", mechanism);
+ } else {
+ log_message (sparams->utils, SASL_LOG_FAIL,
+ "server doesn't support mechanism: %s", mechanism);
+ *context = NULL;
+ if (ctx)
+ delegateldap_dispose (ctx, sparams->utils);
+ }
+
+ return ret;
+}
+
+static void
+delegateldap_free (void *mech_context, const sasl_utils_t *utils)
+{
+ void *mutex;
+
+ if (!the_supported)
+ return;
+
+ /* Lock globals */
+ if (the_supported->mutex)
+ (utils->mutex_lock) (the_supported->mutex);
+
+ /* Free mechanisms */
+ if (the_supported->mechs)
+ ldap_value_free_len (the_supported->mechs);
+ the_supported->mechs = NULL;
+ the_supported->mechs = 0;
+
+ /* And the main global context */
+ mutex = the_supported->mutex;
+ (utils->free) (the_supported);
+ the_supported = NULL;
+
+ /* Unlock globals */
+ if (mutex)
+ (utils->mutex_unlock) (mutex);
+}
+
+static sasl_server_plug_t delegateldap_server_plugins[] =
+{
+ {
+ "DIGEST-MD5", /* mech_name */
+ 0, /* max_ssf */
+ SASL_SEC_NOPLAINTEXT
+ | SASL_SEC_NOANONYMOUS
+ | SASL_SEC_MUTUAL_AUTH, /* security_flags */
+ SASL_FEAT_ALLOWS_PROXY, /* features */
+ "DIGEST-MD5", /* mech_context */
+ &delegateldap_new, /* mech_new */
+ &delegateldap_step, /* mech_step */
+ &delegateldap_dispose, /* mech_dispose */
+ &delegateldap_free, /* mech_free */
+ NULL, /* setpass */
+ NULL, /* user_query */
+ NULL, /* idle */
+ &delegateldap_avail, /* mech avail */
+ NULL /* spare */
+ },
+ {
+ "CRAM-MD5", /* mech_name */
+ 0, /* max_ssf */
+ SASL_SEC_NOPLAINTEXT
+ | SASL_SEC_NOANONYMOUS, /* security_flags */
+ SASL_FEAT_SERVER_FIRST, /* features */
+ "CRAM-MD5", /* mech_context */
+ &delegateldap_new, /* mech_new */
+ &delegateldap_step, /* mech_step */
+ &delegateldap_dispose, /* mech_dispose */
+ &delegateldap_free, /* mech_free */
+ NULL, /* setpass */
+ NULL, /* user_query */
+ NULL, /* idle */
+ &delegateldap_avail, /* mech avail */
+ NULL /* spare */
+ },
+ {
+ "PLAIN", /* mech_name */
+ 0, /* max_ssf */
+ SASL_SEC_NOANONYMOUS, /* security_flags */
+ SASL_FEAT_WANT_CLIENT_FIRST
+ | SASL_FEAT_ALLOWS_PROXY, /* features */
+ "PLAIN", /* mech_context */
+ &delegateldap_new, /* mech_new */
+ &delegateldap_step, /* mech_step */
+ &delegateldap_dispose, /* mech_dispose */
+ &delegateldap_free, /* mech_free */
+ NULL, /* setpass */
+ NULL, /* user_query */
+ NULL, /* idle */
+ &delegateldap_avail, /* mech avail */
+ NULL /* spare */
+ },
+ {
+ "LOGIN", /* mech_name */
+ 0, /* max_ssf */
+ SASL_SEC_NOANONYMOUS, /* security_flags */
+ 0, /* features */
+ "LOGIN", /* mech_context */
+ &delegateldap_new, /* mech_new */
+ &delegateldap_step, /* mech_step */
+ &delegateldap_dispose, /* mech_dispose */
+ &delegateldap_free, /* mech_free */
+ NULL, /* setpass */
+ NULL, /* user_query */
+ NULL, /* idle */
+ &delegateldap_avail, /* mech avail */
+ NULL /* spare */
+ }
+};
+
+extern int
+sasl_server_plug_init (const sasl_utils_t *utils, int maxversion, int *out_version,
+ sasl_server_plug_t **pluglist, int *plugcount)
+{
+ const char *option;
+
+ if (maxversion < SASL_SERVER_PLUG_VERSION) {
+ log_message (utils, SASL_LOG_ERR, "bad version: can't load delegateldap plugin");
+ return SASL_BADVERS;
+ }
+
+ log_message (utils, SASL_LOG_DEBUG, "loading delegateldap plugin");
+
+ if ((utils->getopt) (utils->getopt_context, NULL,
+ OPTION_SERVER, &option, NULL) != SASL_OK || !option) {
+ log_message (utils, SASL_LOG_ERR,
+ "missing '%s' option in config file", OPTION_SERVER);
+ return SASL_BADPARAM;
+ }
+
+ /* From ldapdb plugin */
+ if ((utils->getopt) (utils->getopt_context, NULL,
+ OPTION_LDAPRC, &option, NULL) == SASL_OK && option) {
+ setenv ("LDAPRC", option, 1);
+ }
+
+ the_supported = (utils->malloc) (sizeof (supported_mechanisms));
+ if (!the_supported)
+ return SASL_NOMEM;
+ memset (the_supported, 0, sizeof (supported_mechanisms));
+
+ if (utils->mutex_alloc)
+ the_supported->mutex = (utils->mutex_alloc) ();
+
+ *out_version = SASL_SERVER_PLUG_VERSION;
+ *pluglist = delegateldap_server_plugins;
+ *plugcount = sizeof (delegateldap_server_plugins) / sizeof (delegateldap_server_plugins[0]);
+
+ return SASL_OK;
+}