summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/saslbind.c
diff options
context:
space:
mode:
Diffstat (limited to 'ldap/servers/slapd/saslbind.c')
-rw-r--r--ldap/servers/slapd/saslbind.c908
1 files changed, 908 insertions, 0 deletions
diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c
new file mode 100644
index 00000000..6a488d4f
--- /dev/null
+++ b/ldap/servers/slapd/saslbind.c
@@ -0,0 +1,908 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <slap.h>
+#include <fe.h>
+#include <sasl.h>
+#include <saslplug.h>
+#include <saslmod.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+/* No GSSAPI on Windows */
+#if !defined(_WIN32)
+#define BUILD_GSSAPI 1
+#endif
+
+static char *serverfqdn;
+
+/*
+ * utility functions needed by the sasl library
+ */
+
+int sasl_os_gethost(char *buf, int len)
+{
+ int rc;
+
+ rc = gethostname(buf, len);
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl_os_gethost %s\n", buf, 0, 0);
+ return ( rc == 0 ? SASL_OK : SASL_FAIL );
+}
+
+void *sasl_mutex_alloc(void)
+{
+ return PR_NewLock();
+}
+
+int sasl_mutex_lock(void *mutex)
+{
+ PR_Lock(mutex);
+ return SASL_OK;
+}
+
+int sasl_mutex_unlock(void *mutex)
+{
+ if (PR_Unlock(mutex) == PR_SUCCESS) return SASL_OK;
+ return SASL_FAIL;
+}
+
+void sasl_mutex_free(void *mutex)
+{
+ PR_DestroyLock(mutex);
+}
+
+/*
+ * sasl library callbacks
+ */
+
+static int ids_sasl_getopt(
+ void *context,
+ const char *plugin_name,
+ const char *option,
+ const char **result,
+ unsigned *len
+)
+{
+ unsigned tmplen;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "ids_sasl_getopt: plugin=%s option=%s\n",
+ plugin_name ? plugin_name : "", option, 0);
+
+ if (len == NULL) len = &tmplen;
+
+ *result = NULL;
+ *len = 0;
+
+ if (strcasecmp(option, "enable") == 0) {
+ *result = "USERDB/DIGEST-MD5,GSSAPI/GSSAPI";
+ } else if (strcasecmp(option, "has_plain_passwords") == 0) {
+ *result = "yes";
+ } else if (strcasecmp(option, "LOG_LEVEL") == 0) {
+ if (LDAPDebugLevelIsSet(LDAP_DEBUG_TRACE)) {
+ *result = "6"; /* SASL_LOG_TRACE */
+ }
+ }
+
+ if (*result) *len = strlen(*result);
+
+ return SASL_OK;
+}
+
+static int ids_sasl_log(
+ void *context,
+ int level,
+ const char *message
+)
+{
+ switch (level) {
+ case SASL_LOG_ERR: /* log unusual errors (default) */
+ slapi_log_error(SLAPI_LOG_FATAL, "sasl", "%s", message);
+ break;
+
+ case SASL_LOG_FAIL: /* log all authentication failures */
+ case SASL_LOG_WARN: /* log non-fatal warnings */
+ case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
+ case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
+ case SASL_LOG_TRACE: /* traces of internal protocols */
+ case SASL_LOG_PASS: /* traces of internal protocols, including
+ * passwords */
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl(%d): %s", level, message, 0);
+ break;
+
+ case SASL_LOG_NONE: /* don't log anything */
+ default:
+ break;
+ }
+ return SASL_OK;
+}
+
+static int ids_sasl_proxy_policy(
+ sasl_conn_t *conn,
+ void *context,
+ const char *requested_user, int rlen,
+ const char *auth_identity, int alen,
+ const char *def_realm, int urlen,
+ struct propctx *propctx
+)
+{
+ int retVal = SASL_OK;
+ /* do not permit sasl proxy authorization */
+ /* if the auth_identity is null or empty string, allow the sasl request to go thru */
+ if ( (auth_identity != NULL ) && ( strlen(auth_identity) > 0 ) ) {
+ Slapi_DN authId , reqUser;
+ slapi_sdn_init_dn_byref(&authId,auth_identity);
+ slapi_sdn_init_dn_byref(&reqUser,requested_user);
+ if (slapi_sdn_compare((const Slapi_DN *)&reqUser,(const Slapi_DN *) &authId) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "sasl proxy auth not permitted authid=%s user=%s\n",
+ auth_identity, requested_user, 0);
+ retVal = SASL_NOAUTHZ;
+ }
+ slapi_sdn_done(&authId);
+ slapi_sdn_done(&reqUser);
+ }
+ return retVal;
+}
+
+static void ids_sasl_user_search(
+ char *basedn,
+ int scope,
+ char *filter,
+ LDAPControl **ctrls,
+ char **attrs,
+ int attrsonly,
+ Slapi_Entry **ep,
+ int *foundp
+)
+{
+ Slapi_Entry **entries = NULL;
+ Slapi_PBlock *pb;
+ int i, ret;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter, 0);
+
+ /* TODO: set size and time limits */
+
+ pb = slapi_search_internal(basedn, scope, filter,
+ ctrls, attrs, attrsonly);
+ if (pb == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "null pblock from slapi_search_internal\n", 0, 0, 0);
+ goto out;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
+ if (ret != LDAP_SUCCESS) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search failed basedn=\"%s\" "
+ "filter=\"%s\": %s\n",
+ basedn, filter, ldap_err2string(ret));
+ goto out;
+ }
+
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
+ if (entries == NULL) goto out;
+
+ for (i = 0; entries[i]; i++) {
+ (*foundp)++;
+ if (*ep != NULL) {
+ slapi_entry_free(*ep);
+ }
+ *ep = slapi_entry_dup(entries[i]);
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found dn=%s\n",
+ slapi_entry_get_dn(*ep), 0, 0);
+ }
+
+ out:
+
+ if (pb) slapi_free_search_results_internal(pb);
+ return;
+}
+
+/*
+ * Search for an entry representing the sasl user.
+ */
+static Slapi_Entry *ids_sasl_user_to_entry(
+ sasl_conn_t *conn,
+ void *context,
+ const char *user,
+ const char *user_realm
+)
+{
+ int found = 0;
+ unsigned fsize = 0, ulen, rlen = 0;
+ int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
+ char filter[1024], *fptr = filter;
+ LDAPControl **ctrls = NULL;
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *sdn;
+ char **attrs = NULL;
+ char *userattr = "uid", *realmattr = NULL, *ufilter = NULL;
+ void *node;
+ int regexmatch = 0;
+ char *regex_ldap_search_base = NULL;
+ char *regex_ldap_search_filter = NULL;
+
+ /* TODO: userattr & realmattr should be configurable */
+
+ /*
+ * Check for dn: prefix. See RFC 2829 section 9.
+ */
+ if (strncasecmp(user, "dn:", 3) == 0) {
+ sprintf(fptr, "(objectclass=*)");
+ scope = LDAP_SCOPE_BASE;
+ ids_sasl_user_search((char*)user+3, scope, filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+ } else {
+ int offset = 0;
+ if (strncasecmp(user,"u:",2) == 0 )
+ offset = 2;
+ /* TODO: quote the filter values */
+
+ /* New regex-based identity mapping : we call it here before the old code.
+ * If there's a match, we skip the old way, otherwise we plow ahead for backwards compatibility reasons
+ */
+
+ regexmatch = sasl_map_domap((char*)user, (char*)user_realm, &regex_ldap_search_base, &regex_ldap_search_filter);
+ if (regexmatch) {
+
+ ids_sasl_user_search(regex_ldap_search_base, scope, regex_ldap_search_filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+
+ /* Free the filter etc */
+ slapi_ch_free((void**)&regex_ldap_search_base);
+ slapi_ch_free((void**)&regex_ldap_search_filter);
+ } else {
+
+ /* Ensure no buffer overflow. */
+ /* We don't know what the upper limits on username and
+ * realm lengths are. There don't seem to be any defined
+ * in the relevant standards. We may find in the future
+ * that a 1K buffer is insufficient for some mechanism,
+ * but it seems unlikely given that the values are exposed
+ * to the end user.
+ */
+ ulen = strlen(user+offset);
+ fsize += strlen(userattr) + ulen;
+ if (realmattr && user_realm) {
+ rlen = strlen(user_realm);
+ fsize += strlen(realmattr) + rlen;
+ }
+ if (ufilter) fsize += strlen(ufilter);
+ fsize += 100; /* includes a good safety margin */
+ if (fsize > 1024) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl user name and/or realm too long"
+ " (ulen=%u, rlen=%u)\n", ulen, rlen, 0);
+ return NULL;
+ }
+
+ /* now we can safely write the filter */
+ sprintf(fptr, "(&(%s=%s)", userattr, user+offset);
+ fptr += strlen(fptr);
+ if (realmattr && user_realm) {
+ sprintf(fptr, "(%s=%s)", realmattr, user_realm);
+ fptr += strlen(fptr);
+ }
+ if (ufilter) {
+ if (*ufilter == '(') {
+ sprintf(fptr, "%s", ufilter);
+ } else {
+ sprintf(fptr, "(%s)", ufilter);
+ }
+ fptr += strlen(fptr);
+ }
+ sprintf(fptr, ")");
+
+ /* iterate through the naming contexts */
+ for (sdn = slapi_get_first_suffix(&node, 0); sdn != NULL;
+ sdn = slapi_get_next_suffix(&node, 0)) {
+
+ ids_sasl_user_search((char*)slapi_sdn_get_dn(sdn), scope, filter,
+ ctrls, attrs, attrsonly,
+ &entry, &found);
+ }
+ }
+ }
+
+ if (found == 1) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found this entry: dn:%s, matching filter=%s\n", entry->e_sdn.dn, filter, 0);
+ return entry;
+ }
+
+ if (found == 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries matching filter=%s\n", filter, 0, 0);
+ } else {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found more than one entry matching filter=%s\n", filter, 0, 0);
+ }
+
+ if (entry) slapi_entry_free(entry);
+ return NULL;
+}
+
+static char *buf2str(const char *buf, unsigned buflen)
+{
+ char *ret;
+
+ ret = (char*)slapi_ch_malloc(buflen+1);
+ memcpy(ret, buf, buflen);
+ ret[buflen] = '\0';
+
+ return ret;
+}
+
+/* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
+static int ids_sasl_canon_user(
+ sasl_conn_t *conn,
+ void *context,
+ const char *userbuf, unsigned ulen,
+ const char *authidbuf, unsigned alen,
+ unsigned flags, const char *user_realm,
+ char *out_user, unsigned out_umax, unsigned *out_ulen,
+ char *out_authid, unsigned out_amax, unsigned *out_alen
+)
+{
+ struct propctx *propctx = sasl_auxprop_getctx(conn);
+ Slapi_Entry *entry = NULL;
+ Slapi_DN *sdn = NULL;
+ char *pw = NULL;
+ char *user = NULL;
+ char *authid = NULL;
+ const char *dn;
+ int isroot = 0;
+ char *clear = NULL;
+
+ user = buf2str(userbuf, ulen);
+ if (user == NULL) {
+ goto fail;
+ }
+ authid = buf2str(authidbuf, alen);
+
+ LDAPDebug(LDAP_DEBUG_TRACE,
+ "ids_sasl_canon_user(user=%s, authzid=%s, realm=%s)\n",
+ user, authid, user_realm ? user_realm : "");
+
+ if (strncasecmp(user, "dn:", 3) == 0) {
+ sdn = slapi_sdn_new();
+ slapi_sdn_set_dn_byval(sdn, user+3);
+ isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
+ }
+
+ if (isroot) {
+ /* special case directory manager */
+ dn = slapi_sdn_get_ndn(sdn);
+ pw = config_get_rootpw();
+ } else {
+ /* map the sasl username into an entry */
+ entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
+ if (entry == NULL) {
+ goto fail;
+ }
+ dn = slapi_entry_get_ndn(entry);
+ pw = slapi_entry_attr_get_charptr(entry, "userpassword");
+ }
+
+ if (prop_set(propctx, "dn", dn, -1) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
+ goto fail;
+ }
+
+ clear = pw;
+ if (clear) {
+ if (prop_set(propctx, "userpassword", clear, -1) != 0) {
+ /* Failure is benign here because some mechanisms don't support this property */
+ /*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
+ goto fail */ ;
+ }
+ }
+
+ /* TODO: canonicalize */
+ strcpy(out_user, dn);
+ if (authid )
+ {
+ int offset = 0;
+ /* The authid can start with dn:. In such case remove it */
+ if (strncasecmp(authid,"dn:",3) == 0 )
+ offset = 3;
+ strcpy(out_authid, authid+offset);
+ }
+ *out_ulen = -1;
+ *out_alen = -1;
+
+ slapi_entry_free(entry);
+ slapi_ch_free((void**)&user);
+ slapi_ch_free((void**)&authid);
+ slapi_ch_free((void**)&pw);
+ slapi_sdn_free(&sdn);
+
+ return SASL_OK;
+
+ fail:
+ slapi_entry_free(entry);
+ slapi_ch_free((void**)&user);
+ slapi_ch_free((void**)&authid);
+ slapi_ch_free((void**)&pw);
+ slapi_sdn_free(&sdn);
+
+ return SASL_FAIL;
+}
+
+static sasl_callback_t ids_sasl_callbacks[5] =
+{
+ SASL_CB_GETOPT,
+ (IFP) ids_sasl_getopt,
+ NULL,
+ SASL_CB_LOG,
+ (IFP) ids_sasl_log,
+ NULL,
+ SASL_CB_PROXY_POLICY,
+ (IFP) ids_sasl_proxy_policy,
+ NULL,
+ SASL_CB_SERVER_CANON_USER,
+ (IFP) ids_sasl_canon_user,
+ NULL,
+ SASL_CB_LIST_END,
+ (IFP) NULL,
+ NULL
+};
+
+static const char *dn_propnames[] = { "dn", 0 };
+
+/*
+ * initialize the sasl library
+ */
+int ids_sasl_init(void)
+{
+ static int inited = 0;
+ int result;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
+
+ PR_ASSERT(inited == 0);
+ inited = 1;
+
+ serverfqdn = get_localhost_DNS();
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
+ serverfqdn, 0, 0);
+
+ result = sasl_server_init(ids_sasl_callbacks, "iDS");
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to initialize sasl library\n",
+ 0, 0, 0);
+ return result;
+ }
+
+ result = sasl_server_add_plugin("USERDB", sasl_userdb_init);
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP sasl plugin\n",
+ 0, 0, 0);
+ return result;
+ }
+
+#if defined(BUILD_GSSAPI)
+ result = sasl_server_add_plugin("GSSAPI", sasl_gssapi_init);
+
+ if (result != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP gssapi plugin\n",
+ 0, 0, 0);
+ }
+#endif
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
+
+ return result;
+}
+
+/*
+ * create a sasl server connection
+ */
+void ids_sasl_server_new(Connection *conn)
+{
+ int rc;
+ sasl_conn_t *sasl_conn = NULL;
+ struct propctx *propctx;
+ sasl_security_properties_t secprops = {0};
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
+
+ rc = sasl_server_new("ldap",
+ serverfqdn,
+ NULL, /* user_realm */
+ NULL, /* iplocalport */
+ NULL, /* ipremoteport */
+ ids_sasl_callbacks,
+ SASL_SUCCESS_DATA,
+ &sasl_conn);
+
+ if (rc != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
+ sasl_errstring(rc, NULL, NULL), 0, 0);
+ }
+
+ if (rc == SASL_OK) {
+ propctx = sasl_auxprop_getctx(sasl_conn);
+ if (propctx != NULL) {
+ prop_request(propctx, dn_propnames);
+ }
+ }
+
+ /* Enable security for this connection */
+ secprops.maxbufsize = 2048; /* DBDB: hack */
+ secprops.max_ssf = 0xffffffff;
+ rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
+ if (rc != SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
+ sasl_errstring(rc, NULL, NULL), 0, 0);
+ }
+
+ conn->c_sasl_conn = sasl_conn;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
+
+ return;
+}
+
+/*
+ * return sasl mechanisms available on this connection.
+ * caller must free returned charray.
+ */
+char **ids_sasl_listmech(Slapi_PBlock *pb)
+{
+ char **ret, **others;
+ const char *str;
+ char *dupstr;
+ sasl_conn_t *sasl_conn;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
+
+ PR_ASSERT(pb);
+
+ /* hard-wired mechanisms and slapi plugin registered mechanisms */
+ ret = slapi_get_supported_saslmechanisms_copy();
+
+ if (pb->pb_conn == NULL) return ret;
+
+ sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
+ if (sasl_conn == NULL) return ret;
+
+ /* sasl library mechanisms are connection dependent */
+ PR_Lock(pb->pb_conn->c_mutex);
+ if (sasl_listmech(sasl_conn,
+ NULL, /* username */
+ "", ",", "",
+ &str, NULL, NULL) == SASL_OK) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
+ /* merge into result set */
+ dupstr = slapi_ch_strdup(str);
+ others = str2charray(dupstr, ",");
+ charray_merge(&ret, others, 1);
+ charray_free(others);
+ slapi_ch_free((void**)&dupstr);
+ }
+ PR_Unlock(pb->pb_conn->c_mutex);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, ">= ids_sasl_listmech\n", 0, 0, 0 );
+
+ return ret;
+}
+
+/*
+ * Determine whether a given sasl mechanism is supported by
+ * this sasl connection. Returns true/false.
+ */
+static int
+ids_sasl_mech_supported(Slapi_PBlock *pb, sasl_conn_t *sasl_conn, const char *mech)
+{
+ int i, ret = 0;
+ char **mechs;
+ char *dupstr;
+ const char *str;
+ int sasl_result = 0;
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
+
+
+ /* sasl_listmech is not thread-safe, so we lock here */
+ PR_Lock(pb->pb_conn->c_mutex);
+ sasl_result = sasl_listmech(sasl_conn,
+ NULL, /* username */
+ "", ",", "",
+ &str, NULL, NULL);
+ PR_Unlock(pb->pb_conn->c_mutex);
+ if (sasl_result != SASL_OK) {
+ return 0;
+ }
+
+ dupstr = slapi_ch_strdup(str);
+ mechs = str2charray(dupstr, ",");
+
+ for (i = 0; mechs[i] != NULL; i++) {
+ if (strcasecmp(mech, mechs[i]) == 0) {
+ ret = 1;
+ break;
+ }
+ }
+
+ charray_free(mechs);
+ slapi_ch_free((void**)&dupstr);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
+
+ return ret;
+}
+
+/*
+ * do a sasl bind and return a result
+ */
+void ids_sasl_check_bind(Slapi_PBlock *pb)
+{
+ int rc, isroot;
+ long t;
+ sasl_conn_t *sasl_conn;
+ struct propctx *propctx;
+ sasl_ssf_t *ssfp;
+ char *activemech = NULL, *mech = NULL;
+ char *username, *dn = NULL;
+ const char *sdata, *errstr;
+ unsigned slen;
+ int continuing = 0;
+ int pwresponse_requested = 0;
+ LDAPControl **ctrls;
+ struct berval bvr, *cred;
+ struct propval dnval[2];
+ char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
+ Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
+ Slapi_Backend *be = NULL;
+ char errorbuf[BUFSIZ];
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
+
+ PR_ASSERT(pb);
+ PR_ASSERT(pb->pb_conn);
+
+ continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
+ pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
+
+ sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
+ if (sasl_conn == NULL) {
+ send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "sasl library unavailable", 0, NULL );
+ return;
+ }
+
+ slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
+ slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
+ slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
+ PR_ASSERT(mech);
+ PR_ASSERT(cred);
+
+ /* Work around a bug in the sasl library. We've told the
+ * library that CRAM-MD5 is disabled, but it gives us a
+ * different error code to SASL_NOMECH.
+ */
+ if (!ids_sasl_mech_supported(pb, sasl_conn, mech)) {
+ rc = SASL_NOMECH;
+ goto sasl_check_result;
+ }
+
+ /* can't do any harm */
+ if (cred->bv_len == 0) cred->bv_val = NULL;
+
+ if (continuing) {
+ /*
+ * RFC 2251: a client may abort a sasl bind negotiation by
+ * sending a bindrequest with a different value in the
+ * mechanism field.
+ */
+ sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
+ if (activemech == NULL) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
+ goto sasl_start;
+ }
+ if (strcasecmp(activemech, mech) != 0) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
+ goto sasl_start;
+ }
+
+ rc = sasl_server_step(sasl_conn,
+ cred->bv_val, cred->bv_len,
+ &sdata, &slen);
+ goto sasl_check_result;
+ }
+
+ sasl_start:
+
+ rc = sasl_server_start(sasl_conn, mech,
+ cred->bv_val, cred->bv_len,
+ &sdata, &slen);
+
+ sasl_check_result:
+
+ switch (rc) {
+ case SASL_OK: /* complete */
+
+ /* retrieve the authenticated username */
+ if (sasl_getprop(sasl_conn, SASL_USERNAME,
+ (const void**)&username) != SASL_OK) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "could not obtain sasl username", 0, NULL);
+ break;
+ }
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
+ mech, username, 0);
+
+ /*
+ * Retrieve the DN corresponding to the authenticated user.
+ * This should have been set by the user canon callback
+ * in an auxiliary property called "dn".
+ */
+ propctx = sasl_auxprop_getctx(sasl_conn);
+ if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
+ if (dnval[0].values && dnval[0].values[0]) {
+ dn = slapi_ch_strdup(dnval[0].values[0]);
+ }
+ }
+ if (dn == NULL) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "could not get auth dn from sasl", 0, NULL);
+ break;
+ }
+
+ isroot = slapi_dn_isroot(dn);
+
+ if (!isroot )
+ {
+ /* check if the account is locked */
+ bind_target_entry = get_entry(pb, dn);
+ if ( bind_target_entry == NULL )
+ {
+ break;
+ }
+ if ( check_account_lock(pb, bind_target_entry, pwresponse_requested) == 1) {
+ slapi_entry_free(bind_target_entry);
+ break;
+ }
+ }
+
+ /* see if we negotiated a security layer */
+ if ((sasl_getprop(sasl_conn, SASL_SSF,
+ (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
+ LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
+
+ if (pb->pb_conn->c_flags & CONN_FLAG_SSL) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "sasl encryption not supported over ssl",
+ 0, NULL);
+ if ( bind_target_entry != NULL )
+ slapi_entry_free(bind_target_entry);
+ break;
+ } else {
+ /* Enable SASL I/O on the connection now */
+ /* Note that this doesn't go into effect until the next _read_ operation is done */
+ if (0 != sasl_io_enable(pb->pb_conn) ) {
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
+ "failed to enable sasl i/o",
+ 0, NULL);
+ }
+ }
+ }
+
+ /* set the connection bind credentials */
+ sprintf(authtype, "%s%s", SLAPD_AUTH_SASL, mech);
+ bind_credentials_set(pb->pb_conn, authtype, dn,
+ NULL, NULL, NULL, bind_target_entry);
+
+ /* set the auth response control if requested */
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
+ if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
+ NULL, NULL)) {
+ add_auth_response_control(pb, dn);
+ }
+
+ if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
+ send_nobackend_ldap_result( pb );
+ be = NULL;
+ LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
+ return;
+ }
+
+ if (referral) {
+ send_referrals_from_entry(pb,referral);
+ goto out;
+ }
+
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
+ set_db_default_result_handlers(pb);
+
+ /* check password expiry */
+ if (!isroot) {
+ int pwrc;
+
+ pwrc = need_new_pw (pb, &t, bind_target_entry, pwresponse_requested);
+ if ( bind_target_entry != NULL ) {
+ slapi_entry_free(bind_target_entry);
+ bind_target_entry = NULL;
+ }
+
+ switch (pwrc) {
+ case 1:
+ add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
+ break;
+ case 2:
+ add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
+ break;
+ case -1:
+ goto out;
+ default:
+ break;
+ }
+ }
+
+ /* attach the sasl data */
+ if (slen != 0) {
+ bvr.bv_val = (char*)sdata;
+ bvr.bv_len = slen;
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
+ }
+
+ /* send successful result */
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+
+ /* TODO: enable sasl security layer */
+
+ /* remove the sasl data from the pblock */
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
+
+ break;
+
+ case SASL_CONTINUE: /* another step needed */
+ pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
+
+ /* attach the sasl data */
+ bvr.bv_val = (char*)sdata;
+ bvr.bv_len = slen;
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
+
+ /* send continuation result */
+ send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
+ NULL, 0, NULL );
+
+ /* remove the sasl data from the pblock */
+ slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
+
+ break;
+
+ case SASL_NOMECH:
+
+ send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
+ "sasl mechanism not supported", 0, NULL);
+ break;
+
+ default: /* other error */
+ errstr = sasl_errdetail(sasl_conn);
+
+ send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
+ (char*)errstr, 0, NULL);
+ break;
+ }
+
+ out:
+ if (referral)
+ slapi_entry_free(referral);
+ if (be)
+ slapi_be_Unlock(be);
+
+ LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
+
+ return;
+}
+