xref: /illumos-gate/usr/src/lib/libc/port/gen/nsparse.c (revision 20a7641f9918de8574b8b3b47dbe35c4bfc78df1)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include "lint.h"
28 #include "file64.h"
29 #include "mtlib.h"
30 #include "libc.h"
31 #include <synch.h>
32 #include <sys/types.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <stdio_ext.h>
36 #include <string.h>
37 #include <ctype.h>
38 #include <limits.h>
39 #include <dlfcn.h>
40 #include <errno.h>
41 #include "stdiom.h"
42 
43 #define	__NSS_PRIVATE_INTERFACE
44 #include "nsswitch_priv.h"
45 #undef	__NSS_PRIVATE_INTERFACE
46 
47 #include <syslog.h>
48 
49 #define	islabel(c)	(isalnum(c) || (c) == '_')
50 
51 #define	LIBC_STRDUP(new, existing) \
52 	if ((new = libc_strdup(existing)) == NULL) { \
53 		dup_fail = 1; \
54 		goto barf_line; \
55 	}
56 
57 /*
58  * This file has all the routines that access the configuration
59  * information.
60  */
61 
62 struct cons_cell_v1 { /* private to the parser */
63 	struct __nsw_switchconfig_v1 *sw;
64 	struct cons_cell_v1 *next;
65 };
66 
67 struct cons_cell { /* private to the parser */
68 	struct __nsw_switchconfig *sw;
69 	struct cons_cell *next;
70 };
71 
72 /*
73  * Local routines
74  */
75 
76 static char *skip(char **, char);
77 static char *labelskip(char *);
78 static char *spaceskip(char *);
79 static struct __nsw_switchconfig_v1 *scrounge_cache_v1(const char *);
80 static struct __nsw_switchconfig *scrounge_cache(const char *);
81 static int add_concell_v1(struct __nsw_switchconfig_v1 *);
82 static int add_concell(struct __nsw_switchconfig *);
83 static void freeconf_v1(struct __nsw_switchconfig_v1 *);
84 static void freeconf(struct __nsw_switchconfig *);
85 static int alldigits(char *);
86 
87 static struct cons_cell_v1 *concell_list_v1; /* stays with add_concell() */
88 static struct cons_cell *concell_list; /* stays with add_concell() */
89 
90 /*
91  *
92  * With the "lookup control" feature, the default criteria for NIS, NIS+,
93  * and any new services (e.g. ldap) will be:
94  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever]
95  *
96  * For backward compat, NIS via NIS server in DNS forwarding mode will be:
97  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
98  *
99  * And also for backward compat, the default criteria for DNS will be:
100  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
101  */
102 
103 
104 
105 /*
106  * The BIND resolver normally will retry several times on server non-response.
107  * But now with the "lookup control" feature, we don't want the resolver doing
108  * many retries, rather we want it to return control (reasonably) quickly back
109  * to the switch engine.  However, when TRYAGAIN=N or TRYAGAIN=forever is
110  * not explicitly set by the admin in the conf file, we want the old "resolver
111  * retry a few times" rather than no retries at all.
112  */
113 static int	dns_tryagain_retry = 3;
114 
115 /*
116  * For backward compat (pre "lookup control"), the dns default behavior is
117  * soft lookup.
118  */
119 static void
120 set_dns_default_lkp(struct __nsw_lookup_v1 *lkp)
121 {
122 	if (strcasecmp(lkp->service_name, "dns") == 0) {
123 		lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_NTIMES;
124 		lkp->max_retries = dns_tryagain_retry;
125 	}
126 }
127 
128 /*
129  * Private interface used by nss_common.c, hence this function is not static
130  *
131  * linep   Nota Bene: not const char *
132  * errp  Meanings are abused a bit
133  */
134 struct __nsw_switchconfig_v1 *
135 _nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp)
136 {
137 	struct __nsw_switchconfig_v1 *cfp;
138 	struct __nsw_lookup_v1 *lkp, **lkq;
139 	int end_crit, dup_fail = 0;
140 	action_t act;
141 	char *p, *tokenp;
142 
143 	*errp = __NSW_CONF_PARSE_SUCCESS;
144 
145 	if ((cfp = libc_malloc(sizeof (struct __nsw_switchconfig_v1)))
146 	    == NULL) {
147 		*errp = __NSW_CONF_PARSE_SYSERR;
148 		return (NULL);
149 	}
150 	LIBC_STRDUP(cfp->dbase, name);
151 	lkq = &cfp->lookups;
152 
153 	/* linep points to a naming service name */
154 	for (;;) {
155 		int i;
156 
157 		/* white space following the last service */
158 		if (*linep == '\0' || *linep == '\n') {
159 			return (cfp);
160 		}
161 		if ((lkp = libc_malloc(sizeof (struct __nsw_lookup_v1)))
162 		    == NULL) {
163 			*errp = __NSW_CONF_PARSE_SYSERR;
164 			freeconf_v1(cfp);
165 			return (NULL);
166 		}
167 
168 		*lkq = lkp;
169 		lkq = &lkp->next;
170 
171 		for (i = 0; i < __NSW_STD_ERRS_V1; i++)
172 			if (i == __NSW_SUCCESS)
173 				lkp->actions[i] = __NSW_RETURN;
174 			else if (i == __NSW_TRYAGAIN)
175 				lkp->actions[i] = __NSW_TRYAGAIN_FOREVER;
176 			else
177 				lkp->actions[i] = __NSW_CONTINUE;
178 
179 		/* get criteria for the naming service */
180 		tokenp = skip(&linep, '[');
181 		if (tokenp != NULL) { /* 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) == 0) {
232 						/*
233 						 * Add one more condition
234 						 * so it retries only if it's
235 						 * "dns [TRYAGAIN=continue]"
236 						 */
237 						dns_continue = 1;
238 						act = __NSW_TRYAGAIN_NTIMES;
239 					} else
240 						act = __NSW_CONTINUE;
241 				} else if (strcasecmp(linep,
242 				    __NSW_STR_FOREVER) == 0)
243 					act = __NSW_TRYAGAIN_FOREVER;
244 				else if (alldigits(linep)) {
245 					act = __NSW_TRYAGAIN_NTIMES;
246 					ntimes = atoi(linep);
247 					if (ntimes < 0 || ntimes > INT_MAX)
248 						ntimes = 0;
249 				}
250 				else
251 					goto barf_line;
252 
253 				if (__NSW_SUCCESS_ACTION(act) &&
254 				    strcasecmp(tokenp,
255 				    __NSW_STR_SUCCESS) == 0) {
256 					lkp->actions[__NSW_SUCCESS] = act;
257 				} else if (__NSW_NOTFOUND_ACTION(act) &&
258 				    strcasecmp(tokenp,
259 				    __NSW_STR_NOTFOUND) == 0) {
260 					lkp->actions[__NSW_NOTFOUND] = act;
261 				} else if (__NSW_UNAVAIL_ACTION(act) &&
262 				    strcasecmp(tokenp,
263 				    __NSW_STR_UNAVAIL) == 0) {
264 					lkp->actions[__NSW_UNAVAIL] = act;
265 				} else if (__NSW_TRYAGAIN_ACTION(act) &&
266 				    strcasecmp(tokenp,
267 				    __NSW_STR_TRYAGAIN) == 0) {
268 					lkp->actions[__NSW_TRYAGAIN] = act;
269 					if (strcasecmp(lkp->service_name,
270 					    "nis") == 0)
271 						lkp->actions[
272 						    __NSW_NISSERVDNS_TRYAGAIN]
273 						    = act;
274 					if (act == __NSW_TRYAGAIN_NTIMES)
275 						lkp->max_retries =
276 						    dns_continue ?
277 						    dns_tryagain_retry : ntimes;
278 				} else {
279 					/*EMPTY*/
280 					/*
281 					 * convert string tokenp to integer
282 					 * and put in long_errs
283 					 */
284 				}
285 				if (end_crit) {
286 					linep = spaceskip(p);
287 					if (*linep == '\0' || *linep == '\n')
288 						return (cfp);
289 					break; /* process next naming service */
290 				}
291 				linep = p;
292 			} /* end of while loop for a name service's criteria */
293 		} else {
294 			/*
295 			 * no criteria for this naming service.
296 			 * linep points to name service, but not null
297 			 * terminated.
298 			 */
299 			p = labelskip(linep);
300 			if (*p == '\0' || *p == '\n') {
301 				*p = '\0';
302 				LIBC_STRDUP(lkp->service_name, linep);
303 				set_dns_default_lkp(lkp);
304 				cfp->num_lookups++;
305 				return (cfp);
306 			}
307 			if (*p != ' ' && *p != '\t')
308 				goto barf_line;
309 			*p++ = '\0';
310 			LIBC_STRDUP(lkp->service_name, linep);
311 			set_dns_default_lkp(lkp);
312 			cfp->num_lookups++;
313 			linep = spaceskip(p);
314 		}
315 	} /* end of while(1) loop for a name service */
316 
317 barf_line:
318 	freeconf_v1(cfp);
319 	*errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY;
320 	return (NULL);
321 }
322 
323 /*
324  * Private interface used by nss_common.c, hence this function is not static
325  *
326  * linep   Nota Bene: not const char *
327  * errp  Meanings are abused a bit
328  */
329 struct __nsw_switchconfig *
330 _nsw_getoneconfig(const char *name, char *linep, enum __nsw_parse_err *errp)
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 		tokenp = skip(&linep, '[');
374 		if (tokenp != NULL) { /* got criteria */
375 
376 			/* premature end, illegal char following [ */
377 			if (!islabel(*linep))
378 				goto barf_line;
379 			LIBC_STRDUP(lkp->service_name, tokenp);
380 			cfp->num_lookups++;
381 			end_crit = 0;
382 
383 			/* linep points to a switch_err */
384 			for (;;) {
385 				if ((tokenp = skip(&linep, '=')) == NULL) {
386 					goto barf_line;
387 				}
388 
389 				/* premature end, ill char following = */
390 				if (!islabel(*linep))
391 					goto barf_line;
392 
393 				/* linep points to the string following '=' */
394 				p = labelskip(linep);
395 				if (*p == ']')
396 					end_crit = 1;
397 				else if (*p != ' ' && *p != '\t')
398 					goto barf_line;
399 				*p++ = '\0'; /* null terminate linep */
400 				p = spaceskip(p);
401 				if (!end_crit) {
402 					if (*p == ']') {
403 					end_crit = 1;
404 					*p++ = '\0';
405 					} else if (*p == '\0' || *p == '\n')
406 						return (cfp);
407 					else if (!islabel(*p))
408 					/* p better be the next switch_err */
409 						goto barf_line;
410 				}
411 				if (strcasecmp(linep, __NSW_STR_RETURN) == 0)
412 					act = __NSW_RETURN;
413 				else if (strcasecmp(linep,
414 				    __NSW_STR_CONTINUE) == 0)
415 					act = __NSW_CONTINUE;
416 				else if (strcasecmp(linep,
417 				    __NSW_STR_FOREVER) == 0)
418 					/*
419 					 * =forever or =N might be in conf file
420 					 * but old progs won't expect it.
421 					 */
422 					act = __NSW_RETURN;
423 				else if (alldigits(linep))
424 					act = __NSW_CONTINUE;
425 				else
426 					goto barf_line;
427 				if (strcasecmp(tokenp,
428 				    __NSW_STR_SUCCESS) == 0) {
429 					lkp->actions[__NSW_SUCCESS] = act;
430 				} else if (strcasecmp(tokenp,
431 				    __NSW_STR_NOTFOUND) == 0) {
432 					lkp->actions[__NSW_NOTFOUND] = act;
433 				} else if (strcasecmp(tokenp,
434 				    __NSW_STR_UNAVAIL) == 0) {
435 					lkp->actions[__NSW_UNAVAIL] = act;
436 				} else if (strcasecmp(tokenp,
437 				    __NSW_STR_TRYAGAIN) == 0) {
438 					lkp->actions[__NSW_TRYAGAIN] = act;
439 				} else {
440 					/*EMPTY*/
441 					/*
442 					 * convert string tokenp to integer
443 					 * and put in long_errs
444 					 */
445 				}
446 				if (end_crit) {
447 					linep = spaceskip(p);
448 					if (*linep == '\0' || *linep == '\n')
449 						return (cfp);
450 					break; /* process next naming service */
451 				}
452 				linep = p;
453 			} /* end of while loop for a name service's criteria */
454 		} else {
455 			/*
456 			 * no criteria for this naming service.
457 			 * linep points to name service, but not null
458 			 * terminated.
459 			 */
460 			p = labelskip(linep);
461 			if (*p == '\0' || *p == '\n') {
462 				*p = '\0';
463 				LIBC_STRDUP(lkp->service_name, linep);
464 				cfp->num_lookups++;
465 				return (cfp);
466 			}
467 			if (*p != ' ' && *p != '\t')
468 				goto barf_line;
469 			*p++ = '\0';
470 			LIBC_STRDUP(lkp->service_name, linep);
471 			cfp->num_lookups++;
472 			linep = spaceskip(p);
473 		}
474 	} /* end of while(1) loop for a name service */
475 
476 barf_line:
477 	freeconf(cfp);
478 	*errp = dup_fail ? __NSW_CONF_PARSE_SYSERR : __NSW_CONF_PARSE_NOPOLICY;
479 	return (NULL);
480 }
481 
482 static mutex_t serialize_config_v1 = DEFAULTMUTEX;
483 static mutex_t serialize_config = DEFAULTMUTEX;
484 
485 static void
486 syslog_warning(const char *dbase)
487 {
488 	syslog(LOG_WARNING,
489 	    "libc: bad lookup policy for %s in %s, using defaults..\n",
490 	    dbase, __NSW_CONFIG_FILE);
491 }
492 
493 /*
494  * Since we cannot call malloc() or lock any of the ordinary mutexes
495  * while we hold an lmutex_lock(), we open the file outside the lock
496  * and disable locking on the file; the latter is fine because we're
497  * reading the fp only from a single thread.
498  */
499 static FILE *
500 open_conf(void)
501 {
502 	FILE *fp = fopen(__NSW_CONFIG_FILE, "rF");
503 
504 	if (fp != NULL) {
505 		if (_findbuf(fp) == NULL) {
506 			(void) fclose(fp);
507 			return (NULL);
508 		}
509 		SET_IONOLOCK(fp);
510 	}
511 	return (fp);
512 }
513 
514 struct __nsw_switchconfig_v1 *
515 __nsw_getconfig_v1(const char *dbase, enum __nsw_parse_err *errp)
516 {
517 	struct __nsw_switchconfig_v1 *cfp, *retp = NULL;
518 	int syslog_error = 0;
519 	FILE *fp = NULL;
520 	char *linep;
521 	char lineq[BUFSIZ];
522 
523 	lmutex_lock(&serialize_config_v1);
524 top:
525 	cfp = scrounge_cache_v1(dbase);
526 	if (cfp != NULL) {
527 		*errp = __NSW_CONF_PARSE_SUCCESS;
528 		lmutex_unlock(&serialize_config_v1);
529 		if (fp != NULL)
530 			(void) fclose(fp);
531 		return (cfp);
532 	}
533 
534 	if (fp == NULL) {
535 		struct cons_cell_v1 *cp = concell_list_v1;
536 
537 		lmutex_unlock(&serialize_config_v1);
538 		/* open_conf() must be called w/o locks held */
539 		if ((fp = open_conf()) == NULL) {
540 			*errp = __NSW_CONF_PARSE_NOFILE;
541 			return (NULL);
542 		}
543 		lmutex_lock(&serialize_config_v1);
544 		/* Cache changed? */
545 		if (cp != concell_list_v1)
546 			goto top;
547 	}
548 
549 	*errp = __NSW_CONF_PARSE_NOPOLICY;
550 	while ((linep = fgets(lineq, BUFSIZ, fp)) != NULL) {
551 		enum __nsw_parse_err	line_err;
552 		char			*tokenp, *comment;
553 
554 		/*
555 		 * Ignore portion of line following the comment character '#'.
556 		 */
557 		if ((comment = strchr(linep, '#')) != NULL) {
558 			*comment = '\0';
559 		}
560 		/*
561 		 * skip past blank lines.
562 		 * otherwise, cache as a struct switchconfig.
563 		 */
564 		if ((*linep == '\0') || isspace(*linep)) {
565 			continue;
566 		}
567 		if ((tokenp = skip(&linep, ':')) == NULL) {
568 			continue; /* ignore this line */
569 		}
570 		cfp = scrounge_cache_v1(tokenp);
571 		if (cfp != NULL) {
572 			continue; /* ? somehow this database is in the cache */
573 		}
574 		cfp = _nsw_getoneconfig_v1(tokenp, linep, &line_err);
575 		if (cfp != NULL) {
576 			(void) add_concell_v1(cfp);
577 			if (strcmp(cfp->dbase, dbase) == 0) {
578 				*errp = __NSW_CONF_PARSE_SUCCESS;
579 				retp = cfp;
580 			}
581 		} else {
582 			/*
583 			 * Got an error on this line, if it is a system
584 			 * error we might as well give right now. If it
585 			 * is a parse error on the second entry of the
586 			 * database we are looking for and the first one
587 			 * was a good entry we end up logging the following
588 			 * syslog message and using a default policy instead.
589 			 */
590 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
591 				*errp = __NSW_CONF_PARSE_SYSERR;
592 				break;
593 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
594 			    strcmp(tokenp, dbase) == 0) {
595 				syslog_error = 1;
596 				*errp = __NSW_CONF_PARSE_NOPOLICY;
597 				break;
598 			}
599 			/*
600 			 * Else blithely ignore problems on this line and
601 			 *   go ahead with the next line.
602 			 */
603 		}
604 	}
605 	lmutex_unlock(&serialize_config_v1);
606 	/*
607 	 * We have to drop the lock before calling fclose()/syslog().
608 	 */
609 	(void) fclose(fp);
610 	if (syslog_error)
611 		syslog_warning(dbase);
612 	return (retp);
613 }
614 
615 struct __nsw_switchconfig *
616 __nsw_getconfig(const char *dbase, enum __nsw_parse_err *errp)
617 {
618 	struct __nsw_switchconfig *cfp, *retp = NULL;
619 	int syslog_error = 0;
620 	FILE *fp = NULL;
621 	char *linep;
622 	char lineq[BUFSIZ];
623 
624 	lmutex_lock(&serialize_config);
625 top:
626 	cfp = scrounge_cache(dbase);
627 	if (cfp != NULL) {
628 		*errp = __NSW_CONF_PARSE_SUCCESS;
629 		lmutex_unlock(&serialize_config);
630 		if (fp != NULL)
631 			(void) fclose(fp);
632 		return (cfp);
633 	}
634 
635 	if (fp == NULL) {
636 		struct cons_cell *cp = concell_list;
637 		/* open_conf() must be called w/o locks held */
638 		lmutex_unlock(&serialize_config);
639 		if ((fp = open_conf()) == NULL) {
640 			*errp = __NSW_CONF_PARSE_NOFILE;
641 			return (NULL);
642 		}
643 		lmutex_lock(&serialize_config);
644 		/* Cache changed? */
645 		if (cp != concell_list)
646 			goto top;
647 	}
648 
649 	*errp = __NSW_CONF_PARSE_NOPOLICY;
650 	while ((linep = fgets(lineq, BUFSIZ, fp)) != NULL) {
651 		enum __nsw_parse_err	line_err;
652 		char			*tokenp, *comment;
653 
654 		/*
655 		 * Ignore portion of line following the comment character '#'.
656 		 */
657 		if ((comment = strchr(linep, '#')) != NULL) {
658 			*comment = '\0';
659 		}
660 		/*
661 		 * skip past blank lines.
662 		 * otherwise, cache as a struct switchconfig.
663 		 */
664 		if ((*linep == '\0') || isspace(*linep)) {
665 			continue;
666 		}
667 		tokenp = skip(&linep, ':');
668 		if (tokenp == NULL) {
669 			continue; /* ignore this line */
670 		}
671 		cfp = scrounge_cache(tokenp);
672 		if (cfp != NULL) {
673 			continue; /* ? somehow this database is in the cache */
674 		}
675 		cfp = _nsw_getoneconfig(tokenp, linep, &line_err);
676 		if (cfp != NULL) {
677 			(void) add_concell(cfp);
678 			if (strcmp(cfp->dbase, dbase) == 0) {
679 				*errp = __NSW_CONF_PARSE_SUCCESS;
680 				retp = cfp;
681 			}
682 		} else {
683 			/*
684 			 * Got an error on this line, if it is a system
685 			 * error we might as well give right now. If it
686 			 * is a parse error on the second entry of the
687 			 * database we are looking for and the first one
688 			 * was a good entry we end up logging the following
689 			 * syslog message and using a default policy instead.
690 			 */
691 			if (line_err == __NSW_CONF_PARSE_SYSERR) {
692 				*errp = __NSW_CONF_PARSE_SYSERR;
693 				break;
694 			} else if (line_err == __NSW_CONF_PARSE_NOPOLICY &&
695 			    strcmp(tokenp, dbase) == 0) {
696 				syslog_error = 1;
697 				*errp = __NSW_CONF_PARSE_NOPOLICY;
698 				break;
699 			}
700 			/*
701 			 * Else blithely ignore problems on this line and
702 			 *   go ahead with the next line.
703 			 */
704 		}
705 	}
706 	lmutex_unlock(&serialize_config);
707 	/*
708 	 * We have to drop the lock before calling fclose()/syslog().
709 	 */
710 	(void) fclose(fp);
711 	if (syslog_error)
712 		syslog_warning(dbase);
713 	return (retp);
714 }
715 
716 
717 static struct __nsw_switchconfig_v1 *
718 scrounge_cache_v1(const char *dbase)
719 {
720 	struct cons_cell_v1 *cellp = concell_list_v1;
721 
722 	for (; cellp; cellp = cellp->next)
723 		if (strcmp(dbase, cellp->sw->dbase) == 0)
724 			return (cellp->sw);
725 	return (NULL);
726 }
727 
728 static struct __nsw_switchconfig *
729 scrounge_cache(const char *dbase)
730 {
731 	struct cons_cell *cellp = concell_list;
732 
733 	for (; cellp; cellp = cellp->next)
734 		if (strcmp(dbase, cellp->sw->dbase) == 0)
735 			return (cellp->sw);
736 	return (NULL);
737 }
738 
739 static void
740 freeconf_v1(struct __nsw_switchconfig_v1 *cfp)
741 {
742 	if (cfp) {
743 		if (cfp->dbase)
744 			libc_free(cfp->dbase);
745 		if (cfp->lookups) {
746 			struct __nsw_lookup_v1 *nex, *cur;
747 			for (cur = cfp->lookups; cur; cur = nex) {
748 				libc_free(cur->service_name);
749 				nex = cur->next;
750 				libc_free(cur);
751 			}
752 		}
753 		libc_free(cfp);
754 	}
755 }
756 
757 static void
758 freeconf(struct __nsw_switchconfig *cfp)
759 {
760 	if (cfp) {
761 		if (cfp->dbase)
762 			libc_free(cfp->dbase);
763 		if (cfp->lookups) {
764 			struct __nsw_lookup *nex, *cur;
765 			for (cur = cfp->lookups; cur; cur = nex) {
766 				libc_free(cur->service_name);
767 				nex = cur->next;
768 				libc_free(cur);
769 			}
770 		}
771 		libc_free(cfp);
772 	}
773 }
774 
775 action_t
776 __nsw_extended_action_v1(struct __nsw_lookup_v1 *lkp, int err)
777 {
778 	struct __nsw_long_err *lerrp;
779 
780 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
781 		if (lerrp->nsw_errno == err)
782 			return (lerrp->action);
783 	}
784 	return (__NSW_CONTINUE);
785 }
786 
787 action_t
788 __nsw_extended_action(struct __nsw_lookup *lkp, int err)
789 {
790 	struct __nsw_long_err *lerrp;
791 
792 	for (lerrp = lkp->long_errs; lerrp; lerrp = lerrp->next) {
793 		if (lerrp->nsw_errno == err)
794 			return (lerrp->action);
795 	}
796 	return (__NSW_CONTINUE);
797 }
798 
799 
800 /* give the next non-alpha character */
801 static char *
802 labelskip(char *cur)
803 {
804 	char *p = cur;
805 	while (islabel(*p))
806 		++p;
807 	return (p);
808 }
809 
810 /* give the next non-space character */
811 static char *
812 spaceskip(char *cur)
813 {
814 	char *p = cur;
815 	while (*p == ' ' || *p == '\t')
816 		++p;
817 	return (p);
818 }
819 
820 /*
821  * terminate the *cur pointed string by null only if it is
822  * followed by "key" surrounded by zero or more spaces and
823  * return value is the same as the original *cur pointer and
824  * *cur pointer is advanced to the first non {space, key} char
825  * followed by the key. Otherwise, return NULL and keep
826  * *cur unchanged.
827  */
828 static char *
829 skip(char **cur, char key)
830 {
831 	char *p, *tmp;
832 	char *q = *cur;
833 	int found, tmpfound;
834 
835 	tmp = labelskip(*cur);
836 	p = tmp;
837 	found = (*p == key);
838 	if (found) {
839 		*p++ = '\0'; /* overwrite the key */
840 		p = spaceskip(p);
841 	} else {
842 		while (*p == ' ' || *p == '\t') {
843 			tmpfound = (*++p == key);
844 			if (tmpfound) {
845 				found = tmpfound;
846 					/* null terminate the return token */
847 				*tmp = '\0';
848 				p++; /* skip the key */
849 			}
850 		}
851 	}
852 	if (!found)
853 		return (NULL); /* *cur unchanged */
854 	*cur = p;
855 	return (q);
856 }
857 
858 /* add to the front: LRU */
859 static int
860 add_concell_v1(struct __nsw_switchconfig_v1 *cfp)
861 {
862 	struct cons_cell_v1 *cp;
863 
864 	if (cfp == NULL)
865 		return (1);
866 	if ((cp = libc_malloc(sizeof (struct cons_cell_v1))) == NULL)
867 		return (1);
868 	cp->sw = cfp;
869 	cp->next = concell_list_v1;
870 	concell_list_v1 = cp;
871 	return (0);
872 }
873 
874 /* add to the front: LRU */
875 static int
876 add_concell(struct __nsw_switchconfig *cfp)
877 {
878 	struct cons_cell *cp;
879 
880 	if (cfp == NULL)
881 		return (1);
882 	if ((cp = libc_malloc(sizeof (struct cons_cell))) == NULL)
883 		return (1);
884 	cp->sw = cfp;
885 	cp->next = concell_list;
886 	concell_list = cp;
887 	return (0);
888 }
889 
890 int
891 __nsw_freeconfig_v1(struct __nsw_switchconfig_v1 *conf)
892 {
893 	struct cons_cell_v1 *cellp;
894 
895 	if (conf == NULL) {
896 		return (-1);
897 	}
898 	/*
899 	 * Hacked to make life easy for the code in nss_common.c.  Free conf
900 	 *   iff it was created by calling _nsw_getoneconfig() directly
901 	 *   rather than by calling nsw_getconfig.
902 	 */
903 	lmutex_lock(&serialize_config_v1);
904 	for (cellp = concell_list_v1;  cellp;  cellp = cellp->next) {
905 		if (cellp->sw == conf) {
906 			break;
907 		}
908 	}
909 	lmutex_unlock(&serialize_config_v1);
910 	if (cellp == NULL) {
911 		/* Not in the cache;  free it */
912 		freeconf_v1(conf);
913 		return (1);
914 	} else {
915 		/* In the cache;  don't free it */
916 		return (0);
917 	}
918 }
919 
920 int
921 __nsw_freeconfig(struct __nsw_switchconfig *conf)
922 {
923 	struct cons_cell *cellp;
924 
925 	if (conf == NULL) {
926 		return (-1);
927 	}
928 	/*
929 	 * Hacked to make life easy for the code in nss_common.c.  Free conf
930 	 *   iff it was created by calling _nsw_getoneconfig() directly
931 	 *   rather than by calling nsw_getconfig.
932 	 */
933 	lmutex_lock(&serialize_config);
934 	for (cellp = concell_list;  cellp;  cellp = cellp->next) {
935 		if (cellp->sw == conf) {
936 			break;
937 		}
938 	}
939 	lmutex_unlock(&serialize_config);
940 	if (cellp == NULL) {
941 		/* Not in the cache;  free it */
942 		freeconf(conf);
943 		return (1);
944 	} else {
945 		/* In the cache;  don't free it */
946 		return (0);
947 	}
948 }
949 
950 /* Return 1 if the string contains all digits, else return 0. */
951 static int
952 alldigits(char *s)
953 {
954 	for (; *s; s++)
955 		if (!isdigit(*s))
956 			return (0);
957 	return (1);
958 }
959