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