getgrent.c revision 2830:5228d1267a01
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 2006 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#pragma ident	"%Z%%M%	%I%	%E% SMI"
31
32#include <grp.h>
33#include <pwd.h>
34#include "nis_common.h"
35#include <ctype.h>
36#include <stdlib.h>
37#include <string.h>
38#include <rpc/auth.h>	/* for MAXNETNAMELEN */
39
40static nss_status_t netid_lookup(struct nss_groupsbymem *argp);
41
42static nss_status_t
43getbyname(be, a)
44	nis_backend_ptr_t	be;
45	void			*a;
46{
47	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
48
49	return (_nss_nis_lookup(be, argp, 0,
50				"group.byname", argp->key.name, 0));
51}
52
53static nss_status_t
54getbygid(be, a)
55	nis_backend_ptr_t	be;
56	void			*a;
57{
58	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
59	char			gidstr[12];	/* More than enough */
60
61	(void) snprintf(gidstr, 12, "%d", argp->key.gid);
62	return (_nss_nis_lookup(be, argp, 0, "group.bygid", gidstr, 0));
63}
64
65static nss_status_t
66getbymember(be, a)
67	nis_backend_ptr_t	be;
68	void			*a;
69{
70	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
71
72	if (strcmp(argp->username, "root") == 0) {
73		/*
74		 * Assume that "root" can only sensibly be in /etc/group,
75		 *   not in NIS or NIS+
76		 * If we don't do this, a hung name-service may cause
77		 *   a root login or su to hang.
78		 */
79		return (NSS_NOTFOUND);
80	}
81
82	if (argp->force_slow_way != 1) {
83		switch (netid_lookup(argp)) {
84		case NSS_SUCCESS:
85			/*
86			 * Return SUCESS only if array is full. Explained
87			 * in <nss_dbdefs.h>.
88			 */
89			return ((argp->numgids == argp->maxgids)
90			    ? NSS_SUCCESS
91			    : NSS_NOTFOUND);
92		case NSS_NOTFOUND:
93		case NSS_UNAVAIL:
94			/*
95			 * Failover to group map search if no luck with netid.
96			 */
97			break;
98		case NSS_TRYAGAIN:
99			return (NSS_TRYAGAIN);
100		}
101	}
102
103	return (_nss_nis_do_all(be, argp, argp->username,
104				(nis_do_all_func_t)argp->process_cstr));
105}
106
107static nis_backend_op_t group_ops[] = {
108	_nss_nis_destr,
109	_nss_nis_endent,
110	_nss_nis_setent,
111	_nss_nis_getent_rigid,
112	getbyname,
113	getbygid,
114	getbymember
115};
116
117/*ARGSUSED*/
118nss_backend_t *
119_nss_nis_group_constr(dummy1, dummy2, dummy3)
120	const char	*dummy1, *dummy2, *dummy3;
121{
122	return (_nss_nis_constr(group_ops,
123				sizeof (group_ops) / sizeof (group_ops[0]),
124				"group.byname"));
125}
126
127/*
128 * Add gid to gid_array if it's not already there. gid_array must have room
129 * for one more entry.  Return new size of array.
130 */
131static int
132add_gid(gid_t gid_array[], int numgids, gid_t gid)
133{
134	int i = 0;
135
136	for (i = 0; i < numgids; i++) {
137		if (gid_array[i] == gid) {
138			return (numgids);
139		}
140	}
141	gid_array[numgids++] = gid;
142	return (numgids);
143}
144
145/*
146 * Given buf, a null-terminated string containing the result of a successful
147 * netid lookup, add the gids to the gid_array.  The string may contain extra
148 * whitesapce.  On parse error, the valid portion of the gid_array is not
149 * modified.
150 */
151static int
152parse_netid(const char *buf, gid_t gid_array[], int maxgids, int *numgids_ptr)
153{
154	int	numgids = *numgids_ptr;
155	char	*buf_next;
156	gid_t	gid;
157	long	value;
158
159	/* Scan past "<uid>:" */
160	while (isspace(*buf) || isdigit(*buf)) {
161		buf++;
162	}
163
164	if (*buf++ != ':') {
165		return (NSS_STR_PARSE_PARSE);
166	}
167
168	/* buf should now point to a comma-separated list of gids */
169	while (*buf != '\0' && *buf != '\n') {
170		errno = 0;
171		value = strtol(buf, &buf_next, 10);
172
173		if (buf == buf_next) {
174			return (NSS_STR_PARSE_PARSE);
175		} else if ((value == LONG_MAX && errno == ERANGE) ||
176		    (ulong_t)value > INT_MAX) {
177			return (NSS_STR_PARSE_ERANGE);
178		}
179
180		gid = (gid_t)value;
181		if (numgids < maxgids) {
182			numgids = add_gid(gid_array, numgids, gid);
183		}
184		buf = buf_next;
185		if (*buf == ',') {
186			buf++;
187		}
188	}
189	*numgids_ptr = numgids;
190	return (NSS_STR_PARSE_SUCCESS);
191}
192
193
194/*
195 * Perform a lookup in the netid map.  Fill in the gid_array if successful.
196 * Return values are like those for _nss_nis_lookup().
197 */
198static nss_status_t
199netid_lookup(struct nss_groupsbymem *argp)
200{
201	const char	*domain = _nss_nis_domain();
202	struct passwd	pw;
203	char		pwbuf[NSS_BUFLEN_PASSWD];
204	char		netname[MAXNETNAMELEN + 1];
205	nss_status_t	res;
206	char		*val;
207	int		vallen;
208	int		parse_res;
209	char		*lasts;
210
211	/*
212	 * Need to build up the netname for the user manually. Can't use
213	 * user2netname() rpc library call, since that does all sorts of
214	 * extra stuff based upon its own private name-service switch.
215	 *
216	 * Note that "root" has no user netname so return in error.
217	 */
218	if ((getpwnam_r(argp->username, &pw, pwbuf, sizeof (pwbuf)) == NULL) ||
219	    (pw.pw_uid == 0)) {
220		return (NSS_UNAVAIL);
221	}
222	if (snprintf(netname, MAXNETNAMELEN + 1, "unix.%d@%s",
223	    pw.pw_uid, domain) < 0) {
224		return (NSS_UNAVAIL);
225	}
226
227	if ((res = _nss_nis_ypmatch(domain, "netid.byname", netname,
228			&val, &vallen, 0)) != NSS_SUCCESS) {
229		return (res);
230	}
231
232	(void) strtok_r(val, "#", &lasts);
233
234	parse_res = parse_netid(val, argp->gid_array, argp->maxgids,
235			&argp->numgids);
236	free(val);
237	return ((parse_res == NSS_STR_PARSE_SUCCESS)
238		? NSS_SUCCESS
239		: NSS_NOTFOUND);
240}
241