xref: /illumos-gate/usr/src/lib/libc/port/gen/nsparse.c (revision 7c478bd9)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "synonyms.h"
30 #include "mtlib.h"
31 #include "libc.h"
32 #include <synch.h>
33 #include <sys/types.h>
34 #include <stdlib.h>
35 #include <stdio.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include <dlfcn.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <errno.h>
43 
44 #define	__NSS_PRIVATE_INTERFACE
45 #include "nsswitch_priv.h"
46 #undef	__NSS_PRIVATE_INTERFACE
47 
48 #include <syslog.h>
49 
50 #define	islabel(c) 	(isalnum(c) || (c) == '_')
51 
52 #define	LIBC_STRDUP(new, existing) \
53 	if ((new = libc_strdup(existing)) == NULL) { \
54 		dup_fail = 1; \
55 		goto barf_line; \
56 	}
57 
58 /*
59  * This file has all the routines that access the configuration
60  * information.
61  */
62 
63 struct cons_cell_v1 { /* private to the parser */
64 	struct __nsw_switchconfig_v1 *sw;
65 	struct cons_cell_v1 *next;
66 };
67 
68 struct cons_cell { /* private to the parser */
69 	struct __nsw_switchconfig *sw;
70 	struct cons_cell *next;
71 };
72 
73 /*
74  * Local routines
75  */
76 
77 static char *skip(char **, char);
78 static char *labelskip(char *);
79 static char *spaceskip(char *);
80 static struct __nsw_switchconfig_v1 *scrounge_cache_v1(const char *);
81 static struct __nsw_switchconfig *scrounge_cache(const char *);
82 static int add_concell_v1(struct __nsw_switchconfig_v1 *);
83 static int add_concell(struct __nsw_switchconfig *);
84 static void freeconf_v1(struct __nsw_switchconfig_v1 *);
85 static void freeconf(struct __nsw_switchconfig *);
86 static int alldigits(char *);
87 
88 static struct cons_cell_v1 *concell_list_v1; /* stays with add_concell() */
89 static struct cons_cell *concell_list; /* stays with add_concell() */
90 
91 /*
92  *
93  * With the "lookup control" feature, the default criteria for NIS, NIS+,
94  * and any new services (e.g. ldap) will be:
95  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever]
96  *
97  * For backward compat, NIS via NIS server in DNS forwarding mode will be:
98  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
99  *
100  * And also for backward compat, the default criteria for DNS will be:
101  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
102  */
103 
104 
105 
106 /*
107  * The BIND resolver normally will retry several times on server non-response.
108  * But now with the "lookup control" feature, we don't want the resolver doing
109  * many retries, rather we want it to return control (reasonably) quickly back
110  * to the switch engine.  However, when TRYAGAIN=N or TRYAGAIN=forever is
111  * not explicitly set by the admin in the conf file, we want the old "resolver
112  * retry a few times" rather than no retries at all.
113  */
114 static int 	dns_tryagain_retry = 3;
115 
116 /*
117  * For backward compat (pre "lookup control"), the dns default behavior is
118  * soft lookup.
119  */
120 static void
121 set_dns_default_lkp(struct __nsw_lookup_v1 *lkp)
122 {
123 	if (strcasecmp(lkp->service_name, "dns") == 0) {
124 		lkp->actions[__NSW_TRYAGAIN] =
125 			__NSW_TRYAGAIN_NTIMES;
126 		lkp->max_retries = dns_tryagain_retry;
127 	}
128 }
129 
130 /*
131  * Private interface used by nss_common.c, hence this function is not static
132  */
133 struct __nsw_switchconfig_v1 *
134 _nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp)
135 	/* linep   Nota Bene: not const char *	*/
136 	/* errp  Meanings are abused a bit	*/
137 {
138 	struct __nsw_switchconfig_v1 *cfp;
139 	struct __nsw_lookup_v1 *lkp, **lkq;
140 	int end_crit, dup_fail = 0;
141 	action_t act;
142 	char *p, *tokenp;
143 
144 	*errp = __NSW_CONF_PARSE_SUCCESS;
145 
146 	if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig_v1)))
147 	    == NULL) {
148 		*errp = __NSW_CONF_PARSE_SYSERR;
149 		return (NULL);
150 	}
151 	LIBC_STRDUP(cfp->dbase, name);
152 	lkq = &cfp->lookups;
153 
154 	/* linep points to a naming service name */
155 	for (;;) {
156 		int i;
157 
158 		/* white space following the last service */
159 		if (*linep == '\0' || *linep == '\n') {
160 			return (cfp);
161 		}
162 		if ((lkp = libc_malloc(sizeof (struct __nsw_lookup_v1)))
163 		    == NULL) {
164 			*errp = __NSW_CONF_PARSE_SYSERR;
165 			freeconf_v1(cfp);
166 			return (NULL);
167 		}
168 
169 		*lkq = lkp;
170 		lkq = &lkp->next;
171 
172 		for (i = 0; i < __NSW_STD_ERRS_V1; i++)
173 			if (i == __NSW_SUCCESS)
174 				lkp->actions[i] = __NSW_RETURN;
175 			else if (i == __NSW_TRYAGAIN)
176 				lkp->actions[i] = __NSW_TRYAGAIN_FOREVER;
177 			else
178 				lkp->actions[i] = __NSW_CONTINUE;
179 
180 		/* get criteria for the naming service */
181 		if (tokenp = skip(&linep, '[')) { /* got criteria */
182 
183 			/* premature end, illegal char following [ */
184 			if (!islabel(*linep))
185 				goto barf_line;
186 			LIBC_STRDUP(lkp->service_name, tokenp);
187 			cfp->num_lookups++;
188 
189 			set_dns_default_lkp(lkp);
190 
191 			end_crit = 0;
192 
193 			/* linep points to a switch_err */
194 			for (;;) {
195 				int ntimes = 0; /* try again max N times */
196 				int dns_continue = 0;
197 
198 				if ((tokenp = skip(&linep, '=')) == NULL) {
199 					goto barf_line;
200 				}
201 
202 				/* premature end, ill char following = */
203 				if (!islabel(*linep))
204 					goto barf_line;
205 
206 				/* linep points to the string following '=' */
207 				p = labelskip(linep);
208 				if (*p == ']')
209 					end_crit = 1;
210 				else if (*p != ' ' && *p != '\t')
211 					goto barf_line;
212 				*p++ = '\0'; /* null terminate linep */
213 				p = spaceskip(p);
214 				if (!end_crit) {
215 					if (*p == ']') {
216 					end_crit = 1;
217 					*p++ = '\0';
218 					} else if (*p == '\0' || *p == '\n') {
219 						return (cfp);
220 					} else if (!islabel(*p))
221 					/* p better be the next switch_err */
222 						goto barf_line;
223 				}
224 				if (strcasecmp(linep, __NSW_STR_RETURN) == 0)
225 					act = __NSW_RETURN;
226 				else if (strcasecmp(linep,
227 						    __NSW_STR_CONTINUE) == 0) {
228 					if (strcasecmp(lkp->service_name,
229 						    "dns") == 0 &&
230 						strcasecmp(tokenp,
231 							__NSW_STR_TRYAGAIN)
232 							== 0) {
233 						/*
234 						 * Add one more condition
235 						 * so it retries only if it's
236 						 * "dns [TRYAGAIN=continue]"
237 						 */
238 						dns_continue = 1;
239 						act = __NSW_TRYAGAIN_NTIMES;
240 					} else
241 						act = __NSW_CONTINUE;
242 				} else if (strcasecmp(linep,
243 					    __NSW_STR_FOREVER) == 0)
244 					act = __NSW_TRYAGAIN_FOREVER;
245 				else if (alldigits(linep)) {
246 					act = __NSW_TRYAGAIN_NTIMES;
247 					ntimes = atoi(linep);
248 					if (ntimes < 0 || ntimes > INT_MAX)
249 						ntimes = 0;
250 				}
251 				else
252 					goto barf_line;
253 
254 				if (__NSW_SUCCESS_ACTION(act) &&
255 				    strcasecmp(tokenp,
256 					    __NSW_STR_SUCCESS) == 0) {
257 					lkp->actions[__NSW_SUCCESS] = act;
258 				} else if (__NSW_NOTFOUND_ACTION(act) &&
259 					strcasecmp(tokenp,
260 					    __NSW_STR_NOTFOUND) == 0) {
261 					lkp->actions[__NSW_NOTFOUND] = act;
262 				} else if (__NSW_UNAVAIL_ACTION(act) &&
263 					strcasecmp(tokenp,
264 					    __NSW_STR_UNAVAIL) == 0) {
265 					lkp->actions[__NSW_UNAVAIL] = act;
266 				} else if (__NSW_TRYAGAIN_ACTION(act) &&
267 					strcasecmp(tokenp,
268 					    __NSW_STR_TRYAGAIN) == 0) {
269 					lkp->actions[__NSW_TRYAGAIN] = act;
270 					if (strcasecmp(lkp->service_name,
271 						    "nis") == 0)
272 						lkp->actions[
273 						    __NSW_NISSERVDNS_TRYAGAIN]
274 						    = act;
275 					if (act == __NSW_TRYAGAIN_NTIMES)
276 						lkp->max_retries =
277 						dns_continue ?
278 						dns_tryagain_retry : ntimes;
279 				} else {
280 					/*EMPTY*/
281 					/*
282 					 * convert string tokenp to integer
283 					 * and put in long_errs
284 					 */
285 				}
286 				if (end_crit) {
287 					linep = spaceskip(p);
288 					if (*linep == '\0' || *linep == '\n')
289 						return (cfp);
290 					break; /* process next naming service */
291 				}
292 				linep = p;
293 			} /* end of while loop for a name service's criteria */
294 		} else {
295 			/*
296 			 * no criteria for this naming service.
297 			 * linep points to name service, but not null
298 			 * terminated.
299 			 */
300 			p = labelskip(linep);
301 			if (*p == '\0' || *p == '\n') {
302 				*p = '\0';
303 				LIBC_STRDUP(lkp->service_name, linep);
304 				set_dns_default_lkp(lkp);
305 				cfp->num_lookups++;
306 				return (cfp);
307 			}
308 			if (*p != ' ' && *p != '\t')
309 				goto barf_line;
310 			*p++ = '\0';
311 			LIBC_STRDUP(lkp->service_name, linep);
312 			set_dns_default_lkp(lkp);
313 			cfp->num_lookups++;
314 			linep = spaceskip(p);
315 		}
316 	} /* end of while(1) loop for a name service */
317 
318 barf_line:
319 	freeconf_v1(cfp);
320 	*errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY;
321 	return (NULL);
322 }
323 
324 /*
325  * Private interface used by nss_common.c, hence this function is not static
326  */
327 struct __nsw_switchconfig *
328 _nsw_getoneconfig(const char *name, char *linep, enum __nsw_parse_err *errp)
329 	/* linep   Nota Bene: not const char *	*/
330 	/* errp  Meanings are abused a bit	*/
331 {
332 	struct __nsw_switchconfig *cfp;
333 	struct __nsw_lookup *lkp, **lkq;
334 	int end_crit, dup_fail = 0;
335 	action_t act;
336 	char *p, *tokenp;
337 
338 	*errp = __NSW_CONF_PARSE_SUCCESS;
339 
340 	if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig)))
341 	    == NULL) {
342 		*errp = __NSW_CONF_PARSE_SYSERR;
343 		return (NULL);
344 	}
345 	LIBC_STRDUP(cfp->dbase, name);
346 	lkq = &cfp->lookups;
347 
348 	/* linep points to a naming service name */
349 	for (;;) {
350 		int i;
351 
352 		/* white space following the last service */
353 		if (*linep == '\0' || *linep == '\n') {
354 			return (cfp);
355 		}
356 		if ((lkp = libc_malloc(sizeof (struct __nsw_lookup)))
357 		    == NULL) {
358 			*errp = __NSW_CONF_PARSE_SYSERR;
359 			freeconf(cfp);
360 			return (NULL);
361 		}
362 
363 		*lkq = lkp;
364 		lkq = &lkp->next;
365 
366 		for (i = 0; i < __NSW_STD_ERRS; i++)
367 			if (i == __NSW_SUCCESS)
368 				lkp->actions[i] = 1;
369 			else
370 				lkp->actions[i] = 0;
371 
372 		/* get criteria for the naming service */
373 		if (tokenp = skip(&linep, '[')) { /* got criteria */
374 
375 			/* premature end, illegal char following [ */
376 			if (!islabel(*linep))
377 				goto barf_line;
378 			LIBC_STRDUP(lkp->service_name, tokenp);
379 			cfp->num_lookups++;
380 			end_crit = 0;
381 
382 			/* linep points to a switch_err */
383 			for (;;) {
384 				if ((tokenp = skip(&linep, '=')) == NULL) {
385 					goto barf_line;
386 				}
387 
388 				/* premature end, ill char following = */
389 				if (!islabel(*linep))
390 					goto barf_line;
391 
392 				/* linep points to the string following '=' */
393 				p = labelskip(linep);
394 				if (*p == ']')
395 					end_crit = 1;
396 				else if (*p != ' ' && *p != '\t')
397 					goto barf_line;
398 				*p++ = '\0'; /* null terminate linep */
399 				p = spaceskip(p);
400 				if (!end_crit) {
401 					if (*p == ']') {
402 					end_crit = 1;
403 					*p++ = '\0';
404 					} else if (*p == '\0' || *p == '\n')
405 						return (cfp);
406 					else if (!islabel(*p))
407 					/* p better be the next switch_err */
408 						goto barf_line;
409 				}
410 				if (strcasecmp(linep, __NSW_STR_RETURN) == 0)
411 					act = __NSW_RETURN;
412 				else if (strcasecmp(linep,
413 					    __NSW_STR_CONTINUE) == 0)
414 					act = __NSW_CONTINUE;
415 				else if (strcasecmp(linep,
416 					    __NSW_STR_FOREVER) == 0)
417 					/*
418 					 * =forever or =N might be in conf file
419 					 * but old progs won't expect it.
420 					 */
421 					act = __NSW_RETURN;
422 				else if (alldigits(linep))
423 					act = __NSW_CONTINUE;
424 				else
425 					goto barf_line;
426 				if (strcasecmp(tokenp,
427 					    __NSW_STR_SUCCESS) == 0) {
428 					lkp->actions[__NSW_SUCCESS] = act;
429 				} else if (strcasecmp(tokenp,
430 					    __NSW_STR_NOTFOUND) == 0) {
431 					lkp->actions[__NSW_NOTFOUND] = act;
432 				} else if (strcasecmp(tokenp,
433 					    __NSW_STR_UNAVAIL) == 0) {
434 					lkp->actions[__NSW_UNAVAIL] = act;
435 				} else if (strcasecmp(tokenp,
436 					    __NSW_STR_TRYAGAIN) == 0) {
437 					lkp->actions[__NSW_TRYAGAIN] = act;
438 				} else {
439 					/*EMPTY*/
440 					/*
441 					 * convert string tokenp to integer
442 					 * and put in long_errs
443 					 */
444 				}
445 				if (end_crit) {
446 					linep = spaceskip(p);
447 					if (*linep == '\0' || *linep == '\n')
448 						return (cfp);
449 					break; /* process next naming service */
450 				}
451 				linep = p;
452 			} /* end of while loop for a name service's criteria */
453 		} else {
454 			/*
455 			 * no criteria for this naming service.
456 			 * linep points to name service, but not null
457 			 * terminated.
458 			 */
459 			p = labelskip(linep);
460 			if (*p == '\0' || *p == '\n') {
461 				*p = '\0';
462 				LIBC_STRDUP(lkp->service_name, linep);
463 				cfp->num_lookups++;
464 				return (cfp);
465 			}
466 			if (*p != ' ' && *p != '\t')
467 				goto barf_line;
468 			*p++ = '\0';
469 			LIBC_STRDUP(lkp->service_name, linep);
470 			cfp->num_lookups++;
471 			linep = spaceskip(p);
472 		}
473 	} /* end of while(1) loop for a name service */
474 
475 barf_line:
476 	freeconf(cfp);
477 	*errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY;
478 	return (NULL);
479 }
480 
481 static mutex_t serialize_config_v1 = DEFAULTMUTEX;
482 static mutex_t serialize_config = DEFAULTMUTEX;
483 
484 static void
485 syslog_warning(const char *dbase)
486 {
487 	syslog(LOG_WARNING,
488 	    "libc: bad lookup policy for %s in %s, using defaults..\n",
489 	    dbase, __NSW_CONFIG_FILE);
490 }
491 
492 struct __nsw_switchconfig_v1 *
493 __nsw_getconfig_v1(const char *dbase, enum __nsw_parse_err *errp)
494 {
495 	struct __nsw_switchconfig_v1 *cfp, *retp = NULL;
496 	int syslog_error = 0;
497 	__NSL_FILE *fp;
498 	char *linep;
499 	char lineq[BUFSIZ];
500 
501 	/*
502 	 * ==== I don't feel entirely comfortable disabling signals for the
503 	 *	duration of this, but maybe we have to.  Or maybe we should
504 	 *	use mutex_trylock to detect recursion?  (Not clear what's
505 	 *	the right thing to do when it happens, though).
506 	 */
507 	lmutex_lock(&serialize_config_v1);
508 
509 	if (cfp = scrounge_cache_v1(dbase)) {
510 		*errp = __NSW_CONF_PARSE_SUCCESS;
511 		lmutex_unlock(&serialize_config_v1);
512 		return (cfp);
513 	}
514 
515 	if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) {
516 		*errp = __NSW_CONF_PARSE_NOFILE;
517 		lmutex_unlock(&serialize_config_v1);
518 		return (NULL);
519 	}
520 
521 	*errp = __NSW_CONF_PARSE_NOPOLICY;
522 	while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) {
523 		enum __nsw_parse_err	line_err;
524 		char			*tokenp, *comment;
525 
526 		/*
527 		 * Ignore portion of line following the comment character '#'.
528 		 */
529 		if ((comment = strchr(linep, '#')) != NULL) {
530 			*comment = '\0';
531 		}
532 		/*
533 		 * skip past blank lines.
534 		 * otherwise, cache as a struct switchconfig.
535 		 */
536 		if ((*linep == '\0') || isspace(*linep)) {
537 			continue;
538 		}
539 		if ((tokenp = skip(&linep, ':')) == NULL) {
540 			continue; /* ignore this line */
541 		}
542 		if (cfp = scrounge_cache_v1(tokenp)) {
543 			continue; /* ? somehow this database is in the cache */
544 		}
545 		if (cfp = _nsw_getoneconfig_v1(tokenp, linep, &line_err)) {
546 			(void) add_concell_v1(cfp);
547 			if (strcmp(cfp->dbase, dbase) == 0) {
548 				*errp = __NSW_CONF_PARSE_SUCCESS;
549 				retp = cfp;
550 			}
551 		} else {
552 			/*
553 			 * Got an error on this line, if it is a system
554 			 * error we might as well give right now. If it
555 			 * is a parse error on the second entry of the
556 			 * database we are looking for and the first one
557 			 * was a good entry we end up logging the following
558 			 * syslog message and using a default policy instead.
559 			 */
560 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
561 				*errp = __NSW_CONF_PARSE_SYSERR;
562 				break;
563 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
564 			    strcmp(tokenp, dbase) == 0) {
565 				syslog_error = 1;
566 				*errp = __NSW_CONF_PARSE_NOPOLICY;
567 				break;
568 			}
569 			/*
570 			 * Else blithely ignore problems on this line and
571 			 *   go ahead with the next line.
572 			 */
573 		}
574 	}
575 	(void) __nsl_c_fclose(fp);
576 	lmutex_unlock(&serialize_config_v1);
577 	/*
578 	 * We have to drop the lock before calling syslog().
579 	 */
580 	if (syslog_error)
581 		syslog_warning(dbase);
582 	return (retp);
583 }
584 
585 struct __nsw_switchconfig *
586 __nsw_getconfig(const char *dbase, enum __nsw_parse_err *errp)
587 {
588 	struct __nsw_switchconfig *cfp, *retp = NULL;
589 	int syslog_error = 0;
590 	__NSL_FILE *fp;
591 	char *linep;
592 	char lineq[BUFSIZ];
593 
594 	/*
595 	 * ==== I don't feel entirely comfortable disabling signals for the
596 	 *	duration of this, but maybe we have to.  Or maybe we should
597 	 *	use mutex_trylock to detect recursion?  (Not clear what's
598 	 *	the right thing to do when it happens, though).
599 	 */
600 	lmutex_lock(&serialize_config);
601 
602 	if (cfp = scrounge_cache(dbase)) {
603 		*errp = __NSW_CONF_PARSE_SUCCESS;
604 		lmutex_unlock(&serialize_config);
605 		return (cfp);
606 	}
607 
608 	if ((fp = __nsl_c_fopen(__NSW_CONFIG_FILE, "r")) == NULL) {
609 		*errp = __NSW_CONF_PARSE_NOFILE;
610 		lmutex_unlock(&serialize_config);
611 		return (NULL);
612 	}
613 
614 	*errp = __NSW_CONF_PARSE_NOPOLICY;
615 	while (linep = __nsl_c_fgets(lineq, BUFSIZ, fp)) {
616 		enum __nsw_parse_err	line_err;
617 		char			*tokenp, *comment;
618 
619 		/*
620 		 * Ignore portion of line following the comment character '#'.
621 		 */
622 		if ((comment = strchr(linep, '#')) != NULL) {
623 			*comment = '\0';
624 		}
625 		/*
626 		 * skip past blank lines.
627 		 * otherwise, cache as a struct switchconfig.
628 		 */
629 		if ((*linep == '\0') || isspace(*linep)) {
630 			continue;
631 		}
632 		if ((tokenp = skip(&linep, ':')) == NULL) {
633 			continue; /* ignore this line */
634 		}
635 		if (cfp = scrounge_cache(tokenp)) {
636 			continue; /* ? somehow this database is in the cache */
637 		}
638 		if (cfp = _nsw_getoneconfig(tokenp, linep, &line_err)) {
639 			(void) add_concell(cfp);
640 			if (strcmp(cfp->dbase, dbase) == 0) {
641 				*errp = __NSW_CONF_PARSE_SUCCESS;
642 				retp = cfp;
643 			}
644 		} else {
645 			/*
646 			 * Got an error on this line, if it is a system
647 			 * error we might as well give right now. If it
648 			 * is a parse error on the second entry of the
649 			 * database we are looking for and the first one
650 			 * was a good entry we end up logging the following
651 			 * syslog message and using a default policy instead.
652 			 */
653 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
654 				*errp = __NSW_CONF_PARSE_SYSERR;
655 				break;
656 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
657 			    strcmp(tokenp, dbase) == 0) {
658 				syslog_error = 1;
659 				*errp = __NSW_CONF_PARSE_NOPOLICY;
660 				break;
661 			}
662 			/*
663 			 * Else blithely ignore problems on this line and
664 			 *   go ahead with the next line.
665 			 */
666 		}
667 	}
668 	(void) __nsl_c_fclose(fp);
669 	lmutex_unlock(&serialize_config);
670 	/*
671 	 * We have to drop the lock before calling syslog().
672 	 */
673 	if (syslog_error)
674 		syslog_warning(dbase);
675 	return (retp);
676 }
677 
678 
679 static struct __nsw_switchconfig_v1 *
680 scrounge_cache_v1(const char *dbase)
681 {
682 	struct cons_cell_v1 *cellp = concell_list_v1;
683 
684 	for (; cellp; cellp = cellp->next)
685 		if (strcmp(dbase, cellp->sw->dbase) == 0)
686 			return (cellp->sw);
687 	return (NULL);
688 }
689 
690 static struct __nsw_switchconfig *
691 scrounge_cache(const char *dbase)
692 {
693 	struct cons_cell *cellp = concell_list;
694 
695 	for (; cellp; cellp = cellp->next)
696 		if (strcmp(dbase, cellp->sw->dbase) == 0)
697 			return (cellp->sw);
698 	return (NULL);
699 }
700 
701 static void
702 freeconf_v1(struct __nsw_switchconfig_v1 *cfp)
703 {
704 	if (cfp) {
705 		if (cfp->dbase)
706 			libc_free(cfp->dbase);
707 		if (cfp->lookups) {
708 			struct __nsw_lookup_v1 *nex, *cur;
709 			for (cur = cfp->lookups; cur; cur = nex) {
710 				libc_free(cur->service_name);
711 				nex = cur->next;
712 				libc_free(cur);
713 			}
714 		}
715 		libc_free(cfp);
716 	}
717 }
718 
719 static void
720 freeconf(struct __nsw_switchconfig *cfp)
721 {
722 	if (cfp) {
723 		if (cfp->dbase)
724 			libc_free(cfp->dbase);
725 		if (cfp->lookups) {
726 			struct __nsw_lookup *nex, *cur;
727 			for (cur = cfp->lookups; cur; cur = nex) {
728 				libc_free(cur->service_name);
729 				nex = cur->next;
730 				libc_free(cur);
731 			}
732 		}
733 		libc_free(cfp);
734 	}
735 }
736 
737 action_t
738 __nsw_extended_action_v1(struct __nsw_lookup_v1 *lkp, int err)
739 {
740 	struct __nsw_long_err *lerrp;
741 
742 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
743 		if (lerrp->nsw_errno == err)
744 			return (lerrp->action);
745 	}
746 	return (__NSW_CONTINUE);
747 }
748 
749 action_t
750 __nsw_extended_action(struct __nsw_lookup *lkp, int err)
751 {
752 	struct __nsw_long_err *lerrp;
753 
754 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
755 		if (lerrp->nsw_errno == err)
756 			return (lerrp->action);
757 	}
758 	return (__NSW_CONTINUE);
759 }
760 
761 
762 /* give the next non-alpha character */
763 static char *
764 labelskip(char *cur)
765 {
766 	char *p = cur;
767 	while (islabel(*p))
768 		++p;
769 	return (p);
770 }
771 
772 /* give the next non-space character */
773 static char *
774 spaceskip(char *cur)
775 {
776 	char *p = cur;
777 	while (*p == ' ' || *p == '\t')
778 		++p;
779 	return (p);
780 }
781 
782 /*
783  * terminate the *cur pointed string by null only if it is
784  * followed by "key" surrounded by zero or more spaces and
785  * return value is the same as the original *cur pointer and
786  * *cur pointer is advanced to the first non {space, key} char
787  * followed by the key. Otherwise, return NULL and keep
788  * *cur unchanged.
789  */
790 static char *
791 skip(char **cur, char key)
792 {
793 	char *p, *tmp;
794 	char *q = *cur;
795 	int found, tmpfound;
796 
797 	tmp = labelskip(*cur);
798 	p = tmp;
799 	found = (*p == key);
800 	if (found) {
801 		*p++ = '\0'; /* overwrite the key */
802 		p = spaceskip(p);
803 	} else {
804 		while (*p == ' ' || *p == '\t') {
805 			tmpfound = (*++p == key);
806 			if (tmpfound) {
807 				found = tmpfound;
808 					/* null terminate the return token */
809 				*tmp = '\0';
810 				p++; /* skip the key */
811 			}
812 		}
813 	}
814 	if (!found)
815 		return (NULL); /* *cur unchanged */
816 	*cur = p;
817 	return (q);
818 }
819 
820 /* add to the front: LRU */
821 static int
822 add_concell_v1(struct __nsw_switchconfig_v1 *cfp)
823 {
824 	struct cons_cell_v1 *cp;
825 
826 	if (cfp == NULL)
827 		return (1);
828 	if ((cp = libc_malloc(sizeof (struct cons_cell_v1))) == NULL)
829 		return (1);
830 	cp->sw = cfp;
831 	cp->next = concell_list_v1;
832 	concell_list_v1 = cp;
833 	return (0);
834 }
835 
836 /* add to the front: LRU */
837 static int
838 add_concell(struct __nsw_switchconfig *cfp)
839 {
840 	struct cons_cell *cp;
841 
842 	if (cfp == NULL)
843 		return (1);
844 	if ((cp = libc_malloc(sizeof (struct cons_cell))) == NULL)
845 		return (1);
846 	cp->sw = cfp;
847 	cp->next = concell_list;
848 	concell_list = cp;
849 	return (0);
850 }
851 
852 int
853 __nsw_freeconfig_v1(struct __nsw_switchconfig_v1 *conf)
854 {
855 	struct cons_cell_v1 *cellp;
856 
857 	if (conf == NULL) {
858 		return (-1);
859 	}
860 	/*
861 	 * Hacked to make life easy for the code in nss_common.c.  Free conf
862 	 *   iff it was created by calling _nsw_getoneconfig() directly
863 	 *   rather than by calling nsw_getconfig.
864 	 */
865 	lmutex_lock(&serialize_config_v1);
866 	for (cellp = concell_list_v1;  cellp;  cellp = cellp->next) {
867 		if (cellp->sw == conf) {
868 			break;
869 		}
870 	}
871 	lmutex_unlock(&serialize_config_v1);
872 	if (cellp == NULL) {
873 		/* Not in the cache;  free it */
874 		freeconf_v1(conf);
875 		return (1);
876 	} else {
877 		/* In the cache;  don't free it */
878 		return (0);
879 	}
880 }
881 
882 int
883 __nsw_freeconfig(struct __nsw_switchconfig *conf)
884 {
885 	struct cons_cell *cellp;
886 
887 	if (conf == NULL) {
888 		return (-1);
889 	}
890 	/*
891 	 * Hacked to make life easy for the code in nss_common.c.  Free conf
892 	 *   iff it was created by calling _nsw_getoneconfig() directly
893 	 *   rather than by calling nsw_getconfig.
894 	 */
895 	lmutex_lock(&serialize_config);
896 	for (cellp = concell_list;  cellp;  cellp = cellp->next) {
897 		if (cellp->sw == conf) {
898 			break;
899 		}
900 	}
901 	lmutex_unlock(&serialize_config);
902 	if (cellp == NULL) {
903 		/* Not in the cache;  free it */
904 		freeconf(conf);
905 		return (1);
906 	} else {
907 		/* In the cache;  don't free it */
908 		return (0);
909 	}
910 }
911 
912 /* Return 1 if the string contains all digits, else return 0. */
913 static int
914 alldigits(char *s)
915 {
916 	for (; *s; s++)
917 		if (!isdigit(*s))
918 			return (0);
919 	return (1);
920 }
921 
922 
923 /*
924  * To avoid the 256 open file descriptor limitation in stdio,
925  * we are using a  private limited implementation of stdio calls.
926  * The private implementation is closely based on the implementation
927  * in the standard C library.
928  * To simplify, certain assumptions are made:
929  * - a file may be opened only in read mode.
930  * - Only sequential reads allowed
931  * - file descriptors should not be shared between threads
932  */
933 
934 static int
935 _raise_fd(int fd)
936 {
937 	int nfd;
938 	static const int min_fd = 256;
939 
940 	if (fd >= min_fd)
941 		return (fd);
942 
943 	if ((nfd = fcntl(fd, F_DUPFD, min_fd)) == -1) {
944 		/*
945 		 * If the shell limits [See limit(1)] the
946 		 * descriptors to 256, fcntl will fail
947 		 * and errno will be set to EINVAL. Since
948 		 * the intention is to ignore fcntl failures
949 		 * and continue working with 'fd', we should
950 		 * reset errno to _prevent_ apps relying on errno
951 		 * to treat this as an error.
952 		 */
953 		errno = 0;
954 		return (fd);
955 	}
956 
957 	(void) close(fd);
958 
959 	return (nfd);
960 }
961 
962 __NSL_FILE *
963 __nsl_c_fopen(const char *filename, const char *mode)
964 {
965 	int		fd;
966 	__NSL_FILE	*stream;
967 	void		*buf;
968 
969 	if (mode == NULL || filename == NULL) {
970 		return (NULL);
971 	}
972 
973 	if (strcmp(mode, "r") != 0) {
974 		return (NULL);
975 	}
976 
977 	fd = open(filename, O_RDONLY | O_LARGEFILE, 0666);
978 	if (fd < 0)
979 		return (NULL);
980 
981 	stream = libc_malloc(sizeof (__NSL_FILE));
982 	buf = lmalloc(__NSL_FILE_BUF_SIZE);
983 	if (stream != NULL && buf != NULL) {
984 		stream->_nsl_base = buf;
985 		stream->_nsl_file = _raise_fd(fd);
986 		stream->_nsl_cnt = 0;
987 		stream->_nsl_ptr = stream->_nsl_base;
988 	} else {
989 		(void) close(fd);
990 		if (buf)
991 			lfree(buf, __NSL_FILE_BUF_SIZE);
992 		if (stream)
993 			libc_free(stream);
994 		stream = NULL;
995 	}
996 
997 	return (stream);
998 }
999 
1000 int
1001 __nsl_c_fclose(__NSL_FILE *stream)
1002 {
1003 	int res = 0;
1004 
1005 	if (stream == NULL)
1006 		return (EOF);
1007 
1008 	if (close(stream->_nsl_file) < 0)
1009 		res = EOF;
1010 
1011 	lfree(stream->_nsl_base, __NSL_FILE_BUF_SIZE);
1012 	libc_free(stream);
1013 
1014 	return (res);
1015 }
1016 
1017 char *
1018 __nsl_c_fgets(char *buf, int size, __NSL_FILE *stream)
1019 {
1020 	char *ptr = buf;
1021 	char *p;
1022 	int n;
1023 	int res;
1024 
1025 	size--;		/* room for '\0' */
1026 	while (size > 0) {
1027 		if (stream->_nsl_cnt == 0) {
1028 			stream->_nsl_ptr = stream->_nsl_base;
1029 
1030 			if ((res = read(stream->_nsl_file, stream->_nsl_base,
1031 				__NSL_FILE_BUF_SIZE)) > 0) {
1032 				stream->_nsl_cnt = res;
1033 			} else {
1034 				stream->_nsl_cnt = 0;
1035 				break;
1036 			}
1037 		}
1038 		n = (int)(size < stream->_nsl_cnt ? size : stream->_nsl_cnt);
1039 		if ((p = memccpy(ptr, (char *)stream->_nsl_ptr, '\n',
1040 		    (size_t)n)) != NULL)
1041 			n = (int)(p - ptr);
1042 		ptr += n;
1043 		stream->_nsl_cnt -= n;
1044 		stream->_nsl_ptr += n;
1045 		if (p != NULL)
1046 			break; /* newline found */
1047 		size -= n;
1048 	}
1049 
1050 	if (ptr == buf)	/* never read anything */
1051 		return (NULL);
1052 
1053 	*ptr = '\0';
1054 	return (buf);
1055 }
1056