summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2013-07-31 14:40:12 +0300
committerNalin Dahyabhai <nalin@dahyabhai.net>2013-08-07 11:34:54 -0400
commit7f5516a2416e947c8950d2d7433233ed50f777d7 (patch)
tree29a32f2c63d4471db5822b3b66cc37184078b5dc
parenteb2613bd20ede10a567b263c3dfa843ee1578e35 (diff)
downloadslapi-nis-7f5516a2416e947c8950d2d7433233ed50f777d7.tar.gz
slapi-nis-7f5516a2416e947c8950d2d7433233ed50f777d7.tar.xz
slapi-nis-7f5516a2416e947c8950d2d7433233ed50f777d7.zip
back-sch.c: authenticate users through PAM system-auth service
Since trusted domain users do not exist in the LDAP tree, their authentication is handed over to PAM stack with the hope that PAM is set up properly to authenticate them. Additionally, this patch completely refactors authentication for the original DNs that *are* located in the LDAP tree. Previous way to handle it was through referrals being sent back. However, this method does not work at all. Instead, we set SLAPI_BIND_TARGET_DN to the entry's original DN and hand over pre-bind processing to other directory server's plugins. If slapi-nis set up with a higher precedence to them, authentication will be handled by others.
-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;
}