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