Arthur de Jong

Open Source / Free Software developer

summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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