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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <grp.h>
30 #include "ldap_common.h"
31 
32 /* String which may need to be removed from beginning of group password */
33 #define	_CRYPT		"{CRYPT}"
34 #define	_NO_PASSWD_VAL	""
35 
36 /* Group attributes filters */
37 #define	_G_NAME		"cn"
38 #define	_G_GID		"gidnumber"
39 #define	_G_PASSWD	"userpassword"
40 #define	_G_MEM		"memberuid"
41 
42 #define	_F_GETGRNAM	"(&(objectClass=posixGroup)(cn=%s))"
43 #define	_F_GETGRNAM_SSD	"(&(%%s)(cn=%s))"
44 #define	_F_GETGRGID	"(&(objectClass=posixGroup)(gidNumber=%ld))"
45 #define	_F_GETGRGID_SSD	"(&(%%s)(gidNumber=%ld))"
46 #define	_F_GETGRMEM	"(&(objectClass=posixGroup)(memberUid=%s))"
47 #define	_F_GETGRMEM_SSD	"(&(%%s)(memberUid=%s))"
48 
49 static const char *gr_attrs[] = {
50 	_G_NAME,
51 	_G_GID,
52 	_G_PASSWD,
53 	_G_MEM,
54 	(char *)NULL
55 };
56 
57 
58 /*
59  * _nss_ldap_group2ent is the data marshaling method for the group getXbyY
60  * (e.g., getgrnam(), getgrgid(), getgrent()) backend processes. This method
61  * is called after a successful ldap search has been performed. This method
62  * will parse the ldap search values into struct group = argp->buf.buffer
63  * which the frontend process expects. Three error conditions are expected
64  * and returned to nsswitch.
65  */
66 
67 static int
68 _nss_ldap_group2ent(ldap_backend_ptr be, nss_XbyY_args_t *argp)
69 {
70 	int		i, j;
71 	int		nss_result;
72 	int		buflen = (int)0;
73 	int		firstime = (int)1;
74 	unsigned long	len = 0L;
75 	char		**mp = NULL;
76 	char		*val = (char *)NULL;
77 	char		*buffer = (char *)NULL;
78 	char		*ceiling = (char *)NULL;
79 	struct group	*grp = (struct group *)NULL;
80 	ns_ldap_result_t	*result = be->result;
81 	ns_ldap_attr_t	*attrptr;
82 
83 	buffer = argp->buf.buffer;
84 	buflen = (size_t)argp->buf.buflen;
85 	if (!argp->buf.result) {
86 		nss_result = (int)NSS_STR_PARSE_ERANGE;
87 		goto result_grp2ent;
88 	}
89 	grp = (struct group *)argp->buf.result;
90 	ceiling = buffer + buflen;
91 	mp = grp->gr_mem = (char **)NULL;
92 
93 	/* initialize no group password */
94 	grp->gr_passwd = (char *)NULL;
95 	nss_result = (int)NSS_STR_PARSE_SUCCESS;
96 	(void) memset(argp->buf.buffer, 0, buflen);
97 
98 	attrptr = getattr(result, 0);
99 	if (attrptr == NULL) {
100 		nss_result = (int)NSS_STR_PARSE_PARSE;
101 		goto result_grp2ent;
102 	}
103 
104 	for (i = 0; i < result->entry->attr_count; i++) {
105 		attrptr = getattr(result, i);
106 		if (attrptr == NULL) {
107 			nss_result = (int)NSS_STR_PARSE_PARSE;
108 			goto result_grp2ent;
109 		}
110 		if (strcasecmp(attrptr->attrname, _G_NAME) == 0) {
111 			if ((attrptr->attrvalue[0] == NULL) ||
112 			    (len = strlen(attrptr->attrvalue[0])) < 1) {
113 				nss_result = (int)NSS_STR_PARSE_PARSE;
114 				goto result_grp2ent;
115 			}
116 			grp->gr_name = buffer;
117 			buffer += len + 1;
118 			if (buffer > ceiling) {
119 				nss_result = (int)NSS_STR_PARSE_ERANGE;
120 				goto result_grp2ent;
121 			}
122 			(void) strcpy(grp->gr_name, attrptr->attrvalue[0]);
123 			continue;
124 		}
125 		if (strcasecmp(attrptr->attrname, _G_PASSWD) == 0) {
126 			val = attrptr->attrvalue[0];
127 			/*
128 			 * Preen "{crypt}" if necessary.
129 			 * If the password does not include the {crypt} prefix
130 			 * then the password may be plain text.  And thus
131 			 * perhaps crypt(3c) should be used to encrypt it.
132 			 * Currently the password is copied verbatim.
133 			 */
134 			if (strncasecmp(val, _CRYPT,
135 			    (sizeof (_CRYPT) - 1)) == 0)
136 				val += (sizeof (_CRYPT) - 1);
137 			len = strlen(val);
138 			grp->gr_passwd = buffer;
139 			buffer += len + 1;
140 			if (buffer > ceiling) {
141 				nss_result = (int)NSS_STR_PARSE_ERANGE;
142 				goto result_grp2ent;
143 			}
144 			(void) strcpy(grp->gr_passwd, val);
145 			continue;
146 		}
147 		if (strcasecmp(attrptr->attrname, _G_GID) == 0) {
148 			if (strlen(attrptr->attrvalue[0]) == 0) {
149 				nss_result = (int)NSS_STR_PARSE_PARSE;
150 				goto result_grp2ent;
151 			}
152 			errno = 0;
153 			grp->gr_gid = (gid_t)strtol(attrptr->attrvalue[0],
154 						    (char **)NULL, 10);
155 			if (errno != 0) {
156 				nss_result = (int)NSS_STR_PARSE_PARSE;
157 				goto result_grp2ent;
158 			}
159 			continue;
160 		}
161 		if (strcasecmp(attrptr->attrname, _G_MEM) == 0) {
162 			for (j = 0; j < attrptr->value_count; j++) {
163 				if (firstime) {
164 					mp = grp->gr_mem =
165 						    (char **)ROUND_UP(buffer,
166 						    sizeof (char **));
167 					buffer = (char *)grp->gr_mem +
168 						    sizeof (char *) *
169 						    (attrptr->value_count + 1);
170 					buffer = (char *)ROUND_UP(buffer,
171 						    sizeof (char **));
172 					if (buffer > ceiling) {
173 						nss_result =
174 						    (int)NSS_STR_PARSE_ERANGE;
175 						goto result_grp2ent;
176 					}
177 					firstime = (int)0;
178 				}
179 				if (attrptr->attrvalue[j] == NULL) {
180 					nss_result = (int)NSS_STR_PARSE_PARSE;
181 					goto result_grp2ent;
182 				}
183 				len = strlen(attrptr->attrvalue[j]);
184 				if (len == 0)
185 					continue;
186 				*mp = buffer;
187 				buffer += len + 1;
188 				if (buffer > ceiling) {
189 					nss_result = (int)NSS_STR_PARSE_ERANGE;
190 					goto result_grp2ent;
191 				}
192 				(void) strcpy(*mp++, attrptr->attrvalue[j]);
193 				continue;
194 			}
195 		}
196 	}
197 	/* Don't leave password as null */
198 	if (grp->gr_passwd == (char *)NULL) {
199 		/*
200 		 * The password may be missing; rfc2307bis defines
201 		 * the 'posixGroup' attributes 'authPassword' and
202 		 * 'userPassword' as being optional.  Or a directory
203 		 * access control may be preventing us from reading
204 		 * the password.  Currently we don't know which it is.
205 		 * If it's an access problem then perhaps the password
206 		 * should be set to "*NP*".  But for now a simple empty
207 		 * string is returned.
208 		 */
209 		grp->gr_passwd = buffer;
210 		buffer += sizeof (_NO_PASSWD_VAL);
211 		if (buffer > ceiling) {
212 			nss_result = (int)NSS_STR_PARSE_ERANGE;
213 			goto result_grp2ent;
214 		}
215 		(void) strcpy(grp->gr_passwd, _NO_PASSWD_VAL);
216 	}
217 	if (mp == NULL) {
218 		mp = grp->gr_mem = (char **)ROUND_UP(buffer, sizeof (char **));
219 		buffer = (char *)grp->gr_mem + sizeof (char *);
220 		buffer = (char *)ROUND_UP(buffer, sizeof (char **));
221 		if (buffer > ceiling) {
222 			nss_result = (int)NSS_STR_PARSE_ERANGE;
223 			goto result_grp2ent;
224 		}
225 	}
226 	*mp = NULL;
227 
228 #ifdef DEBUG
229 	(void) fprintf(stdout, "\n[getgrent.c: _nss_ldap_group2ent]\n");
230 	(void) fprintf(stdout, "       gr_name: [%s]\n", grp->gr_name);
231 	if (grp->gr_passwd != (char *)NULL)
232 		(void) fprintf(stdout, "     gr_passwd: [%s]\n",
233 			    grp->gr_passwd);
234 	(void) fprintf(stdout, "        gr_gid: [%ld]\n", grp->gr_gid);
235 	if (mp != NULL) {
236 		for (mp = grp->gr_mem; *mp != NULL; mp++)
237 			(void) fprintf(stdout, "        gr_mem: [%s]\n", *mp);
238 	}
239 #endif /* DEBUG */
240 
241 result_grp2ent:
242 
243 	(void) __ns_ldap_freeResult(&be->result);
244 	return ((int)nss_result);
245 }
246 
247 
248 /*
249  * getbynam gets a group entry by name. This function constructs an ldap
250  * search filter using the name invocation parameter and the getgrnam search
251  * filter defined. Once the filter is constructed, we searche for a matching
252  * entry and marshal the data results into struct group for the frontend
253  * process. The function _nss_ldap_group2ent performs the data marshaling.
254  */
255 
256 static nss_status_t
257 getbynam(ldap_backend_ptr be, void *a)
258 {
259 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
260 	char		searchfilter[SEARCHFILTERLEN];
261 	char		userdata[SEARCHFILTERLEN];
262 	char		groupname[SEARCHFILTERLEN];
263 	int		ret;
264 
265 #ifdef DEBUG
266 	(void) fprintf(stdout, "\n[getgrent.c: getbyname]\n");
267 #endif /* DBEUG */
268 	if (_ldap_filter_name(groupname, argp->key.name, sizeof (groupname))
269 			!= 0)
270 		return ((nss_status_t)NSS_NOTFOUND);
271 
272 	ret = snprintf(searchfilter, sizeof (searchfilter),
273 	    _F_GETGRNAM, groupname);
274 	if (ret >= sizeof (searchfilter) || ret < 0)
275 		return ((nss_status_t)NSS_NOTFOUND);
276 
277 	ret = snprintf(userdata, sizeof (userdata), _F_GETGRNAM_SSD, groupname);
278 	if (ret >= sizeof (userdata) || ret < 0)
279 		return ((nss_status_t)NSS_NOTFOUND);
280 
281 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
282 		_GROUP, searchfilter, NULL,
283 		_merge_SSD_filter, userdata));
284 }
285 
286 
287 /*
288  * getbygid gets a group entry by number. This function constructs an ldap
289  * search filter using the name invocation parameter and the getgrgid search
290  * filter defined. Once the filter is constructed, we searche for a matching
291  * entry and marshal the data results into struct group for the frontend
292  * process. The function _nss_ldap_group2ent performs the data marshaling.
293  */
294 
295 static nss_status_t
296 getbygid(ldap_backend_ptr be, void *a)
297 {
298 	nss_XbyY_args_t	*argp = (nss_XbyY_args_t *)a;
299 	char searchfilter[SEARCHFILTERLEN];
300 	char userdata[SEARCHFILTERLEN];
301 	int ret;
302 
303 #ifdef DEBUG
304 	(void) fprintf(stdout, "\n[getgrent.c: getbygid]\n");
305 #endif /* DBEUG */
306 	ret = snprintf(searchfilter, sizeof (searchfilter),
307 	    _F_GETGRGID, (long)argp->key.uid);
308 	if (ret >= sizeof (searchfilter) || ret < 0)
309 		return ((nss_status_t)NSS_NOTFOUND);
310 
311 	ret = snprintf(userdata, sizeof (userdata),
312 	    _F_GETGRGID_SSD, (long)argp->key.uid);
313 	if (ret >= sizeof (userdata) || ret < 0)
314 		return ((nss_status_t)NSS_NOTFOUND);
315 
316 	return ((nss_status_t)_nss_ldap_lookup(be, argp,
317 		_GROUP, searchfilter, NULL,
318 		_merge_SSD_filter, userdata));
319 
320 }
321 
322 
323 /*
324  * getbymember returns all groups a user is defined in. This function
325  * uses different architectural procedures than the other group backend
326  * system calls because it's a private interface. This function constructs
327  * an ldap search filter using the name invocation parameter. Once the
328  * filter is constructed, we search for all matching groups counting
329  * and storing each group name, gid, etc. Data marshaling is used for
330  * group processing. The function _nss_ldap_group2ent() performs the
331  * data marshaling.
332  *
333  * (const char *)argp->username;	(size_t)strlen(argp->username);
334  * (gid_t)argp->gid_array;		(int)argp->maxgids;
335  * (int)argp->numgids;
336  */
337 
338 static nss_status_t
339 getbymember(ldap_backend_ptr be, void *a)
340 {
341 	int			i, j, k;
342 	int			gcnt = (int)0;
343 	char			**groupvalue, **membervalue;
344 	nss_status_t		lstat;
345 	nss_XbyY_args_t		argb;
346 	static nss_XbyY_buf_t	*gb;
347 	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
348 	char			searchfilter[SEARCHFILTERLEN];
349 	char			userdata[SEARCHFILTERLEN];
350 	char			name[SEARCHFILTERLEN];
351 	ns_ldap_result_t	*result;
352 	ns_ldap_entry_t		*curEntry;
353 	char			*username;
354 	gid_t			gid;
355 	int			ret;
356 
357 #ifdef DEBUG
358 	(void) fprintf(stdout, "\n[getgrent.c: getbymember]\n");
359 #endif /* DBEUG */
360 
361 	NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP);
362 	NSS_XbyY_INIT(&argb, gb->result, gb->buffer, gb->buflen, 0);
363 
364 	if (strcmp(argp->username, "") == 0 ||
365 	    strcmp(argp->username, "root") == 0)
366 		return ((nss_status_t)NSS_NOTFOUND);
367 
368 	if (_ldap_filter_name(name, argp->username, sizeof (name)) != 0)
369 		return ((nss_status_t)NSS_NOTFOUND);
370 
371 	ret = snprintf(searchfilter, sizeof (searchfilter), _F_GETGRMEM, name);
372 	if (ret >= sizeof (searchfilter) || ret < 0)
373 		return ((nss_status_t)NSS_NOTFOUND);
374 
375 	ret = snprintf(userdata, sizeof (userdata), _F_GETGRMEM_SSD, name);
376 	if (ret >= sizeof (userdata) || ret < 0)
377 		return ((nss_status_t)NSS_NOTFOUND);
378 
379 	gcnt = (int)argp->numgids;
380 	lstat = (nss_status_t)_nss_ldap_nocb_lookup(be, &argb,
381 		_GROUP, searchfilter, NULL,
382 		_merge_SSD_filter, userdata);
383 	if (lstat != (nss_status_t)NS_LDAP_SUCCESS)
384 		return ((nss_status_t)lstat);
385 	if (be->result == NULL)
386 		return (NSS_NOTFOUND);
387 	username = (char *)argp->username;
388 	result = (ns_ldap_result_t *)be->result;
389 	curEntry = (ns_ldap_entry_t *)result->entry;
390 	for (i = 0; i < result->entries_count; i++) {
391 		membervalue = __ns_ldap_getAttr(curEntry, "memberUid");
392 		if (membervalue) {
393 			for (j = 0; membervalue[j]; j++) {
394 				if (strcmp(membervalue[j], username) == NULL) {
395 					groupvalue = __ns_ldap_getAttr(curEntry,
396 						"gidnumber");
397 					gid = (gid_t)strtol(groupvalue[0],
398 						(char **)NULL, 10);
399 					if (argp->numgids < argp->maxgids) {
400 					    for (k = 0; k < argp->numgids;
401 							k++) {
402 						if (argp->gid_array[k] == gid)
403 						    /* already exists */
404 						    break;
405 					    }
406 					    if (k == argp->numgids)
407 						argp->gid_array[argp->numgids++]
408 						    = gid;
409 					}
410 					break;
411 				}
412 			}
413 		}
414 		curEntry = curEntry->next;
415 	}
416 
417 	__ns_ldap_freeResult((ns_ldap_result_t **)&be->result);
418 	NSS_XbyY_FREE(&gb);
419 	if (gcnt == argp->numgids)
420 		return ((nss_status_t)NSS_NOTFOUND);
421 
422 	return ((nss_status_t)NSS_SUCCESS);
423 }
424 
425 static ldap_backend_op_t gr_ops[] = {
426 	_nss_ldap_destr,
427 	_nss_ldap_endent,
428 	_nss_ldap_setent,
429 	_nss_ldap_getent,
430 	getbynam,
431 	getbygid,
432 	getbymember
433 };
434 
435 
436 /*ARGSUSED0*/
437 nss_backend_t *
438 _nss_ldap_group_constr(const char *dummy1, const char *dummy2,
439 			const char *dummy3)
440 {
441 
442 	return ((nss_backend_t *)_nss_ldap_constr(gr_ops,
443 		sizeof (gr_ops)/sizeof (gr_ops[0]), _GROUP, gr_attrs,
444 		_nss_ldap_group2ent));
445 }
446