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