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