changeset 13852:dc1b5bc72558

3177 LDAP client: support for uniqueMember attribute Reviewed by: Milan Jurik <milan.jurik@xylab.cz> Approved by: Richard Lowe <richlowe@richlowe.net>
author Paul B. Henson <henson@acm.org>
date Mon, 01 Oct 2012 18:01:32 -0700
parents ba09719a1c17
children 93ee8890b568
files usr/src/lib/nsswitch/ldap/common/getgrent.c usr/src/man/man1m/ldapclient.1m
diffstat 2 files changed, 113 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/lib/nsswitch/ldap/common/getgrent.c	Fri Oct 05 18:45:33 2012 -0400
+++ b/usr/src/lib/nsswitch/ldap/common/getgrent.c	Mon Oct 01 18:01:32 2012 -0700
@@ -25,6 +25,7 @@
 
 #include <grp.h>
 #include "ldap_common.h"
+#include <string.h>
 
 /* String which may need to be removed from beginning of group password */
 #define	_CRYPT		"{CRYPT}"
@@ -40,8 +41,21 @@
 #define	_F_GETGRNAM_SSD	"(&(%%s)(cn=%s))"
 #define	_F_GETGRGID	"(&(objectClass=posixGroup)(gidNumber=%u))"
 #define	_F_GETGRGID_SSD	"(&(%%s)(gidNumber=%u))"
-#define	_F_GETGRMEM	"(&(objectClass=posixGroup)(memberUid=%s))"
-#define	_F_GETGRMEM_SSD	"(&(%%s)(memberUid=%s))"
+/*
+ * Group membership can be defined by either username or DN, so when searching
+ * for groups by member we need to consider both. The first parameter in the
+ * filter is replaced by username, the second by DN.
+ */
+#define	_F_GETGRMEM \
+	"(&(objectClass=posixGroup)(|(memberUid=%s)(memberUid=%s)))"
+#define	_F_GETGRMEM_SSD	"(&(%%s)(|(memberUid=%s)(memberUid=%s)))"
+
+/*
+ * Copied from getpwnam.c, needed to look up user DN.
+ * Would it be better to move to ldap_common.h rather than duplicate?
+ */
+#define	_F_GETPWNAM	"(&(objectClass=posixAccount)(uid=%s))"
+#define	_F_GETPWNAM_SSD	"(&(%%s)(uid=%s))"
 
 static const char *gr_attrs[] = {
 	_G_NAME,
@@ -75,6 +89,7 @@
 	char		**gname, **passwd, **gid, *password, *end;
 	char		gid_nobody[NOBODY_STR_LEN];
 	char		*gid_nobody_v[1];
+	char		*member_str, *strtok_state;
 	ns_ldap_attr_t	*members;
 
 	(void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY);
@@ -140,15 +155,34 @@
 			nss_result = NSS_STR_PARSE_PARSE;
 			goto result_grp2str;
 		}
-		if (firstime) {
-			len = snprintf(buffer, buflen, "%s",
-			    members->attrvalue[i]);
-			TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
-			firstime = 0;
+		/*
+		 * If we find an '=' in the member attribute value, treat it as
+		 * a DN, otherwise as a username.
+		 */
+		if (member_str = strchr(members->attrvalue[i], '=')) {
+			member_str++; /* skip over the '=' */
+			/* Fail if we can't pull a username out of the RDN */
+			if (! (member_str = strtok_r(member_str,
+			    ",", &strtok_state))) {
+				nss_result = NSS_STR_PARSE_PARSE;
+				goto result_grp2str;
+			}
 		} else {
-			len = snprintf(buffer, buflen, ",%s",
-			    members->attrvalue[i]);
-			TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
+			member_str = members->attrvalue[i];
+		}
+		if (*member_str != '\0') {
+			if (firstime) {
+				len = snprintf(buffer, buflen, "%s",
+				    member_str);
+				TEST_AND_ADJUST(len, buffer, buflen,
+				    result_grp2str);
+				firstime = 0;
+			} else {
+				len = snprintf(buffer, buflen, ",%s",
+				    member_str);
+				TEST_AND_ADJUST(len, buffer, buflen,
+				    result_grp2str);
+			}
 		}
 	}
 nomember:
@@ -250,7 +284,8 @@
 {
 	int			i, j, k;
 	int			gcnt = (int)0;
-	char			**groupvalue, **membervalue;
+	char			**groupvalue, **membervalue, *member_str;
+	char			*strtok_state;
 	nss_status_t		lstat;
 	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
 	char			searchfilter[SEARCHFILTERLEN];
@@ -258,7 +293,7 @@
 	char			name[SEARCHFILTERLEN];
 	ns_ldap_result_t	*result;
 	ns_ldap_entry_t		*curEntry;
-	char			*username;
+	char			*username, **dn_attr, *dn;
 	gid_t			gid;
 	int			ret;
 
@@ -269,14 +304,45 @@
 	if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
 		return ((nss_status_t)NSS_NOTFOUND);
 
-	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name);
+	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETPWNAM, name);
 	if (ret >= sizeof (searchfilter) || ret < 0)
 		return ((nss_status_t)NSS_NOTFOUND);
 
-	ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name);
+	ret = snprintf(userdata, sizeof (userdata), _F_GETPWNAM_SSD, name);
 	if (ret >= sizeof (userdata) || ret < 0)
 		return ((nss_status_t)NSS_NOTFOUND);
 
+	/*
+	 * Look up the user DN in ldap. If it's not found, search solely by
+	 * username.
+	 */
+	lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
+	    _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata);
+	if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
+		return ((nss_status_t)lstat);
+
+	if (be->result == NULL ||
+	    !(dn_attr = __ns_ldap_getAttr(be->result->entry, "dn")))
+		dn = name;
+	else
+		dn = dn_attr[0];
+
+	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name,
+	    dn);
+	if (ret >= sizeof (searchfilter) || ret < 0)
+		return ((nss_status_t)NSS_NOTFOUND);
+
+	ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name,
+	    dn);
+	if (ret >= sizeof (userdata) || ret < 0)
+		return ((nss_status_t)NSS_NOTFOUND);
+
+	/*
+	 * Free up resources from user DN search before performing group
+	 * search.
+	 */
+	(void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
+
 	gcnt = (int)argp->numgids;
 	lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
 	    _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata);
@@ -291,7 +357,20 @@
 		membervalue = __ns_ldap_getAttr(curEntry, "memberUid");
 		if (membervalue) {
 			for (j = 0; membervalue[j]; j++) {
-				if (strcmp(membervalue[j], username) == NULL) {
+				/*
+				 * If we find an '=' in the member attribute
+				 * value, treat it as a DN, otherwise as a
+				 * username.
+				 */
+				if (member_str = strchr(membervalue[j], '=')) {
+					member_str++; /* skip over the '=' */
+					member_str = strtok_r(member_str, ",",
+					    &strtok_state);
+				} else {
+					member_str = membervalue[j];
+				}
+				if (member_str &&
+				    strcmp(member_str, username) == NULL) {
 					groupvalue = __ns_ldap_getAttr(curEntry,
 					    "gidnumber");
 					gid = (gid_t)strtol(groupvalue[0],
--- a/usr/src/man/man1m/ldapclient.1m	Fri Oct 05 18:45:33 2012 -0400
+++ b/usr/src/man/man1m/ldapclient.1m	Mon Oct 01 18:01:32 2012 -0700
@@ -377,6 +377,25 @@
 
 the LDAP client would use the LDAP attribute \fBemployeeNumber\fR rather than
 \fBuid\fR for the \fBpasswd\fR service. This is a multivalued attribute.
+.sp
+To use rfc2307bis style groups (with a DN rather than username as the
+attribute value), map the \fBmemberUid\fR attribute to the group attribute
+being used (typically either \fBuniqueMember\fR or \fBmember\fR), for example:
+.sp
+.in +2
+.nf
+attributeMap: group:memberUid=uniqueMember
+.fi
+.in -2
+.sp
+
+Group membership in a given directory is expected to be maintained with
+either username format member attributes, or DN format member attributes. If
+both are present they must describe identical memberships or unexpected
+results may be obtained. For DN format attributes, the username is required
+to be the RDN of the entry. Note that nested groups are not currently
+supported, and unexpected results may be obtained if they are used.
+
 .RE
 
 .sp