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