diff options
-rw-r--r-- | README | 27 | ||||
-rw-r--r-- | compat/Makefile.am | 1 | ||||
-rw-r--r-- | compat/derefctrl.c | 50 | ||||
-rw-r--r-- | compat/ldap_compat.h | 13 | ||||
-rw-r--r-- | configure.ac | 64 | ||||
-rw-r--r-- | nslcd/group.c | 40 | ||||
-rw-r--r-- | nslcd/myldap.c | 294 | ||||
-rw-r--r-- | nslcd/myldap.h | 9 |
8 files changed, 415 insertions, 83 deletions
@@ -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 |