Makefile.am | 1
ldap/servers/slapd/bind.c | 404 +++++++++++++++-----------------------
ldap/servers/slapd/pw_verify.c | 95 ++++++++
ldap/servers/slapd/pw_verify.h | 15 +
ldap/servers/slapd/saslbind.c | 88 ++++++++
ldap/servers/slapd/slapi-plugin.h | 5
6 files changed, 373 insertions(+), 235 deletions(-)
New commits:
commit eb358a5ed6dea6f32c017c8f86e49d436db181fa
Author: William Brown <firstyear(a)redhat.com>
Date: Wed Nov 23 17:03:59 2016 +1000
Ticket 49051 - Enable SASL LOGIN/PLAIN support as a precursor to LDAPSSOTOKEN
Bug Description: We should allow LOGIN/PLAIN support for SASL. This is enabled
if the user has the cyrus-sasl plain libraries installed. The reason for this
is that LDAPSSOTOKEN will use the same userdb callbacks, so if we create them
then we also need to support LOGIN/PLAIN else they will autodetect and will just
be non-functional.
Fix Description: Modularise some of the bind pw verification code from bind.c
to pw_verify.c, then make the call to it from saslbind.c
https://fedorahosted.org/389/ticket/49051
Author: wibrown
Review by: mreynolds (Thanks!)
diff --git a/Makefile.am b/Makefile.am
index 1d76aeb..9cf30ee 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1738,6 +1738,7 @@ ns_slapd_SOURCES = ldap/servers/slapd/abandon.c \
ldap/servers/slapd/passwd_extop.c \
ldap/servers/slapd/psearch.c \
ldap/servers/slapd/pw_mgmt.c \
+ ldap/servers/slapd/pw_verify.c \
ldap/servers/slapd/rootdse.c \
ldap/servers/slapd/sasl_io.c \
ldap/servers/slapd/saslbind.c \
diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c
index 12d6dca..d8811d5 100644
--- a/ldap/servers/slapd/bind.c
+++ b/ldap/servers/slapd/bind.c
@@ -38,6 +38,7 @@
#include "slap.h"
#include "fe.h"
#include "pratom.h"
+#include "pw_verify.h"
#include <sasl.h>
static void log_bind_access(
@@ -49,38 +50,6 @@ static void log_bind_access(
const char *msg
);
-
-/*
- * Function: is_root_dn_pw
- *
- * Returns: 1 if the password for the root dn is correct.
- * 0 otherwise.
- * dn must be normalized
- *
- */
-static int
-is_root_dn_pw( const char *dn, const Slapi_Value *cred )
-{
- int rv= 0;
- char *rootpw = config_get_rootpw();
- if ( rootpw == NULL || !slapi_dn_isroot( dn ) )
- {
- rv = 0;
- }
- else
- {
- Slapi_Value rdnpwbv;
- Slapi_Value *rdnpwvals[2];
- slapi_value_init_string(&rdnpwbv,rootpw);
- rdnpwvals[ 0 ] = &rdnpwbv;
- rdnpwvals[ 1 ] = NULL;
- rv = slapi_pw_find_sv( rdnpwvals, cred ) == 0;
- value_done(&rdnpwbv);
- }
- slapi_ch_free_string( &rootpw );
- return rv;
-}
-
void
do_bind( Slapi_PBlock *pb )
{
@@ -94,7 +63,6 @@ do_bind( Slapi_PBlock *pb )
const char *dn = NULL;
char *saslmech = NULL;
struct berval cred = {0};
- Slapi_Backend *be = NULL;
ber_tag_t ber_rc;
int rc = 0;
Slapi_DN *sdn = NULL;
@@ -107,7 +75,6 @@ do_bind( Slapi_PBlock *pb )
int auto_bind = 0;
int minssf = 0;
int minssf_exclude_rootdse = 0;
- Slapi_DN *original_sdn = NULL;
slapi_log_err(SLAPI_LOG_TRACE, "do_bind", "=>\n");
@@ -612,7 +579,7 @@ do_bind( Slapi_PBlock *pb )
/*
* Check the dn and password
*/
- if ( is_root_dn_pw( slapi_sdn_get_ndn(sdn), &cv )) {
+ if (pw_verify_root_dn( slapi_sdn_get_ndn(sdn), &cv ) ==
SLAPI_BIND_SUCCESS) {
/*
* right dn and passwd - authorize
*/
@@ -648,232 +615,201 @@ do_bind( Slapi_PBlock *pb )
send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0, NULL);
}
goto free_and_return;
- }
+ } /* End isroot and auth_simple */
- /* We could be serving multiple database backends. Select the appropriate one */
- if (slapi_mapping_tree_select(pb, &be, &referral, NULL, 0) != LDAP_SUCCESS)
{
- send_nobackend_ldap_result( pb );
- be = NULL;
- goto free_and_return;
- }
+ /*
+ * call the pre-bind plugins. if they succeed, call
+ * the backend bind function. then call the post-bind
+ * plugins.
+ */
+ if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) == 0 ) {
+ rc = 0;
+
+ /* Check if a pre_bind plugin mapped the DN to another backend */
+ Slapi_DN *pb_sdn;
+ slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &pb_sdn);
+ if (!pb_sdn) {
+ slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind plug-in set
NULL dn\n");
+ slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errorbuf);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0, NULL);
+ goto free_and_return;
+ }
+ sdn = pb_sdn;
+ dn = slapi_sdn_get_dn(sdn);
+ if (!dn) {
+ const char *udn = slapi_sdn_get_udn(sdn);
+ slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind plug-in set
corrupted dn %s\n", udn?udn:"");
+ slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errorbuf);
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0, NULL);
+ goto free_and_return;
+ }
- if (referral) {
- send_referrals_from_entry(pb,referral);
- slapi_entry_free(referral);
- goto free_and_return;
- }
+ /* We could be serving multiple database backends. Select the appropriate one
*/
+ /* pw_verify_be_dn will select the backend we need for us. */
- slapi_pblock_set( pb, SLAPI_BACKEND, be );
+ rc = pw_verify_be_dn(pb, &referral);
- /* not root dn - pass to the backend */
- if ( be->be_bind != NULL ) {
- original_sdn = slapi_sdn_dup(sdn);
- /*
- * call the pre-bind plugins. if they succeed, call
- * the backend bind function. then call the post-bind
- * plugins.
- */
- if ( plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) == 0 ) {
- int sdn_updated = 0;
- rc = 0;
-
- /* Check if a pre_bind plugin mapped the DN to another backend */
- Slapi_DN *pb_sdn;
- slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &pb_sdn);
- if (!pb_sdn) {
- slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind plug-in
set NULL dn\n");
- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errorbuf);
- send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0,
NULL);
- goto free_and_return;
- } else if ((pb_sdn != sdn) || (sdn_updated = slapi_sdn_compare(original_sdn,
pb_sdn))) {
- /*
- * Slapi_DN set in pblock was changed by a pre bind plug-in.
- * It is a plug-in's responsibility to free the original Slapi_DN.
- */
- sdn = pb_sdn;
- dn = slapi_sdn_get_dn(sdn);
- if (!dn) {
- const char *udn = slapi_sdn_get_udn(sdn);
- slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind
plug-in set corrupted dn %s\n", udn?udn:"");
- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errorbuf);
- send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "", 0,
NULL);
- goto free_and_return;
+ if (rc == SLAPI_BIND_NO_BACKEND) {
+ send_nobackend_ldap_result( pb );
+ goto free_and_return;
+ } else if (rc == SLAPI_BIND_REFERRAL) {
+ send_referrals_from_entry(pb,referral);
+ slapi_entry_free(referral);
+ goto free_and_return;
+ } else if (auto_bind || rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS)
{
+ long t;
+ char* authtype = NULL;
+ /* rc is SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS */
+ if(auto_bind) {
+ rc = SLAPI_BIND_SUCCESS;
+ }
+
+ switch ( method ) {
+ case LDAP_AUTH_SIMPLE:
+ if (cred.bv_len != 0) {
+ authtype = SLAPD_AUTH_SIMPLE;
}
- if (!sdn_updated) { /* pb_sdn != sdn; need to compare the dn's. */
- sdn_updated = slapi_sdn_compare(original_sdn, sdn);
+#if defined(ENABLE_AUTOBIND)
+ else if(auto_bind) {
+ authtype = SLAPD_AUTH_OS;
}
- if (sdn_updated) { /* call slapi_be_select only when the DN is updated.
*/
- slapi_be_Unlock(be);
- be = slapi_be_select_exact(sdn);
- if (be) {
- slapi_be_Rlock(be);
- slapi_pblock_set( pb, SLAPI_BACKEND, be );
- } else {
- slapi_create_errormsg(errorbuf, sizeof(errorbuf), "No
matching backend for %s\n", dn);
- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, errorbuf);
- send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, "",
0, NULL);
- goto free_and_return;
- }
+#endif /* ENABLE_AUTOBIND */
+ else {
+ authtype = SLAPD_AUTH_NONE;
}
+ break;
+ case LDAP_AUTH_SASL:
+ /* authtype = SLAPD_AUTH_SASL && saslmech: */
+ PR_snprintf(authtypebuf, sizeof(authtypebuf), "%s%s",
SLAPD_AUTH_SASL, saslmech);
+ authtype = authtypebuf;
+ break;
+ default:
+ break;
}
- slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
- set_db_default_result_handlers(pb);
- if ( (rc != 1) &&
- (auto_bind ||
- (((rc = (*be->be_bind)( pb )) == SLAPI_BIND_SUCCESS) ||
- (rc == SLAPI_BIND_ANONYMOUS))) ) {
- long t;
- char* authtype = NULL;
- /* rc is SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS */
- if(auto_bind) {
- rc = SLAPI_BIND_SUCCESS;
- }
- switch ( method ) {
- case LDAP_AUTH_SIMPLE:
- if (cred.bv_len != 0) {
- authtype = SLAPD_AUTH_SIMPLE;
- }
-#if defined(ENABLE_AUTOBIND)
- else if(auto_bind) {
- authtype = SLAPD_AUTH_OS;
- }
-#endif /* ENABLE_AUTOBIND */
- else {
- authtype = SLAPD_AUTH_NONE;
+ if ( rc == SLAPI_BIND_SUCCESS ) {
+ int myrc = 0;
+ Slapi_Backend *be = NULL;
+ /*
+ * The bind is successful.
+ * We can give it to slapi_check_account_lock and
reslimit_update_from_dn.
+ */
+ /*
+ * Is this account locked ?
+ * could be locked through the account inactivation
+ * or by the password policy
+ *
+ * rc=0: account not locked
+ * rc=1: account locked, can not bind, result has been sent
+ * rc!=0 and rc!=1: error. Result was not sent, lets be_bind
+ * deal with it.
+ *
+ */
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ bind_target_entry = get_entry(pb, slapi_sdn_get_ndn(sdn));
+ myrc = slapi_check_account_lock(pb, bind_target_entry,
pw_response_requested, 1, 1);
+ if (1 == myrc) { /* account is locked */
+ rc = myrc;
+ goto account_locked;
}
- break;
- case LDAP_AUTH_SASL:
- /* authtype = SLAPD_AUTH_SASL && saslmech: */
- PR_snprintf(authtypebuf, sizeof(authtypebuf), "%s%s",
SLAPD_AUTH_SASL, saslmech);
- authtype = authtypebuf;
- break;
- default:
- break;
+ myrc = 0;
}
-
- if ( rc == SLAPI_BIND_SUCCESS ) {
- int myrc = 0;
+ if (!auto_bind) {
/*
- * The bind is successful.
- * We can give it to slapi_check_account_lock and
reslimit_update_from_dn.
+ * There could be a race that bind_target_entry was not added
+ * when bind_target_entry was retrieved before be_bind, but it
+ * was in be_bind. Since be_bind returned SLAPI_BIND_SUCCESS,
+ * the entry is in the DS. So, we need to retrieve it once more.
*/
- /*
- * Is this account locked ?
- * could be locked through the account inactivation
- * or by the password policy
- *
- * rc=0: account not locked
- * rc=1: account locked, can not bind, result has been sent
- * rc!=0 and rc!=1: error. Result was not sent, lets be_bind
- * deal with it.
- *
- */
- if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA) &&
+ !bind_target_entry) {
bind_target_entry = get_entry(pb, slapi_sdn_get_ndn(sdn));
- myrc = slapi_check_account_lock(pb, bind_target_entry,
pw_response_requested, 1, 1);
- if (1 == myrc) { /* account is locked */
- rc = myrc;
- goto account_locked;
+ if (!bind_target_entry) {
+ slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "No such
entry");
+ send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
"", 0, NULL);
+ goto free_and_return;
}
- myrc = 0;
}
- if (!auto_bind) {
- /*
- * There could be a race that bind_target_entry was not added
- * when bind_target_entry was retrieved before be_bind, but it
- * was in be_bind. Since be_bind returned SLAPI_BIND_SUCCESS,
- * the entry is in the DS. So, we need to retrieve it once
more.
- */
- if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)
&&
- !bind_target_entry) {
- bind_target_entry = get_entry(pb, slapi_sdn_get_ndn(sdn));
- if (!bind_target_entry) {
- slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, "No such
entry");
- send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
"", 0, NULL);
- goto free_and_return;
- }
- }
- bind_credentials_set(pb->pb_conn, authtype,
- slapi_ch_strdup(slapi_sdn_get_ndn(sdn)),
- NULL, NULL, NULL, bind_target_entry);
- if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
- /* check if need new password before sending
- the bind success result */
- myrc = need_new_pw(pb, &t, bind_target_entry,
pw_response_requested);
- switch (myrc) {
- case 1:
- (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED,
0);
- break;
- case 2:
- (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING,
t);
- break;
- default:
- break;
- }
+ bind_credentials_set(pb->pb_conn, authtype,
+ slapi_ch_strdup(slapi_sdn_get_ndn(sdn)),
+ NULL, NULL, NULL, bind_target_entry);
+ if (!slapi_be_is_flag_set(be, SLAPI_BE_FLAG_REMOTE_DATA)) {
+ /* check if need new password before sending
+ the bind success result */
+ myrc = need_new_pw(pb, &t, bind_target_entry,
pw_response_requested);
+ switch (myrc) {
+ case 1:
+ (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
+ break;
+ case 2:
+ (void)slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
+ break;
+ default:
+ break;
}
}
- if (auth_response_requested) {
- slapi_add_auth_response_control(pb, slapi_sdn_get_ndn(sdn));
- }
- if (-1 == myrc) {
- /* need_new_pw failed; need_new_pw already send_ldap_result in
it. */
- goto free_and_return;
- }
- } else { /* anonymous */
- /* set bind creds here so anonymous limits are set */
- bind_credentials_set(pb->pb_conn, authtype, NULL, NULL, NULL,
NULL, NULL);
-
- if ( auth_response_requested ) {
- slapi_add_auth_response_control(pb, "");
- }
}
- } else {
-account_locked:
- if(cred.bv_len == 0) {
- /* its an UnAuthenticated Bind, DN specified but no pw */
-
slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds);
- }else{
- /* password must have been invalid */
- /* increment BindSecurityError count */
-
slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
+ if (auth_response_requested) {
+ slapi_add_auth_response_control(pb, slapi_sdn_get_ndn(sdn));
}
- }
+ if (-1 == myrc) {
+ /* need_new_pw failed; need_new_pw already send_ldap_result in it.
*/
+ goto free_and_return;
+ }
+ if (be) {
+ slapi_be_Unlock(be);
+ }
+ } else { /* anonymous */
+ /* set bind creds here so anonymous limits are set */
+ bind_credentials_set(pb->pb_conn, authtype, NULL, NULL, NULL, NULL,
NULL);
- /*
- * if rc != SLAPI_BIND_SUCCESS and != SLAPI_BIND_ANONYMOUS,
- * the result has already been sent by the backend. otherwise,
- * we assume it is success and send it here to avoid a race
- * condition where the client could be told by the
- * backend that the bind succeeded before we set the
- * c_dn field in the connection structure here in
- * the front end.
- */
- if ( rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS) {
- send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
+ if ( auth_response_requested ) {
+ slapi_add_auth_response_control(pb, "");
+ }
+ }
+ } else { /* if auto_bind || rc == slapi_bind_success | slapi_bind_anonymous */
+ if (rc == LDAP_OPERATIONS_ERROR) {
+ send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, "Function not
implemented", 0, NULL );
+ goto free_and_return;
+ }
+account_locked:
+ if(cred.bv_len == 0) {
+ /* its an UnAuthenticated Bind, DN specified but no pw */
+
slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsUnAuthBinds);
+ }else{
+ /* password must have been invalid */
+ /* increment BindSecurityError count */
+
slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors);
}
+ }
- slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
- plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
- } else {
- /* even though preop failed, we should still call the post-op plugins */
- plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
- /* If the prebind plugins fail we MUST send a result! */
- /* Is there a way to get a better result descriptions from say the ADDN
plugin? */
- slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind plug-in
failed\n");
- send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, errorbuf, 0, NULL);
- goto free_and_return;
+ /*
+ * if rc != SLAPI_BIND_SUCCESS and != SLAPI_BIND_ANONYMOUS,
+ * the result has already been sent by the backend. otherwise,
+ * we assume it is success and send it here to avoid a race
+ * condition where the client could be told by the
+ * backend that the bind succeeded before we set the
+ * c_dn field in the connection structure here in
+ * the front end.
+ */
+ if ( rc == SLAPI_BIND_SUCCESS || rc == SLAPI_BIND_ANONYMOUS) {
+ send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
}
+
+ slapi_pblock_set( pb, SLAPI_PLUGIN_OPRETURN, &rc );
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
} else {
- send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL,
- "Function not implemented", 0, NULL );
+ /* even though preop failed, we should still call the post-op plugins */
+ plugin_call_plugins( pb, SLAPI_PLUGIN_POST_BIND_FN );
+ /* If the prebind plugins fail we MUST send a result! */
+ /* Is there a way to get a better result descriptions from say the ADDN plugin?
*/
+ slapi_create_errormsg(errorbuf, sizeof(errorbuf), "Pre-bind plug-in
failed\n");
+ send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL, errorbuf, 0, NULL);
+ goto free_and_return;
}
-free_and_return:;
- slapi_sdn_free(&original_sdn);
- if (be) {
- slapi_be_Unlock(be);
- }
+free_and_return:
if (bind_sdn_in_pb) {
slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn);
}
diff --git a/ldap/servers/slapd/pw_verify.c b/ldap/servers/slapd/pw_verify.c
new file mode 100644
index 0000000..93e5ff3
--- /dev/null
+++ b/ldap/servers/slapd/pw_verify.c
@@ -0,0 +1,95 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2016 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * pw_verify.c
+ *
+ * This contains helpers that take a DN and a password credential from a simple
+ * bind or SASL PLAIN/LOGIN. It steps through the raw credential and returns
+ *
+ * SLAPI_BIND_SUCCESS : The credentials are correct for the DN.
+ * SLAPI_BIND_ANONYMOUS : The credentials are anonymous.
+ * SLAPI_BIND_REFERRAL : The DN provided is going to be a referal, go away!
+ * LDAP_INVALID_CREDENTIALS : The credentials are incorrect for this DN, or not
+ * enough material was provided.
+ * LDAP_OPERATIONS_ERROR : Something went wrong during verification.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "slap.h"
+#include "fe.h"
+
+int
+pw_verify_root_dn(const char *dn, const Slapi_Value *cred)
+{
+ int result = LDAP_OPERATIONS_ERROR;
+ char *root_pw = config_get_rootpw();
+ if (root_pw != NULL && slapi_dn_isroot(dn)) {
+ /* Now build a slapi value to give to slapi_pw_find_sv */
+ Slapi_Value root_dn_pw_bval;
+ slapi_value_init_string(&root_dn_pw_bval, root_pw);
+ Slapi_Value *root_dn_pw_vals[] = {&root_dn_pw_bval, NULL};
+ result = slapi_pw_find_sv(root_dn_pw_vals, cred);
+ value_done(&root_dn_pw_bval);
+ }
+ slapi_ch_free_string(&root_pw);
+ return result;
+}
+
+/*
+ * This will work out which backend is needed, and then work from there.
+ * You must set the SLAPI_BIND_TARGET_SDN, and SLAPI_BIND_CREDENTIALS to
+ * the pblock for this to operate correctly.
+ *
+ * In the future, this will use the credentials and do mfa.
+ *
+ * If you get SLAPI_BIND_SUCCESS or SLAPI_BIND_ANONYMOUS you need to unlock
+ * the backend.
+ * All other results, it's already released.
+ */
+int
+pw_verify_be_dn(Slapi_PBlock *pb, Slapi_Entry **referral)
+{
+ int rc = 0;
+ Slapi_Backend *be = NULL;
+
+ if (slapi_mapping_tree_select(pb, &be, referral, NULL, 0) != LDAP_SUCCESS) {
+ return SLAPI_BIND_NO_BACKEND;
+ }
+
+ if (*referral) {
+ slapi_be_Unlock(be);
+ return SLAPI_BIND_REFERRAL;
+ }
+
+ slapi_pblock_set( pb, SLAPI_BACKEND, be );
+ /* Put the credentials into the pb */
+ if (be->be_bind == NULL) {
+ /* Selected backend doesn't support binds! */
+ slapi_be_Unlock(be);
+ return LDAP_OPERATIONS_ERROR;
+ }
+ slapi_pblock_set(pb, SLAPI_PLUGIN, be->be_database);
+ /* Make sure the result handlers are setup */
+ set_db_default_result_handlers(pb);
+ /* now take the dn, and check it */
+ rc = (*be->be_bind)(pb);
+ /* now attempt the bind. */
+ if (rc != SLAPI_BIND_SUCCESS && rc != SLAPI_BIND_ANONYMOUS) {
+ slapi_be_Unlock(be);
+ }
+ return rc;
+}
+
+int
+pw_verify_dn()
+{
+ return LDAP_OPERATIONS_ERROR;
+}
diff --git a/ldap/servers/slapd/pw_verify.h b/ldap/servers/slapd/pw_verify.h
new file mode 100644
index 0000000..fc34fd1
--- /dev/null
+++ b/ldap/servers/slapd/pw_verify.h
@@ -0,0 +1,15 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright (C) 2016 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * License: GPL (version 3 or any later version).
+ * See LICENSE for details.
+ * END COPYRIGHT BLOCK **/
+
+#ifndef _SLAPD_PW_VERIFY_H_
+#define _SLAPD_PW_VERIFY_H_
+
+int pw_verify_root_dn(const char *dn, const Slapi_Value *cred);
+int pw_verify_be_dn(Slapi_PBlock *pb, Slapi_Entry **referral);
+
+#endif /* _SLAPD_PW_VERIFY_H_ */
diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c
index 55d52bc..9e5d1f0 100644
--- a/ldap/servers/slapd/saslbind.c
+++ b/ldap/servers/slapd/saslbind.c
@@ -19,6 +19,7 @@
#include <slap.h>
#include <fe.h>
+#include <pw_verify.h>
#include <sasl.h>
#include <saslplug.h>
#include <unistd.h>
@@ -510,6 +511,88 @@ static int ids_sasl_getpluginpath(sasl_conn_t *conn, const char
**path)
return SASL_OK;
}
+static int ids_sasl_userdb_checkpass(sasl_conn_t *conn, void *context, const char *user,
const char *pass, unsigned passlen, struct propctx *propctx) {
+ /*
+ * Based on the mech
+ */
+ char *mech = NULL;
+ int isroot = 0;
+ int bind_result = SLAPI_BIND_FAIL;
+ struct berval cred;
+
+ sasl_getprop(conn, SASL_MECHNAME, (const void**)&mech);
+ if (mech == NULL) {
+ slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_userdb_checkpass",
"Unable to read SASL mechanism while verifying userdb password.\n");
+ goto out;
+ }
+
+ slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_userdb_checkpass", "Using
mech %s", mech);
+ if (passlen == 0) {
+ goto out;
+ }
+
+ if (strncasecmp(user, "dn: ", 4) == 0) {
+ isroot = slapi_dn_isroot(user+4);
+ } else {
+ /* The sasl request probably didn't come from us ... */
+ goto out;
+ }
+
+ /* Both types will need the creds. */
+ cred.bv_len = passlen;
+ cred.bv_val = (char *)pass;
+
+ if (isroot) {
+ Slapi_Value sv_cred;
+ /* Turn the creds into a Slapi Value */
+ slapi_value_init_berval(&sv_cred,&cred);
+ bind_result = pw_verify_root_dn(user+4, &sv_cred);
+ value_done(&sv_cred);
+ } else {
+ /* Convert the dn char str to an SDN */
+ ber_tag_t method = LDAP_AUTH_SIMPLE;
+ Slapi_Entry *referral = NULL;
+ Slapi_DN *sdn = slapi_sdn_new();
+ slapi_sdn_set_dn_byval(sdn, user+4);
+ /* Create a pblock */
+ Slapi_PBlock *pb = slapi_pblock_new();
+ /* We have to make a fake operation for the targetsdn spec. */
+ /* This is used within the be_dn function */
+ Slapi_Operation *op = operation_new(OP_FLAG_INTERNAL);
+ operation_set_type(op, SLAPI_OPERATION_BIND);
+ /* For mapping tree to work */
+ operation_set_target_spec(op, sdn);
+ slapi_pblock_set(pb, SLAPI_OPERATION, op);
+ /* Equivalent to SLAPI_BIND_TARGET_SDN
+ * Used by ldbm bind to know who to bind to.
+ */
+ slapi_pblock_set(pb, SLAPI_TARGET_SDN, (void *)sdn);
+ slapi_pblock_set(pb, SLAPI_BIND_CREDENTIALS, &cred);
+ /* To make the ldbm-bind code work, we pretend to be a simple auth right now. */
+ slapi_pblock_set(pb, SLAPI_BIND_METHOD, &method);
+ /* Feed it to pw_verify_be_dn */
+ bind_result = pw_verify_be_dn(pb, &referral);
+ /* Now check the result, and unlock be if needed. */
+ if (bind_result == SLAPI_BIND_SUCCESS || bind_result == SLAPI_BIND_ANONYMOUS) {
+ Slapi_Backend *be = NULL;
+ slapi_pblock_get(pb, SLAPI_BACKEND, &be);
+ slapi_be_Unlock(be);
+ } else if (bind_result == SLAPI_BIND_REFERRAL) {
+ /* If we have a referral do we ignore it for sasl? */
+ slapi_entry_free(referral);
+ }
+ /* Free everything */
+ slapi_sdn_free(&sdn);
+ slapi_pblock_destroy(pb);
+ }
+
+out:
+ if (bind_result == SLAPI_BIND_SUCCESS) {
+ return SASL_OK;
+ }
+ return SASL_FAIL;
+}
+
static sasl_callback_t ids_sasl_callbacks[] =
{
{
@@ -533,6 +616,11 @@ static sasl_callback_t ids_sasl_callbacks[] =
NULL
},
{
+ SASL_CB_SERVER_USERDB_CHECKPASS,
+ (IFP) ids_sasl_userdb_checkpass,
+ NULL
+ },
+ {
SASL_CB_LIST_END,
(IFP) NULL,
NULL
diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h
index 7c087e6..b223f65 100644
--- a/ldap/servers/slapd/slapi-plugin.h
+++ b/ldap/servers/slapd/slapi-plugin.h
@@ -468,8 +468,11 @@ NSPR_API(PRUint32) PR_fprintf(struct PRFileDesc* fd, const char *fmt,
...)
* return codes used by BIND functions
*/
#define SLAPI_BIND_SUCCESS 0 /* front end will send result */
+ /* 1 is reserved */
#define SLAPI_BIND_FAIL 2 /* back end should send result */
-#define SLAPI_BIND_ANONYMOUS 3 /* front end will send result */
+#define SLAPI_BIND_ANONYMOUS 3 /* front end will send result */
+#define SLAPI_BIND_REFERRAL 4 /* caller should send result */
+#define SLAPI_BIND_NO_BACKEND 5 /* caller should send result */
/* commonly used attributes names */