summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/back-sch.c144
1 files changed, 126 insertions, 18 deletions
diff --git a/src/back-sch.c b/src/back-sch.c
index 2bdf631..9d63c6b 100644
--- a/src/back-sch.c
+++ b/src/back-sch.c
@@ -1390,6 +1390,8 @@ struct backend_locate_cbdata {
Slapi_DN *target_dn;
struct backend_entry_data *entry_data;
+ const char *entry_group;
+ const char *entry_set;
};
/* Check if the target DN is an entry in this container's set of entries. If
* it is, pull the entry's data out and save it. */
@@ -1420,6 +1422,8 @@ backend_locate_cb(const char *group, const char *set, bool_t flag,
&original_dn, (void **) &entry_data)) {
if (entry_data != NULL) {
cbdata->entry_data = entry_data;
+ cbdata->entry_group = group;
+ cbdata->entry_set = set;
}
}
slapi_rdn_free(&rdn);
@@ -1428,7 +1432,7 @@ backend_locate_cb(const char *group, const char *set, bool_t flag,
return TRUE;
}
static void
-backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data)
+backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data, const char **group, const char**set)
{
struct backend_locate_cbdata cbdata;
@@ -1441,8 +1445,12 @@ backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data)
slapi_pblock_get(pb, SLAPI_TARGET_DN, &cbdata.target);
cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target);
cbdata.entry_data = NULL;
+ cbdata.entry_group = NULL;
+ cbdata.entry_set = NULL;
map_data_foreach_map(cbdata.state, NULL, backend_locate_cb, &cbdata);
*data = cbdata.entry_data;
+ *group = cbdata.entry_group;
+ *set = cbdata.entry_set;
slapi_sdn_free(&cbdata.target_dn);
}
@@ -1530,37 +1538,139 @@ backend_betxn_pre_write_cb(Slapi_PBlock *pb)
return state->use_be_txns ? backend_write_cb(pb) : 0;
}
+#ifdef USE_PAM
+static int
+backend_bind_cb_pam(Slapi_PBlock *pb, const char *username, char *ndn)
+{
+ int ret = 0;
+ LDAPControl **reqctrls = NULL;
+ struct plugin_state *state;
+ char *conn_dn = NULL;
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
+
+ /* PAM API is thread-unsafe, we need to lock exclusively */
+ wrap_rwlock_wrlock(state->pam_lock);
+ ret = backend_sch_do_pam_auth(pb, username);
+ wrap_rwlock_unlock(state->pam_lock);
+
+ if (ret == LDAP_SUCCESS) {
+ /*
+ * If bind succeeded, change authentication information associated
+ * with this connection.
+ */
+ conn_dn = slapi_ch_strdup(ndn);
+ if (conn_dn == NULL) {
+ ret = LDAP_OPERATIONS_ERROR;
+ } else {
+ if ((slapi_pblock_set(pb, SLAPI_CONN_DN, (void*) conn_dn) != 0) ||
+ (slapi_pblock_set(pb, SLAPI_CONN_AUTHMETHOD, SLAPD_AUTH_SIMPLE) != 0)) {
+ ret = LDAP_OPERATIONS_ERROR;
+ slapi_ch_free_string(&conn_dn);
+ } else {
+ slapi_pblock_get(pb, SLAPI_REQCONTROLS, &reqctrls);
+ if (slapi_control_present(reqctrls, LDAP_CONTROL_AUTH_REQUEST, NULL, NULL)) {
+ slapi_add_auth_response_control(pb, conn_dn);
+ }
+ }
+ }
+
+ /* we are handling the result */
+ slapi_send_ldap_result(pb, ret, NULL, NULL, 0, NULL);
+ }
+ return ret;
+}
+#else
+static int
+backend_bind_cb_pam(Slapi_PBlock *pb, const char *username, char *ndn)
+{
+ slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
+ return LDAP_INVALID_CREDENTIALS;
+}
+#endif
+
static int
backend_bind_cb(Slapi_PBlock *pb)
{
struct backend_entry_data *data;
+ struct plugin_state *state;
int ret;
- struct berval ref;
- struct berval *urls[] = {&ref, NULL};
- const char *ndn;
+ Slapi_DN *sdn = NULL;
+ char *ndn;
+ char *is_nsswitch_origin = NULL;
+ char *username = NULL;
+ char *group = NULL;
+ const char *entry_group = NULL;
+ char *set = NULL;
+ const char *entry_set = NULL;
if (wrap_get_call_level() > 0) {
return 0;
}
+ slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &state);
+ /* The code below handles three separate facts:
+ * 1. For NSSWITCH-discovered users PAM is responsible for authentication.
+ * We want to run PAM auth without any slapi-nis lock taken to avoid
+ * issues when Kerberos KDC is backed by the same LDAP store and
+ * changes in a Kerberos principal would cause cascading effect on
+ * some of entries belonging to a slapi-nis map cache.
+ * 2. If bind target DN exists in LDAP store, its map cache entry
+ * will have orginal entry DN recorded. Enforcing SLAPI_BIND_TARGET_SDN
+ * to it will force other plugins to handle authentication request against
+ * the original because slapi-nis' map cache entry doesn't have paswords
+ * recorded. To make it working, slapi-nis should be registered with higher
+ * plugin ordering priority than other plugins.
+ * 3. If bind target DN is not found in the map cache, bind request is rejected.
+ * */
wrap_inc_call_level();
map_rdlock();
- backend_locate(pb, &data);
+ backend_locate(pb, &data, &entry_group, &entry_set);
if (data != NULL) {
- ndn = slapi_sdn_get_ndn(data->original_entry_dn);
- ref.bv_len = strlen("ldap:///") + strlen(ndn);
- ref.bv_val = malloc(ref.bv_len + 1);
- if (ref.bv_val != NULL) {
- sprintf(ref.bv_val, "ldap:///%s", ndn);
- slapi_send_ldap_result(pb, LDAP_REFERRAL,
- NULL, NULL, 0, urls);
- free(ref.bv_val);
+ is_nsswitch_origin = slapi_entry_attr_get_charptr(data->e, "schema-compat-origin");
+ ndn = slapi_ch_strdup(slapi_sdn_get_ndn(data->original_entry_dn));
+ username = slapi_entry_attr_get_charptr(data->e, "uid");
+ group = slapi_ch_strdup(entry_group);
+ set = slapi_ch_strdup(entry_set);
+ map_unlock();
+ wrap_dec_call_level();
+
+ /* If user comes from NSSWITCH, it will get authentication handled by PAM. */
+ if ((is_nsswitch_origin != NULL) &&
+ (strcasecmp(is_nsswitch_origin, "nsswitch") == 0)) {
+ ret = backend_bind_cb_pam(pb, username, ndn);
+ if (ret == LDAP_NO_SUCH_OBJECT) {
+ /* Evict the entry from the cache */
+ if ((group != NULL) && (set != NULL)) {
+ map_data_unset_entry(state, group, set, ndn);
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN,
+ state->plugin_desc->spd_id,
+ "Error: unable to locate group and set "
+ " when removing cached entry %s\n",
+ ndn);
+ }
+ }
+ slapi_ch_free_string(&ndn);
+ ret = -1;
} else {
- slapi_send_ldap_result(pb, LDAP_BUSY,
- NULL, NULL, 0, NULL);
+ /* Otherwise force rewrite of the SLAPI_BIND_TARGET_SDN
+ * and let other plugins to handle it.
+ * slapi-nis should have plugin ordering set below standard 50 to succeed */
+ slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn);
+ if (sdn != NULL) {
+ slapi_sdn_free(&sdn);
+ }
+ sdn = slapi_sdn_new_dn_byref(ndn);
+ slapi_pblock_set(pb, SLAPI_BIND_TARGET_SDN, (void*) sdn);
+ ret = 0;
}
- ret = -1;
+ slapi_ch_free_string(&set);
+ slapi_ch_free_string(&group);
+ slapi_ch_free_string(&username);
+ slapi_ch_free_string(&is_nsswitch_origin);
} else {
+ map_unlock();
+ wrap_dec_call_level();
if (backend_check_scope_pb(pb)) {
slapi_send_ldap_result(pb, LDAP_INVALID_CREDENTIALS,
NULL, NULL, 0, NULL);
@@ -1569,8 +1679,6 @@ backend_bind_cb(Slapi_PBlock *pb)
ret = 0;
}
}
- map_unlock();
- wrap_dec_call_level();
return ret;
}