xref: /illumos-gate/usr/src/lib/nsswitch/compat/common/compat_common.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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->workarea);
449 		free(be);
450 	}
451 	return (NSS_SUCCESS);	/* In case anyone is dumb enough to check */
452 }
453 
454 static int
455 read_line(f, buffer, buflen)
456 	FILE			*f;
457 	char			*buffer;
458 	int			buflen;
459 {
460 	/*CONSTCOND*/
461 	while (1) {
462 		int	linelen;
463 
464 		if (fgets(buffer, buflen, f) == 0) {
465 			/* End of file */
466 			return (-1);
467 		}
468 		linelen = strlen(buffer);
469 		/* linelen >= 1 (since fgets didn't return 0) */
470 
471 		if (buffer[linelen - 1] == '\n') {
472 			/*
473 			 * ===> The code below that calls read_line() doesn't
474 			 *	play by the rules;  it assumes in places that
475 			 *	the line is null-terminated.  For now we'll
476 			 *	humour it.
477 			 */
478 			buffer[--linelen] = '\0';
479 			return (linelen);
480 		}
481 		if (feof(f)) {
482 			/* Line is last line in file, and has no newline */
483 			return (linelen);
484 		}
485 		/* Line too long for buffer;  toss it and loop for next line */
486 		/* ===== should syslog() in cases where previous code did */
487 		while (fgets(buffer, buflen, f) != 0 &&
488 		    buffer[strlen(buffer) - 1] != '\n') {
489 			;
490 		}
491 	}
492 	/*NOTREACHED*/
493 }
494 
495 static int
496 is_nss_lookup_by_name(int attrdb, nss_dbop_t op)
497 {
498 	int result = 0;
499 
500 	if ((attrdb != 0) &&
501 	    ((op == NSS_DBOP_AUDITUSER_BYNAME) ||
502 	    (op == NSS_DBOP_USERATTR_BYNAME))) {
503 		result = 1;
504 	} else if ((attrdb == 0) &&
505 	    ((op == NSS_DBOP_GROUP_BYNAME) ||
506 	    (op == NSS_DBOP_PASSWD_BYNAME) ||
507 	    (op == NSS_DBOP_SHADOW_BYNAME))) {
508 		result = 1;
509 	}
510 
511 	return (result);
512 }
513 
514 /*ARGSUSED*/
515 nss_status_t
516 _attrdb_compat_XY_all(be, argp, netdb, check, op_num)
517     compat_backend_ptr_t be;
518     nss_XbyY_args_t *argp;
519     int netdb;
520     compat_XY_check_func check;
521     nss_dbop_t op_num;
522 {
523 	int		parsestat;
524 	int		(*func)();
525 	const char	*filter = argp->key.name;
526 	nss_status_t	res;
527 
528 #ifdef	DEBUG
529 	(void) fprintf(stdout, "\n[compat_common.c: _attrdb_compat_XY_all]\n");
530 #endif	/* DEBUG */
531 
532 	if (be->buf == 0 &&
533 	    (be->buf = malloc(be->minbuf)) == 0) {
534 		return (NSS_UNAVAIL);
535 	}
536 	if (check != NULL)
537 		if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS)
538 			return (res);
539 
540 	res = NSS_NOTFOUND;
541 
542 	/*
543 	 * assume a NULL buf.result pointer is an indication
544 	 * that the lookup result should be returned in /etc
545 	 * file format
546 	 */
547 	if (argp->buf.result == NULL) {
548 		be->return_string_data = 1;
549 
550 		/*
551 		 * the code executed later needs the result struct
552 		 * as working area
553 		 */
554 		argp->buf.result = be->workarea;
555 		func = be->str2ent_alt;
556 	} else
557 		func = argp->str2ent;
558 
559 	/*CONSTCOND*/
560 	while (1) {
561 		int	linelen;
562 		char	*instr	= be->buf;
563 
564 		if ((linelen = read_line(be->f, instr, be->minbuf)) < 0) {
565 			/* End of file */
566 			argp->returnval = 0;
567 			argp->erange    = 0;
568 			break;
569 		}
570 		if (filter != 0 && strstr(instr, filter) == 0) {
571 			/*
572 			 * Optimization:  if the entry doesn't contain the
573 			 * filter string then it can't be the entry we want,
574 			 * so don't bother looking more closely at it.
575 			 */
576 			continue;
577 		}
578 		if (netdb) {
579 			char	*first;
580 			char	*last;
581 
582 			if ((last = strchr(instr, '#')) == 0) {
583 				last = instr + linelen;
584 			}
585 			*last-- = '\0';		/* Nuke '\n' or #comment */
586 
587 			/*
588 			 * Skip leading whitespace.  Normally there isn't
589 			 * any, so it's not worth calling strspn().
590 			 */
591 			for (first = instr;  isspace(*first);  first++) {
592 				;
593 			}
594 			if (*first == '\0') {
595 				continue;
596 			}
597 			/*
598 			 * Found something non-blank on the line.  Skip back
599 			 * over any trailing whitespace;  since we know
600 			 * there's non-whitespace earlier in the line,
601 			 * checking for termination is easy.
602 			 */
603 			while (isspace(*last)) {
604 				--last;
605 			}
606 			linelen = last - first + 1;
607 			if (first != instr) {
608 				instr = first;
609 			}
610 		}
611 		argp->returnval = 0;
612 		parsestat = (*func)(instr, linelen, argp->buf.result,
613 					argp->buf.buffer, argp->buf.buflen);
614 		if (parsestat == NSS_STR_PARSE_SUCCESS) {
615 				argp->returnval = argp->buf.result;
616 			if (check == 0 || (*check)(argp)) {
617 				int	len;
618 
619 				if (be->return_string_data != 1) {
620 					res = NSS_SUCCESS;
621 					break;
622 				}
623 
624 				/* copy string data to result buffer */
625 				argp->buf.result = NULL;
626 				argp->returnval = argp->buf.buffer;
627 				if ((len = strlcpy(argp->buf.buffer, instr,
628 					argp->buf.buflen)) >=
629 					argp->buf.buflen) {
630 					argp->returnval = NULL;
631 					res = NSS_NOTFOUND;
632 					argp->erange = 1;
633 					break;
634 				}
635 
636 				argp->returnlen = len;
637 				res = NSS_SUCCESS;
638 				break;
639 			}
640 		} else if (parsestat == NSS_STR_PARSE_ERANGE) {
641 			res = NSS_NOTFOUND;
642 			argp->erange = 1;
643 			break;
644 		}
645 	}
646 	/*
647 	 * stayopen is set to 0 by default in order to close the opened
648 	 * file.  Some applications may break if it is set to 1.
649 	 */
650 	if (check != 0 && !argp->stayopen) {
651 		(void) _nss_compat_endent(be, 0);
652 	}
653 
654 	if (res != NSS_SUCCESS) {
655 		/*
656 		 * tell the nss_search() and nss_getent() below
657 		 * if the result should be returned in the /etc
658 		 * file format
659 		 */
660 		if (be->return_string_data == 1)
661 			argp->buf.result = NULL;
662 
663 		if ((op_num == NSS_DBOP_USERATTR_BYNAME) ||
664 		    (op_num == NSS_DBOP_AUDITUSER_BYNAME)) {
665 			res = nss_search(be->db_rootp,
666 			    be->db_initf,
667 			    op_num,
668 			    argp);
669 		} else {
670 			res = nss_getent(be->db_rootp,
671 			    be->db_initf, &be->db_context, argp);
672 		}
673 		if (res != NSS_SUCCESS) {
674 			argp->returnval	= 0;
675 			argp->erange	= 0;
676 		}
677 	}
678 
679 	return (res);
680 }
681 
682 nss_status_t
683 _nss_compat_XY_all(be, args, check, op_num)
684 	compat_backend_ptr_t	be;
685 	nss_XbyY_args_t		*args;
686 	compat_XY_check_func	check;
687 	nss_dbop_t		op_num;
688 {
689 	nss_status_t		res;
690 	int			parsestat;
691 
692 
693 	if (be->buf == 0 &&
694 	    (be->buf = malloc(be->minbuf)) == 0) {
695 		return (NSS_UNAVAIL); /* really panic, malloc failed */
696 	}
697 
698 	if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
699 		return (res);
700 	}
701 
702 	res = NSS_NOTFOUND;
703 
704 	/*
705 	 * assume a NULL buf.result pointer is an indication
706 	 * that the lookup result should be returned in /etc
707 	 * file format
708 	 */
709 	if (args->buf.result == NULL) {
710 
711 		be->return_string_data = 1;
712 
713 		/*
714 		 * the code executed later needs the result struct
715 		 * as working area
716 		 */
717 		args->buf.result = be->workarea;
718 
719 		be->str2ent_save = args->str2ent;
720 		args->str2ent = be->str2ent_alt;
721 	}
722 
723 	/*CONSTCOND*/
724 	while (1) {
725 		int		linelen;
726 		char		*instr	= be->buf;
727 		char		*colon;
728 
729 		linelen = read_line(be->f, instr, be->minbuf);
730 		if (linelen < 0) {
731 			/* End of file */
732 			args->returnval = 0;
733 			args->erange    = 0;
734 			break;
735 		}
736 
737 		args->returnval = 0;	/* reset for both types of entries */
738 
739 		if (instr[0] != '+' && instr[0] != '-') {
740 			/* Simple, wholesome, God-fearing entry */
741 			parsestat = (*args->str2ent)(instr, linelen,
742 						    args->buf.result,
743 						    args->buf.buffer,
744 						    args->buf.buflen);
745 			if (parsestat == NSS_STR_PARSE_SUCCESS) {
746 				args->returnval = args->buf.result;
747 				if ((*check)(args) != 0) {
748 					int len;
749 					if (be->return_string_data != 1) {
750 						res = NSS_SUCCESS;
751 						break;
752 					}
753 
754 					/*
755 					 * copy string data to
756 					 * result buffer
757 					 */
758 					args->buf.result = NULL;
759 					args->str2ent = be->str2ent_save;
760 					if ((len = strlcpy(args->buf.buffer,
761 						instr, args->buf.buflen)) >=
762 							args->buf.buflen)
763 						parsestat =
764 							NSS_STR_PARSE_ERANGE;
765 					else {
766 						args->returnval =
767 							args->buf.buffer;
768 						args->returnlen = len;
769 						res = NSS_SUCCESS;
770 						break;
771 					}
772 				} else
773 					continue;
774 			}
775 
776 /* ===> Check the Dani logic here... */
777 
778 			if (parsestat == NSS_STR_PARSE_ERANGE) {
779 				args->erange = 1;
780 				res = NSS_NOTFOUND;
781 				break;
782 				/* should we just skip this one long line ? */
783 			} /* else if (parsestat == NSS_STR_PARSE_PARSE) */
784 				/* don't care ! */
785 
786 /* ==> ?? */		continue;
787 		}
788 
789 
790 		/*
791 		 * Process "+", "+name", "+@netgroup", "-name" or "-@netgroup"
792 		 *
793 		 * This code is optimized for lookups by name.
794 		 *
795 		 * For lookups by identifier search key cannot be matched with
796 		 * the name of the "+" or "-" entry. So nss_search() is to be
797 		 * called before extracting the name i.e. via (*be->getnamef)().
798 		 *
799 		 * But for lookups by name, search key is compared with the name
800 		 * of the "+" or "-" entry to acquire a match and thus
801 		 * unnesessary calls to nss_search() is eliminated. Also for
802 		 * matching "-" entries, calls to nss_search() is eliminated.
803 		 */
804 
805 		if ((colon = strchr(instr, ':')) != 0) {
806 			*colon = '\0';	/* terminate field to extract name */
807 		}
808 
809 		if (instr[1] == '@') {
810 			/*
811 			 * Case 1:
812 			 * The entry is of the form "+@netgroup" or
813 			 * "-@netgroup".  If we're performing a lookup by name,
814 			 * we can simply extract the name from the search key
815 			 * (i.e. args->key.name).  If not, then we must call
816 			 * nss_search() before extracting the name via the
817 			 * get_XXname() function. i.e. (*be->getnamef)(args).
818 			 */
819 			if (is_nss_lookup_by_name(0, op_num) != 0) {
820 				/* compare then search */
821 				if (!be->permit_netgroups ||
822 				    !netgr_in(be, instr + 2, args->key.name))
823 					continue;
824 				if (instr[0] == '+') {
825 					/* need to search for "+" entry */
826 					(void) nss_search(be->db_rootp,
827 						be->db_initf, op_num, args);
828 					if (args->returnval == 0)
829 						continue;
830 				}
831 			} else {
832 				/* search then compare */
833 				(void) nss_search(be->db_rootp,
834 					be->db_initf, op_num, args);
835 				if (args->returnval == 0)
836 					continue;
837 				if (!be->permit_netgroups ||
838 				    !netgr_in(be, instr + 2,
839 				    (*be->getnamef)(args)))
840 					continue;
841 			}
842 		} else if (instr[1] == '\0') {
843 			/*
844 			 * Case 2:
845 			 * The entry is of the form "+" or "-".  The former
846 			 * allows all entries from name services.  The latter
847 			 * is illegal and ought to be ignored.
848 			 */
849 			if (instr[0] == '-')
850 				continue;
851 			/* need to search for "+" entry */
852 			(void) nss_search(be->db_rootp, be->db_initf,
853 				op_num, args);
854 			if (args->returnval == 0)
855 				continue;
856 		} else {
857 			/*
858 			 * Case 3:
859 			 * The entry is of the form "+name" or "-name".
860 			 * If we're performing a lookup by name, we can simply
861 			 * extract the name from the search key
862 			 * (i.e. args->key.name).  If not, then we must call
863 			 * nss_search() before extracting the name via the
864 			 * get_XXname() function. i.e. (*be->getnamef)(args).
865 			 */
866 			if (is_nss_lookup_by_name(0, op_num) != 0) {
867 				/* compare then search */
868 				if (strcmp(instr + 1, args->key.name) != 0)
869 					continue;
870 				if (instr[0] == '+') {
871 					/* need to search for "+" entry */
872 					(void) nss_search(be->db_rootp,
873 						be->db_initf, op_num, args);
874 					if (args->returnval == 0)
875 						continue;
876 				}
877 			} else {
878 				/* search then compare */
879 				(void) nss_search(be->db_rootp,
880 					be->db_initf, op_num, args);
881 				if (args->returnval == 0)
882 					continue;
883 				if (strcmp(instr + 1, (*be->getnamef)(args))
884 				    != 0)
885 					continue;
886 			}
887 		}
888 		if (instr[0] == '-') {
889 			/* no need to search for "-" entry */
890 			args->returnval = 0;
891 			args->erange = 0;
892 			res = NSS_NOTFOUND;
893 		} else {
894 			if (colon != 0)
895 				*colon = ':';	/* restoration */
896 			res = do_merge(be, args, instr, linelen);
897 		}
898 		break;
899 	}
900 
901 	/*
902 	 * stayopen is set to 0 by default in order to close the opened
903 	 * file.  Some applications may break if it is set to 1.
904 	 */
905 	if (!args->stayopen) {
906 		(void) _nss_compat_endent(be, 0);
907 	}
908 
909 	if (be->return_string_data == 1) {
910 		args->str2ent = be->str2ent_save;
911 	}
912 
913 	return (res);
914 }
915 
916 nss_status_t
917 _nss_compat_getent(be, a)
918 	compat_backend_ptr_t	be;
919 	void			*a;
920 {
921 	nss_XbyY_args_t		*args = (nss_XbyY_args_t *)a;
922 	nss_status_t		res;
923 	char			*colon = 0; /* <=== need comment re lifetime */
924 
925 	if (be->f == 0) {
926 		if ((res = _nss_compat_setent(be, 0)) != NSS_SUCCESS) {
927 			return (res);
928 		}
929 	}
930 
931 	if (be->buf == 0 &&
932 	    (be->buf = malloc(be->minbuf)) == 0) {
933 		return (NSS_UNAVAIL); /* really panic, malloc failed */
934 	}
935 
936 	/*
937 	 * assume a NULL buf.result pointer is an indication
938 	 * that the lookup result should be returned in /etc
939 	 * file format
940 	 */
941 	if (args->buf.result == NULL) {
942 		be->return_string_data = 1;
943 
944 		/*
945 		 * the code executed later needs the result struct
946 		 * as working area
947 		 */
948 		args->buf.result = be->workarea;
949 	}
950 
951 	/*CONSTCOND*/
952 	while (1) {
953 		char		*instr	= be->buf;
954 		int		linelen;
955 		char		*name;	/* === Need more distinctive label */
956 		const char	*savename;
957 
958 		/*
959 		 * In the code below...
960 		 *    break	means "I found one, I think" (i.e. goto the
961 		 *		code after the end of the switch statement),
962 		 *    continue	means "Next candidate"
963 		 *		(i.e. loop around to the switch statement),
964 		 *    return	means "I'm quite sure" (either Yes or No).
965 		 */
966 		switch (be->state) {
967 
968 		    case GETENT_DONE:
969 			args->returnval	= 0;
970 			args->erange	= 0;
971 			return (NSS_NOTFOUND);
972 
973 		    case GETENT_ATTRDB:
974 			args->key.name = NULL;
975 			res = _attrdb_compat_XY_all(be,
976 			    args, 1, (compat_XY_check_func)NULL, 0);
977 			return (res);
978 
979 		    case GETENT_FILE:
980 			linelen = read_line(be->f, instr, be->minbuf);
981 			if (linelen < 0) {
982 				/* End of file */
983 				be->state = GETENT_DONE;
984 				continue;
985 			}
986 			if ((colon = strchr(instr, ':')) != 0) {
987 				*colon = '\0';
988 			}
989 			if (instr[0] == '-') {
990 				if (instr[1] != '@') {
991 					(void) strset_add(&be->minuses,
992 						instr + 1);
993 				} else if (be->permit_netgroups) {
994 					netgr_set(be, instr + 2);
995 					while (netgr_next_u(be, &name)) {
996 						(void) strset_add(&be->minuses,
997 							name);
998 					}
999 					netgr_end(be);
1000 				} /* Else (silently) ignore the entry */
1001 				continue;
1002 			} else if (instr[0] != '+') {
1003 				int	parsestat;
1004 				/*
1005 				 * Normal entry, no +/- nonsense
1006 				 */
1007 				if (colon != 0) {
1008 					*colon = ':';
1009 				}
1010 				args->returnval = 0;
1011 				parsestat = (*args->str2ent)(instr, linelen,
1012 							args->buf.result,
1013 							args->buf.buffer,
1014 							args->buf.buflen);
1015 				if (parsestat == NSS_STR_PARSE_SUCCESS) {
1016 					int	len;
1017 
1018 					if (be->return_string_data != 1) {
1019 						args->returnval =
1020 							args->buf.result;
1021 						return (NSS_SUCCESS);
1022 					}
1023 
1024 					/*
1025 					 * copy string data to
1026 					 * result buffer
1027 					 */
1028 					args->buf.result = NULL;
1029 					args->returnval =
1030 						args->buf.buffer;
1031 					if ((len = strlcpy(args->buf.buffer,
1032 						instr, args->buf.buflen)) >=
1033 						args->buf.buflen)
1034 						parsestat =
1035 							NSS_STR_PARSE_ERANGE;
1036 					else {
1037 						args->returnlen = len;
1038 						return (NSS_SUCCESS);
1039 					}
1040 				}
1041 				/* ==> ?? Treat ERANGE differently ?? */
1042 				if (parsestat == NSS_STR_PARSE_ERANGE) {
1043 					args->returnval = 0;
1044 					args->erange = 1;
1045 					return (NSS_NOTFOUND);
1046 				}
1047 				/* Skip the offending entry, get next */
1048 				continue;
1049 			} else if (instr[1] == '\0') {
1050 				/* Plain "+" */
1051 				nss_setent(be->db_rootp, be->db_initf,
1052 					&be->db_context);
1053 				be->state = GETENT_ALL;
1054 				be->linelen = linelen;
1055 
1056 				continue;
1057 			} else if (instr[1] == '@') {
1058 				/* "+@netgroup" */
1059 				netgr_set(be, instr + 2);
1060 				be->state = GETENT_NETGROUP;
1061 				be->linelen = linelen;
1062 				continue;
1063 			} else {
1064 				/* "+name" */
1065 				name = instr + 1;
1066 				break;
1067 			}
1068 			/* NOTREACHED */
1069 
1070 		    case GETENT_ALL:
1071 			linelen = be->linelen;
1072 			args->returnval = 0;
1073 			if (be->return_string_data == 1) {
1074 				be->str2ent_save = args->str2ent;
1075 				args->str2ent = be->str2ent_alt;
1076 			}
1077 
1078 			(void) nss_getent(be->db_rootp, be->db_initf,
1079 				&be->db_context, args);
1080 			if (args->returnval == 0) {
1081 				/* ==> ?? Treat ERANGE differently ?? */
1082 				nss_endent(be->db_rootp, be->db_initf,
1083 					&be->db_context);
1084 				be->state = GETENT_FILE;
1085 				if (be->return_string_data == 1)
1086 					args->str2ent = be->str2ent_save;
1087 				continue;
1088 			}
1089 			if (strset_in(&be->minuses, (*be->getnamef)(args)))
1090 				continue;
1091 			name = 0; /* tell code below we've done the lookup */
1092 			if (be->return_string_data == 1)
1093 				args->str2ent = be->str2ent_save;
1094 			break;
1095 
1096 		    case GETENT_NETGROUP:
1097 			linelen = be->linelen;
1098 			if (!netgr_next_u(be, &name)) {
1099 				netgr_end(be);
1100 				be->state = GETENT_FILE;
1101 				continue;
1102 			}
1103 			/* pass "name" variable to code below... */
1104 			break;
1105 		}
1106 
1107 		if (name != 0) {
1108 			if (strset_in(&be->minuses, name)) {
1109 				continue;
1110 			}
1111 			/*
1112 			 * Do a getXXXnam(name).  If we were being pure,
1113 			 *   we'd introduce yet another function-pointer
1114 			 *   that the database-specific code had to supply
1115 			 *   to us.  Instead we'll be grotty and hard-code
1116 			 *   the knowledge that
1117 			 *	(a) The username is always passwd in key.name,
1118 			 *	(b) NSS_DBOP_PASSWD_BYNAME ==
1119 			 *		NSS_DBOP_SHADOW_BYNAME ==
1120 			 *		NSS_DBOP_next_iter.
1121 			 */
1122 			savename = args->key.name;
1123 			args->key.name	= name;
1124 			args->returnval	= 0;
1125 			if (be->return_string_data == 1) {
1126 				be->str2ent_save = args->str2ent;
1127 				args->str2ent = be->str2ent_alt;
1128 			}
1129 
1130 			(void) nss_search(be->db_rootp, be->db_initf,
1131 				NSS_DBOP_next_iter, args);
1132 
1133 			if (be->return_string_data == 1)
1134 				args->str2ent = be->str2ent_save;
1135 			args->key.name = savename;  /* In case anyone cares */
1136 		}
1137 		/*
1138 		 * Found one via "+", "+name" or "@netgroup".
1139 		 * Override some fields if the /etc file says to do so.
1140 		 */
1141 		if (args->returnval == 0) {
1142 			/* ==> ?? Should treat erange differently? */
1143 			continue;
1144 		}
1145 		/* 'colon' was set umpteen iterations ago in GETENT_FILE */
1146 		if (colon != 0) {
1147 			*colon = ':';
1148 			colon = 0;
1149 		}
1150 		return (do_merge(be, args, instr, linelen));
1151 	}
1152 	/*NOTREACHED*/
1153 }
1154 
1155 /* We don't use this directly;  we just copy the bits when we want to	 */
1156 /* initialize the variable (in the compat_backend struct) that we do use */
1157 static DEFINE_NSS_GETENT(context_initval);
1158 
1159 nss_backend_t *
1160 _nss_compat_constr(ops, n_ops, filename, min_bufsize, rootp, initf, netgroups,
1161 		getname_func, merge_func)
1162 	compat_backend_op_t	ops[];
1163 	int			n_ops;
1164 	const char		*filename;
1165 	int			min_bufsize;
1166 	nss_db_root_t		*rootp;
1167 	nss_db_initf_t		initf;
1168 	int			netgroups;
1169 	compat_get_name		getname_func;
1170 	compat_merge_func	merge_func;
1171 {
1172 	compat_backend_ptr_t	be;
1173 
1174 	if ((be = (compat_backend_ptr_t)malloc(sizeof (*be))) == 0) {
1175 		return (0);
1176 	}
1177 	be->ops		= ops;
1178 	be->n_ops	= n_ops;
1179 	be->filename	= filename;
1180 	be->f		= 0;
1181 	be->minbuf	= min_bufsize;
1182 	be->buf		= 0;
1183 
1184 	be->db_rootp	= rootp;
1185 	be->db_initf	= initf;
1186 	be->db_context	= context_initval;
1187 
1188 	be->getnamef	= getname_func;
1189 	be->mergef	= merge_func;
1190 
1191 	be->state = GETENT_FILE;    /* i.e. do Automatic setent(); */
1192 	if (strcmp(be->filename, USERATTR_FILENAME) == 0) {
1193 		be->state = GETENT_ATTRDB;
1194 		be->str2ent_alt = str2userattr_s;
1195 		be->workarea = calloc(1, sizeof (userstr_t));
1196 	} else if (strcmp(be->filename, AUDITUSER_FILENAME) == 0) {
1197 		be->state = GETENT_ATTRDB;
1198 		be->str2ent_alt = str2auuser_s;
1199 		be->workarea = calloc(1, sizeof (au_user_str_t));
1200 	} else if (strcmp(be->filename, PASSWD) == 0) {
1201 		be->str2ent_alt = str2passwd;
1202 		be->workarea = calloc(1, sizeof (struct passwd));
1203 	} else if (strcmp(be->filename, SHADOW) == 0) {
1204 		be->str2ent_alt = str2spwd;
1205 		be->workarea = calloc(1, sizeof (struct spwd));
1206 	} else { /* group */
1207 		be->str2ent_alt = str2group;
1208 		be->workarea = calloc(1, sizeof (struct group));
1209 	}
1210 	if (be->workarea == NULL)
1211 		return (NULL);
1212 
1213 	be->minuses	= 0;
1214 
1215 	be->permit_netgroups = netgroups;
1216 	be->yp_domain	= 0;
1217 	be->getnetgrent_backend	= 0;
1218 	be->netgr_buffer = 0;
1219 	be->return_string_data = 0;
1220 
1221 	return ((nss_backend_t *)be);
1222 }
1223