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
set_dns_default_lkp(struct __nsw_lookup_v1 * lkp)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
freeconf_v1(struct __nsw_switchconfig_v1 * cfp)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 *
labelskip(char * cur)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 *
spaceskip(char * cur)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 *
skip(char ** cur,char key)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
alldigits(char * s)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 *
_nsw_getoneconfig_v1(const char * name,char * linep,enum __nsw_parse_err * errp)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
__nsw_freeconfig_v1(struct __nsw_switchconfig_v1 * conf)372 __nsw_freeconfig_v1(
373 struct __nsw_switchconfig_v1 *conf)
374 {
375 freeconf_v1(conf);
376 return (0);
377 }
378