xref: /illumos-gate/usr/src/lib/libc/port/gen/nsparse.c (revision 0c44d0008f52b6a42b9c01d3b344661217520a68)
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 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include "synonyms.h"
29 #include "file64.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 <stdio_ext.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <limits.h>
40 #include <dlfcn.h>
41 #include <errno.h>
42 #include "stdiom.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 /*
493  * Since we cannot call malloc() or lock any of the ordinary mutexes
494  * while we hold an lmutex_lock(), we open the file outside the lock
495  * and disable locking on the file; the latter is fine because we're
496  * reading the fp only from a single thread.
497  */
498 static FILE *
499 open_conf(void)
500 {
501 	FILE *fp = fopen(__NSW_CONFIG_FILE, "rF");
502 
503 	if (fp != NULL) {
504 		if (_findbuf(fp) == NULL) {
505 			(void) fclose(fp);
506 			return (NULL);
507 		}
508 		SET_IONOLOCK(fp);
509 	}
510 	return (fp);
511 }
512 
513 struct __nsw_switchconfig_v1 *
514 __nsw_getconfig_v1(const char *dbase, enum __nsw_parse_err *errp)
515 {
516 	struct __nsw_switchconfig_v1 *cfp, *retp = NULL;
517 	int syslog_error = 0;
518 	FILE *fp = NULL;
519 	char *linep;
520 	char lineq[BUFSIZ];
521 
522 	lmutex_lock(&serialize_config_v1);
523 top:
524 	if (cfp = scrounge_cache_v1(dbase)) {
525 		*errp = __NSW_CONF_PARSE_SUCCESS;
526 		lmutex_unlock(&serialize_config_v1);
527 		if (fp != NULL)
528 			(void) fclose(fp);
529 		return (cfp);
530 	}
531 
532 	if (fp == NULL) {
533 		struct cons_cell_v1 *cp = concell_list_v1;
534 
535 		lmutex_unlock(&serialize_config_v1);
536 		/* open_conf() must be called w/o locks held */
537 		if ((fp = open_conf()) == NULL) {
538 			*errp = __NSW_CONF_PARSE_NOFILE;
539 			return (NULL);
540 		}
541 		lmutex_lock(&serialize_config_v1);
542 		/* Cache changed? */
543 		if (cp != concell_list_v1)
544 			goto top;
545 	}
546 
547 	*errp = __NSW_CONF_PARSE_NOPOLICY;
548 	while (linep = fgets(lineq, BUFSIZ, fp)) {
549 		enum __nsw_parse_err	line_err;
550 		char			*tokenp, *comment;
551 
552 		/*
553 		 * Ignore portion of line following the comment character '#'.
554 		 */
555 		if ((comment = strchr(linep, '#')) != NULL) {
556 			*comment = '\0';
557 		}
558 		/*
559 		 * skip past blank lines.
560 		 * otherwise, cache as a struct switchconfig.
561 		 */
562 		if ((*linep == '\0') || isspace(*linep)) {
563 			continue;
564 		}
565 		if ((tokenp = skip(&linep, ':')) == NULL) {
566 			continue; /* ignore this line */
567 		}
568 		if (cfp = scrounge_cache_v1(tokenp)) {
569 			continue; /* ? somehow this database is in the cache */
570 		}
571 		if (cfp = _nsw_getoneconfig_v1(tokenp, linep, &line_err)) {
572 			(void) add_concell_v1(cfp);
573 			if (strcmp(cfp->dbase, dbase) == 0) {
574 				*errp = __NSW_CONF_PARSE_SUCCESS;
575 				retp = cfp;
576 			}
577 		} else {
578 			/*
579 			 * Got an error on this line, if it is a system
580 			 * error we might as well give right now. If it
581 			 * is a parse error on the second entry of the
582 			 * database we are looking for and the first one
583 			 * was a good entry we end up logging the following
584 			 * syslog message and using a default policy instead.
585 			 */
586 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
587 				*errp = __NSW_CONF_PARSE_SYSERR;
588 				break;
589 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
590 			    strcmp(tokenp, dbase) == 0) {
591 				syslog_error = 1;
592 				*errp = __NSW_CONF_PARSE_NOPOLICY;
593 				break;
594 			}
595 			/*
596 			 * Else blithely ignore problems on this line and
597 			 *   go ahead with the next line.
598 			 */
599 		}
600 	}
601 	lmutex_unlock(&serialize_config_v1);
602 	/*
603 	 * We have to drop the lock before calling fclose()/syslog().
604 	 */
605 	(void) fclose(fp);
606 	if (syslog_error)
607 		syslog_warning(dbase);
608 	return (retp);
609 }
610 
611 struct __nsw_switchconfig *
612 __nsw_getconfig(const char *dbase, enum __nsw_parse_err *errp)
613 {
614 	struct __nsw_switchconfig *cfp, *retp = NULL;
615 	int syslog_error = 0;
616 	FILE *fp = NULL;
617 	char *linep;
618 	char lineq[BUFSIZ];
619 
620 	lmutex_lock(&serialize_config);
621 top:
622 	if (cfp = scrounge_cache(dbase)) {
623 		*errp = __NSW_CONF_PARSE_SUCCESS;
624 		lmutex_unlock(&serialize_config);
625 		if (fp != NULL)
626 			(void) fclose(fp);
627 		return (cfp);
628 	}
629 
630 	if (fp == NULL) {
631 		struct cons_cell *cp = concell_list;
632 		/* open_conf() must be called w/o locks held */
633 		lmutex_unlock(&serialize_config);
634 		if ((fp = open_conf()) == NULL) {
635 			*errp = __NSW_CONF_PARSE_NOFILE;
636 			return (NULL);
637 		}
638 		lmutex_lock(&serialize_config);
639 		/* Cache changed? */
640 		if (cp != concell_list)
641 			goto top;
642 	}
643 
644 	*errp = __NSW_CONF_PARSE_NOPOLICY;
645 	while (linep = fgets(lineq, BUFSIZ, fp)) {
646 		enum __nsw_parse_err	line_err;
647 		char			*tokenp, *comment;
648 
649 		/*
650 		 * Ignore portion of line following the comment character '#'.
651 		 */
652 		if ((comment = strchr(linep, '#')) != NULL) {
653 			*comment = '\0';
654 		}
655 		/*
656 		 * skip past blank lines.
657 		 * otherwise, cache as a struct switchconfig.
658 		 */
659 		if ((*linep == '\0') || isspace(*linep)) {
660 			continue;
661 		}
662 		if ((tokenp = skip(&linep, ':')) == NULL) {
663 			continue; /* ignore this line */
664 		}
665 		if (cfp = scrounge_cache(tokenp)) {
666 			continue; /* ? somehow this database is in the cache */
667 		}
668 		if (cfp = _nsw_getoneconfig(tokenp, linep, &line_err)) {
669 			(void) add_concell(cfp);
670 			if (strcmp(cfp->dbase, dbase) == 0) {
671 				*errp = __NSW_CONF_PARSE_SUCCESS;
672 				retp = cfp;
673 			}
674 		} else {
675 			/*
676 			 * Got an error on this line, if it is a system
677 			 * error we might as well give right now. If it
678 			 * is a parse error on the second entry of the
679 			 * database we are looking for and the first one
680 			 * was a good entry we end up logging the following
681 			 * syslog message and using a default policy instead.
682 			 */
683 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
684 				*errp = __NSW_CONF_PARSE_SYSERR;
685 				break;
686 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
687 			    strcmp(tokenp, dbase) == 0) {
688 				syslog_error = 1;
689 				*errp = __NSW_CONF_PARSE_NOPOLICY;
690 				break;
691 			}
692 			/*
693 			 * Else blithely ignore problems on this line and
694 			 *   go ahead with the next line.
695 			 */
696 		}
697 	}
698 	lmutex_unlock(&serialize_config);
699 	/*
700 	 * We have to drop the lock before calling fclose()/syslog().
701 	 */
702 	(void) fclose(fp);
703 	if (syslog_error)
704 		syslog_warning(dbase);
705 	return (retp);
706 }
707 
708 
709 static struct __nsw_switchconfig_v1 *
710 scrounge_cache_v1(const char *dbase)
711 {
712 	struct cons_cell_v1 *cellp = concell_list_v1;
713 
714 	for (; cellp; cellp = cellp->next)
715 		if (strcmp(dbase, cellp->sw->dbase) == 0)
716 			return (cellp->sw);
717 	return (NULL);
718 }
719 
720 static struct __nsw_switchconfig *
721 scrounge_cache(const char *dbase)
722 {
723 	struct cons_cell *cellp = concell_list;
724 
725 	for (; cellp; cellp = cellp->next)
726 		if (strcmp(dbase, cellp->sw->dbase) == 0)
727 			return (cellp->sw);
728 	return (NULL);
729 }
730 
731 static void
732 freeconf_v1(struct __nsw_switchconfig_v1 *cfp)
733 {
734 	if (cfp) {
735 		if (cfp->dbase)
736 			libc_free(cfp->dbase);
737 		if (cfp->lookups) {
738 			struct __nsw_lookup_v1 *nex, *cur;
739 			for (cur = cfp->lookups; cur; cur = nex) {
740 				libc_free(cur->service_name);
741 				nex = cur->next;
742 				libc_free(cur);
743 			}
744 		}
745 		libc_free(cfp);
746 	}
747 }
748 
749 static void
750 freeconf(struct __nsw_switchconfig *cfp)
751 {
752 	if (cfp) {
753 		if (cfp->dbase)
754 			libc_free(cfp->dbase);
755 		if (cfp->lookups) {
756 			struct __nsw_lookup *nex, *cur;
757 			for (cur = cfp->lookups; cur; cur = nex) {
758 				libc_free(cur->service_name);
759 				nex = cur->next;
760 				libc_free(cur);
761 			}
762 		}
763 		libc_free(cfp);
764 	}
765 }
766 
767 action_t
768 __nsw_extended_action_v1(struct __nsw_lookup_v1 *lkp, int err)
769 {
770 	struct __nsw_long_err *lerrp;
771 
772 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
773 		if (lerrp->nsw_errno == err)
774 			return (lerrp->action);
775 	}
776 	return (__NSW_CONTINUE);
777 }
778 
779 action_t
780 __nsw_extended_action(struct __nsw_lookup *lkp, int err)
781 {
782 	struct __nsw_long_err *lerrp;
783 
784 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
785 		if (lerrp->nsw_errno == err)
786 			return (lerrp->action);
787 	}
788 	return (__NSW_CONTINUE);
789 }
790 
791 
792 /* give the next non-alpha character */
793 static char *
794 labelskip(char *cur)
795 {
796 	char *p = cur;
797 	while (islabel(*p))
798 		++p;
799 	return (p);
800 }
801 
802 /* give the next non-space character */
803 static char *
804 spaceskip(char *cur)
805 {
806 	char *p = cur;
807 	while (*p == ' ' || *p == '\t')
808 		++p;
809 	return (p);
810 }
811 
812 /*
813  * terminate the *cur pointed string by null only if it is
814  * followed by "key" surrounded by zero or more spaces and
815  * return value is the same as the original *cur pointer and
816  * *cur pointer is advanced to the first non {space, key} char
817  * followed by the key. Otherwise, return NULL and keep
818  * *cur unchanged.
819  */
820 static char *
821 skip(char **cur, char key)
822 {
823 	char *p, *tmp;
824 	char *q = *cur;
825 	int found, tmpfound;
826 
827 	tmp = labelskip(*cur);
828 	p = tmp;
829 	found = (*p == key);
830 	if (found) {
831 		*p++ = '\0'; /* overwrite the key */
832 		p = spaceskip(p);
833 	} else {
834 		while (*p == ' ' || *p == '\t') {
835 			tmpfound = (*++p == key);
836 			if (tmpfound) {
837 				found = tmpfound;
838 					/* null terminate the return token */
839 				*tmp = '\0';
840 				p++; /* skip the key */
841 			}
842 		}
843 	}
844 	if (!found)
845 		return (NULL); /* *cur unchanged */
846 	*cur = p;
847 	return (q);
848 }
849 
850 /* add to the front: LRU */
851 static int
852 add_concell_v1(struct __nsw_switchconfig_v1 *cfp)
853 {
854 	struct cons_cell_v1 *cp;
855 
856 	if (cfp == NULL)
857 		return (1);
858 	if ((cp = libc_malloc(sizeof (struct cons_cell_v1))) == NULL)
859 		return (1);
860 	cp->sw = cfp;
861 	cp->next = concell_list_v1;
862 	concell_list_v1 = cp;
863 	return (0);
864 }
865 
866 /* add to the front: LRU */
867 static int
868 add_concell(struct __nsw_switchconfig *cfp)
869 {
870 	struct cons_cell *cp;
871 
872 	if (cfp == NULL)
873 		return (1);
874 	if ((cp = libc_malloc(sizeof (struct cons_cell))) == NULL)
875 		return (1);
876 	cp->sw = cfp;
877 	cp->next = concell_list;
878 	concell_list = cp;
879 	return (0);
880 }
881 
882 int
883 __nsw_freeconfig_v1(struct __nsw_switchconfig_v1 *conf)
884 {
885 	struct cons_cell_v1 *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_v1);
896 	for (cellp = concell_list_v1;  cellp;  cellp = cellp->next) {
897 		if (cellp->sw == conf) {
898 			break;
899 		}
900 	}
901 	lmutex_unlock(&serialize_config_v1);
902 	if (cellp == NULL) {
903 		/* Not in the cache;  free it */
904 		freeconf_v1(conf);
905 		return (1);
906 	} else {
907 		/* In the cache;  don't free it */
908 		return (0);
909 	}
910 }
911 
912 int
913 __nsw_freeconfig(struct __nsw_switchconfig *conf)
914 {
915 	struct cons_cell *cellp;
916 
917 	if (conf == NULL) {
918 		return (-1);
919 	}
920 	/*
921 	 * Hacked to make life easy for the code in nss_common.c.  Free conf
922 	 *   iff it was created by calling _nsw_getoneconfig() directly
923 	 *   rather than by calling nsw_getconfig.
924 	 */
925 	lmutex_lock(&serialize_config);
926 	for (cellp = concell_list;  cellp;  cellp = cellp->next) {
927 		if (cellp->sw == conf) {
928 			break;
929 		}
930 	}
931 	lmutex_unlock(&serialize_config);
932 	if (cellp == NULL) {
933 		/* Not in the cache;  free it */
934 		freeconf(conf);
935 		return (1);
936 	} else {
937 		/* In the cache;  don't free it */
938 		return (0);
939 	}
940 }
941 
942 /* Return 1 if the string contains all digits, else return 0. */
943 static int
944 alldigits(char *s)
945 {
946 	for (; *s; s++)
947 		if (!isdigit(*s))
948 			return (0);
949 	return (1);
950 }
951