xref: /illumos-gate/usr/src/cmd/nscd/nscd_nswparse.c (revision 36e852a172cba914383d7341c988128b2c667fbd)
1cb5caa98Sdjl /*
2cb5caa98Sdjl  * CDDL HEADER START
3cb5caa98Sdjl  *
4cb5caa98Sdjl  * The contents of this file are subject to the terms of the
5cb5caa98Sdjl  * Common Development and Distribution License (the "License").
6cb5caa98Sdjl  * You may not use this file except in compliance with the License.
7cb5caa98Sdjl  *
8cb5caa98Sdjl  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9cb5caa98Sdjl  * or http://www.opensolaris.org/os/licensing.
10cb5caa98Sdjl  * See the License for the specific language governing permissions
11cb5caa98Sdjl  * and limitations under the License.
12cb5caa98Sdjl  *
13cb5caa98Sdjl  * When distributing Covered Code, include this CDDL HEADER in each
14cb5caa98Sdjl  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15cb5caa98Sdjl  * If applicable, add the following below this CDDL HEADER, with the
16cb5caa98Sdjl  * fields enclosed by brackets "[]" replaced with your own identifying
17cb5caa98Sdjl  * information: Portions Copyright [yyyy] [name of copyright owner]
18cb5caa98Sdjl  *
19cb5caa98Sdjl  * CDDL HEADER END
20cb5caa98Sdjl  */
21cb5caa98Sdjl /*
22*36e852a1SRaja Andra  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23cb5caa98Sdjl  * Use is subject to license terms.
24cb5caa98Sdjl  */
25cb5caa98Sdjl 
26cb5caa98Sdjl #include <stdlib.h>
27cb5caa98Sdjl #include <limits.h>
28cb5caa98Sdjl #include <string.h>
29cb5caa98Sdjl #include <ctype.h>
30cb5caa98Sdjl 
31cb5caa98Sdjl #define	__NSS_PRIVATE_INTERFACE
32cb5caa98Sdjl #include "nsswitch_priv.h"
33cb5caa98Sdjl #undef	__NSS_PRIVATE_INTERFACE
34cb5caa98Sdjl 
35cb5caa98Sdjl #define	islabel(c) 	(isalnum(c) || (c) == '_')
36cb5caa98Sdjl 
37cb5caa98Sdjl /*
38cb5caa98Sdjl  * The _nsw_getoneconfig_v1() in this file parses the switch policy
39cb5caa98Sdjl  * configuration for a switch database, e.g.,
40cb5caa98Sdjl  *
41cb5caa98Sdjl  * hosts: nis [NOTFOUND=return] files
42cb5caa98Sdjl  * or
43cb5caa98Sdjl  * printers: user files nis
44cb5caa98Sdjl  */
45cb5caa98Sdjl 
46cb5caa98Sdjl /*
47cb5caa98Sdjl  * Local routines
48cb5caa98Sdjl  */
49cb5caa98Sdjl static char *skip(char **, char);
50cb5caa98Sdjl static char *labelskip(char *);
51cb5caa98Sdjl static char *spaceskip(char *);
52cb5caa98Sdjl static void freeconf_v1(struct __nsw_switchconfig_v1 *);
53cb5caa98Sdjl static int alldigits(char *);
54cb5caa98Sdjl 
55cb5caa98Sdjl /*
56cb5caa98Sdjl  *
57*36e852a1SRaja Andra  * With the "lookup control" feature, the default criteria for NIS
58cb5caa98Sdjl  * and any new services (e.g. ldap) will be:
59cb5caa98Sdjl  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever]
60cb5caa98Sdjl  *
61cb5caa98Sdjl  * For backward compat, NIS via NIS server in DNS forwarding mode will be:
62cb5caa98Sdjl  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
63cb5caa98Sdjl  *
64cb5caa98Sdjl  * And also for backward compat, the default criteria for DNS will be:
65cb5caa98Sdjl  *     [SUCCESS=return  NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
66cb5caa98Sdjl  */
67cb5caa98Sdjl 
68cb5caa98Sdjl 
69cb5caa98Sdjl 
70cb5caa98Sdjl /*
71cb5caa98Sdjl  * The BIND resolver normally will retry several times on server non-response.
72cb5caa98Sdjl  * But now with the "lookup control" feature, we don't want the resolver doing
73cb5caa98Sdjl  * many retries, rather we want it to return control (reasonably) quickly back
74cb5caa98Sdjl  * to the switch engine.  However, when TRYAGAIN=N or TRYAGAIN=forever is
75cb5caa98Sdjl  * not explicitly set by the admin in the conf file, we want the old "resolver
76cb5caa98Sdjl  * retry a few times" rather than no retries at all.
77cb5caa98Sdjl  */
78cb5caa98Sdjl static int 	dns_tryagain_retry = 3;
79cb5caa98Sdjl 
80cb5caa98Sdjl /*
81cb5caa98Sdjl  * For backward compat (pre "lookup control"), the dns default behavior is
82cb5caa98Sdjl  * soft lookup.
83cb5caa98Sdjl  */
84cb5caa98Sdjl static void
set_dns_default_lkp(struct __nsw_lookup_v1 * lkp)85cb5caa98Sdjl set_dns_default_lkp(struct __nsw_lookup_v1 *lkp)
86cb5caa98Sdjl {
87cb5caa98Sdjl 	if (strcasecmp(lkp->service_name, "dns") == 0) {
88cb5caa98Sdjl 		lkp->actions[__NSW_TRYAGAIN] =
89cb5caa98Sdjl 		    __NSW_TRYAGAIN_NTIMES;
90cb5caa98Sdjl 		lkp->max_retries = dns_tryagain_retry;
91cb5caa98Sdjl 	}
92cb5caa98Sdjl }
93cb5caa98Sdjl 
94cb5caa98Sdjl static void
freeconf_v1(struct __nsw_switchconfig_v1 * cfp)95cb5caa98Sdjl freeconf_v1(struct __nsw_switchconfig_v1 *cfp)
96cb5caa98Sdjl {
97cb5caa98Sdjl 	if (cfp) {
98cb5caa98Sdjl 		if (cfp->dbase)
99cb5caa98Sdjl 			free(cfp->dbase);
100cb5caa98Sdjl 		if (cfp->lookups) {
101cb5caa98Sdjl 			struct __nsw_lookup_v1 *nex, *cur;
102cb5caa98Sdjl 			for (cur = cfp->lookups; cur; cur = nex) {
103cb5caa98Sdjl 				free(cur->service_name);
104cb5caa98Sdjl 				nex = cur->next;
105cb5caa98Sdjl 				free(cur);
106cb5caa98Sdjl 			}
107cb5caa98Sdjl 		}
108cb5caa98Sdjl 		free(cfp);
109cb5caa98Sdjl 	}
110cb5caa98Sdjl }
111cb5caa98Sdjl 
112cb5caa98Sdjl /* give the next non-alpha character */
113cb5caa98Sdjl static char *
labelskip(char * cur)114cb5caa98Sdjl labelskip(char *cur)
115cb5caa98Sdjl {
116cb5caa98Sdjl 	char *p = cur;
117cb5caa98Sdjl 	while (islabel(*p))
118cb5caa98Sdjl 		++p;
119cb5caa98Sdjl 	return (p);
120cb5caa98Sdjl }
121cb5caa98Sdjl 
122cb5caa98Sdjl /* give the next non-space character */
123cb5caa98Sdjl static char *
spaceskip(char * cur)124cb5caa98Sdjl spaceskip(char *cur)
125cb5caa98Sdjl {
126cb5caa98Sdjl 	char *p = cur;
127cb5caa98Sdjl 	while (*p == ' ' || *p == '\t')
128cb5caa98Sdjl 		++p;
129cb5caa98Sdjl 	return (p);
130cb5caa98Sdjl }
131cb5caa98Sdjl 
132cb5caa98Sdjl /*
133cb5caa98Sdjl  * terminate the *cur pointed string by null only if it is
134cb5caa98Sdjl  * followed by "key" surrounded by zero or more spaces and
135cb5caa98Sdjl  * return value is the same as the original *cur pointer and
136cb5caa98Sdjl  * *cur pointer is advanced to the first non {space, key} char
137cb5caa98Sdjl  * followed by the key. Otherwise, return NULL and keep
138cb5caa98Sdjl  * *cur unchanged.
139cb5caa98Sdjl  */
140cb5caa98Sdjl static char *
skip(char ** cur,char key)141cb5caa98Sdjl skip(char **cur, char key)
142cb5caa98Sdjl {
143cb5caa98Sdjl 	char *p, *tmp;
144cb5caa98Sdjl 	char *q = *cur;
145cb5caa98Sdjl 	int found, tmpfound;
146cb5caa98Sdjl 
147cb5caa98Sdjl 	tmp = labelskip(*cur);
148cb5caa98Sdjl 	p = tmp;
149cb5caa98Sdjl 	found = (*p == key);
150cb5caa98Sdjl 	if (found) {
151cb5caa98Sdjl 		*p++ = '\0'; /* overwrite the key */
152cb5caa98Sdjl 		p = spaceskip(p);
153cb5caa98Sdjl 	} else {
154cb5caa98Sdjl 		while (*p == ' ' || *p == '\t') {
155cb5caa98Sdjl 			tmpfound = (*++p == key);
156cb5caa98Sdjl 			if (tmpfound) {
157cb5caa98Sdjl 				found = tmpfound;
158cb5caa98Sdjl 					/* null terminate the return token */
159cb5caa98Sdjl 				*tmp = '\0';
160cb5caa98Sdjl 				p++; /* skip the key */
161cb5caa98Sdjl 			}
162cb5caa98Sdjl 		}
163cb5caa98Sdjl 	}
164cb5caa98Sdjl 	if (!found)
165cb5caa98Sdjl 		return (NULL); /* *cur unchanged */
166cb5caa98Sdjl 	*cur = p;
167cb5caa98Sdjl 	return (q);
168cb5caa98Sdjl }
169cb5caa98Sdjl 
170cb5caa98Sdjl /* Return 1 if the string contains all digits, else return 0. */
171cb5caa98Sdjl static int
alldigits(char * s)172cb5caa98Sdjl alldigits(char *s)
173cb5caa98Sdjl {
174cb5caa98Sdjl 	for (; *s; s++)
175cb5caa98Sdjl 		if (!isdigit(*s))
176cb5caa98Sdjl 			return (0);
177cb5caa98Sdjl 	return (1);
178cb5caa98Sdjl }
179cb5caa98Sdjl 
180cb5caa98Sdjl struct __nsw_switchconfig_v1 *
_nsw_getoneconfig_v1(const char * name,char * linep,enum __nsw_parse_err * errp)181cb5caa98Sdjl _nsw_getoneconfig_v1(const char *name, char *linep, enum __nsw_parse_err *errp)
182cb5caa98Sdjl 	/* linep   Nota Bene: not const char *	*/
183cb5caa98Sdjl 	/* errp  Meanings are abused a bit	*/
184cb5caa98Sdjl {
185cb5caa98Sdjl 	struct __nsw_switchconfig_v1 *cfp;
186cb5caa98Sdjl 	struct __nsw_lookup_v1 *lkp, **lkq;
187cb5caa98Sdjl 	int end_crit;
188cb5caa98Sdjl 	action_t act;
189cb5caa98Sdjl 	char *p, *tokenp;
190cb5caa98Sdjl 
191cb5caa98Sdjl 	*errp = __NSW_CONF_PARSE_SUCCESS;
192cb5caa98Sdjl 
193cb5caa98Sdjl 	if ((cfp = calloc(1, sizeof (struct __nsw_switchconfig_v1)))
194cb5caa98Sdjl 	    == NULL) {
195cb5caa98Sdjl 		*errp = __NSW_CONF_PARSE_SYSERR;
196cb5caa98Sdjl 		return (NULL);
197cb5caa98Sdjl 	}
198cb5caa98Sdjl 	cfp->dbase = strdup(name);
199cb5caa98Sdjl 	lkq = &cfp->lookups;
200cb5caa98Sdjl 
201cb5caa98Sdjl 	/* linep points to a naming service name */
202cb5caa98Sdjl 	for (;;) {
203cb5caa98Sdjl 		int i;
204cb5caa98Sdjl 
205cb5caa98Sdjl 		/* white space following the last service */
206cb5caa98Sdjl 		if (*linep == '\0' || *linep == '\n') {
207cb5caa98Sdjl 			return (cfp);
208cb5caa98Sdjl 		}
209cb5caa98Sdjl 		if ((lkp = calloc(1, sizeof (struct __nsw_lookup_v1)))
210cb5caa98Sdjl 		    == NULL) {
211cb5caa98Sdjl 			*errp = __NSW_CONF_PARSE_SYSERR;
212cb5caa98Sdjl 			freeconf_v1(cfp);
213cb5caa98Sdjl 			return (NULL);
214cb5caa98Sdjl 		}
215cb5caa98Sdjl 
216cb5caa98Sdjl 		*lkq = lkp;
217cb5caa98Sdjl 		lkq = &lkp->next;
218cb5caa98Sdjl 
219cb5caa98Sdjl 		for (i = 0; i < __NSW_STD_ERRS_V1; i++)
220cb5caa98Sdjl 			if (i == __NSW_SUCCESS)
221cb5caa98Sdjl 				lkp->actions[i] = __NSW_RETURN;
222cb5caa98Sdjl 			else if (i == __NSW_TRYAGAIN)
223cb5caa98Sdjl 				lkp->actions[i] = __NSW_TRYAGAIN_FOREVER;
224cb5caa98Sdjl 			else
225cb5caa98Sdjl 				lkp->actions[i] = __NSW_CONTINUE;
226cb5caa98Sdjl 
227cb5caa98Sdjl 		/* get criteria for the naming service */
228cb5caa98Sdjl 		if (tokenp = skip(&linep, '[')) { /* got criteria */
229cb5caa98Sdjl 
230cb5caa98Sdjl 			/* premature end, illegal char following [ */
231cb5caa98Sdjl 			if (!islabel(*linep))
232cb5caa98Sdjl 				goto barf_line;
233cb5caa98Sdjl 			lkp->service_name = strdup(tokenp);
234cb5caa98Sdjl 			cfp->num_lookups++;
235cb5caa98Sdjl 
236cb5caa98Sdjl 			set_dns_default_lkp(lkp);
237cb5caa98Sdjl 
238cb5caa98Sdjl 			end_crit = 0;
239cb5caa98Sdjl 
240cb5caa98Sdjl 			/* linep points to a switch_err */
241cb5caa98Sdjl 			for (;;) {
242cb5caa98Sdjl 				int ntimes = 0; /* try again max N times */
243cb5caa98Sdjl 				int dns_continue = 0;
244cb5caa98Sdjl 
245cb5caa98Sdjl 				if ((tokenp = skip(&linep, '=')) == NULL) {
246cb5caa98Sdjl 					goto barf_line;
247cb5caa98Sdjl 				}
248cb5caa98Sdjl 
249cb5caa98Sdjl 				/* premature end, ill char following = */
250cb5caa98Sdjl 				if (!islabel(*linep))
251cb5caa98Sdjl 					goto barf_line;
252cb5caa98Sdjl 
253cb5caa98Sdjl 				/* linep points to the string following '=' */
254cb5caa98Sdjl 				p = labelskip(linep);
255cb5caa98Sdjl 				if (*p == ']')
256cb5caa98Sdjl 					end_crit = 1;
257cb5caa98Sdjl 				else if (*p != ' ' && *p != '\t')
258cb5caa98Sdjl 					goto barf_line;
259cb5caa98Sdjl 				*p++ = '\0'; /* null terminate linep */
260cb5caa98Sdjl 				p = spaceskip(p);
261cb5caa98Sdjl 				if (!end_crit) {
262cb5caa98Sdjl 					if (*p == ']') {
263cb5caa98Sdjl 					end_crit = 1;
264cb5caa98Sdjl 					*p++ = '\0';
265cb5caa98Sdjl 					} else if (*p == '\0' || *p == '\n') {
266cb5caa98Sdjl 						return (cfp);
267cb5caa98Sdjl 					} else if (!islabel(*p))
268cb5caa98Sdjl 					/* p better be the next switch_err */
269cb5caa98Sdjl 						goto barf_line;
270cb5caa98Sdjl 				}
271cb5caa98Sdjl 				if (strcasecmp(linep, __NSW_STR_RETURN) == 0)
272cb5caa98Sdjl 					act = __NSW_RETURN;
273cb5caa98Sdjl 				else if (strcasecmp(linep,
274cb5caa98Sdjl 						    __NSW_STR_CONTINUE) == 0) {
275cb5caa98Sdjl 					if (strcasecmp(lkp->service_name,
276cb5caa98Sdjl 						    "dns") == 0 &&
277cb5caa98Sdjl 						strcasecmp(tokenp,
278cb5caa98Sdjl 							__NSW_STR_TRYAGAIN)
279cb5caa98Sdjl 							== 0) {
280cb5caa98Sdjl 						/*
281cb5caa98Sdjl 						 * Add one more condition
282cb5caa98Sdjl 						 * so it retries only if it's
283cb5caa98Sdjl 						 * "dns [TRYAGAIN=continue]"
284cb5caa98Sdjl 						 */
285cb5caa98Sdjl 						dns_continue = 1;
286cb5caa98Sdjl 						act = __NSW_TRYAGAIN_NTIMES;
287cb5caa98Sdjl 					} else
288cb5caa98Sdjl 						act = __NSW_CONTINUE;
289cb5caa98Sdjl 				} else if (strcasecmp(linep,
290cb5caa98Sdjl 					    __NSW_STR_FOREVER) == 0)
291cb5caa98Sdjl 					act = __NSW_TRYAGAIN_FOREVER;
292cb5caa98Sdjl 				else if (alldigits(linep)) {
293cb5caa98Sdjl 					act = __NSW_TRYAGAIN_NTIMES;
294cb5caa98Sdjl 					ntimes = atoi(linep);
295cb5caa98Sdjl 					if (ntimes < 0 || ntimes > INT_MAX)
296cb5caa98Sdjl 						ntimes = 0;
297cb5caa98Sdjl 				}
298cb5caa98Sdjl 				else
299cb5caa98Sdjl 					goto barf_line;
300cb5caa98Sdjl 
301cb5caa98Sdjl 				if (__NSW_SUCCESS_ACTION(act) &&
302cb5caa98Sdjl 				    strcasecmp(tokenp,
303cb5caa98Sdjl 					    __NSW_STR_SUCCESS) == 0) {
304cb5caa98Sdjl 					lkp->actions[__NSW_SUCCESS] = act;
305cb5caa98Sdjl 				} else if (__NSW_NOTFOUND_ACTION(act) &&
306cb5caa98Sdjl 					strcasecmp(tokenp,
307cb5caa98Sdjl 					    __NSW_STR_NOTFOUND) == 0) {
308cb5caa98Sdjl 					lkp->actions[__NSW_NOTFOUND] = act;
309cb5caa98Sdjl 				} else if (__NSW_UNAVAIL_ACTION(act) &&
310cb5caa98Sdjl 					strcasecmp(tokenp,
311cb5caa98Sdjl 					    __NSW_STR_UNAVAIL) == 0) {
312cb5caa98Sdjl 					lkp->actions[__NSW_UNAVAIL] = act;
313cb5caa98Sdjl 				} else if (__NSW_TRYAGAIN_ACTION(act) &&
314cb5caa98Sdjl 					strcasecmp(tokenp,
315cb5caa98Sdjl 					    __NSW_STR_TRYAGAIN) == 0) {
316cb5caa98Sdjl 					lkp->actions[__NSW_TRYAGAIN] = act;
317cb5caa98Sdjl 					if (strcasecmp(lkp->service_name,
318cb5caa98Sdjl 						    "nis") == 0)
319cb5caa98Sdjl 						lkp->actions[
320cb5caa98Sdjl 						    __NSW_NISSERVDNS_TRYAGAIN]
321cb5caa98Sdjl 						    = act;
322cb5caa98Sdjl 					if (act == __NSW_TRYAGAIN_NTIMES)
323cb5caa98Sdjl 						lkp->max_retries =
324cb5caa98Sdjl 						dns_continue ?
325cb5caa98Sdjl 						dns_tryagain_retry : ntimes;
326cb5caa98Sdjl 				} else {
327cb5caa98Sdjl 					/*EMPTY*/
328cb5caa98Sdjl 					/*
329cb5caa98Sdjl 					 * convert string tokenp to integer
330cb5caa98Sdjl 					 * and put in long_errs
331cb5caa98Sdjl 					 */
332cb5caa98Sdjl 				}
333cb5caa98Sdjl 				if (end_crit) {
334cb5caa98Sdjl 					linep = spaceskip(p);
335cb5caa98Sdjl 					if (*linep == '\0' || *linep == '\n')
336cb5caa98Sdjl 						return (cfp);
337cb5caa98Sdjl 					break; /* process next naming service */
338cb5caa98Sdjl 				}
339cb5caa98Sdjl 				linep = p;
340cb5caa98Sdjl 			} /* end of while loop for a name service's criteria */
341cb5caa98Sdjl 		} else {
342cb5caa98Sdjl 			/*
343cb5caa98Sdjl 			 * no criteria for this naming service.
344cb5caa98Sdjl 			 * linep points to name service, but not null
345cb5caa98Sdjl 			 * terminated.
346cb5caa98Sdjl 			 */
347cb5caa98Sdjl 			p = labelskip(linep);
348cb5caa98Sdjl 			if (*p == '\0' || *p == '\n') {
349cb5caa98Sdjl 				*p = '\0';
350cb5caa98Sdjl 				lkp->service_name = strdup(linep);
351cb5caa98Sdjl 				set_dns_default_lkp(lkp);
352cb5caa98Sdjl 				cfp->num_lookups++;
353cb5caa98Sdjl 				return (cfp);
354cb5caa98Sdjl 			}
355cb5caa98Sdjl 			if (*p != ' ' && *p != '\t')
356cb5caa98Sdjl 				goto barf_line;
357cb5caa98Sdjl 			*p++ = '\0';
358cb5caa98Sdjl 			lkp->service_name = strdup(linep);
359cb5caa98Sdjl 			set_dns_default_lkp(lkp);
360cb5caa98Sdjl 			cfp->num_lookups++;
361cb5caa98Sdjl 			linep = spaceskip(p);
362cb5caa98Sdjl 		}
363cb5caa98Sdjl 	} /* end of while(1) loop for a name service */
364cb5caa98Sdjl 
365cb5caa98Sdjl barf_line:
366cb5caa98Sdjl 	freeconf_v1(cfp);
367cb5caa98Sdjl 	*errp = __NSW_CONF_PARSE_NOPOLICY;
368cb5caa98Sdjl 	return (NULL);
369cb5caa98Sdjl }
370cb5caa98Sdjl 
371cb5caa98Sdjl int
__nsw_freeconfig_v1(struct __nsw_switchconfig_v1 * conf)372cb5caa98Sdjl __nsw_freeconfig_v1(
373cb5caa98Sdjl 	struct __nsw_switchconfig_v1 *conf)
374cb5caa98Sdjl {
375cb5caa98Sdjl 	freeconf_v1(conf);
376cb5caa98Sdjl 	return (0);
377cb5caa98Sdjl }
378