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.
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.
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]
23 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
34 #include <sys/types.h>
37 #include <stdio_ext.h>
45 #define __NSS_PRIVATE_INTERFACE
46 #include "nsswitch_priv.h"
47 #undef __NSS_PRIVATE_INTERFACE
51 #define islabel(c) (isalnum(c) || (c) == '_')
53 #define LIBC_STRDUP(new, existing) \
54 if ((new = libc_strdup(existing)) == NULL) { \
60 * This file has all the routines that access the configuration
64 struct cons_cell_v1
{ /* private to the parser */
65 struct __nsw_switchconfig_v1
*sw
;
66 struct cons_cell_v1
*next
;
69 struct cons_cell
{ /* private to the parser */
70 struct __nsw_switchconfig
*sw
;
71 struct cons_cell
*next
;
78 static char *skip(char **, char);
79 static char *labelskip(char *);
80 static char *spaceskip(char *);
81 static struct __nsw_switchconfig_v1
*scrounge_cache_v1(const char *);
82 static struct __nsw_switchconfig
*scrounge_cache(const char *);
83 static int add_concell_v1(struct __nsw_switchconfig_v1
*);
84 static int add_concell(struct __nsw_switchconfig
*);
85 static void freeconf_v1(struct __nsw_switchconfig_v1
*);
86 static void freeconf(struct __nsw_switchconfig
*);
87 static int alldigits(char *);
89 static struct cons_cell_v1
*concell_list_v1
; /* stays with add_concell() */
90 static struct cons_cell
*concell_list
; /* stays with add_concell() */
94 * With the "lookup control" feature, the default criteria for NIS, NIS+,
95 * and any new services (e.g. ldap) will be:
96 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=forever]
98 * For backward compat, NIS via NIS server in DNS forwarding mode will be:
99 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
101 * And also for backward compat, the default criteria for DNS will be:
102 * [SUCCESS=return NOTFOUND=continue UNAVAIL=continue TRYAGAIN=continue]
108 * The BIND resolver normally will retry several times on server non-response.
109 * But now with the "lookup control" feature, we don't want the resolver doing
110 * many retries, rather we want it to return control (reasonably) quickly back
111 * to the switch engine. However, when TRYAGAIN=N or TRYAGAIN=forever is
112 * not explicitly set by the admin in the conf file, we want the old "resolver
113 * retry a few times" rather than no retries at all.
115 static int dns_tryagain_retry
= 3;
118 * For backward compat (pre "lookup control"), the dns default behavior is
122 set_dns_default_lkp(struct __nsw_lookup_v1
*lkp
)
124 if (strcasecmp(lkp
->service_name
, "dns") == 0) {
125 lkp
->actions
[__NSW_TRYAGAIN
] = __NSW_TRYAGAIN_NTIMES
;
126 lkp
->max_retries
= dns_tryagain_retry
;
131 * Private interface used by nss_common.c, hence this function is not static
133 struct __nsw_switchconfig_v1
*
134 _nsw_getoneconfig_v1(const char *name
, char *linep
, enum __nsw_parse_err
*errp
)
135 /* linep Nota Bene: not const char * */
136 /* errp Meanings are abused a bit */
138 struct __nsw_switchconfig_v1
*cfp
;
139 struct __nsw_lookup_v1
*lkp
, **lkq
;
140 int end_crit
, dup_fail
= 0;
144 *errp
= __NSW_CONF_PARSE_SUCCESS
;
146 if ((cfp
= libc_malloc(sizeof (struct __nsw_switchconfig_v1
)))
148 *errp
= __NSW_CONF_PARSE_SYSERR
;
151 LIBC_STRDUP(cfp
->dbase
, name
);
154 /* linep points to a naming service name */
158 /* white space following the last service */
159 if (*linep
== '\0' || *linep
== '\n') {
162 if ((lkp
= libc_malloc(sizeof (struct __nsw_lookup_v1
)))
164 *errp
= __NSW_CONF_PARSE_SYSERR
;
172 for (i
= 0; i
< __NSW_STD_ERRS_V1
; i
++)
173 if (i
== __NSW_SUCCESS
)
174 lkp
->actions
[i
] = __NSW_RETURN
;
175 else if (i
== __NSW_TRYAGAIN
)
176 lkp
->actions
[i
] = __NSW_TRYAGAIN_FOREVER
;
178 lkp
->actions
[i
] = __NSW_CONTINUE
;
180 /* get criteria for the naming service */
181 if (tokenp
= skip(&linep
, '[')) { /* got criteria */
183 /* premature end, illegal char following [ */
184 if (!islabel(*linep
))
186 LIBC_STRDUP(lkp
->service_name
, tokenp
);
189 set_dns_default_lkp(lkp
);
193 /* linep points to a switch_err */
195 int ntimes
= 0; /* try again max N times */
196 int dns_continue
= 0;
198 if ((tokenp
= skip(&linep
, '=')) == NULL
) {
202 /* premature end, ill char following = */
203 if (!islabel(*linep
))
206 /* linep points to the string following '=' */
207 p
= labelskip(linep
);
210 else if (*p
!= ' ' && *p
!= '\t')
212 *p
++ = '\0'; /* null terminate linep */
218 } else if (*p
== '\0' || *p
== '\n') {
220 } else if (!islabel(*p
))
221 /* p better be the next switch_err */
224 if (strcasecmp(linep
, __NSW_STR_RETURN
) == 0)
226 else if (strcasecmp(linep
,
227 __NSW_STR_CONTINUE
) == 0) {
228 if (strcasecmp(lkp
->service_name
,
234 * Add one more condition
235 * so it retries only if it's
236 * "dns [TRYAGAIN=continue]"
239 act
= __NSW_TRYAGAIN_NTIMES
;
241 act
= __NSW_CONTINUE
;
242 } else if (strcasecmp(linep
,
243 __NSW_STR_FOREVER
) == 0)
244 act
= __NSW_TRYAGAIN_FOREVER
;
245 else if (alldigits(linep
)) {
246 act
= __NSW_TRYAGAIN_NTIMES
;
247 ntimes
= atoi(linep
);
248 if (ntimes
< 0 || ntimes
> INT_MAX
)
254 if (__NSW_SUCCESS_ACTION(act
) &&
256 __NSW_STR_SUCCESS
) == 0) {
257 lkp
->actions
[__NSW_SUCCESS
] = act
;
258 } else if (__NSW_NOTFOUND_ACTION(act
) &&
260 __NSW_STR_NOTFOUND
) == 0) {
261 lkp
->actions
[__NSW_NOTFOUND
] = act
;
262 } else if (__NSW_UNAVAIL_ACTION(act
) &&
264 __NSW_STR_UNAVAIL
) == 0) {
265 lkp
->actions
[__NSW_UNAVAIL
] = act
;
266 } else if (__NSW_TRYAGAIN_ACTION(act
) &&
268 __NSW_STR_TRYAGAIN
) == 0) {
269 lkp
->actions
[__NSW_TRYAGAIN
] = act
;
270 if (strcasecmp(lkp
->service_name
,
273 __NSW_NISSERVDNS_TRYAGAIN
]
275 if (act
== __NSW_TRYAGAIN_NTIMES
)
278 dns_tryagain_retry
: ntimes
;
282 * convert string tokenp to integer
283 * and put in long_errs
287 linep
= spaceskip(p
);
288 if (*linep
== '\0' || *linep
== '\n')
290 break; /* process next naming service */
293 } /* end of while loop for a name service's criteria */
296 * no criteria for this naming service.
297 * linep points to name service, but not null
300 p
= labelskip(linep
);
301 if (*p
== '\0' || *p
== '\n') {
303 LIBC_STRDUP(lkp
->service_name
, linep
);
304 set_dns_default_lkp(lkp
);
308 if (*p
!= ' ' && *p
!= '\t')
311 LIBC_STRDUP(lkp
->service_name
, linep
);
312 set_dns_default_lkp(lkp
);
314 linep
= spaceskip(p
);
316 } /* end of while(1) loop for a name service */
320 *errp
= dup_fail
? __NSW_CONF_PARSE_SYSERR
: __NSW_CONF_PARSE_NOPOLICY
;
325 * Private interface used by nss_common.c, hence this function is not static
327 struct __nsw_switchconfig
*
328 _nsw_getoneconfig(const char *name
, char *linep
, enum __nsw_parse_err
*errp
)
329 /* linep Nota Bene: not const char * */
330 /* errp Meanings are abused a bit */
332 struct __nsw_switchconfig
*cfp
;
333 struct __nsw_lookup
*lkp
, **lkq
;
334 int end_crit
, dup_fail
= 0;
338 *errp
= __NSW_CONF_PARSE_SUCCESS
;
340 if ((cfp
= libc_malloc(sizeof (struct __nsw_switchconfig
)))
342 *errp
= __NSW_CONF_PARSE_SYSERR
;
345 LIBC_STRDUP(cfp
->dbase
, name
);
348 /* linep points to a naming service name */
352 /* white space following the last service */
353 if (*linep
== '\0' || *linep
== '\n') {
356 if ((lkp
= libc_malloc(sizeof (struct __nsw_lookup
)))
358 *errp
= __NSW_CONF_PARSE_SYSERR
;
366 for (i
= 0; i
< __NSW_STD_ERRS
; i
++)
367 if (i
== __NSW_SUCCESS
)
372 /* get criteria for the naming service */
373 if (tokenp
= skip(&linep
, '[')) { /* got criteria */
375 /* premature end, illegal char following [ */
376 if (!islabel(*linep
))
378 LIBC_STRDUP(lkp
->service_name
, tokenp
);
382 /* linep points to a switch_err */
384 if ((tokenp
= skip(&linep
, '=')) == NULL
) {
388 /* premature end, ill char following = */
389 if (!islabel(*linep
))
392 /* linep points to the string following '=' */
393 p
= labelskip(linep
);
396 else if (*p
!= ' ' && *p
!= '\t')
398 *p
++ = '\0'; /* null terminate linep */
404 } else if (*p
== '\0' || *p
== '\n')
406 else if (!islabel(*p
))
407 /* p better be the next switch_err */
410 if (strcasecmp(linep
, __NSW_STR_RETURN
) == 0)
412 else if (strcasecmp(linep
,
413 __NSW_STR_CONTINUE
) == 0)
414 act
= __NSW_CONTINUE
;
415 else if (strcasecmp(linep
,
416 __NSW_STR_FOREVER
) == 0)
418 * =forever or =N might be in conf file
419 * but old progs won't expect it.
422 else if (alldigits(linep
))
423 act
= __NSW_CONTINUE
;
426 if (strcasecmp(tokenp
,
427 __NSW_STR_SUCCESS
) == 0) {
428 lkp
->actions
[__NSW_SUCCESS
] = act
;
429 } else if (strcasecmp(tokenp
,
430 __NSW_STR_NOTFOUND
) == 0) {
431 lkp
->actions
[__NSW_NOTFOUND
] = act
;
432 } else if (strcasecmp(tokenp
,
433 __NSW_STR_UNAVAIL
) == 0) {
434 lkp
->actions
[__NSW_UNAVAIL
] = act
;
435 } else if (strcasecmp(tokenp
,
436 __NSW_STR_TRYAGAIN
) == 0) {
437 lkp
->actions
[__NSW_TRYAGAIN
] = act
;
441 * convert string tokenp to integer
442 * and put in long_errs
446 linep
= spaceskip(p
);
447 if (*linep
== '\0' || *linep
== '\n')
449 break; /* process next naming service */
452 } /* end of while loop for a name service's criteria */
455 * no criteria for this naming service.
456 * linep points to name service, but not null
459 p
= labelskip(linep
);
460 if (*p
== '\0' || *p
== '\n') {
462 LIBC_STRDUP(lkp
->service_name
, linep
);
466 if (*p
!= ' ' && *p
!= '\t')
469 LIBC_STRDUP(lkp
->service_name
, linep
);
471 linep
= spaceskip(p
);
473 } /* end of while(1) loop for a name service */
477 *errp
= dup_fail
? __NSW_CONF_PARSE_SYSERR
: __NSW_CONF_PARSE_NOPOLICY
;
481 static mutex_t serialize_config_v1
= DEFAULTMUTEX
;
482 static mutex_t serialize_config
= DEFAULTMUTEX
;
485 syslog_warning(const char *dbase
)
488 "libc: bad lookup policy for %s in %s, using defaults..\n",
489 dbase
, __NSW_CONFIG_FILE
);
493 * Since we cannot call malloc() or lock any of the ordinary mutexes
494 * while we hold an lmutex_lock(), we open the file outside the lock
495 * and disable locking on the file; the latter is fine because we're
496 * reading the fp only from a single thread.
501 FILE *fp
= fopen(__NSW_CONFIG_FILE
, "rF");
504 if (_findbuf(fp
) == NULL
) {
513 struct __nsw_switchconfig_v1
*
514 __nsw_getconfig_v1(const char *dbase
, enum __nsw_parse_err
*errp
)
516 struct __nsw_switchconfig_v1
*cfp
, *retp
= NULL
;
517 int syslog_error
= 0;
522 lmutex_lock(&serialize_config_v1
);
524 if (cfp
= scrounge_cache_v1(dbase
)) {
525 *errp
= __NSW_CONF_PARSE_SUCCESS
;
526 lmutex_unlock(&serialize_config_v1
);
533 struct cons_cell_v1
*cp
= concell_list_v1
;
535 lmutex_unlock(&serialize_config_v1
);
536 /* open_conf() must be called w/o locks held */
537 if ((fp
= open_conf()) == NULL
) {
538 *errp
= __NSW_CONF_PARSE_NOFILE
;
541 lmutex_lock(&serialize_config_v1
);
543 if (cp
!= concell_list_v1
)
547 *errp
= __NSW_CONF_PARSE_NOPOLICY
;
548 while (linep
= fgets(lineq
, BUFSIZ
, fp
)) {
549 enum __nsw_parse_err line_err
;
550 char *tokenp
, *comment
;
553 * Ignore portion of line following the comment character '#'.
555 if ((comment
= strchr(linep
, '#')) != NULL
) {
559 * skip past blank lines.
560 * otherwise, cache as a struct switchconfig.
562 if ((*linep
== '\0') || isspace(*linep
)) {
565 if ((tokenp
= skip(&linep
, ':')) == NULL
) {
566 continue; /* ignore this line */
568 if (cfp
= scrounge_cache_v1(tokenp
)) {
569 continue; /* ? somehow this database is in the cache */
571 if (cfp
= _nsw_getoneconfig_v1(tokenp
, linep
, &line_err
)) {
572 (void) add_concell_v1(cfp
);
573 if (strcmp(cfp
->dbase
, dbase
) == 0) {
574 *errp
= __NSW_CONF_PARSE_SUCCESS
;
579 * Got an error on this line, if it is a system
580 * error we might as well give right now. If it
581 * is a parse error on the second entry of the
582 * database we are looking for and the first one
583 * was a good entry we end up logging the following
584 * syslog message and using a default policy instead.
586 if (line_err
== __NSW_CONF_PARSE_SYSERR
) {
587 *errp
= __NSW_CONF_PARSE_SYSERR
;
589 } else if (line_err
== __NSW_CONF_PARSE_NOPOLICY
&&
590 strcmp(tokenp
, dbase
) == 0) {
592 *errp
= __NSW_CONF_PARSE_NOPOLICY
;
596 * Else blithely ignore problems on this line and
597 * go ahead with the next line.
601 lmutex_unlock(&serialize_config_v1
);
603 * We have to drop the lock before calling fclose()/syslog().
607 syslog_warning(dbase
);
611 struct __nsw_switchconfig
*
612 __nsw_getconfig(const char *dbase
, enum __nsw_parse_err
*errp
)
614 struct __nsw_switchconfig
*cfp
, *retp
= NULL
;
615 int syslog_error
= 0;
620 lmutex_lock(&serialize_config
);
622 if (cfp
= scrounge_cache(dbase
)) {
623 *errp
= __NSW_CONF_PARSE_SUCCESS
;
624 lmutex_unlock(&serialize_config
);
631 struct cons_cell
*cp
= concell_list
;
632 /* open_conf() must be called w/o locks held */
633 lmutex_unlock(&serialize_config
);
634 if ((fp
= open_conf()) == NULL
) {
635 *errp
= __NSW_CONF_PARSE_NOFILE
;
638 lmutex_lock(&serialize_config
);
640 if (cp
!= concell_list
)
644 *errp
= __NSW_CONF_PARSE_NOPOLICY
;
645 while (linep
= fgets(lineq
, BUFSIZ
, fp
)) {
646 enum __nsw_parse_err line_err
;
647 char *tokenp
, *comment
;
650 * Ignore portion of line following the comment character '#'.
652 if ((comment
= strchr(linep
, '#')) != NULL
) {
656 * skip past blank lines.
657 * otherwise, cache as a struct switchconfig.
659 if ((*linep
== '\0') || isspace(*linep
)) {
662 if ((tokenp
= skip(&linep
, ':')) == NULL
) {
663 continue; /* ignore this line */
665 if (cfp
= scrounge_cache(tokenp
)) {
666 continue; /* ? somehow this database is in the cache */
668 if (cfp
= _nsw_getoneconfig(tokenp
, linep
, &line_err
)) {
669 (void) add_concell(cfp
);
670 if (strcmp(cfp
->dbase
, dbase
) == 0) {
671 *errp
= __NSW_CONF_PARSE_SUCCESS
;
676 * Got an error on this line, if it is a system
677 * error we might as well give right now. If it
678 * is a parse error on the second entry of the
679 * database we are looking for and the first one
680 * was a good entry we end up logging the following
681 * syslog message and using a default policy instead.
683 if (line_err
== __NSW_CONF_PARSE_SYSERR
) {
684 *errp
= __NSW_CONF_PARSE_SYSERR
;
686 } else if (line_err
== __NSW_CONF_PARSE_NOPOLICY
&&
687 strcmp(tokenp
, dbase
) == 0) {
689 *errp
= __NSW_CONF_PARSE_NOPOLICY
;
693 * Else blithely ignore problems on this line and
694 * go ahead with the next line.
698 lmutex_unlock(&serialize_config
);
700 * We have to drop the lock before calling fclose()/syslog().
704 syslog_warning(dbase
);
709 static struct __nsw_switchconfig_v1
*
710 scrounge_cache_v1(const char *dbase
)
712 struct cons_cell_v1
*cellp
= concell_list_v1
;
714 for (; cellp
; cellp
= cellp
->next
)
715 if (strcmp(dbase
, cellp
->sw
->dbase
) == 0)
720 static struct __nsw_switchconfig
*
721 scrounge_cache(const char *dbase
)
723 struct cons_cell
*cellp
= concell_list
;
725 for (; cellp
; cellp
= cellp
->next
)
726 if (strcmp(dbase
, cellp
->sw
->dbase
) == 0)
732 freeconf_v1(struct __nsw_switchconfig_v1
*cfp
)
736 libc_free(cfp
->dbase
);
738 struct __nsw_lookup_v1
*nex
, *cur
;
739 for (cur
= cfp
->lookups
; cur
; cur
= nex
) {
740 libc_free(cur
->service_name
);
750 freeconf(struct __nsw_switchconfig
*cfp
)
754 libc_free(cfp
->dbase
);
756 struct __nsw_lookup
*nex
, *cur
;
757 for (cur
= cfp
->lookups
; cur
; cur
= nex
) {
758 libc_free(cur
->service_name
);
768 __nsw_extended_action_v1(struct __nsw_lookup_v1
*lkp
, int err
)
770 struct __nsw_long_err
*lerrp
;
772 for (lerrp
= lkp
->long_errs
; lerrp
; lerrp
= lerrp
->next
) {
773 if (lerrp
->nsw_errno
== err
)
774 return (lerrp
->action
);
776 return (__NSW_CONTINUE
);
780 __nsw_extended_action(struct __nsw_lookup
*lkp
, int err
)
782 struct __nsw_long_err
*lerrp
;
784 for (lerrp
= lkp
->long_errs
; lerrp
; lerrp
= lerrp
->next
) {
785 if (lerrp
->nsw_errno
== err
)
786 return (lerrp
->action
);
788 return (__NSW_CONTINUE
);
792 /* give the next non-alpha character */
802 /* give the next non-space character */
807 while (*p
== ' ' || *p
== '\t')
813 * terminate the *cur pointed string by null only if it is
814 * followed by "key" surrounded by zero or more spaces and
815 * return value is the same as the original *cur pointer and
816 * *cur pointer is advanced to the first non {space, key} char
817 * followed by the key. Otherwise, return NULL and keep
821 skip(char **cur
, char key
)
827 tmp
= labelskip(*cur
);
831 *p
++ = '\0'; /* overwrite the key */
834 while (*p
== ' ' || *p
== '\t') {
835 tmpfound
= (*++p
== key
);
838 /* null terminate the return token */
840 p
++; /* skip the key */
845 return (NULL
); /* *cur unchanged */
850 /* add to the front: LRU */
852 add_concell_v1(struct __nsw_switchconfig_v1
*cfp
)
854 struct cons_cell_v1
*cp
;
858 if ((cp
= libc_malloc(sizeof (struct cons_cell_v1
))) == NULL
)
861 cp
->next
= concell_list_v1
;
862 concell_list_v1
= cp
;
866 /* add to the front: LRU */
868 add_concell(struct __nsw_switchconfig
*cfp
)
870 struct cons_cell
*cp
;
874 if ((cp
= libc_malloc(sizeof (struct cons_cell
))) == NULL
)
877 cp
->next
= concell_list
;
883 __nsw_freeconfig_v1(struct __nsw_switchconfig_v1
*conf
)
885 struct cons_cell_v1
*cellp
;
891 * Hacked to make life easy for the code in nss_common.c. Free conf
892 * iff it was created by calling _nsw_getoneconfig() directly
893 * rather than by calling nsw_getconfig.
895 lmutex_lock(&serialize_config_v1
);
896 for (cellp
= concell_list_v1
; cellp
; cellp
= cellp
->next
) {
897 if (cellp
->sw
== conf
) {
901 lmutex_unlock(&serialize_config_v1
);
903 /* Not in the cache; free it */
907 /* In the cache; don't free it */
913 __nsw_freeconfig(struct __nsw_switchconfig
*conf
)
915 struct cons_cell
*cellp
;
921 * Hacked to make life easy for the code in nss_common.c. Free conf
922 * iff it was created by calling _nsw_getoneconfig() directly
923 * rather than by calling nsw_getconfig.
925 lmutex_lock(&serialize_config
);
926 for (cellp
= concell_list
; cellp
; cellp
= cellp
->next
) {
927 if (cellp
->sw
== conf
) {
931 lmutex_unlock(&serialize_config
);
933 /* Not in the cache; free it */
937 /* In the cache; don't free it */
942 /* Return 1 if the string contains all digits, else return 0. */