xref: /illumos-gate/usr/src/lib/nsswitch/ldap/common/getgrent.c (revision b6805bf78d2bbbeeaea8909a05623587b42d58b3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <grp.h>
27 #include "ldap_common.h"
28 #include <string.h>
29 
30 /* String which may need to be removed from beginning of group password */
31 #define	_CRYPT		"{CRYPT}"
32 #define	_NO_PASSWD_VAL	""
33 
34 /* Group attributes filters */
35 #define	_G_NAME		"cn"
36 #define	_G_GID		"gidnumber"
37 #define	_G_PASSWD	"userpassword"
38 #define	_G_MEM		"memberuid"
39 
40 #define	_F_GETGRNAM	"(&(objectClass=posixGroup)(cn=%s))"
41 #define	_F_GETGRNAM_SSD	"(&(%%s)(cn=%s))"
42 #define	_F_GETGRGID	"(&(objectClass=posixGroup)(gidNumber=%u))"
43 #define	_F_GETGRGID_SSD	"(&(%%s)(gidNumber=%u))"
44 /*
45  * Group membership can be defined by either username or DN, so when searching
46  * for groups by member we need to consider both. The first parameter in the
47  * filter is replaced by username, the second by DN.
48  */
49 #define	_F_GETGRMEM \
50 	"(&(objectClass=posixGroup)(|(memberUid=%s)(memberUid=%s)))"
51 #define	_F_GETGRMEM_SSD	"(&(%%s)(|(memberUid=%s)(memberUid=%s)))"
52 
53 /*
54  * Copied from getpwnam.c, needed to look up user DN.
55  * Would it be better to move to ldap_common.h rather than duplicate?
56  */
57 #define	_F_GETPWNAM	"(&(objectClass=posixAccount)(uid=%s))"
58 #define	_F_GETPWNAM_SSD	"(&(%%s)(uid=%s))"
59 
60 static const char *gr_attrs[] = {
61 	_G_NAME,
62 	_G_GID,
63 	_G_PASSWD,
64 	_G_MEM,
65 	(char *)NULL
66 };
67 
68 
69 /*
70  * _nss_ldap_group2str is the data marshaling method for the group getXbyY
71  * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method
72  * is called after a successful ldap search has been performed. This method
73  * will parse the ldap search values into the file format.
74  * e.g.
75  *
76  * adm::4:root,adm,daemon
77  *
78  */
79 
80 static int
81 _nss_ldap_group2str(ldap_backend_ptr be, nss_XbyY_args_t *argp)
82 {
83 	int		i;
84 	int		nss_result;
85 	int		buflen = 0, len;
86 	int		firstime = 1;
87 	char		*buffer = NULL;
88 	ns_ldap_result_t	*result = be->result;
89 	char		**gname, **passwd, **gid, *password, *end;
90 	char		gid_nobody[NOBODY_STR_LEN];
91 	char		*gid_nobody_v[1];
92 	char		*member_str, *strtok_state;
93 	ns_ldap_attr_t	*members;
94 
95 	(void) snprintf(gid_nobody, sizeof (gid_nobody), "%u", GID_NOBODY);
96 	gid_nobody_v[0] = gid_nobody;
97 
98 	if (result == NULL)
99 		return (NSS_STR_PARSE_PARSE);
100 	buflen = argp->buf.buflen;
101 
102 	if (argp->buf.result != NULL) {
103 		if ((be->buffer = calloc(1, buflen)) == NULL) {
104 			nss_result = NSS_STR_PARSE_PARSE;
105 			goto result_grp2str;
106 		}
107 		buffer = be->buffer;
108 	} else
109 		buffer = argp->buf.buffer;
110 
111 	nss_result = NSS_STR_PARSE_SUCCESS;
112 	(void) memset(buffer, 0, buflen);
113 
114 	gname = __ns_ldap_getAttr(result->entry, _G_NAME);
115 	if (gname == NULL || gname[0] == NULL || (strlen(gname[0]) < 1)) {
116 		nss_result = NSS_STR_PARSE_PARSE;
117 		goto result_grp2str;
118 	}
119 	passwd = __ns_ldap_getAttr(result->entry, _G_PASSWD);
120 	if (passwd == NULL || passwd[0] == NULL || (strlen(passwd[0]) == 0)) {
121 		/* group password could be NULL, replace it with "" */
122 		password = _NO_PASSWD_VAL;
123 	} else {
124 		/*
125 		 * Preen "{crypt}" if necessary.
126 		 * If the password does not include the {crypt} prefix
127 		 * then the password may be plain text.  And thus
128 		 * perhaps crypt(3c) should be used to encrypt it.
129 		 * Currently the password is copied verbatim.
130 		 */
131 		if (strncasecmp(passwd[0], _CRYPT, strlen(_CRYPT)) == 0)
132 			password = passwd[0] + strlen(_CRYPT);
133 		else
134 			password = passwd[0];
135 	}
136 	gid = __ns_ldap_getAttr(result->entry, _G_GID);
137 	if (gid == NULL || gid[0] == NULL || (strlen(gid[0]) < 1)) {
138 		nss_result = NSS_STR_PARSE_PARSE;
139 		goto result_grp2str;
140 	}
141 	/* Validate GID */
142 	if (strtoul(gid[0], &end, 10) > MAXUID)
143 		gid = gid_nobody_v;
144 	len = snprintf(buffer, buflen, "%s:%s:%s:", gname[0], password, gid[0]);
145 	TEST_AND_ADJUST(len, buffer, buflen, result_grp2str);
146 
147 	members = __ns_ldap_getAttrStruct(result->entry, _G_MEM);
148 	if (members == NULL || members->attrvalue == NULL) {
149 		/* no member is fine, skip processing the member list */
150 		goto nomember;
151 	}
152 
153 	for (i = 0; i < members->value_count; i++) {
154 		if (members->attrvalue[i] == NULL) {
155 			nss_result = NSS_STR_PARSE_PARSE;
156 			goto result_grp2str;
157 		}
158 		/*
159 		 * If we find an '=' in the member attribute value, treat it as
160 		 * a DN, otherwise as a username.
161 		 */
162 		if (member_str = strchr(members->attrvalue[i], '=')) {
163 			member_str++; /* skip over the '=' */
164 			/* Fail if we can't pull a username out of the RDN */
165 			if (! (member_str = strtok_r(member_str,
166 			    ",", &strtok_state))) {
167 				nss_result = NSS_STR_PARSE_PARSE;
168 				goto result_grp2str;
169 			}
170 		} else {
171 			member_str = members->attrvalue[i];
172 		}
173 		if (*member_str != '\0') {
174 			if (firstime) {
175 				len = snprintf(buffer, buflen, "%s",
176 				    member_str);
177 				TEST_AND_ADJUST(len, buffer, buflen,
178 				    result_grp2str);
179 				firstime = 0;
180 			} else {
181 				len = snprintf(buffer, buflen, ",%s",
182 				    member_str);
183 				TEST_AND_ADJUST(len, buffer, buflen,
184 				    result_grp2str);
185 			}
186 		}
187 	}
188 nomember:
189 	/* The front end marshaller doesn't need the trailing nulls */
190 	if (argp->buf.result != NULL)
191 		be->buflen = strlen(be->buffer);
192 result_grp2str:
193 	(void) __ns_ldap_freeResult(&be->result);
194 	return (nss_result);
195 }
196 
197 /*
198  * getbynam gets a group entry by name. This function constructs an ldap
199  * search filter using the name invocation parameter and the getgrnam search
200  * filter defined. Once the filter is constructed, we searche for a matching
201  * entry and marshal the data results into struct group for the frontend
202  * process. The function _nss_ldap_group2ent performs the data marshaling.
203  */
204 
205 static nss_status_t
206 getbynam(ldap_backend_ptr be, void *a)
207 {
208 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
209 	char		searchfilter[SEARCHFILTERLEN];
210 	char		userdata[SEARCHFILTERLEN];
211 	char		groupname[SEARCHFILTERLEN];
212 	int		ret;
213 
214 	if (_ldap_filter_name(groupname, argp->key.name, sizeof (groupname)) !=
215 	    0)
216 		return ((nss_status_t)NSS_NOTFOUND);
217 
218 	ret = snprintf(searchfilter, sizeof (searchfilter),
219 	    _F_GETGRNAM, groupname);
220 	if (ret >= sizeof (searchfilter) || ret < 0)
221 		return ((nss_status_t)NSS_NOTFOUND);
222 
223 	ret = snprintf(userdata, sizeof (userdata), _F_GETGRNAM_SSD, groupname);
224 	if (ret >= sizeof (userdata) || ret < 0)
225 		return ((nss_status_t)NSS_NOTFOUND);
226 
227 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
228 	    _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata));
229 }
230 
231 
232 /*
233  * getbygid gets a group entry by number. This function constructs an ldap
234  * search filter using the name invocation parameter and the getgrgid search
235  * filter defined. Once the filter is constructed, we searche for a matching
236  * entry and marshal the data results into struct group for the frontend
237  * process. The function _nss_ldap_group2ent performs the data marshaling.
238  */
239 
240 static nss_status_t
241 getbygid(ldap_backend_ptr be, void *a)
242 {
243 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
244 	char searchfilter[SEARCHFILTERLEN];
245 	char userdata[SEARCHFILTERLEN];
246 	int ret;
247 
248 	if (argp->key.uid > MAXUID)
249 		return ((nss_status_t)NSS_NOTFOUND);
250 
251 	ret = snprintf(searchfilter, sizeof (searchfilter),
252 	    _F_GETGRGID, argp->key.uid);
253 	if (ret >= sizeof (searchfilter) || ret < 0)
254 		return ((nss_status_t)NSS_NOTFOUND);
255 
256 	ret = snprintf(userdata, sizeof (userdata),
257 	    _F_GETGRGID_SSD, argp->key.uid);
258 	if (ret >= sizeof (userdata) || ret < 0)
259 		return ((nss_status_t)NSS_NOTFOUND);
260 
261 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
262 	    _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata));
263 
264 }
265 
266 
267 /*
268  * getbymember returns all groups a user is defined in. This function
269  * uses different architectural procedures than the other group backend
270  * system calls because it's a private interface. This function constructs
271  * an ldap search filter using the name invocation parameter. Once the
272  * filter is constructed, we search for all matching groups counting
273  * and storing each group name, gid, etc. Data marshaling is used for
274  * group processing. The function _nss_ldap_group2ent() performs the
275  * data marshaling.
276  *
277  * (const char *)argp->username;	(size_t)strlen(argp->username);
278  * (gid_t)argp->gid_array;		(int)argp->maxgids;
279  * (int)argp->numgids;
280  */
281 
282 static nss_status_t
283 getbymember(ldap_backend_ptr be, void *a)
284 {
285 	int			i, j, k;
286 	int			gcnt = (int)0;
287 	char			**groupvalue, **membervalue, *member_str;
288 	char			*strtok_state;
289 	nss_status_t		lstat;
290 	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
291 	char			searchfilter[SEARCHFILTERLEN];
292 	char			userdata[SEARCHFILTERLEN];
293 	char			name[SEARCHFILTERLEN];
294 	ns_ldap_result_t	*result;
295 	ns_ldap_entry_t		*curEntry;
296 	char			*username, **dn_attr, *dn;
297 	gid_t			gid;
298 	int			ret;
299 
300 	if (strcmp(argp->username, "") == 0 ||
301 	    strcmp(argp->username, "root") == 0)
302 		return ((nss_status_t)NSS_NOTFOUND);
303 
304 	if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
305 		return ((nss_status_t)NSS_NOTFOUND);
306 
307 	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETPWNAM, name);
308 	if (ret >= sizeof (searchfilter) || ret < 0)
309 		return ((nss_status_t)NSS_NOTFOUND);
310 
311 	ret = snprintf(userdata, sizeof (userdata), _F_GETPWNAM_SSD, name);
312 	if (ret >= sizeof (userdata) || ret < 0)
313 		return ((nss_status_t)NSS_NOTFOUND);
314 
315 	/*
316 	 * Look up the user DN in ldap. If it's not found, search solely by
317 	 * username.
318 	 */
319 	lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
320 	    _PASSWD, searchfilter, NULL, _merge_SSD_filter, userdata);
321 	if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
322 		return ((nss_status_t)lstat);
323 
324 	if (be->result == NULL ||
325 	    !(dn_attr = __ns_ldap_getAttr(be->result->entry, "dn")))
326 		dn = name;
327 	else
328 		dn = dn_attr[0];
329 
330 	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name,
331 	    dn);
332 	if (ret >= sizeof (searchfilter) || ret < 0)
333 		return ((nss_status_t)NSS_NOTFOUND);
334 
335 	ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name,
336 	    dn);
337 	if (ret >= sizeof (userdata) || ret < 0)
338 		return ((nss_status_t)NSS_NOTFOUND);
339 
340 	/*
341 	 * Free up resources from user DN search before performing group
342 	 * search.
343 	 */
344 	(void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
345 
346 	gcnt = (int)argp->numgids;
347 	lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, NULL,
348 	    _GROUP, searchfilter, NULL, _merge_SSD_filter, userdata);
349 	if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
350 		return ((nss_status_t)lstat);
351 	if (be->result == NULL)
352 		return (NSS_NOTFOUND);
353 	username = (char *)argp->username;
354 	result = (ns_ldap_result_t *)be->result;
355 	curEntry = (ns_ldap_entry_t *)result->entry;
356 	for (i = 0; i < result->entries_count; i++) {
357 		membervalue = __ns_ldap_getAttr(curEntry, "memberUid");
358 		if (membervalue) {
359 			for (j = 0; membervalue[j]; j++) {
360 				/*
361 				 * If we find an '=' in the member attribute
362 				 * value, treat it as a DN, otherwise as a
363 				 * username.
364 				 */
365 				if (member_str = strchr(membervalue[j], '=')) {
366 					member_str++; /* skip over the '=' */
367 					member_str = strtok_r(member_str, ",",
368 					    &strtok_state);
369 				} else {
370 					member_str = membervalue[j];
371 				}
372 				if (member_str &&
373 				    strcmp(member_str, username) == NULL) {
374 					groupvalue = __ns_ldap_getAttr(curEntry,
375 					    "gidnumber");
376 					gid = (gid_t)strtol(groupvalue[0],
377 					    (char **)NULL, 10);
378 					if (argp->numgids < argp->maxgids) {
379 						for (k = 0; k < argp->numgids;
380 						    k++) {
381 							if (argp->gid_array[k]
382 							    == gid)
383 						    /* already exists */
384 						break;
385 					}
386 					if (k == argp->numgids)
387 						argp->gid_array[argp->numgids++]
388 						    = gid;
389 					}
390 					break;
391 				}
392 			}
393 		}
394 		curEntry = curEntry->next;
395 	}
396 
397 	(void) __ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
398 	if (gcnt == argp->numgids)
399 		return ((nss_status_t)NSS_NOTFOUND);
400 
401 	/*
402 	 * Return NSS_SUCCESS only if array is full.
403 	 * Explained in <nss_dbdefs.h>.
404 	 */
405 	return ((nss_status_t)((argp->numgids == argp->maxgids)
406 	    ? NSS_SUCCESS
407 	    : NSS_NOTFOUND));
408 }
409 
410 static ldap_backend_op_t gr_ops[] = {
411 	_nss_ldap_destr,
412 	_nss_ldap_endent,
413 	_nss_ldap_setent,
414 	_nss_ldap_getent,
415 	getbynam,
416 	getbygid,
417 	getbymember
418 };
419 
420 
421 /*ARGSUSED0*/
422 nss_backend_t *
423 _nss_ldap_group_constr(const char *dummy1, const char *dummy2,
424 			const char *dummy3)
425 {
426 
427 	return ((nss_backend_t *)_nss_ldap_constr(gr_ops,
428 	    sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs,
429 	    _nss_ldap_group2str));
430 }
431