getgrent.c revision 11262:b7ebfbf2359e
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 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*
27 * nis/getgrent.c -- "nis" backend for nsswitch "group" database
28 */
29
30#include <grp.h>
31#include <pwd.h>
32#include "nis_common.h"
33#include <ctype.h>
34#include <stdlib.h>
35#include <string.h>
36#include <rpc/auth.h>	/* for MAXNETNAMELEN */
37
38static nss_status_t netid_lookup(struct nss_groupsbymem *argp);
39
40static nss_status_t
41getbyname(be, a)
42	nis_backend_ptr_t	be;
43	void			*a;
44{
45	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
46
47	return (_nss_nis_lookup(be, argp, 0,
48				"group.byname", argp->key.name, 0));
49}
50
51static nss_status_t
52getbygid(be, a)
53	nis_backend_ptr_t	be;
54	void			*a;
55{
56	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
57	char			gidstr[12];	/* More than enough */
58
59	if (argp->key.gid > MAXUID)
60		return (NSS_NOTFOUND);
61	(void) snprintf(gidstr, 12, "%d", argp->key.gid);
62	return (_nss_nis_lookup(be, argp, 0, "group.bygid", gidstr, 0));
63}
64
65/*
66 * Validates group entry replacing gid > MAXUID by GID_NOBODY.
67 */
68int
69validate_group_ids(char **linepp, int *linelenp, int allocbuf)
70{
71	char	*linep, *limit, *gidp, *newline;
72	ulong_t	gid;
73	int	oldgidlen, idlen;
74	int	linelen = *linelenp, newlinelen;
75
76	linep = *linepp;
77	limit = linep + linelen;
78
79	/* +/- entries valid for compat source only */
80	if (linelen == 0 || *linep == '+' || *linep == '-')
81		return (NSS_STR_PARSE_SUCCESS);
82
83	while (linep < limit && *linep++ != ':') /* skip groupname */
84		continue;
85	while (linep < limit && *linep++ != ':') /* skip password */
86		continue;
87	if (linep == limit)
88		return (NSS_STR_PARSE_PARSE);
89
90	gidp = linep;
91	gid = strtoul(gidp, (char **)&linep, 10); /* grab gid */
92	oldgidlen = linep - gidp;
93	if (linep >= limit || oldgidlen == 0)
94		return (NSS_STR_PARSE_PARSE);
95
96	if (gid <= MAXUID)
97		return (NSS_STR_PARSE_SUCCESS);
98
99	idlen = snprintf(NULL, 0, "%u", GID_NOBODY);
100	newlinelen = linelen + idlen - oldgidlen;
101	if (newlinelen > linelen) {
102		/* need a larger buffer */
103		if (!allocbuf || (newline = malloc(newlinelen + 1)) == NULL)
104			return (NSS_STR_PARSE_ERANGE);
105		/* Replace ephemeral ids by ID_NOBODY in the new buffer */
106		*(gidp - 1) = '\0';
107		(void) snprintf(newline, newlinelen + 1, "%s:%u%s",
108		    *linepp, GID_NOBODY, linep);
109		free(*linepp);
110		*linepp = newline;
111		*linelenp = newlinelen;
112		return (NSS_STR_PARSE_SUCCESS);
113	}
114
115	/* Replace ephemeral gid by GID_NOBODY in the same buffer */
116	(void) bcopy(linep, gidp + idlen, limit - linep + 1);
117	(void) snprintf(gidp, idlen + 1, "%u", GID_NOBODY);
118	*(gidp + idlen) = ':';
119	*linelenp = newlinelen;
120	return (NSS_STR_PARSE_SUCCESS);
121}
122
123static nss_status_t
124getbymember(be, a)
125	nis_backend_ptr_t	be;
126	void			*a;
127{
128	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
129
130	if (strcmp(argp->username, "root") == 0) {
131		/*
132		 * Assume that "root" can only sensibly be in /etc/group,
133		 *   not in NIS
134		 * If we don't do this, a hung name-service may cause
135		 *   a root login or su to hang.
136		 */
137		return (NSS_NOTFOUND);
138	}
139
140	if (argp->force_slow_way != 1) {
141		switch (netid_lookup(argp)) {
142		case NSS_SUCCESS:
143			/*
144			 * Return SUCESS only if array is full. Explained
145			 * in <nss_dbdefs.h>.
146			 */
147			return ((argp->numgids == argp->maxgids)
148			    ? NSS_SUCCESS
149			    : NSS_NOTFOUND);
150		case NSS_NOTFOUND:
151		case NSS_UNAVAIL:
152			/*
153			 * Failover to group map search if no luck with netid.
154			 */
155			break;
156		case NSS_TRYAGAIN:
157			return (NSS_TRYAGAIN);
158		}
159	}
160
161	return (_nss_nis_do_all(be, argp, argp->username,
162				(nis_do_all_func_t)argp->process_cstr));
163}
164
165static nis_backend_op_t group_ops[] = {
166	_nss_nis_destr,
167	_nss_nis_endent,
168	_nss_nis_setent,
169	_nss_nis_getent_rigid,
170	getbyname,
171	getbygid,
172	getbymember
173};
174
175/*ARGSUSED*/
176nss_backend_t *
177_nss_nis_group_constr(dummy1, dummy2, dummy3)
178	const char	*dummy1, *dummy2, *dummy3;
179{
180	return (_nss_nis_constr(group_ops,
181				sizeof (group_ops) / sizeof (group_ops[0]),
182				"group.byname"));
183}
184
185/*
186 * Add gid to gid_array if it's not already there. gid_array must have room
187 * for one more entry.  Return new size of array.
188 */
189static int
190add_gid(gid_t gid_array[], int numgids, gid_t gid)
191{
192	int i = 0;
193
194	for (i = 0; i < numgids; i++) {
195		if (gid_array[i] == gid) {
196			return (numgids);
197		}
198	}
199	gid_array[numgids++] = gid;
200	return (numgids);
201}
202
203/*
204 * Given buf, a null-terminated string containing the result of a successful
205 * netid lookup, add the gids to the gid_array.  The string may contain extra
206 * whitesapce.  On parse error, the valid portion of the gid_array is not
207 * modified.
208 */
209static int
210parse_netid(const char *buf, gid_t gid_array[], int maxgids, int *numgids_ptr)
211{
212	int	numgids = *numgids_ptr;
213	char	*buf_next;
214	gid_t	gid;
215	long	value;
216
217	/* Scan past "<uid>:" */
218	while (isspace(*buf) || isdigit(*buf)) {
219		buf++;
220	}
221
222	if (*buf++ != ':') {
223		return (NSS_STR_PARSE_PARSE);
224	}
225
226	/* buf should now point to a comma-separated list of gids */
227	while (*buf != '\0' && *buf != '\n') {
228		errno = 0;
229		value = strtol(buf, &buf_next, 10);
230
231		if (buf == buf_next) {
232			return (NSS_STR_PARSE_PARSE);
233		} else if ((value == LONG_MAX && errno == ERANGE) ||
234		    (ulong_t)value > INT_MAX) {
235			return (NSS_STR_PARSE_ERANGE);
236		}
237
238		gid = (gid_t)value;
239		if (numgids < maxgids) {
240			numgids = add_gid(gid_array, numgids, gid);
241		}
242		buf = buf_next;
243		if (*buf == ',') {
244			buf++;
245		}
246	}
247	*numgids_ptr = numgids;
248	return (NSS_STR_PARSE_SUCCESS);
249}
250
251
252/*
253 * Perform a lookup in the netid map.  Fill in the gid_array if successful.
254 * Return values are like those for _nss_nis_lookup().
255 */
256static nss_status_t
257netid_lookup(struct nss_groupsbymem *argp)
258{
259	const char	*domain = _nss_nis_domain();
260	struct passwd	pw;
261	char		pwbuf[NSS_BUFLEN_PASSWD];
262	char		netname[MAXNETNAMELEN + 1];
263	nss_status_t	res;
264	char		*val;
265	int		vallen;
266	int		parse_res;
267	char		*lasts;
268
269	/*
270	 * Need to build up the netname for the user manually. Can't use
271	 * user2netname() rpc library call, since that does all sorts of
272	 * extra stuff based upon its own private name-service switch.
273	 *
274	 * Note that "root" has no user netname so return in error.
275	 */
276	if ((getpwnam_r(argp->username, &pw, pwbuf, sizeof (pwbuf)) == NULL) ||
277	    (pw.pw_uid == 0)) {
278		return (NSS_UNAVAIL);
279	}
280	if (snprintf(netname, MAXNETNAMELEN + 1, "unix.%d@%s",
281	    pw.pw_uid, domain) < 0) {
282		return (NSS_UNAVAIL);
283	}
284
285	if ((res = _nss_nis_ypmatch(domain, "netid.byname", netname,
286	    &val, &vallen, 0)) != NSS_SUCCESS) {
287		return (res);
288	}
289
290	(void) strtok_r(val, "#", &lasts);
291
292	parse_res = parse_netid(val, argp->gid_array, argp->maxgids,
293	    &argp->numgids);
294	free(val);
295	return ((parse_res == NSS_STR_PARSE_SUCCESS)
296	    ? NSS_SUCCESS : NSS_NOTFOUND);
297}
298