diff options
-rw-r--r-- | src/back-sch.c | 144 |
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; } |