summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac14
-rw-r--r--doc/ipa/sch-ipa.txt93
-rw-r--r--src/Makefile.am4
-rw-r--r--src/back-sch-idview.c376
-rw-r--r--src/back-sch-nss.c63
-rw-r--r--src/back-sch.c62
-rw-r--r--src/back-sch.h35
7 files changed, 618 insertions, 29 deletions
diff --git a/configure.ac b/configure.ac
index 84b84d1..71dbdc7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -383,6 +383,20 @@ if test "x$use_nsswitch" != xno ; then
AC_DEFINE(USE_NSSWITCH,1,[Use nsswitch API to lookup users and groups not found in the LDAP tree])
fi
+use_idviews=true
+AC_ARG_WITH(idviews,
+ AS_HELP_STRING([--with-idviews], [Use FreeIPA ID views to override POSIX IDs of users and groups]),
+ use_idviews=$withval,use_idviews=yes)
+if test "x$use_idviews" = xyes ; then
+ AC_MSG_RESULT([FreeIPA ID views support is enabled])
+ AC_DEFINE(USE_IPA_IDVIEWS,1,[Use FreeIPA ID views to override POSIX attributes of users and groups per view.])
+ AC_DEFINE(IPA_IDVIEWS_ATTR_ANCHORUUID, ["ipaAnchorUUID"],[FreeIPA attr unique pointer for id overrides])
+ AC_DEFINE(IPA_IDVIEWS_ATTR_ORIGINALUID, ["ipaOriginalUid"],[FreeIPA attr original uid value for user id overrides])
+else
+ AC_MSG_RESULT([FreeIPA ID views support is disabled])
+fi
+AM_CONDITIONAL([USE_IPA_IDVIEWS], [test "x$use_idviews" != xno])
+
mylibdir=`eval echo "$libdir" | sed "s,NONE,${ac_default_prefix},g"`
mylibdir=`eval echo "$mylibdir" | sed "s,NONE,${ac_prefix},g"`
case "$server" in
diff --git a/doc/ipa/sch-ipa.txt b/doc/ipa/sch-ipa.txt
index b5a585b..f560580 100644
--- a/doc/ipa/sch-ipa.txt
+++ b/doc/ipa/sch-ipa.txt
@@ -87,3 +87,96 @@ on IPA masters.
As 'system-auth' PAM service is not used directly by any other application, it
is safe to use it for trusted domain users via compatibility path.
+
+== Support for ID views ==
+
+When FreeIPA 4.1 is in use, Schema compatibility plugin can be configured to
+override POSIX attributes according to an identity view (ID View) which
+contains overrides for users and groups.
+
+The overrides are managed by FreeIPA separately for users and groups:
+
+* management of ID views:
+ ipa idview-add 'my view'
+
+* management of an override for a user:
+ ipa idoverrideuser-add 'my view' auser --login=foo --shell=/bin/ksh \
+ --homedir=/home/foo --uid=12345
+
+* management of an override for a group:
+ ipa idoverridegroup-add 'my view' agroup --group-name=bgroup --gid=54321
+
+FreeIPA transparently supports overrides for users and groups from trusted AD
+domains. This means that for trusted domains, IPA doesn't require to place
+POSIX attributes in Active Directory. Instead, a global ID view named 'Default
+Trust View' is used to contain POSIX attributes for existing AD users.
+Additionally, specialized ID views can be added on top of the 'Default Trust
+View' and then assigned to specific hosts.
+
+Schema compatibility plugin does support ID overrides from a single view. The
+feature is designed to allow host-specific ID view, where the view is specified
+through the search base.
+
+For native IPA users default POSIX attributes are stored natively, thus only a
+host-specific ID view is applied, if any. For trusted AD users 'Default Trust
+View' ID view is applied automatically by SSSD running on the IPA master, thus
+Schema compatibility plugin only applies a host-specific ID view, if specified.
+
+In FreeIPA Schema compatibility is configured to serve entries through the
+host-specific ID view with base DN of cn=<ID view>,cn=views,cn=compat,$SUFFIX.
+
+=== ID views implementation ===
+Detailed design of ID views in FreeIPA can be seen here:
+http://www.freeipa.org/page/V4/Migrating_existing_environments_to_Trust
+
+In Schema compatibility plugin support for ID views is done on top of existing
+map cache. It is expected that there are less overrides than non-overridden
+entries for IPA users and groups. For trusted AD users POSIX attributes from
+'Default Trust View' are applied by SSSD on IPA master. Thus, if there are no
+host-specific overrides, trusted AD users treated by Schema compatibility
+plugin as before -- as entries which content comes from nssswitch API.
+
+This approach allows to only keep original entry in the memory and apply
+host-specific override only at the time when entry with explicitly requested ID
+view is returned as part of a search result.
+
+In order to map original entry to an override, FreeIPA configuration for Schema
+compatibility plugin adds ipaAnchorUUID attribute and ipaOverrideTarget object
+class to every generated entry. ipaAnchorUUID is based on ipaUniqueID for IPA
+objects and on SID for trusted AD objects:
+
+* ipaAnchorUUID=:IPA:<domain>:<ipaUniqueID> for IPA object (user, group)_
+
+* ipaAnchorUUID=:SID:<SID> for trusted AD user or group
+
+For ID overrides FreeIPA maintains ipaAnchorUUID with the same value so that an
+override can be found by simple matching of the ipaAnchorUUID attribute's
+value. FreeIPA also stores original uid value for user objects in ID override
+as ipaOriginalUid attribute, to allow mapping back memberUid values for groups.
+
+When a query request comes, the view in the base DN is detected and remembered.
+Base DN is rewritten to exclude the cn=<ID view>,cn=views so that a normal
+search can be performed against cached entries. Additionally, search filter is
+analyzed to replace references to rewritten uid (for user) and cn (for group)
+attributes by references to the original objects. The references come from the
+ID view overrides, if they exist.
+
+Once search results are gathered for the map, they are processed in order to
+apply an override. For users entry attributes overridden with the values from
+an override. For groups additional processing is performed on values of
+memberUid attribute.
+
+As opposed to member attribute, memberUid attribute contains only values of uid
+attribute of the original member entry. Given that an ID override may redefine
+uid value, corresponding memberUid value of a group needs to be rewritten to
+include redefined uid value. In order to do that, original memberUid value is
+compared with ipaOriginalUid attribute's value to find an override
+corresponding to the original user object. If such override is detected, memberUid
+value is replaced by the uid value of the override.
+
+When attributes of the entry are processed and optionally amended with overridden
+values, DN of the entry is rewritten as well, to reflect the fact that entry is
+served through the view.
+
+For all returned entries ipaAnchorUUID attribute and ipaOverrideTarget objectclass
+are removed. Resulting entry is sent to the client.
diff --git a/src/Makefile.am b/src/Makefile.am
index e4fe1a9..6f4926e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -78,6 +78,10 @@ schemacompat_plugin_la_SOURCES += back-sch-pam.c
schemacompat_plugin_la_LIBADD += $(PAM_LIBS)
endif
+if USE_IPA_IDVIEWS
+schemacompat_plugin_la_SOURCES += back-sch-idview.c
+endif
+
noinst_LTLIBRARIES = dummy-nis-plugin.la
dummy_nis_plugin_la_SOURCES = \
disp-nis.c \
diff --git a/src/back-sch-idview.c b/src/back-sch-idview.c
new file mode 100644
index 0000000..9f4025a
--- /dev/null
+++ b/src/back-sch-idview.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2014 Red Hat, Inc.
+ *
+ * This Program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This Program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this Program; if not, write to the
+ *
+ * Free Software Foundation, Inc.
+ * 59 Temple Place, Suite 330
+ * Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+
+#ifdef HAVE_DIRSRV_SLAPI_PLUGIN_H
+#include <nspr.h>
+#include <nss.h>
+#include <dirsrv/slapi-plugin.h>
+#else
+#include <slapi-plugin.h>
+#endif
+
+#include <rpc/xdr.h>
+#include "../yp/yp.h"
+
+#ifdef HAVE_TCPD_H
+#include <tcpd.h>
+#endif
+
+#include "backend.h"
+#include "back-shr.h"
+#include "format.h"
+#include "plugin.h"
+#include "map.h"
+#include "back-sch.h"
+
+void
+idview_get_overrides(struct backend_search_cbdata *cbdata)
+{
+ char *dn = NULL;
+ int ret = 0;
+ const Slapi_DN *suffix = NULL;
+ Slapi_PBlock *pb;
+
+ if (cbdata->idview == NULL)
+ return;
+
+ pb = wrap_pblock_new(cbdata->pb);
+ if (pb == NULL)
+ return;
+
+ wrap_inc_call_level();
+
+ suffix = slapi_get_suffix_by_dn(cbdata->target_dn);
+ dn = slapi_ch_smprintf("cn=%s,cn=views,cn=accounts,%s", cbdata->idview, slapi_sdn_get_dn(suffix));
+ /* Fetch all attributes; there is a bug in 389-ds: it gives out all attributes for the entry anyway
+ * when search returns Slapi_Entry* objects. Instead, we'll do removal later */
+ slapi_search_internal_set_pb(pb, dn, LDAP_SCOPE_SUBTREE,
+ "(objectclass=ipaOverrideAnchor)", NULL, 0,
+ NULL, NULL, cbdata->state->plugin_identity, 0);
+ ret = slapi_search_internal_pb(pb);
+ slapi_ch_free_string(&dn);
+
+ if (ret == 0) {
+ /* Steal search result entries to avoid re-allocating them */
+ slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &(cbdata->overrides));
+ slapi_pblock_set(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, NULL);
+ }
+
+ wrap_dec_call_level();
+ slapi_pblock_destroy(pb);
+}
+
+void
+idview_free_overrides(struct backend_search_cbdata *cbdata)
+{
+ int i = 0;
+ if (cbdata->overrides != NULL) {
+ for(i=0; cbdata->overrides[i] != NULL; i++) {
+ slapi_entry_free(cbdata->overrides[i]);
+ }
+ slapi_ch_free((void**)&(cbdata->overrides));
+ }
+}
+
+void
+idview_process_overrides(struct backend_search_cbdata *cbdata,
+ const char *key, const char *map, const char *domain,
+ Slapi_Entry *entry)
+{
+#define VIEW_TEMPLATE_KEY_MAP_DOMAIN 0
+#define VIEW_TEMPLATE_KEY_MAP_DOMAIN_NEWKEY 3
+#define VIEW_TEMPLATE_MAP_DOMAIN 1
+#define VIEW_TEMPLATE_DOMAIN 2
+ /* After view was applied, entry's DN needs to reflect the view */
+ const char *dn_template[] = {"%s,%s,cn=%s,cn=views,%s", /* an entry for user or group */
+ "%s,cn=%s,cn=views,%s", /* an entry for a map (container for users or groups) */
+ "cn=%s,cn=views,%s", /* an entry is a base of the compat tree */
+ "%s=%s,%s,cn=%s,cn=views,%s", /* an entry for user or group which RDN was overridden with new value */
+ };
+ const char *filterout_attrs[] = {"objectclass", "creatorsname", "modifiersname",
+ "createtimestamp", "modifytimestamp", "parentid",
+ "entryusn", "ipaanchoruuid", NULL };
+ char *new_dn = NULL, *new_key = NULL, *sep = NULL, *new_val = NULL;
+ Slapi_Entry *override_entry = NULL;
+ Slapi_Attr *anchor = NULL, *id_attr = NULL;
+ Slapi_Value *anchor_value = NULL, *id_value = NULL;
+ int i, result, dn_template_id;
+
+ if (cbdata->overrides == NULL) {
+ /* Only retrieve overrides for the view first time when neccessary */
+ idview_get_overrides(cbdata);
+ if (cbdata->overrides == NULL)
+ return;
+ }
+
+ /* 1. See if the entry has ipaAnchorUUID and selected idview has an override for it */
+ /* The code below intentionally uses Slapi_Value instead of comparing string values to
+ * avoid allocating additional memory */
+ result = slapi_entry_attr_find(entry, IPA_IDVIEWS_ATTR_ANCHORUUID, &anchor);
+ if ((result == 0) && (anchor != NULL) && (cbdata->overrides != NULL)) {
+ result = slapi_attr_first_value(anchor, &anchor_value);
+ for(i = 0; cbdata->overrides[i] != NULL; i++) {
+ result = slapi_entry_attr_find(cbdata->overrides[i], IPA_IDVIEWS_ATTR_ANCHORUUID, &id_attr);
+ if ((result == 0) && (id_attr != NULL)) {
+ result = slapi_attr_first_value(id_attr, &id_value);
+ result = slapi_value_compare(id_attr, anchor_value, id_value);
+ if (result == 0) {
+ override_entry = cbdata->overrides[i];
+ break;
+ }
+ }
+ }
+ }
+
+ /* 2. If there is indeed an override, replace attribute values except for the ones that should be ignored */
+ if (override_entry != NULL) {
+ Slapi_Attr *entry_attr = NULL, *override_attr = NULL;
+ result = slapi_entry_first_attr(override_entry, &override_attr);
+ while (result == 0) {
+ Slapi_ValueSet *override_valueset = NULL;
+ char *override_type = NULL;
+
+ /* Filter out override attributes that we don't care about */
+ result = slapi_attr_get_type(override_attr, &override_type);
+ for (i = 0; filterout_attrs[i] != NULL; i++) {
+ if (strcasecmp(override_type, filterout_attrs[i]) == 0) {
+ break;
+ }
+ }
+
+ if (filterout_attrs[i] == NULL) {
+ /* Replace the attribute's value with the override */
+ result = slapi_entry_attr_find(entry, override_type, &entry_attr);
+ if (result == 0) {
+ result = slapi_attr_get_valueset(override_attr, &override_valueset);
+ result = slapi_attr_set_valueset(entry_attr, override_valueset);
+ }
+ }
+ result = slapi_entry_next_attr(override_entry, override_attr, &override_attr);
+ }
+ }
+
+ /* 3. If entry has memberUid, we need to replace memberUid values too, if they were overridden */
+ result = slapi_entry_attr_find(entry, "memberUid", &anchor);
+ if ((result == 0) && (anchor != NULL) && (cbdata->overrides != NULL)) {
+ int value_idx = 0;
+ Slapi_ValueSet *new_valueset = slapi_valueset_new();
+
+ if (new_valueset != NULL) {
+ /* For each memberUid value, find an override with ipaOriginalUid attribute of the same value */
+ value_idx = slapi_attr_first_value(anchor, &anchor_value);
+ while (value_idx != -1) {
+ bool_t value_found = FALSE;
+ for(i = 0; cbdata->overrides[i] != NULL; i++) {
+ result = slapi_entry_attr_find(cbdata->overrides[i], IPA_IDVIEWS_ATTR_ORIGINALUID, &id_attr);
+ if ((result == 0) && (id_attr != NULL)) {
+ result = slapi_attr_first_value(id_attr, &id_value);
+ result = slapi_value_compare(id_attr, anchor_value, id_value);
+ if (result == 0) {
+ /* If there is an override with ipaOriginalUid: <memberUid value>, use its 'uid' value to override */
+ result = slapi_entry_attr_find(cbdata->overrides[i], "uid", &id_attr);
+ if ((result == 0) && (id_attr != NULL)) {
+ result = slapi_attr_first_value(id_attr, &id_value);
+ if (result == 0) {
+ /* finally: we have an override with ipaOriginalUid: <memberUid value> _and_
+ * this override is changing the 'uid' attribute so we have something to replace */
+ slapi_valueset_add_value(new_valueset, id_value);
+ value_found = TRUE;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (value_found == FALSE) {
+ slapi_valueset_add_value(new_valueset, anchor_value);
+ }
+ value_idx = slapi_attr_next_value(anchor, value_idx, &anchor_value);
+ }
+
+ slapi_attr_set_valueset(anchor, new_valueset);
+ slapi_valueset_free(new_valueset);
+ }
+ }
+
+ /* 4. Even if there were no overrides, since we are serving throught the view, replace DN value */
+ dn_template_id = (key == NULL ? 1 : 0) + (map == NULL ? 1 : 0);
+ switch (dn_template_id) {
+ case VIEW_TEMPLATE_KEY_MAP_DOMAIN:
+ /* update RDN with proper value from the entry after overrides were applied */
+ sep = strchr(key, '=');
+ if (sep != NULL) {
+ sep[0] = '\0';
+ new_val = slapi_entry_attr_get_charptr(entry, key);
+ new_dn = slapi_ch_smprintf(dn_template[VIEW_TEMPLATE_KEY_MAP_DOMAIN_NEWKEY], key, new_val, map, cbdata->idview, domain);
+ slapi_ch_free_string(&new_val);
+ sep[0] = '=';
+ } else {
+ new_dn = slapi_ch_smprintf(dn_template[dn_template_id], key, map, cbdata->idview, domain);
+ }
+ break;
+ case VIEW_TEMPLATE_MAP_DOMAIN:
+ new_dn = slapi_ch_smprintf(dn_template[dn_template_id], map, cbdata->idview, domain);
+ break;
+ case VIEW_TEMPLATE_DOMAIN:
+ new_dn = slapi_ch_smprintf(dn_template[dn_template_id], cbdata->idview, domain);
+ break;
+ };
+ slapi_entry_set_dn(entry, new_dn);
+}
+
+void
+idview_replace_target_dn(char **target, char **idview)
+{
+ char *idview_p = NULL;
+ char *cnviews = NULL;
+ char *new_target = NULL;
+
+ cnviews = strstr(*target, ",cn=views,");
+ if (cnviews != NULL && cnviews != *target) {
+ cnviews[0] = '\0';
+ idview_p = strrchr(*target, ',');
+ if (idview_p == NULL) {
+ idview_p = *target;
+ } else {
+ idview_p++;
+ }
+ if (strstr(idview_p, "cn=") != idview_p) {
+ cnviews[0] = ',';
+ return;
+ }
+ *idview = slapi_ch_strdup(&idview_p[3]);
+ if (idview_p != *target) {
+ idview_p[0] = '\0';
+ new_target = slapi_ch_smprintf("%s%s", *target, cnviews+10);
+ idview_p--;
+ idview_p[0] = ',';
+ } else {
+ new_target = slapi_ch_smprintf("%s", cnviews+10);
+ }
+ cnviews[0] = ',';
+ *target = new_target;
+ }
+}
+
+struct filter_cb_data {
+ struct backend_search_cbdata *cbdata;
+ Slapi_Filter *filter;
+};
+
+static void
+idview_process_filter_cb(Slapi_Filter *filter, const char *filter_type, struct berval *bval, struct backend_search_filter_config *config)
+{
+ int res, i;
+ Slapi_Value *filter_val, *value, *anchor_val;
+ Slapi_Attr *anchor, *attr = NULL;
+ struct backend_search_cbdata *cbdata = (struct backend_search_cbdata *) config->callback_data;
+
+ if (cbdata == NULL || cbdata->idview == NULL) {
+ return;
+ }
+
+ if (filter_type == NULL || config->name == NULL) {
+ return;
+ }
+
+ if (cbdata->overrides == NULL) {
+ /* Only retrieve overrides for the view first time when neccessary */
+ idview_get_overrides(cbdata);
+ }
+
+ if (cbdata->overrides == NULL) {
+ return;
+ }
+
+ filter_val = slapi_value_new_berval(bval);
+
+ /* If filter contains an attribute name which is overridden in the view and filter value
+ * corresponds to the override, replace the filter by (ipaAnchorUUID=...) from the override
+ * to point to the original because otherwise an entry will not be found in the slapi-nis map */
+ for(i=0; cbdata->overrides[i] != NULL; i++) {
+ res = slapi_entry_attr_find(cbdata->overrides[i], filter_type, &attr);
+ if ((res == 0) && (attr != NULL)) {
+ res = slapi_attr_first_value(attr, &value);
+ res = slapi_value_compare(attr, value, filter_val);
+ if (res == 0) {
+ res = slapi_entry_attr_find(cbdata->overrides[i], "ipaAnchorUUID", &anchor);
+ if (res == 0) {
+ res = slapi_attr_first_value(anchor, &anchor_val);
+ slapi_filter_changetype(filter, "ipaanchoruuid");
+ slapi_ber_bvdone(bval);
+ slapi_ber_bvcpy(bval, slapi_value_get_berval(anchor_val));
+ config->override_found = TRUE;
+ break;
+ }
+
+ }
+ }
+ }
+
+ slapi_value_free(&filter_val);
+
+}
+
+/* Traverse through the filter and replace overridden attribute/value pairs with references to the original
+ * entries. This allows to properly handle overrides of uid and cn attributes where searches look like
+ * (&(objectclass=posixAccount)(uid=foobar)) -- if uid=foobar is part of an override for uid=admin, we need
+ * to point back to uid=admin to be able to find original entry in the slapi-nis cache.
+ *
+ * Note that in reality we don't use original value of the uid/cn attribue. Instead, we use ipaAnchorUUID
+ * to refer to the original entry. */
+void
+idview_replace_filter(struct backend_search_cbdata *cbdata)
+{
+ struct filter_cb_data filter_data = {cbdata, NULL};
+ struct backend_search_filter_config config =
+ {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL, NULL, NULL};
+ int res = 0;
+
+
+ config.callback = idview_process_filter_cb;
+ config.callback_data = cbdata;
+
+ res = backend_analyze_search_filter(cbdata->filter, &config);
+
+ if (filter_data.filter != NULL) {
+ Slapi_Filter *f = cbdata->filter;
+ cbdata->filter = filter_data.filter;
+ slapi_filter_free(f, 1);
+ }
+
+ if (config.name != NULL) {
+ slapi_ch_free_string(&config.name);
+ }
+
+}
diff --git a/src/back-sch-nss.c b/src/back-sch-nss.c
index f192bb9..db57cb8 100644
--- a/src/back-sch-nss.c
+++ b/src/back-sch-nss.c
@@ -52,17 +52,6 @@
#include "back-sch.h"
#include "format.h"
-struct backend_search_filter_config {
- bool_t search_user;
- bool_t search_group;
- bool_t search_uid;
- bool_t search_gid;
- bool_t search_members;
- bool_t name_set;
- bool_t wrong_search;
- char *name;
-};
-
static int
bvstrcasecmp(const struct berval *bval, const char *s)
{
@@ -127,6 +116,10 @@ backend_search_filter_has_cn_uid(Slapi_Filter *filter, void *arg)
}
}
+ if (config->callback != NULL) {
+ config->callback(filter, filter_type, bval, config);
+ }
+
if ((config->search_uid ||
config->search_gid ||
config->search_user ||
@@ -211,8 +204,6 @@ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
slapi_entry_add_string(entry,
"objectClass", "posixAccount");
slapi_entry_add_string(entry,
- "objectClass", "extensibleObject");
- slapi_entry_add_string(entry,
"uid", pwd->pw_name);
slapi_entry_attr_set_uint(entry,
"uidNumber", pwd->pw_uid);
@@ -240,7 +231,20 @@ backend_make_user_entry_from_nsswitch_passwd(struct passwd *pwd,
#ifdef HAVE_SSS_NSS_IDMAP
rc = sss_nss_getsidbyid(pwd->pw_uid, &sid_str, &id_type);
if ((rc == 0) && (sid_str != NULL)) {
+#ifdef USE_IPA_IDVIEWS
+ char *anchor = NULL;
+ /* For overrides of AD users to work correctly, we need to generate
+ * ipaAnchorUUID value so that idviews can be properly searched for the override */
+ anchor = slapi_ch_smprintf(":SID:%s", sid_str);
+ if (anchor != NULL) {
+ slapi_entry_add_string(entry, "objectClass", "ipaOverrideTarget");
+ slapi_entry_add_string(entry, "ipaAnchorUUID", anchor);
+ slapi_ch_free_string(&anchor);
+ }
+#else
+ slapi_entry_add_string(entry, "objectClass", "extensibleObject");
slapi_entry_add_string(entry, "ipaNTSecurityIdentifier", sid_str);
+#endif
free(sid_str);
}
#endif
@@ -335,8 +339,6 @@ backend_make_group_entry_from_nsswitch_group(struct group *grp,
slapi_entry_add_string(entry,
"objectClass", "posixGroup");
slapi_entry_add_string(entry,
- "objectClass", "extensibleObject");
- slapi_entry_add_string(entry,
"cn", grp->gr_name);
slapi_entry_attr_set_uint(entry,
"gidNumber", grp->gr_gid);
@@ -352,7 +354,20 @@ backend_make_group_entry_from_nsswitch_group(struct group *grp,
#ifdef HAVE_SSS_NSS_IDMAP
rc = sss_nss_getsidbyid(grp->gr_gid, &sid_str, &id_type);
if ((rc == 0) && (sid_str != NULL)) {
+#ifdef USE_IPA_IDVIEWS
+ char *anchor = NULL;
+ /* For overrides of AD users to work correctly, we need to generate
+ * ipaAnchorUUID value so that idviews can be properly searched for the override */
+ anchor = slapi_ch_smprintf(":SID:%s", sid_str);
+ if (anchor != NULL) {
+ slapi_entry_add_string(entry, "objectClass", "ipaOverrideTarget");
+ slapi_entry_add_string(entry, "ipaAnchorUUID", anchor);
+ slapi_ch_free_string(&anchor);
+ }
+#else
+ slapi_entry_add_string(entry, "objectClass", "extensibleObject");
slapi_entry_add_string(entry, "ipaNTSecurityIdentifier", sid_str);
+#endif
free(sid_str);
}
#endif
@@ -558,6 +573,16 @@ nsswitch_type_to_name(enum sch_search_nsswitch_t type)
return "(unknown)";
}
+int
+backend_analyze_search_filter(Slapi_Filter *filter, struct backend_search_filter_config *config)
+{
+ int result, rc;
+ result = slapi_filter_apply(filter,
+ backend_search_filter_has_cn_uid,
+ config, &rc);
+ return (result != SLAPI_FILTER_SCAN_STOP) ? 1 : 0;
+}
+
/* Check if the filter is one (like uid=<value>) that should trigger an
* nsswitch lookup, and if it is, make a note that we should perform such a
* lookup. */
@@ -566,15 +591,15 @@ backend_search_nsswitch(struct backend_set_data *set_data,
struct backend_search_cbdata *cbdata)
{
int result, rc;
- struct backend_search_filter_config config = {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL};
+ struct backend_search_filter_config config =
+ {FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, NULL, NULL, NULL};
struct backend_staged_search *staged = NULL;
char *idptr = NULL;
unsigned long id;
/* First, we search the filter to see if it includes a cn|uid=<value> test. */
- result = slapi_filter_apply(cbdata->filter,
- backend_search_filter_has_cn_uid, &config, &rc);
- if ((result != SLAPI_FILTER_SCAN_STOP)) {
+ result = backend_analyze_search_filter(cbdata->filter, &config);
+ if (result != 0) {
return;
}
diff --git a/src/back-sch.c b/src/back-sch.c
index 78f2627..1c388da 100644
--- a/src/back-sch.c
+++ b/src/back-sch.c
@@ -259,15 +259,6 @@ backend_set_config_read_config(struct plugin_state *state, Slapi_Entry *e,
free(nsswitch_min_id);
}
- if (ret.check_nsswitch != SCH_NSSWITCH_NONE) {
- /* If we're adding nsswitch-based entries to this map, make
- * sure that we copy the schema-compat-origin and SID
- * attributes, so that we can read the former during the BIND
- * callback. */
- backend_shr_add_strlist(&ret.attribute_format, "objectClass=%ifeq(\"%{ipaNTSecurityIdentifier}\",\"\",\"\",\"extensibleObject\")");
- backend_shr_add_strlist(&ret.attribute_format, "ipaNTSecurityIdentifier=%{ipaNTSecurityIdentifier}");
- }
-
*pret = backend_copy_set_config(&ret);
if (*pret == NULL) {
if (strlen(container) > 0) {
@@ -1005,6 +996,7 @@ backend_search_entry_cb(const char *domain, const char *map, bool_t secure,
void *backend_data, void *cb_data)
{
Slapi_DN *sdn;
+ Slapi_Entry *entry;
struct backend_search_cbdata *cbdata;
struct backend_entry_data *entry_data;
int result;
@@ -1043,9 +1035,25 @@ backend_search_entry_cb(const char *domain, const char *map, bool_t secure,
cbdata->state->plugin_desc->spd_id,
"search matched %s\n",
slapi_sdn_get_ndn(sdn));
- slapi_send_ldap_search_entry(cbdata->pb, entry_data->e, NULL,
+ entry = entry_data->e;
+#ifdef USE_IPA_IDVIEWS
+ if (cbdata->idview != NULL) {
+ entry = slapi_entry_dup(entry_data->e);
+ idview_process_overrides(cbdata, key, map, domain, entry);
+ }
+
+ if (slapi_entry_attr_exists(entry, IPA_IDVIEWS_ATTR_ANCHORUUID) == 1) {
+ slapi_entry_attr_delete(entry, IPA_IDVIEWS_ATTR_ANCHORUUID);
+ slapi_entry_delete_string(entry, "objectClass", "ipaOverrideTarget");
+ }
+#endif
+ slapi_send_ldap_search_entry(cbdata->pb, entry, NULL,
cbdata->attrs, cbdata->attrsonly);
cbdata->n_entries++;
+
+ if (entry != entry_data->e) {
+ slapi_entry_free(entry);
+ }
break;
}
@@ -1104,6 +1112,13 @@ backend_search_set_cb(const char *group, const char *set, bool_t flag,
slapi_log_error(SLAPI_LOG_PLUGIN,
cbdata->state->plugin_desc->spd_id,
"search matched %s\n", ndn);
+#ifdef USE_IPA_IDVIEWS
+ if (cbdata->idview != NULL) {
+ idview_process_overrides(cbdata, NULL,
+ set_data->common.set,
+ set_data->common.group, set_entry);
+ }
+#endif
slapi_send_ldap_search_entry(cbdata->pb, set_entry,
NULL, cbdata->attrs,
cbdata->attrsonly);
@@ -1216,6 +1231,11 @@ backend_search_group_cb(const char *group, void *cb_data)
slapi_log_error(SLAPI_LOG_PLUGIN,
cbdata->state->plugin_desc->spd_id,
"search matched %s\n", group);
+#ifdef USE_IPA_IDVIEWS
+ if (cbdata->idview != NULL) {
+ idview_process_overrides(cbdata, NULL, NULL, group, group_entry);
+ }
+#endif
slapi_send_ldap_search_entry(cbdata->pb, group_entry,
NULL, cbdata->attrs,
cbdata->attrsonly);
@@ -1313,11 +1333,16 @@ backend_search_cb(Slapi_PBlock *pb)
cbdata.n_entries = 0;
cbdata.staged = NULL;
cbdata.cur_staged = NULL;
+ cbdata.idview = NULL;
+ cbdata.overrides = NULL;
/* Okay, we can search. */
slapi_log_error(SLAPI_LOG_PLUGIN, cbdata.state->plugin_desc->spd_id,
"searching from \"%s\" for \"%s\" with scope %d%s\n",
cbdata.target, cbdata.strfilter, cbdata.scope,
backend_sch_scope_as_string(cbdata.scope));
+#ifdef USE_IPA_IDVIEWS
+ idview_replace_target_dn(&cbdata.target, &cbdata.idview);
+#endif
cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target);
/* Check if there's a backend handling this search. */
if (!slapi_be_exist(cbdata.target_dn)) {
@@ -1326,10 +1351,18 @@ backend_search_cb(Slapi_PBlock *pb)
"slapi_be_exists(\"%s\") = 0, "
"ignoring search\n", cbdata.target);
slapi_sdn_free(&cbdata.target_dn);
+ slapi_ch_free_string(&cbdata.idview);
+#ifdef USE_IPA_IDVIEWS
+ idview_free_overrides(&cbdata);
+#endif
return 0;
}
+
/* Walk the list of groups. */
wrap_inc_call_level();
+#ifdef USE_IPA_IDVIEWS
+ idview_replace_filter(&cbdata);
+#endif
if (map_rdlock() == 0) {
map_data_foreach_domain(cbdata.state, backend_search_group_cb,
&cbdata);
@@ -1468,6 +1501,10 @@ backend_search_cb(Slapi_PBlock *pb)
cbdata.n_entries, NULL);
}
slapi_sdn_free(&cbdata.target_dn);
+ slapi_ch_free_string(&cbdata.idview);
+#ifdef USE_IPA_IDVIEWS
+ idview_free_overrides(&cbdata);
+#endif
free(cbdata.closest_match);
free(cbdata.text);
return cbdata.answer ? -1 : 0;
@@ -1525,6 +1562,7 @@ static void
backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data, const char **group, const char**set)
{
struct backend_locate_cbdata cbdata;
+ char *idview = NULL;
slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &cbdata.state);
if (cbdata.state->plugin_base == NULL) {
@@ -1533,6 +1571,9 @@ backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data, const char **
return;
}
slapi_pblock_get(pb, SLAPI_TARGET_DN, &cbdata.target);
+#ifdef USE_IPA_IDVIEWS
+ idview_replace_target_dn(&cbdata.target, &idview);
+#endif
cbdata.target_dn = slapi_sdn_new_dn_byval(cbdata.target);
cbdata.entry_data = NULL;
cbdata.entry_group = NULL;
@@ -1542,6 +1583,7 @@ backend_locate(Slapi_PBlock *pb, struct backend_entry_data **data, const char **
*group = cbdata.entry_group;
*set = cbdata.entry_set;
slapi_sdn_free(&cbdata.target_dn);
+ slapi_ch_free_string(&idview);
}
/* Check if the target DN is part of this group's tree. If it is, return an
diff --git a/src/back-sch.h b/src/back-sch.h
index 2f4a3df..f74e82d 100644
--- a/src/back-sch.h
+++ b/src/back-sch.h
@@ -69,6 +69,8 @@ struct backend_search_cbdata {
Slapi_PBlock *pb;
struct plugin_state *state;
char *target, *strfilter, **attrs;
+ char *idview;
+ Slapi_Entry **overrides;
int scope, sizelimit, timelimit, attrsonly;
bool_t check_access;
enum sch_search_nsswitch_t check_nsswitch;
@@ -87,6 +89,29 @@ struct backend_search_cbdata {
struct backend_staged_search *cur_staged;
};
+struct backend_search_filter_config {
+ bool_t search_user;
+ bool_t search_group;
+ bool_t search_uid;
+ bool_t search_gid;
+ bool_t search_members;
+ bool_t name_set;
+ bool_t wrong_search;
+ bool_t override_found;
+ char *name;
+ /* If callback is defined, it is called on each filter after analyzing it */
+ void (*callback)(Slapi_Filter *filter, const char *filter_type, struct berval *bval, struct backend_search_filter_config *config);
+ void *callback_data;
+};
+
+/* Analyzes the filter to decide what kind of NSS search is it
+ * Returns 0 on success, 1 on failure
+ * struct backend_search_filter_config is populated with information about the filter
+ * config.name should be freed with slapi_ch_free_string()
+ */
+
+int backend_analyze_search_filter(Slapi_Filter *filter, struct backend_search_filter_config *config);
+
void backend_search_nsswitch(struct backend_set_data *set_data,
struct backend_search_cbdata *cbdata);
@@ -95,4 +120,14 @@ bool_t backend_retrieve_from_nsswitch(struct backend_staged_search *staged,
int backend_sch_do_pam_auth(Slapi_PBlock *pb, const char *username);
+#ifdef USE_IPA_IDVIEWS
+void idview_get_overrides(struct backend_search_cbdata *cbdata);
+void idview_free_overrides(struct backend_search_cbdata *cbdata);
+void idview_process_overrides(struct backend_search_cbdata *cbdata,
+ const char *key, const char *map, const char *domain,
+ Slapi_Entry *entry);
+void idview_replace_target_dn(char **target, char **idview);
+void idview_replace_filter(struct backend_search_cbdata *cbdata);
+#endif
+
#endif