1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  *
26*0Sstevel@tonic-gate  * Common code and structures used by name-service-switch "compat" backends.
27*0Sstevel@tonic-gate  *
28*0Sstevel@tonic-gate  * Most of the code in the "compat" backend is a perverted form of code from
29*0Sstevel@tonic-gate  * the "files" backend;  this file is no exception.
30*0Sstevel@tonic-gate  */
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #include <stdio.h>
35*0Sstevel@tonic-gate #include <stdlib.h>
36*0Sstevel@tonic-gate #include <string.h>
37*0Sstevel@tonic-gate #include <ctype.h>
38*0Sstevel@tonic-gate #include <bsm/libbsm.h>
39*0Sstevel@tonic-gate #include <user_attr.h>
40*0Sstevel@tonic-gate #include "compat_common.h"
41*0Sstevel@tonic-gate #include "../../../libnsl/include/nsl_stdio_prv.h"
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate /*
44*0Sstevel@tonic-gate  * This should be in a header.
45*0Sstevel@tonic-gate  */
46*0Sstevel@tonic-gate 
47*0Sstevel@tonic-gate extern int yp_get_default_domain(char **domain);
48*0Sstevel@tonic-gate 
49*0Sstevel@tonic-gate /*
50*0Sstevel@tonic-gate  * Routines to manage list of "-" users for get{pw, sp, gr}ent().  Current
51*0Sstevel@tonic-gate  *   implementation is completely moronic; we use a linked list.  But then
52*0Sstevel@tonic-gate  *   that's what it's always done in 4.x...
53*0Sstevel@tonic-gate  */
54*0Sstevel@tonic-gate 
55*0Sstevel@tonic-gate struct setofstrings {
56*0Sstevel@tonic-gate 	char			*name;
57*0Sstevel@tonic-gate 	struct setofstrings	*next;
58*0Sstevel@tonic-gate 	/*
59*0Sstevel@tonic-gate 	 * === Should get smart and malloc the string and pointer as one
60*0Sstevel@tonic-gate 	 *	object rather than two.
61*0Sstevel@tonic-gate 	 */
62*0Sstevel@tonic-gate };
63*0Sstevel@tonic-gate typedef struct setofstrings	*strset_t;
64*0Sstevel@tonic-gate 
65*0Sstevel@tonic-gate static void
66*0Sstevel@tonic-gate strset_free(ssp)
67*0Sstevel@tonic-gate 	strset_t	*ssp;
68*0Sstevel@tonic-gate {
69*0Sstevel@tonic-gate 	strset_t	cur, nxt;
70*0Sstevel@tonic-gate 
71*0Sstevel@tonic-gate 	for (cur = *ssp;  cur != 0;  cur = nxt) {
72*0Sstevel@tonic-gate 		nxt = cur->next;
73*0Sstevel@tonic-gate 		free(cur->name);
74*0Sstevel@tonic-gate 		free(cur);
75*0Sstevel@tonic-gate 	}
76*0Sstevel@tonic-gate 	*ssp = 0;
77*0Sstevel@tonic-gate }
78*0Sstevel@tonic-gate 
79*0Sstevel@tonic-gate static boolean_t
80*0Sstevel@tonic-gate strset_add(ssp, nam)
81*0Sstevel@tonic-gate 	strset_t	*ssp;
82*0Sstevel@tonic-gate 	const char	*nam;
83*0Sstevel@tonic-gate {
84*0Sstevel@tonic-gate 	strset_t	new;
85*0Sstevel@tonic-gate 
86*0Sstevel@tonic-gate 	if (0 == (new = (strset_t)malloc(sizeof (*new)))) {
87*0Sstevel@tonic-gate 		return (B_FALSE);
88*0Sstevel@tonic-gate 	}
89*0Sstevel@tonic-gate 	if (0 == (new->name = malloc(strlen(nam) + 1))) {
90*0Sstevel@tonic-gate 		free(new);
91*0Sstevel@tonic-gate 		return (B_FALSE);
92*0Sstevel@tonic-gate 	}
93*0Sstevel@tonic-gate 	strcpy(new->name, nam);
94*0Sstevel@tonic-gate 	new->next = *ssp;
95*0Sstevel@tonic-gate 	*ssp = new;
96*0Sstevel@tonic-gate 	return (B_TRUE);
97*0Sstevel@tonic-gate }
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate static boolean_t
100*0Sstevel@tonic-gate strset_in(ssp, nam)
101*0Sstevel@tonic-gate 	const strset_t	*ssp;
102*0Sstevel@tonic-gate 	const char	*nam;
103*0Sstevel@tonic-gate {
104*0Sstevel@tonic-gate 	strset_t	cur;
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	for (cur = *ssp;  cur != 0;  cur = cur->next) {
107*0Sstevel@tonic-gate 		if (strcmp(cur->name, nam) == 0) {
108*0Sstevel@tonic-gate 			return (B_TRUE);
109*0Sstevel@tonic-gate 		}
110*0Sstevel@tonic-gate 	}
111*0Sstevel@tonic-gate 	return (B_FALSE);
112*0Sstevel@tonic-gate }
113*0Sstevel@tonic-gate 
114*0Sstevel@tonic-gate 
115*0Sstevel@tonic-gate struct compat_backend {
116*0Sstevel@tonic-gate 	compat_backend_op_t	*ops;
117*0Sstevel@tonic-gate 	int			n_ops;
118*0Sstevel@tonic-gate 	const char		*filename;
119*0Sstevel@tonic-gate 	__NSL_FILE		*f;
120*0Sstevel@tonic-gate 	int			minbuf;
121*0Sstevel@tonic-gate 	char			*buf;
122*0Sstevel@tonic-gate 	int			linelen;	/* <== Explain use, lifetime */
123*0Sstevel@tonic-gate 
124*0Sstevel@tonic-gate 	nss_db_initf_t		db_initf;
125*0Sstevel@tonic-gate 	nss_db_root_t		*db_rootp;	/* Shared between instances */
126*0Sstevel@tonic-gate 	nss_getent_t		db_context;	/* Per-instance enumeration */
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 	compat_get_name		getnamef;
129*0Sstevel@tonic-gate 	compat_merge_func	mergef;
130*0Sstevel@tonic-gate 
131*0Sstevel@tonic-gate 	/* We wouldn't need all this hokey state stuff if we */
132*0Sstevel@tonic-gate 	/*   used another thread to implement a coroutine... */
133*0Sstevel@tonic-gate 	enum {
134*0Sstevel@tonic-gate 		GETENT_FILE,
135*0Sstevel@tonic-gate 		GETENT_NETGROUP,
136*0Sstevel@tonic-gate 		GETENT_ATTRDB,
137*0Sstevel@tonic-gate 		GETENT_ALL,
138*0Sstevel@tonic-gate 		GETENT_DONE
139*0Sstevel@tonic-gate 	}			state;
140*0Sstevel@tonic-gate 	strset_t		minuses;
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate 	int			permit_netgroups;
143*0Sstevel@tonic-gate 	const char		*yp_domain;
144*0Sstevel@tonic-gate 	nss_backend_t		*getnetgrent_backend;
145*0Sstevel@tonic-gate 	char			*netgr_buffer;
146*0Sstevel@tonic-gate };
147*0Sstevel@tonic-gate 
148*0Sstevel@tonic-gate 
149*0Sstevel@tonic-gate /*
150*0Sstevel@tonic-gate  * Lookup and enumeration routines for +@group and -@group.
151*0Sstevel@tonic-gate  *
152*0Sstevel@tonic-gate  * This code knows a lot more about lib/libc/port/gen/getnetgrent.c than
153*0Sstevel@tonic-gate  *   is really healthy.  The set/get/end routines below duplicate code
154*0Sstevel@tonic-gate  *   from that file, but keep the state information per-backend-instance
155*0Sstevel@tonic-gate  *   instead of just per-process.
156*0Sstevel@tonic-gate  */
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate extern void _nss_initf_netgroup(nss_db_params_t *);
159*0Sstevel@tonic-gate /*
160*0Sstevel@tonic-gate  * Should really share the db_root in getnetgrent.c in order to get the
161*0Sstevel@tonic-gate  *   resource-management quotas right, but this will have to do.
162*0Sstevel@tonic-gate  */
163*0Sstevel@tonic-gate static DEFINE_NSS_DB_ROOT(netgr_db_root);
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate static boolean_t
166*0Sstevel@tonic-gate netgr_in(compat_backend_ptr_t be, const char *group, const char *user)
167*0Sstevel@tonic-gate {
168*0Sstevel@tonic-gate 	if (be->yp_domain == 0) {
169*0Sstevel@tonic-gate 		if (yp_get_default_domain((char **)&be->yp_domain) != 0) {
170*0Sstevel@tonic-gate 			return (B_FALSE);
171*0Sstevel@tonic-gate 		}
172*0Sstevel@tonic-gate 	}
173*0Sstevel@tonic-gate 	return (innetgr(group, 0, user, be->yp_domain));
174*0Sstevel@tonic-gate }
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate static boolean_t
177*0Sstevel@tonic-gate netgr_all_in(compat_backend_ptr_t be, const char *group)
178*0Sstevel@tonic-gate {
179*0Sstevel@tonic-gate 	/*
180*0Sstevel@tonic-gate 	 * 4.x does this;  ours not to reason why...
181*0Sstevel@tonic-gate 	 */
182*0Sstevel@tonic-gate 	return (netgr_in(be, group, "*"));
183*0Sstevel@tonic-gate }
184*0Sstevel@tonic-gate 
185*0Sstevel@tonic-gate static void
186*0Sstevel@tonic-gate netgr_set(be, netgroup)
187*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
188*0Sstevel@tonic-gate 	const char		*netgroup;
189*0Sstevel@tonic-gate {
190*0Sstevel@tonic-gate 	/*
191*0Sstevel@tonic-gate 	 * ===> Need comment to explain that this first "if" is optimizing
192*0Sstevel@tonic-gate 	 *	for the same-netgroup-as-last-time case
193*0Sstevel@tonic-gate 	 */
194*0Sstevel@tonic-gate 	if (be->getnetgrent_backend != 0 &&
195*0Sstevel@tonic-gate 	    NSS_INVOKE_DBOP(be->getnetgrent_backend,
196*0Sstevel@tonic-gate 			    NSS_DBOP_SETENT,
197*0Sstevel@tonic-gate 			    (void *) netgroup) != NSS_SUCCESS) {
198*0Sstevel@tonic-gate 		NSS_INVOKE_DBOP(be->getnetgrent_backend, NSS_DBOP_DESTRUCTOR,
199*0Sstevel@tonic-gate 				0);
200*0Sstevel@tonic-gate 		be->getnetgrent_backend = 0;
201*0Sstevel@tonic-gate 	}
202*0Sstevel@tonic-gate 	if (be->getnetgrent_backend == 0) {
203*0Sstevel@tonic-gate 		struct nss_setnetgrent_args	args;
204*0Sstevel@tonic-gate 
205*0Sstevel@tonic-gate 		args.netgroup	= netgroup;
206*0Sstevel@tonic-gate 		args.iterator	= 0;
207*0Sstevel@tonic-gate 		nss_search(&netgr_db_root, _nss_initf_netgroup,
208*0Sstevel@tonic-gate 			NSS_DBOP_NETGROUP_SET, &args);
209*0Sstevel@tonic-gate 		be->getnetgrent_backend = args.iterator;
210*0Sstevel@tonic-gate 	}
211*0Sstevel@tonic-gate }
212*0Sstevel@tonic-gate 
213*0Sstevel@tonic-gate static boolean_t
214*0Sstevel@tonic-gate netgr_next_u(be, up)
215*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
216*0Sstevel@tonic-gate 	char			**up;
217*0Sstevel@tonic-gate {
218*0Sstevel@tonic-gate 	if (be->netgr_buffer == 0 &&
219*0Sstevel@tonic-gate 	    (be->netgr_buffer = malloc(NSS_BUFLEN_NETGROUP)) == 0) {
220*0Sstevel@tonic-gate 		/* Out of memory */
221*0Sstevel@tonic-gate 		return (B_FALSE);
222*0Sstevel@tonic-gate 	}
223*0Sstevel@tonic-gate 
224*0Sstevel@tonic-gate 	do {
225*0Sstevel@tonic-gate 		struct nss_getnetgrent_args	args;
226*0Sstevel@tonic-gate 
227*0Sstevel@tonic-gate 		args.buffer	= be->netgr_buffer;
228*0Sstevel@tonic-gate 		args.buflen	= NSS_BUFLEN_NETGROUP;
229*0Sstevel@tonic-gate 		args.status	= NSS_NETGR_NO;
230*0Sstevel@tonic-gate 
231*0Sstevel@tonic-gate 		if (be->getnetgrent_backend != 0) {
232*0Sstevel@tonic-gate 			NSS_INVOKE_DBOP(be->getnetgrent_backend,
233*0Sstevel@tonic-gate 					NSS_DBOP_GETENT, &args);
234*0Sstevel@tonic-gate 		}
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 		if (args.status == NSS_NETGR_FOUND) {
237*0Sstevel@tonic-gate 			*up	  = args.retp[NSS_NETGR_USER];
238*0Sstevel@tonic-gate 		} else {
239*0Sstevel@tonic-gate 			return (B_FALSE);
240*0Sstevel@tonic-gate 		}
241*0Sstevel@tonic-gate 	} while (*up == 0);
242*0Sstevel@tonic-gate 	return (B_TRUE);
243*0Sstevel@tonic-gate }
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate static void
246*0Sstevel@tonic-gate netgr_end(be)
247*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
248*0Sstevel@tonic-gate {
249*0Sstevel@tonic-gate 	if (be->getnetgrent_backend != 0) {
250*0Sstevel@tonic-gate 		NSS_INVOKE_DBOP(be->getnetgrent_backend,
251*0Sstevel@tonic-gate 				NSS_DBOP_DESTRUCTOR, 0);
252*0Sstevel@tonic-gate 		be->getnetgrent_backend = 0;
253*0Sstevel@tonic-gate 	}
254*0Sstevel@tonic-gate 	if (be->netgr_buffer != 0) {
255*0Sstevel@tonic-gate 		free(be->netgr_buffer);
256*0Sstevel@tonic-gate 		be->netgr_buffer = 0;
257*0Sstevel@tonic-gate 	}
258*0Sstevel@tonic-gate }
259*0Sstevel@tonic-gate 
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate #define	MAXFIELDS 9	/* Sufficient for passwd (7), shadow (9), group (4) */
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate static nss_status_t
264*0Sstevel@tonic-gate do_merge(be, args, instr, linelen)
265*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
266*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args;
267*0Sstevel@tonic-gate 	const char		*instr;
268*0Sstevel@tonic-gate 	int			linelen;
269*0Sstevel@tonic-gate {
270*0Sstevel@tonic-gate 	char			*fields[MAXFIELDS];
271*0Sstevel@tonic-gate 	int			i;
272*0Sstevel@tonic-gate 	int			overrides;
273*0Sstevel@tonic-gate 	const char		*p;
274*0Sstevel@tonic-gate 	const char		*end = instr + linelen;
275*0Sstevel@tonic-gate 	nss_status_t		res;
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	/*
278*0Sstevel@tonic-gate 	 * Potential optimization:  only perform the field-splitting nonsense
279*0Sstevel@tonic-gate 	 *   once per input line (at present, "+" and "+@netgroup" entries
280*0Sstevel@tonic-gate 	 *   will cause us to do this multiple times in getent() requests).
281*0Sstevel@tonic-gate 	 */
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate 	for (i = 0;  i < MAXFIELDS;  i++) {
284*0Sstevel@tonic-gate 		fields[i] = 0;
285*0Sstevel@tonic-gate 	}
286*0Sstevel@tonic-gate 	for (p = instr, overrides = 0, i = 0; /* no test */; i++) {
287*0Sstevel@tonic-gate 		const char	*q = memchr(p, ':', end - p);
288*0Sstevel@tonic-gate 		const char	*r = (q == 0) ? end : q;
289*0Sstevel@tonic-gate 		ssize_t		len = r - p;
290*0Sstevel@tonic-gate 
291*0Sstevel@tonic-gate 		if (len > 0) {
292*0Sstevel@tonic-gate 			char	*s = malloc(len + 1);
293*0Sstevel@tonic-gate 			if (s == 0) {
294*0Sstevel@tonic-gate 				overrides = -1;	/* Indicates "you lose" */
295*0Sstevel@tonic-gate 				break;
296*0Sstevel@tonic-gate 			}
297*0Sstevel@tonic-gate 			memcpy(s, p, len);
298*0Sstevel@tonic-gate 			s[len] = '\0';
299*0Sstevel@tonic-gate 			fields[i] = s;
300*0Sstevel@tonic-gate 			overrides++;
301*0Sstevel@tonic-gate 		}
302*0Sstevel@tonic-gate 		if (q == 0) {
303*0Sstevel@tonic-gate 			/* End of line */
304*0Sstevel@tonic-gate 			break;
305*0Sstevel@tonic-gate 		} else {
306*0Sstevel@tonic-gate 			/* Skip the colon at (*q) */
307*0Sstevel@tonic-gate 			p = q + 1;
308*0Sstevel@tonic-gate 		}
309*0Sstevel@tonic-gate 	}
310*0Sstevel@tonic-gate 	if (overrides == 1) {
311*0Sstevel@tonic-gate 		/* No real overrides, return (*args) intact */
312*0Sstevel@tonic-gate 		res = NSS_SUCCESS;
313*0Sstevel@tonic-gate 	} else if (overrides > 1) {
314*0Sstevel@tonic-gate 		/*
315*0Sstevel@tonic-gate 		 * The zero'th field is always nonempty (+/-...), but at least
316*0Sstevel@tonic-gate 		 *   one other field was also nonempty, i.e. wants to override
317*0Sstevel@tonic-gate 		 */
318*0Sstevel@tonic-gate 		switch ((*be->mergef)(be, args, (const char **)fields)) {
319*0Sstevel@tonic-gate 		    case NSS_STR_PARSE_SUCCESS:
320*0Sstevel@tonic-gate 			args->returnval	= args->buf.result;
321*0Sstevel@tonic-gate 			args->erange	= 0;
322*0Sstevel@tonic-gate 			res = NSS_SUCCESS;
323*0Sstevel@tonic-gate 			break;
324*0Sstevel@tonic-gate 		    case NSS_STR_PARSE_ERANGE:
325*0Sstevel@tonic-gate 			args->returnval	= 0;
326*0Sstevel@tonic-gate 			args->erange	= 1;
327*0Sstevel@tonic-gate 			res = NSS_NOTFOUND;
328*0Sstevel@tonic-gate 			break;
329*0Sstevel@tonic-gate 		    case NSS_STR_PARSE_PARSE:
330*0Sstevel@tonic-gate 			args->returnval	= 0;
331*0Sstevel@tonic-gate 			args->erange	= 0;
332*0Sstevel@tonic-gate /* ===> Very likely the wrong thing to do... */
333*0Sstevel@tonic-gate 			res = NSS_NOTFOUND;
334*0Sstevel@tonic-gate 			break;
335*0Sstevel@tonic-gate 		}
336*0Sstevel@tonic-gate 	} else {
337*0Sstevel@tonic-gate 		args->returnval	= 0;
338*0Sstevel@tonic-gate 		args->erange	= 0;
339*0Sstevel@tonic-gate 		res = NSS_UNAVAIL;	/* ==> Right? */
340*0Sstevel@tonic-gate 	}
341*0Sstevel@tonic-gate 
342*0Sstevel@tonic-gate 	for (i = 0;  i < MAXFIELDS;  i++) {
343*0Sstevel@tonic-gate 		if (fields[i] != 0) {
344*0Sstevel@tonic-gate 			free(fields[i]);
345*0Sstevel@tonic-gate 		}
346*0Sstevel@tonic-gate 	}
347*0Sstevel@tonic-gate 
348*0Sstevel@tonic-gate 	return (res);
349*0Sstevel@tonic-gate }
350*0Sstevel@tonic-gate 
351*0Sstevel@tonic-gate /*ARGSUSED*/
352*0Sstevel@tonic-gate nss_status_t
353*0Sstevel@tonic-gate _nss_compat_setent(be, dummy)
354*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
355*0Sstevel@tonic-gate 	void			*dummy;
356*0Sstevel@tonic-gate {
357*0Sstevel@tonic-gate 	if (be->f == 0) {
358*0Sstevel@tonic-gate 		if (be->filename == 0) {
359*0Sstevel@tonic-gate 			/* Backend isn't initialized properly? */
360*0Sstevel@tonic-gate 			return (NSS_UNAVAIL);
361*0Sstevel@tonic-gate 		}
362*0Sstevel@tonic-gate 		if ((be->f = __nsl_fopen(be->filename, "r")) == 0) {
363*0Sstevel@tonic-gate 			return (NSS_UNAVAIL);
364*0Sstevel@tonic-gate 		}
365*0Sstevel@tonic-gate 	} else {
366*0Sstevel@tonic-gate 		__nsl_rewind(be->f);
367*0Sstevel@tonic-gate 	}
368*0Sstevel@tonic-gate 	strset_free(&be->minuses);
369*0Sstevel@tonic-gate 	/* ===> ??? nss_endent(be->db_rootp, be->db_initf, &be->db_context); */
370*0Sstevel@tonic-gate 
371*0Sstevel@tonic-gate 	if ((strcmp(be->filename, USERATTR_FILENAME) == 0) ||
372*0Sstevel@tonic-gate 	    (strcmp(be->filename, AUDITUSER_FILENAME) == 0))
373*0Sstevel@tonic-gate 		be->state = GETENT_ATTRDB;
374*0Sstevel@tonic-gate 	else
375*0Sstevel@tonic-gate 		be->state = GETENT_FILE;
376*0Sstevel@tonic-gate 
377*0Sstevel@tonic-gate 	/* ===> ??  netgroup stuff? */
378*0Sstevel@tonic-gate 	return (NSS_SUCCESS);
379*0Sstevel@tonic-gate }
380*0Sstevel@tonic-gate 
381*0Sstevel@tonic-gate /*ARGSUSED*/
382*0Sstevel@tonic-gate nss_status_t
383*0Sstevel@tonic-gate _nss_compat_endent(be, dummy)
384*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
385*0Sstevel@tonic-gate 	void			*dummy;
386*0Sstevel@tonic-gate {
387*0Sstevel@tonic-gate 	if (be->f != 0) {
388*0Sstevel@tonic-gate 		__nsl_fclose(be->f);
389*0Sstevel@tonic-gate 		be->f = 0;
390*0Sstevel@tonic-gate 	}
391*0Sstevel@tonic-gate 	if (be->buf != 0) {
392*0Sstevel@tonic-gate 		free(be->buf);
393*0Sstevel@tonic-gate 		be->buf = 0;
394*0Sstevel@tonic-gate 	}
395*0Sstevel@tonic-gate 	nss_endent(be->db_rootp, be->db_initf, &be->db_context);
396*0Sstevel@tonic-gate 
397*0Sstevel@tonic-gate 	be->state = GETENT_FILE; /* Probably superfluous but comforting */
398*0Sstevel@tonic-gate 	strset_free(&be->minuses);
399*0Sstevel@tonic-gate 	netgr_end(be);
400*0Sstevel@tonic-gate 
401*0Sstevel@tonic-gate 	/*
402*0Sstevel@tonic-gate 	 * Question: from the point of view of resource-freeing vs. time to
403*0Sstevel@tonic-gate 	 *   start up again, how much should we do in endent() and how much
404*0Sstevel@tonic-gate 	 *   in the destructor?
405*0Sstevel@tonic-gate 	 */
406*0Sstevel@tonic-gate 	return (NSS_SUCCESS);
407*0Sstevel@tonic-gate }
408*0Sstevel@tonic-gate 
409*0Sstevel@tonic-gate /*ARGSUSED*/
410*0Sstevel@tonic-gate nss_status_t
411*0Sstevel@tonic-gate _nss_compat_destr(be, dummy)
412*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
413*0Sstevel@tonic-gate 	void			*dummy;
414*0Sstevel@tonic-gate {
415*0Sstevel@tonic-gate 	if (be != 0) {
416*0Sstevel@tonic-gate 		if (be->f != 0) {
417*0Sstevel@tonic-gate 			_nss_compat_endent(be, 0);
418*0Sstevel@tonic-gate 		}
419*0Sstevel@tonic-gate 		nss_delete(be->db_rootp);
420*0Sstevel@tonic-gate 		nss_delete(&netgr_db_root);
421*0Sstevel@tonic-gate 		free(be);
422*0Sstevel@tonic-gate 	}
423*0Sstevel@tonic-gate 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
424*0Sstevel@tonic-gate }
425*0Sstevel@tonic-gate 
426*0Sstevel@tonic-gate static int
427*0Sstevel@tonic-gate read_line(f, buffer, buflen)
428*0Sstevel@tonic-gate 	__NSL_FILE		*f;
429*0Sstevel@tonic-gate 	char			*buffer;
430*0Sstevel@tonic-gate 	int			buflen;
431*0Sstevel@tonic-gate {
432*0Sstevel@tonic-gate 	/*CONSTCOND*/
433*0Sstevel@tonic-gate 	while (1) {
434*0Sstevel@tonic-gate 		int	linelen;
435*0Sstevel@tonic-gate 
436*0Sstevel@tonic-gate 		if (__nsl_fgets(buffer, buflen, f) == 0) {
437*0Sstevel@tonic-gate 			/* End of file */
438*0Sstevel@tonic-gate 			return (-1);
439*0Sstevel@tonic-gate 		}
440*0Sstevel@tonic-gate 		linelen = strlen(buffer);
441*0Sstevel@tonic-gate 		/* linelen >= 1 (since fgets didn't return 0) */
442*0Sstevel@tonic-gate 
443*0Sstevel@tonic-gate 		if (buffer[linelen - 1] == '\n') {
444*0Sstevel@tonic-gate 			/*
445*0Sstevel@tonic-gate 			 * ===> The code below that calls read_line() doesn't
446*0Sstevel@tonic-gate 			 *	play by the rules;  it assumes in places that
447*0Sstevel@tonic-gate 			 *	the line is null-terminated.  For now we'll
448*0Sstevel@tonic-gate 			 *	humour it.
449*0Sstevel@tonic-gate 			 */
450*0Sstevel@tonic-gate 			buffer[--linelen] = '\0';
451*0Sstevel@tonic-gate 			return (linelen);
452*0Sstevel@tonic-gate 		}
453*0Sstevel@tonic-gate 		if (__nsl_feof(f)) {
454*0Sstevel@tonic-gate 			/* Line is last line in file, and has no newline */
455*0Sstevel@tonic-gate 			return (linelen);
456*0Sstevel@tonic-gate 		}
457*0Sstevel@tonic-gate 		/* Line too long for buffer;  toss it and loop for next line */
458*0Sstevel@tonic-gate 		/* ===== should syslog() in cases where previous code did */
459*0Sstevel@tonic-gate 		while (__nsl_fgets(buffer, buflen, f) != 0 &&
460*0Sstevel@tonic-gate 		    buffer[strlen(buffer) - 1] != '\n') {
461*0Sstevel@tonic-gate 			;
462*0Sstevel@tonic-gate 		}
463*0Sstevel@tonic-gate 	}
464*0Sstevel@tonic-gate }
465*0Sstevel@tonic-gate 
466*0Sstevel@tonic-gate static int _is_nss_lookup_by_name(int attrdb, nss_dbop_t op) {
467*0Sstevel@tonic-gate 	int result = 0;
468*0Sstevel@tonic-gate 
469*0Sstevel@tonic-gate 	if ((attrdb != 0) &&
470*0Sstevel@tonic-gate 		((op == NSS_DBOP_AUDITUSER_BYNAME) ||
471*0Sstevel@tonic-gate 		(op == NSS_DBOP_USERATTR_BYNAME))) {
472*0Sstevel@tonic-gate 		result = 1;
473*0Sstevel@tonic-gate 	} else if ((attrdb == 0) &&
474*0Sstevel@tonic-gate 		((op == NSS_DBOP_GROUP_BYNAME) ||
475*0Sstevel@tonic-gate 		(op == NSS_DBOP_PASSWD_BYNAME) ||
476*0Sstevel@tonic-gate 		(op == NSS_DBOP_SHADOW_BYNAME))) {
477*0Sstevel@tonic-gate 		result = 1;
478*0Sstevel@tonic-gate 	}
479*0Sstevel@tonic-gate 
480*0Sstevel@tonic-gate 	return (result);
481*0Sstevel@tonic-gate }
482*0Sstevel@tonic-gate 
483*0Sstevel@tonic-gate /*ARGSUSED*/
484*0Sstevel@tonic-gate nss_status_t
485*0Sstevel@tonic-gate _attrdb_compat_XY_all(be, argp, netdb, check, op_num)
486*0Sstevel@tonic-gate     compat_backend_ptr_t be;
487*0Sstevel@tonic-gate     nss_XbyY_args_t *argp;
488*0Sstevel@tonic-gate     int netdb;
489*0Sstevel@tonic-gate     compat_XY_check_func check;
490*0Sstevel@tonic-gate     nss_dbop_t op_num;
491*0Sstevel@tonic-gate {
492*0Sstevel@tonic-gate 	int		parsestat;
493*0Sstevel@tonic-gate 	int		(*func)();
494*0Sstevel@tonic-gate 	const char	*filter = argp->key.name;
495*0Sstevel@tonic-gate 	nss_status_t	res;
496*0Sstevel@tonic-gate 
497*0Sstevel@tonic-gate #ifdef	DEBUG
498*0Sstevel@tonic-gate 	(void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n");
499*0Sstevel@tonic-gate #endif	/* DEBUG */
500*0Sstevel@tonic-gate 
501*0Sstevel@tonic-gate 	if (be->buf == 0 &&
502*0Sstevel@tonic-gate 	    (be->buf = malloc(be->minbuf)) == 0) {
503*0Sstevel@tonic-gate 		return (NSS_UNAVAIL);
504*0Sstevel@tonic-gate 	}
505*0Sstevel@tonic-gate 	if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
506*0Sstevel@tonic-gate 		return (res);
507*0Sstevel@tonic-gate 	}
508*0Sstevel@tonic-gate 	res = NSS_NOTFOUND;
509*0Sstevel@tonic-gate 
510*0Sstevel@tonic-gate 	/*CONSTCOND*/
511*0Sstevel@tonic-gate 	while (1) {
512*0Sstevel@tonic-gate 		int	linelen;
513*0Sstevel@tonic-gate 		char	*instr	= be->buf;
514*0Sstevel@tonic-gate 
515*0Sstevel@tonic-gate 		if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) {
516*0Sstevel@tonic-gate 			/* End of file */
517*0Sstevel@tonic-gate 			argp->returnval = 0;
518*0Sstevel@tonic-gate 			argp->erange    = 0;
519*0Sstevel@tonic-gate 			break;
520*0Sstevel@tonic-gate 		}
521*0Sstevel@tonic-gate 		if (filter != 0 && strstr(instr, filter) == 0) {
522*0Sstevel@tonic-gate 			/*
523*0Sstevel@tonic-gate 			 * Optimization:  if the entry doesn't contain the
524*0Sstevel@tonic-gate 			 * filter string then it can't be the entry we want,
525*0Sstevel@tonic-gate 			 * so don't bother looking more closely at it.
526*0Sstevel@tonic-gate 			 */
527*0Sstevel@tonic-gate 			continue;
528*0Sstevel@tonic-gate 		}
529*0Sstevel@tonic-gate 		if (netdb) {
530*0Sstevel@tonic-gate 			char	*first;
531*0Sstevel@tonic-gate 			char	*last;
532*0Sstevel@tonic-gate 
533*0Sstevel@tonic-gate 			if ((last = strchr(instr, '#')) == 0) {
534*0Sstevel@tonic-gate 				last = instr + linelen;
535*0Sstevel@tonic-gate 			}
536*0Sstevel@tonic-gate 			*last-- = '\0';		/* Nuke '\n' or #comment */
537*0Sstevel@tonic-gate 
538*0Sstevel@tonic-gate 			/*
539*0Sstevel@tonic-gate 			 * Skip leading whitespace.  Normally there isn't
540*0Sstevel@tonic-gate 			 * any, so it's not worth calling strspn().
541*0Sstevel@tonic-gate 			 */
542*0Sstevel@tonic-gate 			for (first = instr;  isspace(*first);  first++) {
543*0Sstevel@tonic-gate 				;
544*0Sstevel@tonic-gate 			}
545*0Sstevel@tonic-gate 			if (*first == '\0') {
546*0Sstevel@tonic-gate 				continue;
547*0Sstevel@tonic-gate 			}
548*0Sstevel@tonic-gate 			/*
549*0Sstevel@tonic-gate 			 * Found something non-blank on the line.  Skip back
550*0Sstevel@tonic-gate 			 * over any trailing whitespace;  since we know
551*0Sstevel@tonic-gate 			 * there's non-whitespace earlier in the line,
552*0Sstevel@tonic-gate 			 * checking for termination is easy.
553*0Sstevel@tonic-gate 			 */
554*0Sstevel@tonic-gate 			while (isspace(*last)) {
555*0Sstevel@tonic-gate 				--last;
556*0Sstevel@tonic-gate 			}
557*0Sstevel@tonic-gate 			linelen = last - first + 1;
558*0Sstevel@tonic-gate 			if (first != instr) {
559*0Sstevel@tonic-gate 				instr = first;
560*0Sstevel@tonic-gate 			}
561*0Sstevel@tonic-gate 		}
562*0Sstevel@tonic-gate 		argp->returnval = 0;
563*0Sstevel@tonic-gate 		func = argp->str2ent;
564*0Sstevel@tonic-gate 		parsestat = (*func)(instr, linelen, argp->buf.result,
565*0Sstevel@tonic-gate 					argp->buf.buffer, argp->buf.buflen);
566*0Sstevel@tonic-gate 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
567*0Sstevel@tonic-gate 			argp->returnval = argp->buf.result;
568*0Sstevel@tonic-gate 			if (check == 0 || (*check)(argp)) {
569*0Sstevel@tonic-gate 				res = NSS_SUCCESS;
570*0Sstevel@tonic-gate 				break;
571*0Sstevel@tonic-gate 			}
572*0Sstevel@tonic-gate 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
573*0Sstevel@tonic-gate 			argp->erange = 1;
574*0Sstevel@tonic-gate 			break;
575*0Sstevel@tonic-gate 		}
576*0Sstevel@tonic-gate 	}
577*0Sstevel@tonic-gate 	/*
578*0Sstevel@tonic-gate 	 * stayopen is set to 0 by default in order to close the opened
579*0Sstevel@tonic-gate 	 * file.  Some applications may break if it is set to 1.
580*0Sstevel@tonic-gate 	 */
581*0Sstevel@tonic-gate 	if (check != 0 && !argp->stayopen) {
582*0Sstevel@tonic-gate 		(void) _nss_compat_endent(be, 0);
583*0Sstevel@tonic-gate 	}
584*0Sstevel@tonic-gate 
585*0Sstevel@tonic-gate 	if (res != NSS_SUCCESS) {
586*0Sstevel@tonic-gate 		if ((op_num == NSS_DBOP_USERATTR_BYNAME) ||
587*0Sstevel@tonic-gate 		    (op_num == NSS_DBOP_AUDITUSER_BYNAME)) {
588*0Sstevel@tonic-gate 			res = nss_search(be->db_rootp,
589*0Sstevel@tonic-gate 			    be->db_initf,
590*0Sstevel@tonic-gate 			    op_num,
591*0Sstevel@tonic-gate 			    argp);
592*0Sstevel@tonic-gate 		} else {
593*0Sstevel@tonic-gate 			res = nss_getent(be->db_rootp,
594*0Sstevel@tonic-gate 			    be->db_initf, &be->db_context, argp);
595*0Sstevel@tonic-gate 		}
596*0Sstevel@tonic-gate 		if (res != NSS_SUCCESS) {
597*0Sstevel@tonic-gate 			argp->returnval	= 0;
598*0Sstevel@tonic-gate 			argp->erange	= 0;
599*0Sstevel@tonic-gate 		}
600*0Sstevel@tonic-gate 	}
601*0Sstevel@tonic-gate 
602*0Sstevel@tonic-gate 	return (res);
603*0Sstevel@tonic-gate }
604*0Sstevel@tonic-gate 
605*0Sstevel@tonic-gate nss_status_t
606*0Sstevel@tonic-gate _nss_compat_XY_all(be, args, check, op_num)
607*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
608*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args;
609*0Sstevel@tonic-gate 	compat_XY_check_func	check;
610*0Sstevel@tonic-gate 	nss_dbop_t		op_num;
611*0Sstevel@tonic-gate {
612*0Sstevel@tonic-gate 	nss_status_t		res;
613*0Sstevel@tonic-gate 	int			parsestat;
614*0Sstevel@tonic-gate 
615*0Sstevel@tonic-gate 	if (be->buf == 0 &&
616*0Sstevel@tonic-gate 	    (be->buf = malloc(be->minbuf)) == 0) {
617*0Sstevel@tonic-gate 		return (NSS_UNAVAIL); /* really panic, malloc failed */
618*0Sstevel@tonic-gate 	}
619*0Sstevel@tonic-gate 
620*0Sstevel@tonic-gate 	if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
621*0Sstevel@tonic-gate 		return (res);
622*0Sstevel@tonic-gate 	}
623*0Sstevel@tonic-gate 
624*0Sstevel@tonic-gate 	res = NSS_NOTFOUND;
625*0Sstevel@tonic-gate 
626*0Sstevel@tonic-gate 	/*CONSTCOND*/
627*0Sstevel@tonic-gate 	while (1) {
628*0Sstevel@tonic-gate 		int		linelen;
629*0Sstevel@tonic-gate 		char		*instr	= be->buf;
630*0Sstevel@tonic-gate 		char		*colon;
631*0Sstevel@tonic-gate 
632*0Sstevel@tonic-gate 		linelen = read_line(be->f, instr, be->minbuf);
633*0Sstevel@tonic-gate 		if (linelen < 0) {
634*0Sstevel@tonic-gate 			/* End of file */
635*0Sstevel@tonic-gate 			args->returnval = 0;
636*0Sstevel@tonic-gate 			args->erange    = 0;
637*0Sstevel@tonic-gate 			break;
638*0Sstevel@tonic-gate 		}
639*0Sstevel@tonic-gate 
640*0Sstevel@tonic-gate 		args->returnval = 0;	/* reset for both types of entries */
641*0Sstevel@tonic-gate 
642*0Sstevel@tonic-gate 		if (instr[0] != '+' && instr[0] != '-') {
643*0Sstevel@tonic-gate 			/* Simple, wholesome, God-fearing entry */
644*0Sstevel@tonic-gate 			parsestat = (*args->str2ent)(instr, linelen,
645*0Sstevel@tonic-gate 						    args->buf.result,
646*0Sstevel@tonic-gate 						    args->buf.buffer,
647*0Sstevel@tonic-gate 						    args->buf.buflen);
648*0Sstevel@tonic-gate 			if (parsestat == NSS_STR_PARSE_SUCCESS) {
649*0Sstevel@tonic-gate 				args->returnval = args->buf.result;
650*0Sstevel@tonic-gate 				if ((*check)(args) != 0) {
651*0Sstevel@tonic-gate 					res = NSS_SUCCESS;
652*0Sstevel@tonic-gate 					break;
653*0Sstevel@tonic-gate 				}
654*0Sstevel@tonic-gate 
655*0Sstevel@tonic-gate /* ===> Check the Dani logic here... */
656*0Sstevel@tonic-gate 
657*0Sstevel@tonic-gate 			} else if (parsestat == NSS_STR_PARSE_ERANGE) {
658*0Sstevel@tonic-gate 				args->erange = 1;
659*0Sstevel@tonic-gate 				res = NSS_NOTFOUND;
660*0Sstevel@tonic-gate 				break;
661*0Sstevel@tonic-gate 				/* should we just skip this one long line ? */
662*0Sstevel@tonic-gate 			} /* else if (parsestat == NSS_STR_PARSE_PARSE) */
663*0Sstevel@tonic-gate 				/* don't care ! */
664*0Sstevel@tonic-gate 
665*0Sstevel@tonic-gate /* ==> ?? */		continue;
666*0Sstevel@tonic-gate 		}
667*0Sstevel@tonic-gate 
668*0Sstevel@tonic-gate 		/*
669*0Sstevel@tonic-gate 		 * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup"
670*0Sstevel@tonic-gate 		 *
671*0Sstevel@tonic-gate 		 * This code is optimized for lookups by name.
672*0Sstevel@tonic-gate 		 *
673*0Sstevel@tonic-gate 		 * For lookups by identifier search key cannot be matched with
674*0Sstevel@tonic-gate 		 * the name of the "+" or "-" entry. So nss_search() is to be
675*0Sstevel@tonic-gate 		 * called before extracting the name i.e. via (*be->getnamef)().
676*0Sstevel@tonic-gate 		 *
677*0Sstevel@tonic-gate 		 * But for lookups by name, search key is compared with the name
678*0Sstevel@tonic-gate 		 * of the "+" or "-" entry to acquire a match and thus
679*0Sstevel@tonic-gate 		 * unnesessary calls to nss_search() is eliminated. Also for
680*0Sstevel@tonic-gate 		 * matching "-" entries, calls to nss_search() is eliminated.
681*0Sstevel@tonic-gate 		 */
682*0Sstevel@tonic-gate 
683*0Sstevel@tonic-gate 		if ((colon = strchr(instr, ':')) != 0) {
684*0Sstevel@tonic-gate 			*colon = '\0';	/* terminate field to extract name */
685*0Sstevel@tonic-gate 		}
686*0Sstevel@tonic-gate 
687*0Sstevel@tonic-gate 		if (instr[1] == '@') {
688*0Sstevel@tonic-gate 			/*
689*0Sstevel@tonic-gate 			 * Case 1:
690*0Sstevel@tonic-gate 			 * The entry is of the form "+@netgroup" or
691*0Sstevel@tonic-gate 			 * "-@netgroup".  If we're performing a lookup by name,
692*0Sstevel@tonic-gate 			 * we can simply extract the name from the search key
693*0Sstevel@tonic-gate 			 * (i.e. args->key.name).  If not, then we must call
694*0Sstevel@tonic-gate 			 * nss_search() before extracting the name via the
695*0Sstevel@tonic-gate 			 * get_XXname() function. i.e. (*be->getnamef)(args).
696*0Sstevel@tonic-gate 			 */
697*0Sstevel@tonic-gate 		    if (_is_nss_lookup_by_name(0, op_num) != 0) {
698*0Sstevel@tonic-gate 			/* compare then search */
699*0Sstevel@tonic-gate 			if (!be->permit_netgroups ||
700*0Sstevel@tonic-gate 				!netgr_in(be, instr + 2, args->key.name))
701*0Sstevel@tonic-gate 				continue;
702*0Sstevel@tonic-gate 			if (instr[0] == '+') {
703*0Sstevel@tonic-gate 				/* need to search for "+" entry */
704*0Sstevel@tonic-gate 				nss_search(be->db_rootp, be->db_initf, op_num,
705*0Sstevel@tonic-gate 					args);
706*0Sstevel@tonic-gate 				if (args->returnval == 0)
707*0Sstevel@tonic-gate 					continue;
708*0Sstevel@tonic-gate 			}
709*0Sstevel@tonic-gate 		    } else {
710*0Sstevel@tonic-gate 			/* search then compare */
711*0Sstevel@tonic-gate 			nss_search(be->db_rootp, be->db_initf, op_num, args);
712*0Sstevel@tonic-gate 			if (args->returnval == 0)
713*0Sstevel@tonic-gate 				continue;
714*0Sstevel@tonic-gate 			if (!be->permit_netgroups ||
715*0Sstevel@tonic-gate 				!netgr_in(be, instr + 2, (*be->getnamef)(args)))
716*0Sstevel@tonic-gate 				continue;
717*0Sstevel@tonic-gate 		    }
718*0Sstevel@tonic-gate 		}	/* end of case 1 */
719*0Sstevel@tonic-gate 		else if (instr[1] == '\0') {
720*0Sstevel@tonic-gate 			/*
721*0Sstevel@tonic-gate 			 * Case 2:
722*0Sstevel@tonic-gate 			 * The entry is of the form "+" or "-".  The former
723*0Sstevel@tonic-gate 			 * allows all entries from name services.  The latter
724*0Sstevel@tonic-gate 			 * is illegal and ought to be ignored.
725*0Sstevel@tonic-gate 			 */
726*0Sstevel@tonic-gate 			if (instr[0] == '-')
727*0Sstevel@tonic-gate 				continue;
728*0Sstevel@tonic-gate 			/* need to search for "+" entry */
729*0Sstevel@tonic-gate 			nss_search(be->db_rootp, be->db_initf, op_num, args);
730*0Sstevel@tonic-gate 			if (args->returnval == 0)
731*0Sstevel@tonic-gate 				continue;
732*0Sstevel@tonic-gate 		}	/* end of case 2 */
733*0Sstevel@tonic-gate 		else {
734*0Sstevel@tonic-gate 			/*
735*0Sstevel@tonic-gate 			 * Case 3:
736*0Sstevel@tonic-gate 			 * The entry is of the form "+name" or "-name".
737*0Sstevel@tonic-gate 			 * If we're performing a lookup by name, we can simply
738*0Sstevel@tonic-gate 			 * extract the name from the search key
739*0Sstevel@tonic-gate 			 * (i.e. args->key.name).  If not, then we must call
740*0Sstevel@tonic-gate 			 * nss_search() before extracting the name via the
741*0Sstevel@tonic-gate 			 * get_XXname() function. i.e. (*be->getnamef)(args).
742*0Sstevel@tonic-gate 			 */
743*0Sstevel@tonic-gate 			if (_is_nss_lookup_by_name(0, op_num) != 0) {
744*0Sstevel@tonic-gate 				/* compare then search */
745*0Sstevel@tonic-gate 				if (strcmp(instr + 1, args->key.name) != 0)
746*0Sstevel@tonic-gate 					continue;
747*0Sstevel@tonic-gate 				if (instr[0] == '+') {
748*0Sstevel@tonic-gate 					/* need to search for "+" entry */
749*0Sstevel@tonic-gate 					nss_search(be->db_rootp, be->db_initf,
750*0Sstevel@tonic-gate 						op_num, args);
751*0Sstevel@tonic-gate 					if (args->returnval == 0)
752*0Sstevel@tonic-gate 						continue;
753*0Sstevel@tonic-gate 				}
754*0Sstevel@tonic-gate 			} else {
755*0Sstevel@tonic-gate 				/* search then compare */
756*0Sstevel@tonic-gate 				nss_search(be->db_rootp, be->db_initf, op_num,
757*0Sstevel@tonic-gate 					args);
758*0Sstevel@tonic-gate 				if (args->returnval == 0)
759*0Sstevel@tonic-gate 					continue;
760*0Sstevel@tonic-gate 				if (strcmp(instr + 1, (*be->getnamef)(args))
761*0Sstevel@tonic-gate 					!= 0)
762*0Sstevel@tonic-gate 					continue;
763*0Sstevel@tonic-gate 			}
764*0Sstevel@tonic-gate 		} 	/* end of case 3 */
765*0Sstevel@tonic-gate 		if (instr[0] == '-') {
766*0Sstevel@tonic-gate 			/* no need to search for "-" entry */
767*0Sstevel@tonic-gate 			args->returnval = 0;
768*0Sstevel@tonic-gate 			args->erange = 0;
769*0Sstevel@tonic-gate 			res = NSS_NOTFOUND;
770*0Sstevel@tonic-gate 		} else {
771*0Sstevel@tonic-gate 			if (colon != 0)
772*0Sstevel@tonic-gate 			*colon = ':';	/* restoration */
773*0Sstevel@tonic-gate 			res = do_merge(be, args, instr, linelen);
774*0Sstevel@tonic-gate 		}
775*0Sstevel@tonic-gate 		break;
776*0Sstevel@tonic-gate 	}
777*0Sstevel@tonic-gate 
778*0Sstevel@tonic-gate 	/*
779*0Sstevel@tonic-gate 	 * stayopen is set to 0 by default in order to close the opened
780*0Sstevel@tonic-gate 	 * file.  Some applications may break if it is set to 1.
781*0Sstevel@tonic-gate 	 */
782*0Sstevel@tonic-gate 	if (!args->stayopen) {
783*0Sstevel@tonic-gate 		(void) _nss_compat_endent(be, 0);
784*0Sstevel@tonic-gate 	}
785*0Sstevel@tonic-gate 
786*0Sstevel@tonic-gate 	return (res);
787*0Sstevel@tonic-gate }
788*0Sstevel@tonic-gate 
789*0Sstevel@tonic-gate nss_status_t
790*0Sstevel@tonic-gate _nss_compat_getent(be, a)
791*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
792*0Sstevel@tonic-gate 	void			*a;
793*0Sstevel@tonic-gate {
794*0Sstevel@tonic-gate 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
795*0Sstevel@tonic-gate 	nss_status_t		res;
796*0Sstevel@tonic-gate 	char			*colon = 0; /* <=== need comment re lifetime */
797*0Sstevel@tonic-gate 
798*0Sstevel@tonic-gate 	if (be->f == 0) {
799*0Sstevel@tonic-gate 		if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
800*0Sstevel@tonic-gate 			return (res);
801*0Sstevel@tonic-gate 		}
802*0Sstevel@tonic-gate 	}
803*0Sstevel@tonic-gate 
804*0Sstevel@tonic-gate 	if (be->buf == 0 &&
805*0Sstevel@tonic-gate 	    (be->buf = malloc(be->minbuf)) == 0) {
806*0Sstevel@tonic-gate 		return (NSS_UNAVAIL); /* really panic, malloc failed */
807*0Sstevel@tonic-gate 	}
808*0Sstevel@tonic-gate 
809*0Sstevel@tonic-gate 	/*CONSTCOND*/
810*0Sstevel@tonic-gate 	while (1) {
811*0Sstevel@tonic-gate 		char		*instr	= be->buf;
812*0Sstevel@tonic-gate 		int		linelen;
813*0Sstevel@tonic-gate 		char		*name;	/* === Need more distinctive label */
814*0Sstevel@tonic-gate 		const char	*savename;
815*0Sstevel@tonic-gate 
816*0Sstevel@tonic-gate 		/*
817*0Sstevel@tonic-gate 		 * In the code below...
818*0Sstevel@tonic-gate 		 *    break	means "I found one, I think" (i.e. goto the
819*0Sstevel@tonic-gate 		 *		code after the end of the switch statement),
820*0Sstevel@tonic-gate 		 *    continue	means "Next candidate"
821*0Sstevel@tonic-gate 		 *		(i.e. loop around to the switch statement),
822*0Sstevel@tonic-gate 		 *    return	means "I'm quite sure" (either Yes or No).
823*0Sstevel@tonic-gate 		 */
824*0Sstevel@tonic-gate 		switch (be->state) {
825*0Sstevel@tonic-gate 
826*0Sstevel@tonic-gate 		    case GETENT_DONE:
827*0Sstevel@tonic-gate 			args->returnval	= 0;
828*0Sstevel@tonic-gate 			args->erange	= 0;
829*0Sstevel@tonic-gate 			return (NSS_NOTFOUND);
830*0Sstevel@tonic-gate 
831*0Sstevel@tonic-gate 		    case GETENT_ATTRDB:
832*0Sstevel@tonic-gate 			res = _attrdb_compat_XY_all(be,
833*0Sstevel@tonic-gate 			    args, 1, (compat_XY_check_func)NULL, 0);
834*0Sstevel@tonic-gate 			return (res);
835*0Sstevel@tonic-gate 
836*0Sstevel@tonic-gate 		    case GETENT_FILE:
837*0Sstevel@tonic-gate 			linelen = read_line(be->f, instr, be->minbuf);
838*0Sstevel@tonic-gate 			if (linelen < 0) {
839*0Sstevel@tonic-gate 				/* End of file */
840*0Sstevel@tonic-gate 				be->state = GETENT_DONE;
841*0Sstevel@tonic-gate 				continue;
842*0Sstevel@tonic-gate 			}
843*0Sstevel@tonic-gate 			if ((colon = strchr(instr, ':')) != 0) {
844*0Sstevel@tonic-gate 				*colon = '\0';
845*0Sstevel@tonic-gate 			}
846*0Sstevel@tonic-gate 			if (instr[0] == '-') {
847*0Sstevel@tonic-gate 				if (instr[1] != '@') {
848*0Sstevel@tonic-gate 					strset_add(&be->minuses, instr + 1);
849*0Sstevel@tonic-gate 				} else if (be->permit_netgroups) {
850*0Sstevel@tonic-gate 					netgr_set(be, instr + 2);
851*0Sstevel@tonic-gate 					while (netgr_next_u(be, &name)) {
852*0Sstevel@tonic-gate 						strset_add(&be->minuses,
853*0Sstevel@tonic-gate 							name);
854*0Sstevel@tonic-gate 					}
855*0Sstevel@tonic-gate 					netgr_end(be);
856*0Sstevel@tonic-gate 				} /* Else (silently) ignore the entry */
857*0Sstevel@tonic-gate 				continue;
858*0Sstevel@tonic-gate 			} else if (instr[0] != '+') {
859*0Sstevel@tonic-gate 				int	parsestat;
860*0Sstevel@tonic-gate 				/*
861*0Sstevel@tonic-gate 				 * Normal entry, no +/- nonsense
862*0Sstevel@tonic-gate 				 */
863*0Sstevel@tonic-gate 				if (colon != 0) {
864*0Sstevel@tonic-gate 					*colon = ':';
865*0Sstevel@tonic-gate 				}
866*0Sstevel@tonic-gate 				args->returnval = 0;
867*0Sstevel@tonic-gate 				parsestat = (*args->str2ent)(instr, linelen,
868*0Sstevel@tonic-gate 							args->buf.result,
869*0Sstevel@tonic-gate 							args->buf.buffer,
870*0Sstevel@tonic-gate 							args->buf.buflen);
871*0Sstevel@tonic-gate 				if (parsestat == NSS_STR_PARSE_SUCCESS) {
872*0Sstevel@tonic-gate 					args->returnval = args->buf.result;
873*0Sstevel@tonic-gate 					return (NSS_SUCCESS);
874*0Sstevel@tonic-gate 				}
875*0Sstevel@tonic-gate 				/* ==> ?? Treat ERANGE differently ?? */
876*0Sstevel@tonic-gate 				if (parsestat == NSS_STR_PARSE_ERANGE) {
877*0Sstevel@tonic-gate 					args->returnval = 0;
878*0Sstevel@tonic-gate 					args->erange = 1;
879*0Sstevel@tonic-gate 					return (NSS_NOTFOUND);
880*0Sstevel@tonic-gate 				}
881*0Sstevel@tonic-gate 				/* Skip the offending entry, get next */
882*0Sstevel@tonic-gate 				continue;
883*0Sstevel@tonic-gate 			} else if (instr[1] == '\0') {
884*0Sstevel@tonic-gate 				/* Plain "+" */
885*0Sstevel@tonic-gate 				nss_setent(be->db_rootp, be->db_initf,
886*0Sstevel@tonic-gate 					&be->db_context);
887*0Sstevel@tonic-gate 				be->state = GETENT_ALL;
888*0Sstevel@tonic-gate 				be->linelen = linelen;
889*0Sstevel@tonic-gate 				continue;
890*0Sstevel@tonic-gate 			} else if (instr[1] == '@') {
891*0Sstevel@tonic-gate 				/* "+@netgroup" */
892*0Sstevel@tonic-gate 				netgr_set(be, instr + 2);
893*0Sstevel@tonic-gate 				be->state = GETENT_NETGROUP;
894*0Sstevel@tonic-gate 				be->linelen = linelen;
895*0Sstevel@tonic-gate 				continue;
896*0Sstevel@tonic-gate 			} else {
897*0Sstevel@tonic-gate 				/* "+name" */
898*0Sstevel@tonic-gate 				name = instr + 1;
899*0Sstevel@tonic-gate 				break;
900*0Sstevel@tonic-gate 			}
901*0Sstevel@tonic-gate 			/* NOTREACHED */
902*0Sstevel@tonic-gate 
903*0Sstevel@tonic-gate 		    case GETENT_ALL:
904*0Sstevel@tonic-gate 			linelen = be->linelen;
905*0Sstevel@tonic-gate 			args->returnval = 0;
906*0Sstevel@tonic-gate 			nss_getent(be->db_rootp, be->db_initf,
907*0Sstevel@tonic-gate 				&be->db_context, args);
908*0Sstevel@tonic-gate 			if (args->returnval == 0) {
909*0Sstevel@tonic-gate 				/* ==> ?? Treat ERANGE differently ?? */
910*0Sstevel@tonic-gate 				nss_endent(be->db_rootp, be->db_initf,
911*0Sstevel@tonic-gate 					&be->db_context);
912*0Sstevel@tonic-gate 				be->state = GETENT_FILE;
913*0Sstevel@tonic-gate 				continue;
914*0Sstevel@tonic-gate 			}
915*0Sstevel@tonic-gate 			if (strset_in(&be->minuses, (*be->getnamef)(args))) {
916*0Sstevel@tonic-gate 				continue;
917*0Sstevel@tonic-gate 			}
918*0Sstevel@tonic-gate 			name = 0; /* tell code below we've done the lookup */
919*0Sstevel@tonic-gate 			break;
920*0Sstevel@tonic-gate 
921*0Sstevel@tonic-gate 		    case GETENT_NETGROUP:
922*0Sstevel@tonic-gate 			linelen = be->linelen;
923*0Sstevel@tonic-gate 			if (!netgr_next_u(be, &name)) {
924*0Sstevel@tonic-gate 				netgr_end(be);
925*0Sstevel@tonic-gate 				be->state = GETENT_FILE;
926*0Sstevel@tonic-gate 				continue;
927*0Sstevel@tonic-gate 			}
928*0Sstevel@tonic-gate 			/* pass "name" variable to code below... */
929*0Sstevel@tonic-gate 			break;
930*0Sstevel@tonic-gate 		}
931*0Sstevel@tonic-gate 
932*0Sstevel@tonic-gate 		if (name != 0) {
933*0Sstevel@tonic-gate 			if (strset_in(&be->minuses, name)) {
934*0Sstevel@tonic-gate 				continue;
935*0Sstevel@tonic-gate 			}
936*0Sstevel@tonic-gate 			/*
937*0Sstevel@tonic-gate 			 * Do a getXXXnam(name).  If we were being pure,
938*0Sstevel@tonic-gate 			 *   we'd introduce yet another function-pointer
939*0Sstevel@tonic-gate 			 *   that the database-specific code had to supply
940*0Sstevel@tonic-gate 			 *   to us.  Instead we'll be grotty and hard-code
941*0Sstevel@tonic-gate 			 *   the knowledge that
942*0Sstevel@tonic-gate 			 *	(a) The username is always passwd in key.name,
943*0Sstevel@tonic-gate 			 *	(b) NSS_DBOP_PASSWD_BYNAME ==
944*0Sstevel@tonic-gate 			 *		NSS_DBOP_SHADOW_BYNAME ==
945*0Sstevel@tonic-gate 			 *		NSS_DBOP_next_iter.
946*0Sstevel@tonic-gate 			 */
947*0Sstevel@tonic-gate 			savename = args->key.name;
948*0Sstevel@tonic-gate 			args->key.name	= name;
949*0Sstevel@tonic-gate 			args->returnval	= 0;
950*0Sstevel@tonic-gate 			nss_search(be->db_rootp, be->db_initf,
951*0Sstevel@tonic-gate 				NSS_DBOP_next_iter, args);
952*0Sstevel@tonic-gate 			args->key.name = savename;  /* In case anyone cares */
953*0Sstevel@tonic-gate 		}
954*0Sstevel@tonic-gate 		/*
955*0Sstevel@tonic-gate 		 * Found one via "+", "+name" or "@netgroup".
956*0Sstevel@tonic-gate 		 * Override some fields if the /etc file says to do so.
957*0Sstevel@tonic-gate 		 */
958*0Sstevel@tonic-gate 		if (args->returnval == 0) {
959*0Sstevel@tonic-gate 			/* ==> ?? Should treat erange differently? */
960*0Sstevel@tonic-gate 			continue;
961*0Sstevel@tonic-gate 		}
962*0Sstevel@tonic-gate 		/* 'colon' was set umpteen iterations ago in GETENT_FILE */
963*0Sstevel@tonic-gate 		if (colon != 0) {
964*0Sstevel@tonic-gate 			*colon = ':';
965*0Sstevel@tonic-gate 			colon = 0;
966*0Sstevel@tonic-gate 		}
967*0Sstevel@tonic-gate 		return (do_merge(be, args, instr, linelen));
968*0Sstevel@tonic-gate 	}
969*0Sstevel@tonic-gate }
970*0Sstevel@tonic-gate 
971*0Sstevel@tonic-gate /* We don't use this directly;  we just copy the bits when we want to	 */
972*0Sstevel@tonic-gate /* initialize the variable (in the compat_backend struct) that we do use */
973*0Sstevel@tonic-gate static DEFINE_NSS_GETENT(context_initval);
974*0Sstevel@tonic-gate 
975*0Sstevel@tonic-gate nss_backend_t *
976*0Sstevel@tonic-gate _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups,
977*0Sstevel@tonic-gate 		getname_func, merge_func)
978*0Sstevel@tonic-gate 	compat_backend_op_t	ops[];
979*0Sstevel@tonic-gate 	int			n_ops;
980*0Sstevel@tonic-gate 	const char		*filename;
981*0Sstevel@tonic-gate 	int			min_bufsize;
982*0Sstevel@tonic-gate 	nss_db_root_t		*rootp;
983*0Sstevel@tonic-gate 	nss_db_initf_t		initf;
984*0Sstevel@tonic-gate 	int			netgroups;
985*0Sstevel@tonic-gate 	compat_get_name		getname_func;
986*0Sstevel@tonic-gate 	compat_merge_func	merge_func;
987*0Sstevel@tonic-gate {
988*0Sstevel@tonic-gate 	compat_backend_ptr_t	be;
989*0Sstevel@tonic-gate 
990*0Sstevel@tonic-gate 	if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) {
991*0Sstevel@tonic-gate 		return (0);
992*0Sstevel@tonic-gate 	}
993*0Sstevel@tonic-gate 	be->ops		= ops;
994*0Sstevel@tonic-gate 	be->n_ops	= n_ops;
995*0Sstevel@tonic-gate 	be->filename	= filename;
996*0Sstevel@tonic-gate 	be->f		= 0;
997*0Sstevel@tonic-gate 	be->minbuf	= min_bufsize;
998*0Sstevel@tonic-gate 	be->buf		= 0;
999*0Sstevel@tonic-gate 
1000*0Sstevel@tonic-gate 	be->db_rootp	= rootp;
1001*0Sstevel@tonic-gate 	be->db_initf	= initf;
1002*0Sstevel@tonic-gate 	be->db_context	= context_initval;
1003*0Sstevel@tonic-gate 
1004*0Sstevel@tonic-gate 	be->getnamef	= getname_func;
1005*0Sstevel@tonic-gate 	be->mergef	= merge_func;
1006*0Sstevel@tonic-gate 
1007*0Sstevel@tonic-gate 	if ((strcmp(be->filename, USERATTR_FILENAME) == 0) ||
1008*0Sstevel@tonic-gate 	    (strcmp(be->filename, AUDITUSER_FILENAME) == 0))
1009*0Sstevel@tonic-gate 		be->state = GETENT_ATTRDB;
1010*0Sstevel@tonic-gate 	else
1011*0Sstevel@tonic-gate 		be->state = GETENT_FILE;    /* i.e. do Automatic setent(); */
1012*0Sstevel@tonic-gate 
1013*0Sstevel@tonic-gate 	be->minuses	= 0;
1014*0Sstevel@tonic-gate 
1015*0Sstevel@tonic-gate 	be->permit_netgroups = netgroups;
1016*0Sstevel@tonic-gate 	be->yp_domain	= 0;
1017*0Sstevel@tonic-gate 	be->getnetgrent_backend	= 0;
1018*0Sstevel@tonic-gate 	be->netgr_buffer = 0;
1019*0Sstevel@tonic-gate 
1020*0Sstevel@tonic-gate 	return ((nss_backend_t *)be);
1021*0Sstevel@tonic-gate }
1022