summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArthur de Jong <arthur@arthurdejong.org>2014-01-05 22:11:20 +0100
committerArthur de Jong <arthur@arthurdejong.org>2014-01-05 22:11:20 +0100
commitc6c317ec9efb8190bdc1834091c4761b60637e7f (patch)
tree5525692071b163d3a464153aa77d39f8936820af
parentbe94912a9d236bbe3d5b0e17b771727b0054906d (diff)
parent309b4bbbc040ce9f37ccf25399eacc5294bfc34f (diff)
Implement deref control handling
This uses the LDAP_CONTROL_X_DEREF control as described in draft-masarati-ldap-deref-00 to request the LDAP server to dereference group member attribute values to uid attribute values. This should reduce the number of searches that are required for expanding group members that use the member attribute. This mechanism could also be used to extract information on nested groups but the gains are less clear there. Not all LDAP servers support this control. In OpenLDAP, load the (currently undocumented) deref overlay and enable it for the database to take advantage of this improvement. There is a functional difference when using this control. Any returned deferred uid value returned by the LDAP server is accepted as a member. No checks are performed to see if the user matches the search base and search filters set for passwd entries.
-rw-r--r--README27
-rw-r--r--compat/Makefile.am1
-rw-r--r--compat/derefctrl.c50
-rw-r--r--compat/ldap_compat.h13
-rw-r--r--configure.ac64
-rw-r--r--nslcd/group.c40
-rw-r--r--nslcd/myldap.c294
-rw-r--r--nslcd/myldap.h9
8 files changed, 415 insertions, 83 deletions
diff --git a/README b/README
index d5a996a..62184b9 100644
--- a/README
+++ b/README
@@ -15,7 +15,7 @@
Copyright (C) 1997-2006 Luke Howard
Copyright (C) 2006-2007 West Consulting
- Copyright (C) 2006-2013 Arthur de Jong
+ Copyright (C) 2006-2014 Arthur de Jong
Copyright (C) 2009 Howard Chu
Copyright (C) 2010 Symas Corporation
@@ -344,18 +344,25 @@ group membership
Currently, two ways of specifying group membership are supported. The first,
by using the memberUid attribute, is the simplest and by far the fastest
-(takes the least number of lookups). This attribute maps to user names with
-the same values as the uid attribute would hold for posixAccount entries.
+(takes the least number of lookups). The attribute values are user names with
+the format as the uid attribute for posixAccount entries and are returned
+without further processing.
-The second method is to use DN values in the member attribute (attribute
-names can be changed by using the attribute mapping options as described in
-the manual page). This is potentially a lot slower because in the worst case
-every DN has to be looked up in the LDAP server to find the proper value for
-the uid attribute.
+The second method is to use DN values in the member attribute (attribute names
+can be changed by using the attribute mapping options as described in the
+manual page). This is potentially a lot slower because in the worst case every
+DN has to be looked up in the LDAP server to find the proper value for the uid
+attribute.
+
+If the LDAP server supports the deref control (provided by the deref overlay
+in OpenLDAP) the DN to uid expansing is performed by the LDAP server.
If the DN value already contains a uid value (e.g. uid=arthur, dc=example,
-dc=com) the lookup is skipped and the value from the DN is used. A cache is
-maintained that saves the DN to uid translations for 15 minutes.
+dc=com) a further lookup is skipped and the uid value from the DN is used.
+
+For other DN values an extra lookup is performed to expand it to a uid. These
+lookups are cached and are configurable with the cache dn2uid configuration
+option.
The member attribute may also contain the DN of another group entry. These
nested groups are parsed recursively depending on the nss_nested_groups
diff --git a/compat/Makefile.am b/compat/Makefile.am
index 361c9be..bc8e4cc 100644
--- a/compat/Makefile.am
+++ b/compat/Makefile.am
@@ -29,6 +29,7 @@ EXTRA_DIST = getopt_long.c getopt_long.h \
nss_compat.h socket.h \
ldap_compat.h pagectrl.c ldap_passwd_s.c ldap_initialize.c \
ldap_parse_passwordpolicy_control.c ldap_passwordpolicy_err2txt.c \
+ derefctrl.c \
pam_compat.h pam_get_authtok.c pam_prompt.c
libcompat_a_SOURCES = getpeercred.c getpeercred.h
diff --git a/compat/derefctrl.c b/compat/derefctrl.c
new file mode 100644
index 0000000..9676c55
--- /dev/null
+++ b/compat/derefctrl.c
@@ -0,0 +1,50 @@
+/*
+ derefctrl.c - replacement function
+
+ Copyright (C) 2013 Arthur de Jong
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301 USA
+*/
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <lber.h>
+#include <ldap.h>
+#include <string.h>
+
+#include "compat/ldap_compat.h"
+#include "compat/attrs.h"
+
+#ifdef REPLACE_LDAP_CREATE_DEREF_CONTROL
+int replacement_ldap_create_deref_control(LDAP *ld, LDAPDerefSpec *ds,
+ int iscritical, LDAPControl **ctrlp)
+{
+ int rc;
+ struct berval value;
+ if (ctrlp == NULL)
+ return LDAP_PARAM_ERROR;
+ rc = ldap_create_deref_control_value(ld, ds, &value);
+ if (rc != LDAP_SUCCESS)
+ return rc;
+ rc = ldap_control_create(LDAP_CONTROL_X_DEREF, iscritical, &value, 0, ctrlp);
+ if (rc != LDAP_SUCCESS)
+ {
+ ber_memfree(value.bv_val);
+ }
+ return rc;
+}
+#endif /* REPLACE_LDAP_CREATE_DEREF_CONTROL */
diff --git a/compat/ldap_compat.h b/compat/ldap_compat.h
index 6e9c6b1..b69974f 100644
--- a/compat/ldap_compat.h
+++ b/compat/ldap_compat.h
@@ -1,7 +1,7 @@
/*
ldap_compat.h - provide a replacement definitions for some ldap functions
- Copyright (C) 2009, 2010, 2012, 2013 Arthur de Jong
+ Copyright (C) 2009-2013 Arthur de Jong
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -80,6 +80,14 @@ int ldap_parse_passwordpolicy_control(LDAP *ld, LDAPControl *ctrl,
const char *ldap_passwordpolicy_err2txt(LDAPPasswordPolicyError error);
#endif /* HAVE_LDAP_PASSWORDPOLICY_ERR2TXT */
+#ifdef REPLACE_LDAP_CREATE_DEREF_CONTROL
+/* provide a replacement implementation of ldap_create_deref_control() */
+int replacement_ldap_create_deref_control(LDAP *ld, LDAPDerefSpec *ds,
+ int iscritical, LDAPControl **ctrlp);
+#define ldap_create_deref_control(ld, dc, iscritical, ctrlp) \
+ replacement_ldap_create_deref_control(ld, dc, iscritical, ctrlp)
+#endif /* REPLACE_LDAP_CREATE_DEREF_CONTROL */
+
/* compatibility definition */
#ifndef LDAP_SASL_QUIET
#define LDAP_SASL_QUIET 2U
@@ -106,5 +114,8 @@ const char *ldap_passwordpolicy_err2txt(LDAPPasswordPolicyError error);
#ifndef LDAP_CONTROL_PASSWORDPOLICYRESPONSE
#define LDAP_CONTROL_PASSWORDPOLICYRESPONSE "1.3.6.1.4.1.42.2.27.8.5.1"
#endif /* LDAP_CONTROL_PASSWORDPOLICYRESPONSE */
+#ifndef LDAP_CONTROL_X_DEREF
+#define LDAP_CONTROL_X_DEREF "1.3.6.1.4.1.4203.666.5.16"
+#endif /* LDAP_CONTROL_X_DEREF */
#endif /* COMPAT__LDAP_COMPAT_H */
diff --git a/configure.ac b/configure.ac
index 411c9e5..cd776e0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -803,9 +803,11 @@ then
AC_CHECK_FUNCS(ldap_get_values ldap_value_free)
AC_CHECK_FUNCS(ldap_get_values_len ldap_count_values_len ldap_value_free_len)
AC_CHECK_FUNCS(ldap_err2string ldap_abandon)
- AC_CHECK_FUNCS(ldap_create_control ldap_control_find)
- AC_CHECK_FUNCS(ldap_controls_free ldap_control_free)
+ AC_CHECK_FUNCS(ldap_control_create ldap_create_control ldap_control_find)
+ AC_CHECK_FUNCS(ldap_controls_free ldap_control_free ldap_get_entry_controls)
AC_CHECK_FUNCS(ldap_parse_passwordpolicy_control ldap_passwordpolicy_err2txt)
+ AC_CHECK_FUNCS(ldap_create_deref_control ldap_create_deref_control_value)
+ AC_CHECK_FUNCS(ldap_parse_deref_control ldap_derefresponse_free)
# replace ldap_create_page_control() and ldap_parse_page_control()
AC_CHECK_FUNCS(ldap_create_page_control ldap_parse_page_control,, [AC_LIBOBJ(pagectrl)])
@@ -860,6 +862,64 @@ then
[Define to 1 if ldap_set_rebind_proc() returns void.])
fi
+ # check for broken implementations of ldap_create_deref_control()
+ if test "x$ac_cv_func_ldap_create_deref_control" = "xyes"
+ then
+ # this bug cannot be determined on compile time so we run a
+ # small test program
+ AC_CACHE_CHECK(
+ [ldap_create_deref_control() implementation],
+ nslcd_cv_ldap_create_deref_control_working,
+ [AC_RUN_IFELSE(
+ [AC_LANG_PROGRAM([[
+ #include <stdio.h>
+ #include <lber.h>
+ #include <ldap.h>
+ ]], [[
+ int rc;
+ LDAP *ld;
+ LDAPControl *ctrls[2] = {NULL, NULL};
+ struct LDAPDerefSpec ds[2];
+ char *attrs[2] = {"uid", NULL};
+ ld = ldap_init("localhost", LDAP_PORT);
+ if (ld == NULL)
+ {
+ fprintf(stderr, "ldap_init() failed\n");
+ return 2;
+ }
+ ds[0].derefAttr = "member";
+ ds[0].attributes = attrs;
+ ds[1].derefAttr = NULL;
+ rc = ldap_create_deref_control(ld, ds, 0, &ctrls[0]);
+ if (rc != LDAP_SUCCESS)
+ {
+ fprintf(stderr, "ldap_create_deref_control() failed: %s\n",
+ ldap_err2string(rc));
+ return 2;
+ }
+ if (ldap_control_find(LDAP_CONTROL_X_DEREF, ctrls, NULL) != NULL)
+ return 0;
+ if (ldap_control_find(LDAP_CONTROL_PAGEDRESULTS, ctrls, NULL) != NULL)
+ {
+ fprintf(stderr, "ldap_create_deref_control() created LDAP_CONTROL_PAGEDRESULTS control\n");
+ return 3;
+ }
+ fprintf(stderr, "ldap_create_deref_control() created unknown control\n");
+ return 2;
+ ]])],
+ [nslcd_cv_ldap_create_deref_control_working=ok],
+ [if test "$?" -eq 3; then nslcd_cv_ldap_create_deref_control_working=broken
+ else nslcd_cv_ldap_create_deref_control_working=unknown; fi],
+ [nslcd_cv_ldap_create_deref_control_working=cross])])
+ if test "x$nslcd_cv_ldap_create_deref_control_working" != "xok"
+ then
+ AC_MSG_NOTICE([using replacement ldap_create_deref_control()])
+ AC_LIBOBJ(derefctrl)
+ AC_DEFINE(REPLACE_LDAP_CREATE_DEREF_CONTROL, 1,
+ [Define to 1 if ldap_create_deref_control() is broken.])
+ fi
+ fi
+
# save nslcd LIBS and CFLAGS and restore originals
nslcd_CFLAGS="$CFLAGS"
nslcd_LIBS="$LIBS"
diff --git a/nslcd/group.c b/nslcd/group.c
index 1455930..ffaeb80 100644
--- a/nslcd/group.c
+++ b/nslcd/group.c
@@ -78,6 +78,9 @@ static const char *default_group_userPassword = "*"; /* unmatchable */
/* the attribute list to request with searches */
static const char **group_attrs = NULL;
+/* the attribute list for bymember searches (without member attributes) */
+static const char **group_bymember_attrs = NULL;
+
/* create a search filter for searching a group entry
by name, return -1 on errors */
static int mkfilter_group_byname(const char *name,
@@ -181,6 +184,18 @@ void group_init(void)
exit(EXIT_FAILURE);
}
set_free(set);
+ /* set up bymember attribute list */
+ set = set_new();
+ attmap_add_attributes(set, attmap_group_cn);
+ attmap_add_attributes(set, attmap_group_userPassword);
+ attmap_add_attributes(set, attmap_group_gidNumber);
+ group_bymember_attrs = set_tolist(set);
+ if (group_bymember_attrs == NULL)
+ {
+ log_log(LOG_CRIT, "malloc() failed to allocate memory");
+ exit(EXIT_FAILURE);
+ }
+ set_free(set);
}
static int do_write_group(TFILE *fp, MYLDAP_ENTRY *entry,
@@ -219,6 +234,7 @@ static void getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session,
char buf[BUFLEN_NAME];
int i;
const char **values;
+ const char ***derefs;
/* add the memberUid values */
values = myldap_get_values(entry, attmap_group_memberUid);
if (values != NULL)
@@ -231,6 +247,26 @@ static void getmembers(MYLDAP_ENTRY *entry, MYLDAP_SESSION *session,
/* skip rest if attmap_group_member is blank */
if (strcasecmp(attmap_group_member, "\"\"") == 0)
return;
+ /* add deref'd entries if we have them*/
+ derefs = myldap_get_deref_values(entry, attmap_group_member, attmap_passwd_uid);
+ if (derefs != NULL)
+ {
+ /* add deref'd uid attributes */
+ for (i = 0; derefs[0][i] != NULL; i++)
+ set_add(members, derefs[0][i]);
+ /* add non-deref'd attribute values as subgroups */
+ for (i = 0; derefs[1][i] != NULL; i++)
+ {
+ if ((seen == NULL) || (!set_contains(seen, derefs[1][i])))
+ {
+ if (seen != NULL)
+ set_add(seen, derefs[1][i]);
+ if (subgroups != NULL)
+ set_add(subgroups, derefs[1][i]);
+ }
+ }
+ return; /* no need to parse the member attribute ourselves */
+ }
/* add the member values */
values = myldap_get_values(entry, attmap_group_member);
if (values != NULL)
@@ -447,7 +483,7 @@ int nslcd_group_bymember(TFILE *fp, MYLDAP_SESSION *session)
{
/* do the LDAP search */
search = myldap_search(session, base, group_scope, filter,
- group_attrs, NULL);
+ group_bymember_attrs, NULL);
if (search == NULL)
{
if (seen != NULL)
@@ -497,7 +533,7 @@ int nslcd_group_bymember(TFILE *fp, MYLDAP_SESSION *session)
/* do the LDAP searches */
for (i = 0; (base = group_bases[i]) != NULL; i++)
{
- search = myldap_search(session, base, group_scope, filter, group_attrs, NULL);
+ search = myldap_search(session, base, group_scope, filter, group_bymember_attrs, NULL);
if (search != NULL)
{
while ((entry = myldap_get_entry(search, NULL)) != NULL)
diff --git a/nslcd/myldap.c b/nslcd/myldap.c
index 620a3b7..9e0bc6e 100644
--- a/nslcd/myldap.c
+++ b/nslcd/myldap.c
@@ -4,8 +4,8 @@
which has been forked into the nss-pam-ldapd library.
Copyright (C) 1997-2006 Luke Howard
- Copyright (C) 2006, 2007 West Consulting
- Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+ Copyright (C) 2006-2007 West Consulting
+ Copyright (C) 2006-2014 Arthur de Jong
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -72,6 +72,7 @@
#include "cfg.h"
#include "common/set.h"
#include "compat/ldap_compat.h"
+#include "attmap.h"
/* the maximum number of searches per session */
#define MAX_SEARCHES_IN_SESSION 4
@@ -130,9 +131,9 @@ struct myldap_search {
done per returned entry. */
#define MAX_ATTRIBUTES_PER_ENTRY 16
-/* The maximum number of ranged attribute values that may be stoted
- per entry. */
-#define MAX_RANGED_ATTRIBUTES_PER_ENTRY 8
+/* The maximum number of buffers (used for ranged attribute values and
+ values returned by bervalues_to_values()) that may be stored per entry. */
+#define MAX_BUFFERS_PER_ENTRY 8
/* A single entry from the LDAP database as returned by
myldap_get_entry(). */
@@ -146,8 +147,8 @@ struct myldap_entry {
char **exploded_rdn;
/* a cache of attribute to value list */
char **attributevalues[MAX_ATTRIBUTES_PER_ENTRY];
- /* a reference to ranged attribute values so we can free() them later on */
- char **rangedattributevalues[MAX_RANGED_ATTRIBUTES_PER_ENTRY];
+ /* a reference to buffers so we can free() them later on */
+ char **buffers[MAX_BUFFERS_PER_ENTRY];
};
/* Flag to record first search operation */
@@ -206,8 +207,8 @@ static MYLDAP_ENTRY *myldap_entry_new(MYLDAP_SEARCH *search)
entry->exploded_rdn = NULL;
for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
entry->attributevalues[i] = NULL;
- for (i = 0; i < MAX_RANGED_ATTRIBUTES_PER_ENTRY; i++)
- entry->rangedattributevalues[i] = NULL;
+ for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
+ entry->buffers[i] = NULL;
/* return the fresh entry */
return entry;
}
@@ -225,10 +226,10 @@ static void myldap_entry_free(MYLDAP_ENTRY *entry)
for (i = 0; i < MAX_ATTRIBUTES_PER_ENTRY; i++)
if (entry->attributevalues[i] != NULL)
ldap_value_free(entry->attributevalues[i]);
- /* free all ranged attribute values */
- for (i = 0; i < MAX_RANGED_ATTRIBUTES_PER_ENTRY; i++)
- if (entry->rangedattributevalues[i] != NULL)
- free(entry->rangedattributevalues[i]);
+ /* free all buffers */
+ for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
+ if (entry->buffers[i] != NULL)
+ free(entry->buffers[i]);
/* we don't need the result anymore, ditch it. */
ldap_msgfree(entry->search->msg);
entry->search->msg = NULL;
@@ -1053,9 +1054,14 @@ void myldap_get_policy_response(MYLDAP_SESSION *session, int *response,
static int do_try_search(MYLDAP_SEARCH *search)
{
+ int ctrlidx = 0;
int rc;
- LDAPControl *serverCtrls[2];
- LDAPControl **pServerCtrls;
+ LDAPControl *serverctrls[3];
+#ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
+ int i;
+ struct LDAPDerefSpec ds[2];
+ char *deref_attrs[2];
+#endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
int msgid;
/* ensure that we have an open connection */
rc = do_open(search->session);
@@ -1065,35 +1071,63 @@ static int do_try_search(MYLDAP_SEARCH *search)
if ((nslcd_cfg->pagesize > 0) && (search->scope != LDAP_SCOPE_BASE))
{
rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
- NULL, 0, &serverCtrls[0]);
+ search->cookie, 0, &serverctrls[ctrlidx]);
if (rc == LDAP_SUCCESS)
- {
- serverCtrls[1] = NULL;
- pServerCtrls = serverCtrls;
- }
+ ctrlidx++;
else
{
myldap_err(LOG_WARNING, search->session->ld, rc,
"ldap_create_page_control() failed");
- /* clear error flag */
- rc = LDAP_SUCCESS;
- if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
- log_log(LOG_WARNING, "failed to clear the error flag");
- pServerCtrls = NULL;
+ serverctrls[ctrlidx] = NULL;
+ /* if we were paging, failure building the second control is fatal */
+ if (search->cookie != NULL)
+ return rc;
}
}
- else
- pServerCtrls = NULL;
+#ifdef HAVE_LDAP_CREATE_DEREF_CONTROL
+ /* if doing group searches, add deref control to search request
+ (this is currently a bit of a hack and hard-coded for group searches
+ which are detected by requesting the attmap_group_member member
+ attribute) */
+ for (i = 0; search->attrs[i] != NULL; i++)
+ if (strcasecmp(search->attrs[i], attmap_group_member) == 0)
+ {
+ /* attributes from dereff'd entries */
+ deref_attrs[0] = (void *)attmap_passwd_uid;
+ deref_attrs[1] = NULL;
+ /* build deref control */
+ ds[0].derefAttr = (void *)attmap_group_member;
+ ds[0].attributes = deref_attrs;
+ ds[1].derefAttr = NULL;
+ ds[1].attributes = NULL;
+ rc = ldap_create_deref_control(search->session->ld, ds, 0, &serverctrls[ctrlidx]);
+ if (rc == LDAP_SUCCESS)
+ ctrlidx++;
+ else
+ {
+ myldap_err(LOG_WARNING, search->session->ld, rc,
+ "ldap_create_deref_control() failed");
+ serverctrls[ctrlidx] = NULL;
+ }
+ }
+#endif /* HAVE_LDAP_CREATE_DEREF_CONTROL */
+ /* NULL terminate control list */
+ serverctrls[ctrlidx] = NULL;
+ /* clear error flag (perhaps control setting failed) */
+ if (ctrlidx > 0)
+ {
+ rc = LDAP_SUCCESS;
+ if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER, &rc) != LDAP_SUCCESS)
+ log_log(LOG_WARNING, "failed to clear the error flag");
+ }
/* perform the search */
rc = ldap_search_ext(search->session->ld, search->base, search->scope,
search->filter, (char **)(search->attrs),
- 0, pServerCtrls, NULL, NULL, LDAP_NO_LIMIT, &msgid);
+ 0, serverctrls[0] == NULL ? NULL : serverctrls,
+ NULL, NULL, LDAP_NO_LIMIT, &msgid);
/* free the controls if we had them */
- if (pServerCtrls != NULL)
- {
- ldap_control_free(serverCtrls[0]);
- serverCtrls[0] = NULL;
- }
+ for (ctrlidx = 0; serverctrls[ctrlidx] != NULL; ctrlidx++)
+ ldap_control_free(serverctrls[ctrlidx]);
/* handle errors */
if (rc != LDAP_SUCCESS)
{
@@ -1381,10 +1415,8 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
{
int rc;
int parserc;
- int msgid;
struct timeval tv, *tvp;
LDAPControl **resultcontrols;
- LDAPControl *serverctrls[2];
ber_int_t count;
/* check parameters */
if ((search == NULL) || (search->session == NULL) || (search->session->ld == NULL))
@@ -1500,7 +1532,8 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
&count, &(search->cookie));
if (rc != LDAP_SUCCESS)
{
- myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
+ if (rc != LDAP_CONTROL_NOT_FOUND)
+ myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_parse_page_control() failed");
/* clear error flag */
rc = LDAP_SUCCESS;
if (ldap_set_option(search->session->ld, LDAP_OPT_ERROR_NUMBER,
@@ -1526,29 +1559,9 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
return NULL;
}
/* try the next page */
- serverctrls[0] = NULL;
- serverctrls[1] = NULL;
- rc = ldap_create_page_control(search->session->ld, nslcd_cfg->pagesize,
- search->cookie, 0, &serverctrls[0]);
- if (rc != LDAP_SUCCESS)
- {
- if (serverctrls[0] != NULL)
- ldap_control_free(serverctrls[0]);
- myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_create_page_control() failed");
- myldap_search_close(search);
- if (rcp != NULL)
- *rcp = rc;
- return NULL;
- }
- /* set up a new search for the next page */
- rc = ldap_search_ext(search->session->ld,
- search->base, search->scope, search->filter,
- search->attrs, 0, serverctrls, NULL, NULL,
- LDAP_NO_LIMIT, &msgid);
- ldap_control_free(serverctrls[0]);
+ rc = do_try_search(search);
if (rc != LDAP_SUCCESS)
{
- myldap_err(LOG_WARNING, search->session->ld, rc, "ldap_search_ext() failed");
/* close connection on connection problems */
if ((rc == LDAP_UNAVAILABLE) || (rc == LDAP_SERVER_DOWN))
do_close(search->session);
@@ -1557,7 +1570,6 @@ MYLDAP_ENTRY *myldap_get_entry(MYLDAP_SEARCH *search, int *rcp)
*rcp = rc;
return NULL;
}
- search->msgid = msgid;
/* we continue with another pass */
break;
case LDAP_RES_SEARCH_REFERENCE:
@@ -1790,14 +1802,14 @@ const char **myldap_get_values(MYLDAP_ENTRY *entry, const char *attr)
if (values == NULL)
return NULL;
/* store values entry so we can free it later on */
- for (i = 0; i < MAX_RANGED_ATTRIBUTES_PER_ENTRY; i++)
- if (entry->rangedattributevalues[i] == NULL)
+ for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
+ if (entry->buffers[i] == NULL)
{
- entry->rangedattributevalues[i] = values;
- return (const char **)entry->rangedattributevalues[i];
+ entry->buffers[i] = values;
+ return (const char **)entry->buffers[i];
}
/* we found no room to store the values */
- log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_RANGED_ATTRIBUTES_PER_ENTRY");
+ log_log(LOG_ERR, "ldap_get_values() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
free(values);
return NULL;
}
@@ -1914,14 +1926,14 @@ const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *attr)
if (values == NULL)
return NULL;
/* store values entry so we can free it later on */
- for (i = 0; i < MAX_RANGED_ATTRIBUTES_PER_ENTRY; i++)
- if (entry->rangedattributevalues[i] == NULL)
+ for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
+ if (entry->buffers[i] == NULL)
{
- entry->rangedattributevalues[i] = (char **)values;
+ entry->buffers[i] = (char **)values;
return values;
}
/* we found no room to store the values */
- log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_RANGED_ATTRIBUTES_PER_ENTRY");
+ log_log(LOG_ERR, "myldap_get_values_len() couldn't store results, increase MAX_BUFFERS_PER_ENTRY");
free(values);
return NULL;
}
@@ -2068,6 +2080,154 @@ int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass)
return 0;
}
+#ifdef HAVE_LDAP_PARSE_DEREF_CONTROL
+const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
+ const char *derefattr, const char *getattr)
+{
+ LDAPControl **entryctrls;
+ LDAPDerefRes *deref, *d;
+ LDAPDerefVal *a;
+ int i, pass;
+ int rc;
+ int found;
+ int counts[2];
+ size_t sizes[2], size;
+ char *buffer = NULL;
+ char ***results = NULL;
+ rc = ldap_get_entry_controls(entry->search->session->ld, entry->search->msg,
+ &entryctrls);
+ if (rc != LDAP_SUCCESS)
+ {
+ myldap_err(LOG_WARNING, entry->search->session->ld, rc,
+ "ldap_get_entry_controls() failed");
+ return NULL;
+ }
+ if (entryctrls == NULL)
+ return NULL;
+ /* see if we can find a deref control */
+ rc = ldap_parse_deref_control(entry->search->session->ld, entryctrls,
+ &deref);
+ if ((rc != LDAP_SUCCESS) || (deref == NULL))
+ {
+ if ((rc != LDAP_SUCCESS) && (rc != LDAP_CONTROL_NOT_FOUND))
+ myldap_err(LOG_WARNING, entry->search->session->ld, rc,
+ "ldap_parse_deref_control() failed");
+ /* clear error flag */
+ rc = LDAP_SUCCESS;
+ if (ldap_set_option(entry->search->session->ld, LDAP_OPT_ERROR_NUMBER,
+ &rc) != LDAP_SUCCESS)
+ log_log(LOG_WARNING, "failed to clear the error flag");
+ ldap_controls_free(entryctrls);
+ return NULL;
+ }
+ /* two passes: one to calculate size, one to store data */
+ for (pass=0; pass < 2; pass++)
+ {
+ /* reset counters and size */
+ for (i = 0; i < 2; i++)
+ {
+ counts[i] = 0;
+ sizes[i] = 0;
+ }
+ /* go over all deref'd attributes and find the one we're looking for */
+ for (d = deref; d != NULL; d = d->next)
+ if ((d->derefAttr != NULL) && (d->derefVal.bv_val != NULL) &&
+ (strcasecmp(derefattr, d->derefAttr) == 0))
+ {
+ /* we should have one d per original attribute value */
+ found = 0;
+ /* go over deref'd attribute values to find the ones we're looking for */
+ for (a = d->attrVals; a != NULL; a = a->next)
+ if ((a->type != NULL) && (a->vals != NULL) &&
+ (strcasecmp(getattr, a->type) == 0))
+ for (i=0; a->vals[i].bv_val != NULL; i++)
+ {
+ found = 1;
+ if (results == NULL)
+ {
+ log_log(LOG_DEBUG, "deref %s %s=%s -> %s=%s",
+ myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val,
+ a->type, a->vals[i].bv_val);
+ counts[0]++;
+ sizes[0] += strlen(a->vals[i].bv_val) + 1;
+ }
+ else
+ {
+ strcpy(buffer, a->vals[i].bv_val);
+ results[0][counts[0]++] = buffer;
+ buffer += strlen(buffer) + 1;
+ }
+ }
+ if (!found)
+ {
+ if (results == NULL)
+ {
+ log_log(LOG_DEBUG, "no %s deref %s %s=%s", getattr,
+ myldap_get_dn(entry), d->derefAttr, d->derefVal.bv_val);
+ counts[1]++;
+ sizes[1] += strlen(d->derefVal.bv_val) + 1;
+ }
+ else
+ {
+ strcpy(buffer, d->derefVal.bv_val);
+ results[1][counts[1]++] = buffer;
+ buffer += strlen(buffer) + 1;
+ }
+ }
+ }
+ /* allocate memory after first pass */
+ if (results == NULL)
+ {
+ size = sizeof(char **) * 3;
+ for (i = 0; i < 2; i++)
+ size += sizeof(char *) * (counts[i] + 1);
+ for (i = 0; i < 2; i++)
+ size += sizeof(char) * sizes[i];
+ buffer = (char *)malloc(size);
+ if (buffer == NULL)
+ {
+ log_log(LOG_CRIT, "myldap_get_deref_values(): malloc() failed to allocate memory");
+ return NULL;
+ }
+ /* allocate the list of lists */
+ results = (void *)buffer;
+ buffer += sizeof(char **) * 3;
+ /* allocate the lists */
+ for (i = 0; i < 2; i++)
+ {
+ results[i] = (char **)buffer;
+ buffer += sizeof(char *) * (counts[i] + 1);
+ }
+ results[i] = NULL;
+ }
+ }
+ /* NULL terminate the lists */
+ results[0][counts[0]] = NULL;
+ results[1][counts[1]] = NULL;
+ /* free control data */
+ ldap_derefresponse_free(deref);
+ ldap_controls_free(entryctrls);
+ /* store results so we can free it later on */
+ for (i = 0; i < MAX_BUFFERS_PER_ENTRY; i++)
+ if (entry->buffers[i] == NULL)
+ {
+ entry->buffers[i] = (void *)results;
+ return (const char ***)results;
+ }
+ /* we found no room to store the values */
+ log_log(LOG_ERR, "myldap_get_deref_values() couldn't store results, "
+ "increase MAX_BUFFERS_PER_ENTRY");
+ free(results);
+ return NULL;
+}
+#else /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
+const char ***myldap_get_deref_values(MYLDAP_ENTRY UNUSED(*entry),
+ const char UNUSED(*derefattr), const char UNUSED(*getattr))
+{
+ return NULL;
+}
+#endif /* not HAVE_LDAP_PARSE_DEREF_CONTROL */
+
int myldap_escape(const char *src, char *buffer, size_t buflen)
{
size_t pos = 0;
diff --git a/nslcd/myldap.h b/nslcd/myldap.h
index 8c4551a..c7358af 100644
--- a/nslcd/myldap.h
+++ b/nslcd/myldap.h
@@ -2,7 +2,7 @@
myldap.h - simple interface to do LDAP requests
This file is part of the nss-pam-ldapd library.
- Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012, 2013 Arthur de Jong
+ Copyright (C) 2007-2014 Arthur de Jong
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
@@ -133,6 +133,13 @@ MUST_USE const char **myldap_get_values_len(MYLDAP_ENTRY *entry, const char *att
/* Checks to see if the entry has the specified object class. */
MUST_USE int myldap_has_objectclass(MYLDAP_ENTRY *entry, const char *objectclass);
+/* See if the entry has any deref controls attached to it and deref attr
+ derefattr to get the getattr values. Will return two lists of attribute
+ values. One list of deref'ed attribute values and one list of original
+ attribute values that could not be deref'ed. */
+MUST_USE const char ***myldap_get_deref_values(MYLDAP_ENTRY *entry,
+ const char *derefattr, const char *getattr);
+
/* Get the RDN's value: eg. if the DN was cn=lukeh, ou=People, dc=example,
dc=com getrdnvalue(entry, cn) would return lukeh. If the attribute was not
found in the DN or if some error occurs NULL is returned. This method may