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 *	getgrent.c
23 *
24 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
25 * Use is subject to license terms.
26 *
27 * lib/nsswitch/compat/getgrent.c -- name-service-switch backend for getgrnam()
28 *   et al that does 4.x compatibility.  It looks in /etc/group; if it finds
29 *   group entries there that begin with "+" or "-", it consults other
30 *   services.  By default it uses NIS (YP), but the user can override this
31 *   with a "group_compat" entry in /etc/nsswitch.conf, e.g.
32 *			group_compat: nisplus
33 *
34 * This code tries to produce the same results as the 4.x code, even when
35 *   the latter seems ill thought-out.  Bug-compatible, in other words.
36 *   Though we do try to be more reasonable about the format of "+" and "-"
37 *   entries here, i.e. you don't have to pad them with spurious colons and
38 *   bogus uid/gid values.
39 *
40 * Caveats:
41 *    -	More than one source may be specified, with the usual switch semantics,
42 *	but having multiple sources here is definitely odd.
43 *    -	People who recursively specify "compat" deserve what they get.
44 */
45
46#pragma ident	"%Z%%M%	%I%	%E% SMI"
47
48#include <grp.h>
49#include <stdlib.h>
50#include <unistd.h>		/* for GF_PATH */
51#include <strings.h>
52#include "compat_common.h"
53
54static DEFINE_NSS_DB_ROOT(db_root);
55
56static void
57_nss_initf_group_compat(p)
58	nss_db_params_t	*p;
59{
60	p->name		  = NSS_DBNAM_GROUP;
61	p->config_name	  = NSS_DBNAM_GROUP_COMPAT;
62	p->default_config = NSS_DEFCONF_GROUP_COMPAT;
63}
64
65static const char *
66get_grname(argp)
67	nss_XbyY_args_t		*argp;
68{
69	struct group		*g = (struct group *)argp->returnval;
70
71	return (g->gr_name);
72}
73
74static int
75check_grname(argp)
76	nss_XbyY_args_t		*argp;
77{
78	struct group		*g = (struct group *)argp->returnval;
79
80	return (strcmp(g->gr_name, argp->key.name) == 0);
81}
82
83static nss_status_t
84getbyname(be, a)
85	compat_backend_ptr_t	be;
86	void			*a;
87{
88	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
89
90	return (_nss_compat_XY_all(be, argp, check_grname,
91				NSS_DBOP_GROUP_BYNAME));
92}
93
94static int
95check_grgid(argp)
96	nss_XbyY_args_t		*argp;
97{
98	struct group		*g = (struct group *)argp->returnval;
99
100	return (g->gr_gid == argp->key.gid);
101}
102
103static nss_status_t
104getbygid(be, a)
105	compat_backend_ptr_t	be;
106	void			*a;
107{
108	nss_XbyY_args_t		*argp = (nss_XbyY_args_t *)a;
109
110	return (_nss_compat_XY_all(be, argp, check_grgid,
111				NSS_DBOP_GROUP_BYGID));
112}
113
114static nss_status_t
115getbymember(be, a)
116	compat_backend_ptr_t	be;
117	void			*a;
118{
119	struct nss_groupsbymem	*argp = (struct nss_groupsbymem *)a;
120	int			numgids = argp->numgids;
121	int			maxgids = argp->maxgids;
122	gid_t			*gid_array = argp->gid_array;
123	struct nss_XbyY_args	grargs;
124	struct group		*g;
125	nss_XbyY_buf_t	*gb = NULL, *b = NULL;
126
127	/*
128	 * Generic implementation:  enumerate using getent(), then check each
129	 *   group returned by getent() to see whether it contains the user.
130	 *   There are much faster ways, but at least this one gets the right
131	 *   answer.
132	 */
133	if (numgids >= maxgids) {
134		/* full gid_array;  nobody should have bothered to call us */
135		return (NSS_SUCCESS);
136	}
137
138	b = NSS_XbyY_ALLOC(&gb, sizeof (struct group), NSS_BUFLEN_GROUP);
139	if (b == 0)
140		return (NSS_UNAVAIL);
141
142	NSS_XbyY_INIT(&grargs, gb->result, gb->buffer, gb->buflen,
143		argp->str2ent);
144	g = (struct group *)gb->result;
145
146	(void) _nss_compat_setent(be, 0);
147	while (_nss_compat_getent(be, &grargs) == NSS_SUCCESS) {
148		char		**mem;
149
150		if (grargs.returnval == 0) {
151			continue;
152		}
153		for (mem = g->gr_mem;  *mem != 0;  mem++) {
154			if (strcmp(*mem, argp->username) == 0) {
155				int	gid = g->gr_gid;
156				int	i;
157				for (i = 0;  i < numgids;  i++) {
158					if (gid == gid_array[i]) {
159						break;
160					}
161				}
162				if (i == numgids) {
163					gid_array[numgids++] = gid;
164					argp->numgids = numgids;
165					if (numgids >= maxgids) {
166						/* filled the gid_array */
167						(void) _nss_compat_endent(be,
168								0);
169						NSS_XbyY_FREE(&gb);
170						return (NSS_SUCCESS);
171					}
172					/* Done with this group, try next */
173					break;
174				}
175			}
176		}
177	}
178	(void) _nss_compat_endent(be, 0);
179	NSS_XbyY_FREE(&gb);
180	return (NSS_NOTFOUND);	/* Really means "gid_array not full yet" */
181}
182
183/*ARGSUSED*/
184static int
185merge_grents(be, argp, fields)
186	compat_backend_ptr_t	be;
187	nss_XbyY_args_t		*argp;
188	const char		**fields;
189{
190	struct group		*g	= (struct group *)argp->buf.result;
191	char			*buf;
192	char			*s;
193	int			parsestat;
194	int			dlen;
195
196	/*
197	 * We're allowed to override the passwd (has anyone ever actually used
198	 *   the passwd in a group entry?) and the membership list, but not
199	 *   the groupname or the gid.
200	 * That's what the SunOS 4.x code did;  who are we to question it...
201	 *
202	 * Efficiency is heartlessly abandoned in the quest for simplicity.
203	 */
204	if (fields[1] == 0 && fields[3] == 0 &&
205			be->return_string_data != 1) {
206		/* No legal overrides, leave *argp unscathed */
207		return (NSS_STR_PARSE_SUCCESS);
208	}
209	if ((buf = malloc(NSS_LINELEN_GROUP)) == 0) {
210		return (NSS_STR_PARSE_PARSE);
211		/* Really "out of memory", but PARSE_PARSE will have to do */
212	}
213	s = buf;
214	(void) snprintf(s, NSS_LINELEN_GROUP, "%s:%s:%d:",
215		g->gr_name,
216		fields[1] != 0 ? fields[1] : g->gr_passwd,
217		g->gr_gid);
218	s += strlen(s);
219	if (fields[3] != 0) {
220		(void) strcpy(s, fields[3]);
221		s += strlen(s);
222	} else {
223		char	**memp;
224
225		for (memp = g->gr_mem;  *memp != 0;  memp++) {
226			size_t	len = strlen(*memp);
227			if (s + len + 1 <= buf + NSS_LINELEN_GROUP) {
228				if (memp != g->gr_mem) {
229					*s++ = ',';
230				}
231				(void) memcpy(s, *memp, len);
232				s += len;
233			} else {
234				free(buf);
235				return (NSS_STR_PARSE_ERANGE);
236			}
237		}
238	}
239
240	dlen = s - buf;
241
242	/*
243	 * if asked, return the data in /etc file format
244	 */
245	if (be->return_string_data == 1) {
246		/* reset the result ptr to the original value */
247		argp->buf.result = NULL;
248
249		if (dlen > argp->buf.buflen) {
250			parsestat = NSS_STR_PARSE_ERANGE;
251		} else {
252			(void) strncpy(argp->buf.buffer, buf, dlen);
253			argp->returnval = argp->buf.buffer;
254			argp->returnlen = dlen;
255			parsestat = NSS_SUCCESS;
256		}
257	} else {
258		parsestat = (*argp->str2ent)(buf, dlen,
259				    argp->buf.result,
260				    argp->buf.buffer,
261				    argp->buf.buflen);
262	}
263
264	free(buf);
265	return (parsestat);
266}
267
268static compat_backend_op_t group_ops[] = {
269	_nss_compat_destr,
270	_nss_compat_endent,
271	_nss_compat_setent,
272	_nss_compat_getent,
273	getbyname,
274	getbygid,
275	getbymember
276};
277
278/*ARGSUSED*/
279nss_backend_t *
280_nss_compat_group_constr(dummy1, dummy2, dummy3)
281	const char	*dummy1, *dummy2, *dummy3;
282{
283	return (_nss_compat_constr(group_ops,
284				sizeof (group_ops) / sizeof (group_ops[0]),
285				GF_PATH,
286				NSS_LINELEN_GROUP,
287				&db_root,
288				_nss_initf_group_compat,
289				0,
290				get_grname,
291				merge_grents));
292}
293