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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
36 #define __NSS_PRIVATE_INTERFACE
37 #include "nsswitch_priv.h"
38 #undef __NSS_PRIVATE_INTERFACE
40 #include <nss_common.h>
41 #include <nss_dbdefs.h>
49 #include <sys/types.h>
55 #include <getxby_door.h>
58 * This is a gross hack because including stdiom.h here causes redefinition
59 * of __FILE conflicts.
61 extern __FILE __iob
[_NFILE
];
64 * configurable values for default buffer sizes
68 * PSARC/2005/133 updated the buffering mechanisms to handle
69 * up to 2^64 buffering. But sets a practical limit of 512*1024.
70 * The expectation is the practical limit will be dynamic from
71 * nscd. For now, set the group limit to this value.
74 #define NSS_BUFLEN_PRACTICAL (512*1024)
76 static size_t __nss_buflen_group
= NSS_BUFLEN_PRACTICAL
;
77 static size_t __nss_buflen_default
= NSS_BUFLEN_DOOR
;
80 * policy component function interposing definitions:
81 * nscd if so desired can interpose it's own switch functions over
82 * the internal unlocked counterparts. This will allow nscd to replace
83 * the switch policy state engine with one that uses it's internal
85 * Only nscd can change this through it's use of nss_config.
86 * The golden rule is: ptr == NULL checking is used in the switch to
87 * see if a function was interposed. But nscd is responsible for seeing
88 * that mutex locking to change the values are observed when the data is
89 * changed. Especially if it happens > once. The switch does not lock
90 * the pointer with mutexs.
96 void (*nss_delete_fp
)(nss_db_root_t
*rootp
);
97 nss_status_t (*nss_search_fp
)(nss_db_root_t
*rootp
,
98 nss_db_initf_t initf
, int search_fnum
,
100 void (*nss_setent_u_fp
)(nss_db_root_t
*,
101 nss_db_initf_t
, nss_getent_t
*);
102 nss_status_t (*nss_getent_u_fp
)(nss_db_root_t
*,
103 nss_db_initf_t
, nss_getent_t
*, void *);
104 void (*nss_endent_u_fp
)(nss_db_root_t
*,
105 nss_db_initf_t
, nss_getent_t
*);
106 void (*end_iter_u_fp
)(nss_db_root_t
*rootp
,
107 struct nss_getent_context
*contextp
);
111 static mutex_t nss_policyf_lock
= DEFAULTMUTEX
;
112 static nss_policyf_t nss_policyf_ptrs
=
116 * nsswitch db_root state machine definitions:
117 * The golden rule is: if you hold a pointer to an nss_db_state struct and
118 * you don't hold the lock, you'd better have incremented the refcount
119 * while you held the lock; otherwise, it may vanish or change
120 * significantly when you least expect it.
122 * The pointer in nss_db_root_t is one such, so the reference count >= 1.
123 * Ditto the pointer in struct nss_getent_context.
127 * State for one nsswitch database (e.g. "passwd", "hosts")
129 struct nss_db_state
{
130 nss_db_root_t orphan_root
; /* XXX explain */
131 unsigned refcount
; /* One for the pointer in */
132 /* nss_db_root_t, plus one */
133 /* for each active thread. */
135 struct __nsw_switchconfig_v1
*config
;
136 int max_src
; /* is == config->num_lookups */
137 struct nss_src_state
*src
; /* Pointer to array[max_src] */
141 * State for one of the sources (e.g. "nis", "compat") for a database
143 struct nss_src_state
{
144 struct __nsw_lookup_v1
*lkp
;
147 int n_waiting
; /* ... on wanna_be */
150 nss_backend_t
*single
; /* Efficiency hack for common case */
151 /* when limit_dead_backends == 1 */
152 nss_backend_t
**multi
; /* array[limit_dead_backends] of */
153 } dormant
; /* pointers to dormant backends */
154 nss_backend_constr_t be_constr
;
155 nss_backend_finder_t
*finder
;
159 static struct nss_db_state
*_nss_db_state_constr(nss_db_initf_t
);
160 void _nss_db_state_destr(struct nss_db_state
*);
162 /* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */
164 #define NSS_ROOTLOCK(r, sp) (cancel_safe_mutex_lock(&(r)->lock), \
167 #define NSS_UNLOCK(r) (cancel_safe_mutex_unlock(&(r)->lock))
169 #define NSS_CHECKROOT(rp, s) ((s) != (*(rp))->s && \
170 (cancel_safe_mutex_unlock(&(*(rp))->lock), \
171 cancel_safe_mutex_lock(&(s)->orphan_root.lock), \
172 *(rp) = &(s)->orphan_root))
174 #define NSS_RELOCK(rp, s) (cancel_safe_mutex_lock(&(*(rp))->lock), \
175 NSS_CHECKROOT(rp, s))
177 #define NSS_STATE_REF_u(s) (++(s)->refcount)
179 #define NSS_UNREF_UNLOCK(r, s) (--(s)->refcount != 0 \
180 ? ((void)NSS_UNLOCK(r)) \
181 : ((void)NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
183 #define NSS_LOCK_CHECK(r, f, sp) (NSS_ROOTLOCK((r), (sp)), \
185 (r->s = *(sp) = _nss_db_state_constr(f)))
186 /* === In the future, NSS_LOCK_CHECK() may also have to check that */
187 /* === the config info hasn't changed (by comparing version numbers) */
191 * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
192 * This remains for backwards compatibility. But generally nscd will
193 * decide if/how this gets used.
195 static int checked_env
= 0; /* protected by "rootlock" */
197 /* allowing __nss_debug_file to be set could be a security hole. */
198 FILE *__nss_debug_file
= &__iob
[1];
199 int __nss_debug_eng_loop
;
201 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
202 /* allowing __nis_debug_file to be set could be a security hole. */
203 FILE *__nis_debug_file
= &__iob
[1];
204 int __nis_debug_bind
;
206 int __nis_debug_calls
;
208 char *__nis_preftype
;
209 char *__nis_server
; /* if set, use only this server for binding */
223 static struct option nss_options
[] = {
225 /* allowing __nss_debug_file to be set could be a security hole. */
226 { "debug_file", OPT_FILE
, &__nss_debug_file
},
228 { "debug_eng_loop", OPT_INT
, &__nss_debug_eng_loop
},
232 static struct option nis_options
[] = {
234 /* allowing __nis_debug_file to be set could be a security hole. */
235 { "debug_file", OPT_FILE
, &__nis_debug_file
},
237 { "debug_bind", OPT_INT
, &__nis_debug_bind
},
238 { "debug_rpc", OPT_INT
, &__nis_debug_rpc
},
239 { "debug_calls", OPT_INT
, &__nis_debug_calls
},
240 { "server", OPT_STRING
, &__nis_server
},
241 { "pref_srvr", OPT_STRING
, &__nis_prefsrv
},
242 { "pref_type", OPT_STRING
, &__nis_preftype
},
247 * switch configuration parameter "database" definitions:
248 * The switch maintains a simmple read/write parameter database
249 * that nscd and the switch components can use to communicate
250 * nscd data to other components for configuration or out of band
251 * [IE no in the context of a getXbyY or putXbyY operation] data.
252 * The data passed are pointers to a lock data buffer and a length.
253 * Use of this is treated as SunwPrivate between nscd and the switch
254 * unless other wise stated.
257 typedef struct nss_cfgparam
{
264 typedef struct nss_cfglist
{
266 nss_cfgparam_t
*list
;
271 #define NSS_CFG_INCR 16
273 static nss_cfglist_t
*nss_cfg
= NULL
;
274 static int nss_cfgcount
= 0;
275 static int nss_cfgmax
= 0;
276 static mutex_t nss_cfglock
= DEFAULTMUTEX
;
278 static int nss_cfg_policy_init();
281 * A config parameters are in the form component:parameter
282 * as in: nss:parameter - switch (internal FE/policy/BE) parameter
283 * nscd:param - nscd application parameter
284 * ldap:param - nss_ldap BE parameter
285 * passwd:param - get/put passwd FE parameter
288 #define NSS_CONFIG_BRK ':'
291 * The policy components initial parameter list
293 static nss_config_t nss_policy_params
[] = {
294 { "nss:policyfunc", NSS_CONFIG_ADD
, &nss_policyf_lock
,
295 (void *)&nss_policyf_ptrs
, (size_t)sizeof (nss_policyf_t
) },
296 { NULL
, NSS_CONFIG_ADD
, (mutex_t
*)NULL
, NULL
, 0 },
300 * NSS parameter configuration routines
303 /* compare config name (component:parameter) to a component name */
305 nss_cfgcn_cmp(const char *cfgname
, const char *compname
)
310 /* this code assumes valid pointers */
311 if ((c
= strchr(cfgname
, NSS_CONFIG_BRK
)) == NULL
)
313 len
= (size_t)(c
- cfgname
);
314 len2
= strlen(compname
);
317 return (strncmp(cfgname
, compname
, len
));
320 /* init configuration arena */
327 /* First time caller? */
328 if (nss_cfg
!= NULL
) {
333 /* Initialize internal tables */
334 lmutex_lock(&nss_cfglock
);
335 if (nss_cfg
!= NULL
) {
336 lmutex_unlock(&nss_cfglock
);
340 cfg
= libc_malloc(NSS_CFG_INCR
* sizeof (nss_cfglist_t
));
343 lmutex_unlock(&nss_cfglock
);
346 for (i
= 0; i
< NSS_CFG_INCR
; i
++) {
347 cfg
[i
].list
= libc_malloc(
348 NSS_CFG_INCR
* sizeof (nss_cfgparam_t
));
349 if (cfg
[i
].list
== NULL
) {
351 libc_free(cfg
[i
].list
);
354 lmutex_unlock(&nss_cfglock
);
357 cfg
[i
].max
= NSS_CFG_INCR
;
359 nss_cfgmax
= NSS_CFG_INCR
;
362 lmutex_unlock(&nss_cfglock
);
364 /* Initialize Policy Engine values */
365 if (nss_cfg_policy_init() < 0) {
371 /* find the name'd component list - create it if non-existent */
372 static nss_cfglist_t
*
373 nss_cfgcomp_get(char *name
, int add
)
380 /* Make sure system is init'd */
381 if (nss_cfg_init() < 0)
382 return ((nss_cfglist_t
*)NULL
);
384 /* and check component:name validity */
385 if (name
== NULL
|| (c
= strchr(name
, NSS_CONFIG_BRK
)) == NULL
)
386 return ((nss_cfglist_t
*)NULL
);
388 lmutex_lock(&nss_cfglock
);
390 for (i
= 0; i
< nss_cfgcount
; i
++) {
391 if (next
->name
&& nss_cfgcn_cmp(name
, next
->name
) == 0) {
392 lmutex_unlock(&nss_cfglock
);
398 lmutex_unlock(&nss_cfglock
);
402 /* not found, create a fresh one */
403 if (nss_cfgcount
>= nss_cfgmax
) {
405 nsize
= (nss_cfgmax
+ NSS_CFG_INCR
) * sizeof (nss_cfgparam_t
);
406 next
= (nss_cfglist_t
*)libc_realloc(nss_cfg
, nsize
);
409 lmutex_unlock(&nss_cfglock
);
410 return ((nss_cfglist_t
*)NULL
);
412 (void) memset((next
+ nss_cfgcount
), '\0',
413 NSS_CFG_INCR
* sizeof (nss_cfglist_t
));
414 nss_cfgmax
+= NSS_CFG_INCR
;
417 next
= nss_cfg
+ nss_cfgcount
;
418 len
= (size_t)(c
- name
) + 1;
419 if ((next
->name
= libc_malloc(len
)) == NULL
) {
421 lmutex_unlock(&nss_cfglock
);
422 return ((nss_cfglist_t
*)NULL
);
425 (void) strlcpy(next
->name
, name
, len
);
426 lmutex_unlock(&nss_cfglock
);
430 /* find the name'd parameter - create it if non-existent */
431 static nss_cfgparam_t
*
432 nss_cfgparam_get(char *name
, int add
)
435 nss_cfgparam_t
*next
;
439 if ((comp
= nss_cfgcomp_get(name
, add
)) == NULL
)
440 return ((nss_cfgparam_t
*)NULL
);
441 lmutex_lock(&nss_cfglock
);
444 for (i
= 0; i
< count
; i
++) {
445 if (next
->name
&& strcmp(name
, next
->name
) == 0) {
446 lmutex_unlock(&nss_cfglock
);
452 lmutex_unlock(&nss_cfglock
);
456 /* not found, create a fresh one */
457 if (count
>= comp
->max
) {
459 nsize
= (comp
->max
+ NSS_CFG_INCR
) * sizeof (nss_cfgparam_t
);
460 next
= (nss_cfgparam_t
*)libc_realloc(comp
->list
, nsize
);
463 lmutex_unlock(&nss_cfglock
);
464 return ((nss_cfgparam_t
*)NULL
);
466 comp
->max
+= NSS_CFG_INCR
;
469 next
= comp
->list
+ comp
->count
;
470 if ((next
->name
= libc_strdup(name
)) == NULL
) {
472 lmutex_unlock(&nss_cfglock
);
473 return ((nss_cfgparam_t
*)NULL
);
476 lmutex_unlock(&nss_cfglock
);
480 /* find the name'd parameter - delete it if it exists */
482 nss_cfg_del(nss_config_t
*cfgp
)
486 nss_cfgparam_t
*next
, *cur
;
489 /* exit if component name does not already exist */
490 if ((name
= cfgp
->name
) == NULL
||
491 (comp
= nss_cfgcomp_get(name
, 0)) == NULL
)
495 lmutex_lock(&nss_cfglock
);
498 for (i
= 0; i
< count
; i
++) {
499 if (next
->name
&& strcmp(name
, next
->name
) == 0) {
500 break; /* found it... */
505 /* not found, already deleted */
506 lmutex_unlock(&nss_cfglock
);
510 /* copy down the remaining parameters, and clean up */
511 /* don't try to clean up component tables */
514 for (j
= i
+1; j
< count
; j
++) {
519 /* erase the last one */
521 libc_free(cur
->name
);
524 cur
->lock
= (mutex_t
*)NULL
;
528 lmutex_unlock(&nss_cfglock
);
532 nss_cfg_get(nss_config_t
*next
)
534 nss_cfgparam_t
*param
;
537 if ((param
= nss_cfgparam_get(next
->name
, 0)) == NULL
)
539 next
->lock
= param
->lock
;
540 next
->buffer
= param
->buffer
;
541 next
->length
= param
->length
;
546 nss_cfg_put(nss_config_t
*next
, int add
)
548 nss_cfgparam_t
*param
;
551 if ((param
= nss_cfgparam_get(next
->name
, add
)) == NULL
)
553 param
->lock
= next
->lock
;
554 param
->buffer
= next
->buffer
;
555 param
->length
= next
->length
;
560 * Policy engine configurator - set and get interface
561 * argument is a NULL terminated list of set/get requests
562 * with input/result buffers and lengths. nss_cname is the
563 * specifier of a set or get operation and the property being
564 * managed. The intent is limited functions and expandability.
568 nss_config(nss_config_t
**plist
, int cnt
)
573 /* interface is only available to nscd */
574 if (_nsc_proc_is_cache() <= 0) {
575 return (NSS_UNAVAIL
);
577 if (plist
== NULL
|| cnt
<= 0)
578 return (NSS_SUCCESS
);
579 for (i
= 0; i
< cnt
; i
++) {
583 if (next
->name
== NULL
) {
589 /* get current lock/buffer/length fields */
590 if (nss_cfg_get(next
) < 0) {
595 /* set new lock/buffer/length fields */
596 if (nss_cfg_put(next
, 0) < 0) {
601 /* add parameter & set new lock/buffer/length fields */
602 if (nss_cfg_put(next
, 1) < 0) {
606 case NSS_CONFIG_DELETE
:
607 /* delete parameter - should always work... */
610 case NSS_CONFIG_LIST
:
616 return (NSS_SUCCESS
);
620 * This routine is called immediately after nss_cfg_init but prior to
621 * any commands from nscd being processed. The intent here is to
622 * initialize the nss:* parameters allowed by the policy component
623 * so that nscd can then proceed and modify them if so desired.
625 * We know we can only get here if we are nscd so we can skip the
630 nss_cfg_policy_init()
632 nss_config_t
*next
= &nss_policy_params
[0];
634 for (; next
&& next
->name
!= NULL
; next
++) {
635 if (nss_cfg_put(next
, 1) < 0)
642 * NSS_OPTION & NIS_OPTION environment variable functions
647 set_option(struct option
*opt
, char *name
, char *val
)
655 for (; opt
->name
; opt
++) {
656 if (strcmp(name
, opt
->name
) == 0) {
659 p
= libc_strdup(val
);
660 *((char **)opt
->address
) = p
;
664 if (strcmp(val
, "") == 0)
668 *((int *)opt
->address
) = n
;
672 fp
= fopen(val
, "wF");
673 *((FILE **)opt
->address
) = fp
;
684 __parse_environment(struct option
*opt
, char *p
)
697 while (*p
&& *p
!= '=' && !isspace(*p
))
700 * play it safe and keep it simple, bail if an opt name
703 if ((p
-base
) >= sizeof (optname
))
706 (void) strncpy(optname
, base
, p
-base
);
707 optname
[p
-base
] = '\0';
712 while (*p
&& !isspace(*p
))
715 * play it safe and keep it simple, bail if an opt
718 if ((p
-base
) >= sizeof (optval
))
721 (void) strncpy(optval
, base
, p
-base
);
722 optval
[p
-base
] = '\0';
727 set_option(opt
, optname
, optval
);
733 nss_get_environment()
737 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
738 p
= getenv("NSS_OPTIONS");
741 __parse_environment(nss_options
, p
);
745 * sole external routine called from libnsl/nis/cache/cache_api.cc in the
746 * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
747 * Only after checking "checked_env" (which must be done with mutex
748 * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
751 __nis_get_environment()
755 p
= getenv("NIS_OPTIONS");
758 __parse_environment(nis_options
, p
);
763 * Switch policy component backend state machine functions
766 static nss_backend_t
*
767 nss_get_backend_u(nss_db_root_t
**rootpp
, struct nss_db_state
*s
, int n_src
)
769 struct nss_src_state
*src
= &s
->src
[n_src
];
774 if (src
->n_dormant
> 0) {
777 if (s
->p
.max_dormant_per_src
== 1) {
778 be
= src
->dormant
.single
;
780 be
= src
->dormant
.multi
[src
->n_dormant
];
785 if (src
->be_constr
== 0) {
786 nss_backend_finder_t
*bf
;
788 for (bf
= s
->p
.finders
; bf
!= 0; bf
= bf
->next
) {
789 nss_backend_constr_t c
;
791 c
= (*bf
->lookup
) (bf
->lookup_priv
, s
->p
.name
,
792 src
->lkp
->service_name
, &src
->finder_priv
);
799 if (src
->be_constr
== 0) {
800 /* Couldn't find the backend anywhere */
806 if (src
->n_active
< s
->p
.max_active_per_src
) {
807 be
= (*src
->be_constr
)(s
->p
.name
,
808 src
->lkp
->service_name
, 0 /* === unimplemented */);
812 } else if (src
->n_active
== 0) {
813 /* Something's wrong; we should be */
814 /* able to create at least one */
815 /* instance of the backend */
819 * Else it's odd that we can't create another backend
820 * instance, but don't sweat it; instead, queue for
821 * an existing backend instance.
826 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE
,
828 (void) cond_wait(&src
->wanna_be
, &(*rootpp
)->lock
);
829 (void) pthread_setcancelstate(cancel_state
, NULL
);
830 NSS_CHECKROOT(rootpp
, s
);
834 * Loop and see whether things got better for us, or whether
835 * someone else got scheduled first and we have to try
838 * === ?? Should count iterations, assume bug if many ??
845 nss_put_backend_u(struct nss_db_state
*s
, int n_src
, nss_backend_t
*be
)
847 struct nss_src_state
*src
= &s
->src
[n_src
];
855 if (src
->n_dormant
< s
->p
.max_dormant_per_src
) {
856 if (s
->p
.max_dormant_per_src
== 1) {
857 src
->dormant
.single
= be
;
859 } else if (src
->dormant
.multi
!= 0 ||
860 (src
->dormant
.multi
=
861 libc_malloc(s
->p
.max_dormant_per_src
*
862 sizeof (nss_backend_t
*))) != NULL
) {
863 src
->dormant
.multi
[src
->n_dormant
] = be
;
866 /* Can't store it, so toss it */
867 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_DESTRUCTOR
, 0);
870 /* We've stored as many as we want, so toss it */
871 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_DESTRUCTOR
, 0);
873 if (src
->n_waiting
> 0) {
874 (void) cond_signal(&src
->wanna_be
);
878 static struct nss_db_state
*
879 _nss_db_state_constr(nss_db_initf_t initf
)
881 struct nss_db_state
*s
;
882 struct __nsw_switchconfig_v1
*config
= 0;
883 struct __nsw_lookup_v1
*lkp
;
884 enum __nsw_parse_err err
;
885 const char *config_name
;
888 if ((s
= libc_malloc(sizeof (*s
))) == 0) {
891 (void) mutex_init(&s
->orphan_root
.lock
, USYNC_THREAD
, 0);
893 s
->p
.max_active_per_src
= 10;
894 s
->p
.max_dormant_per_src
= 1;
895 s
->p
.finders
= nss_default_finders
;
897 if (s
->p
.name
== 0) {
898 _nss_db_state_destr(s
);
903 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
904 nss_get_environment();
908 config_name
= s
->p
.config_name
? s
->p
.config_name
: s
->p
.name
;
909 if (! (s
->p
.flags
& NSS_USE_DEFAULT_CONFIG
)) {
910 config
= __nsw_getconfig_v1(config_name
, &err
);
911 /* === ? test err ? */
914 /* getconfig failed, or frontend demanded default config */
916 char *str
; /* _nsw_getoneconfig() clobbers its argument */
918 if ((str
= libc_strdup(s
->p
.default_config
)) != 0) {
919 config
= _nsw_getoneconfig_v1(config_name
, str
, &err
);
923 _nss_db_state_destr(s
);
928 if ((s
->max_src
= config
->num_lookups
) <= 0 ||
929 (s
->src
= libc_malloc(s
->max_src
* sizeof (*s
->src
))) == 0) {
930 _nss_db_state_destr(s
);
933 for (n_src
= 0, lkp
= config
->lookups
;
934 n_src
< s
->max_src
; n_src
++, lkp
= lkp
->next
) {
935 s
->src
[n_src
].lkp
= lkp
;
936 (void) cond_init(&s
->src
[n_src
].wanna_be
, USYNC_THREAD
, 0);
943 _nss_src_state_destr(struct nss_src_state
*src
, int max_dormant
)
945 if (max_dormant
== 1) {
946 if (src
->n_dormant
!= 0) {
947 (void) NSS_INVOKE_DBOP(src
->dormant
.single
,
948 NSS_DBOP_DESTRUCTOR
, 0);
950 } else if (src
->dormant
.multi
!= 0) {
953 for (n
= 0; n
< src
->n_dormant
; n
++) {
954 (void) NSS_INVOKE_DBOP(src
->dormant
.multi
[n
],
955 NSS_DBOP_DESTRUCTOR
, 0);
957 libc_free(src
->dormant
.multi
);
960 /* cond_destroy(&src->wanna_be); */
962 if (src
->finder
!= 0) {
963 (*src
->finder
->delete)(src
->finder_priv
, src
->be_constr
);
968 * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
969 * nss_db_state structure.
970 * Assumes that s has been ref-counted down to zero (in particular,
971 * rootp->s has already been dealt with).
973 * Nobody else holds a pointer to *s (if they did, refcount != 0),
974 * so we can clean up state *after* we drop the lock (also, by the
975 * time we finish freeing the state structures, the lock may have
976 * ceased to exist -- if we were using the orphan_root).
980 _nss_db_state_destr(struct nss_db_state
*s
)
986 /* === mutex_destroy(&s->orphan_root.lock); */
987 if (s
->p
.cleanup
!= 0) {
988 (*s
->p
.cleanup
)(&s
->p
);
990 if (s
->config
!= 0) {
991 (void) __nsw_freeconfig_v1(s
->config
);
996 for (n_src
= 0; n_src
< s
->max_src
; n_src
++) {
997 _nss_src_state_destr(&s
->src
[n_src
],
998 s
->p
.max_dormant_per_src
);
1007 * _nss_status_vec() returns a bit vector of all status codes returned during
1008 * the most recent call to nss_search().
1009 * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1011 * These functions are private. Don't use them externally without discussing
1012 * it with the switch maintainers.
1017 return (tsdalloc(_T_NSS_STATUS_VEC
, sizeof (uint_t
), NULL
));
1021 _nss_status_vec(void)
1023 unsigned int *status_vec_p
= _nss_status_vec_p();
1025 return ((status_vec_p
!= NULL
) ? *status_vec_p
: (1 << NSS_UNAVAIL
));
1029 output_loop_diag_a(int n
,
1031 struct __nsw_lookup_v1
*lkp
)
1033 (void) fprintf(__nss_debug_file
,
1034 "NSS_retry(%d): '%s': trying '%s' ... ",
1035 n
, dbase
, lkp
->service_name
);
1036 (void) fflush(__nss_debug_file
);
1041 output_loop_diag_b(nss_status_t res
,
1042 struct __nsw_lookup_v1
*lkp
)
1044 (void) fprintf(__nss_debug_file
, "result=");
1047 (void) fprintf(__nss_debug_file
, "SUCCESS");
1050 (void) fprintf(__nss_debug_file
, "NOTFOUND");
1053 (void) fprintf(__nss_debug_file
, "UNAVAIL");
1056 (void) fprintf(__nss_debug_file
, "TRYAGAIN");
1058 case NSS_NISSERVDNS_TRYAGAIN
:
1059 (void) fprintf(__nss_debug_file
, "NISSERVDNS_TRYAGAIN");
1062 (void) fprintf(__nss_debug_file
, "undefined");
1064 (void) fprintf(__nss_debug_file
, ", action=");
1065 switch (lkp
->actions
[res
]) {
1066 case __NSW_CONTINUE
:
1067 (void) fprintf(__nss_debug_file
, "CONTINUE");
1070 (void) fprintf(__nss_debug_file
, "RETURN");
1072 case __NSW_TRYAGAIN_FOREVER
:
1073 (void) fprintf(__nss_debug_file
, "TRYAGAIN_FOREVER");
1075 case __NSW_TRYAGAIN_NTIMES
:
1076 (void) fprintf(__nss_debug_file
, "TRYAGAIN_NTIMES (N=%d)",
1079 case __NSW_TRYAGAIN_PAUSED
:
1080 (void) fprintf(__nss_debug_file
, "TRYAGAIN_PAUSED");
1083 (void) fprintf(__nss_debug_file
, "undefined");
1085 (void) fprintf(__nss_debug_file
, "\n");
1088 #define NSS_BACKOFF(n, b, t) \
1089 ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1092 retry_test(nss_status_t res
, int n
, struct __nsw_lookup_v1
*lkp
)
1094 if (res
!= NSS_TRYAGAIN
&& res
!= NSS_NISSERVDNS_TRYAGAIN
) {
1095 if (res
== NSS_SUCCESS
) {
1096 __NSW_UNPAUSE_ACTION(lkp
->actions
[__NSW_TRYAGAIN
]);
1097 __NSW_UNPAUSE_ACTION(
1098 lkp
->actions
[__NSW_NISSERVDNS_TRYAGAIN
]);
1103 if ((res
== NSS_TRYAGAIN
&&
1104 lkp
->actions
[__NSW_TRYAGAIN
] == __NSW_TRYAGAIN_FOREVER
) ||
1105 (res
== NSS_NISSERVDNS_TRYAGAIN
&&
1106 lkp
->actions
[__NSW_NISSERVDNS_TRYAGAIN
] == __NSW_TRYAGAIN_FOREVER
))
1109 if (res
== NSS_TRYAGAIN
&&
1110 lkp
->actions
[__NSW_TRYAGAIN
] == __NSW_TRYAGAIN_NTIMES
)
1111 if (n
<= lkp
->max_retries
)
1114 lkp
->actions
[__NSW_TRYAGAIN
] = __NSW_TRYAGAIN_PAUSED
;
1118 if (res
== NSS_NISSERVDNS_TRYAGAIN
&&
1119 lkp
->actions
[__NSW_NISSERVDNS_TRYAGAIN
] == __NSW_TRYAGAIN_NTIMES
)
1120 if (n
<= lkp
->max_retries
)
1123 lkp
->actions
[__NSW_NISSERVDNS_TRYAGAIN
] =
1124 __NSW_TRYAGAIN_PAUSED
;
1132 * Switch policy component functional interfaces
1136 nss_delete(nss_db_root_t
*rootp
)
1138 struct nss_db_state
*s
;
1140 /* no name service cache daemon divert here */
1141 /* local nss_delete decrements state reference counts */
1142 /* and may free up opened switch resources. */
1144 NSS_ROOTLOCK(rootp
, &s
);
1149 NSS_UNREF_UNLOCK(rootp
, s
);
1154 nss_search(nss_db_root_t
*rootp
, nss_db_initf_t initf
, int search_fnum
,
1157 nss_status_t res
= NSS_UNAVAIL
;
1158 struct nss_db_state
*s
;
1160 unsigned int *status_vec_p
;
1162 /* name service cache daemon divert */
1163 res
= _nsc_search(rootp
, initf
, search_fnum
, search_args
);
1164 if (res
!= NSS_TRYLOCAL
)
1167 /* fall through - process locally */
1168 errno
= 0; /* just in case ... */
1170 status_vec_p
= _nss_status_vec_p();
1172 if (status_vec_p
== NULL
) {
1173 return (NSS_UNAVAIL
);
1177 NSS_LOCK_CHECK(rootp
, initf
, &s
);
1184 for (n_src
= 0; n_src
< s
->max_src
; n_src
++) {
1186 nss_backend_op_t funcp
;
1189 if ((be
= nss_get_backend_u(&rootp
, s
, n_src
)) != 0) {
1190 if ((funcp
= NSS_LOOKUP_DBOP(be
, search_fnum
)) != 0) {
1192 int no_backoff
= 19;
1193 int max_backoff
= 5; /* seconds */
1197 * Backend operation may take a while;
1198 * drop the lock so we don't serialize
1199 * more than necessary.
1203 /* After several tries, backoff... */
1204 if (n_loop
> no_backoff
) {
1205 if (__nss_debug_eng_loop
> 1)
1209 "sleeping %d ...\n",
1214 (void) sleep(NSS_BACKOFF(n_loop
,
1215 no_backoff
, max_backoff
));
1218 if (__nss_debug_eng_loop
)
1219 output_loop_diag_a(n_loop
,
1224 res
= (*funcp
)(be
, search_args
);
1225 NSS_RELOCK(&rootp
, s
);
1227 if (__nss_debug_eng_loop
)
1228 output_loop_diag_b(res
,
1230 } while (retry_test(res
, n_loop
,
1231 s
->src
[n_src
].lkp
));
1233 nss_put_backend_u(s
, n_src
, be
);
1235 *status_vec_p
|= (1 << res
);
1236 if (__NSW_ACTION_V1(s
->src
[n_src
].lkp
, res
) == __NSW_RETURN
) {
1237 if (__nss_debug_eng_loop
)
1238 (void) fprintf(__nss_debug_file
,
1239 "NSS: '%s': return.\n",
1243 if (__nss_debug_eng_loop
)
1244 (void) fprintf(__nss_debug_file
,
1245 "NSS: '%s': continue ...\n",
1248 NSS_UNREF_UNLOCK(rootp
, s
);
1254 * Start of nss_{setent|getent|endent}
1258 * State (here called "context") for one setent/getent.../endent sequence.
1259 * In principle there could be multiple contexts active for a single
1260 * database; in practice, since Posix and UI have helpfully said that
1261 * getent() state is global rather than, say, per-thread or user-supplied,
1262 * we have at most one of these per nss_db_state.
1263 * XXX ? Is this statement still true?
1265 * NSS2 - a client's context is maintained as a cookie delivered by and
1266 * passed to nscd. The cookie is a 64 bit (nssuint_t) unique opaque value
1269 * NSCD_NEW_COOKIE - cookie value uninitialized
1270 * NSCD_LOCAL_COOKIE - setent is a local setent
1271 * all other - NSCD unique opaque id for this setent
1272 * A client's context is also associated with a seq_num. This is a nscd
1273 * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1274 * to validate the sequencing of the context. The client treats this as
1275 * a pass through value.
1277 * XXX ?? Use Cookie as cross-check info so that we can detect an
1278 * nss_context that missed an nss_delete() or similar.
1281 struct nss_getent_context
{
1282 int n_src
; /* >= max_src ==> end of sequence */
1284 struct nss_db_state
*s
;
1287 nssuint_t cookie_setent
;
1288 nss_db_params_t param
;
1291 static void nss_setent_u(nss_db_root_t
*,
1294 static nss_status_t
nss_getent_u(nss_db_root_t
*,
1298 static void nss_endent_u(nss_db_root_t
*,
1303 nss_setent(nss_db_root_t
*rootp
, nss_db_initf_t initf
, nss_getent_t
*contextpp
)
1305 if (contextpp
== 0) {
1308 cancel_safe_mutex_lock(&contextpp
->lock
);
1309 nss_setent_u(rootp
, initf
, contextpp
);
1310 cancel_safe_mutex_unlock(&contextpp
->lock
);
1314 nss_getent(nss_db_root_t
*rootp
, nss_db_initf_t initf
, nss_getent_t
*contextpp
,
1317 nss_status_t status
;
1319 if (contextpp
== 0) {
1320 return (NSS_UNAVAIL
);
1322 cancel_safe_mutex_lock(&contextpp
->lock
);
1323 status
= nss_getent_u(rootp
, initf
, contextpp
, args
);
1324 cancel_safe_mutex_unlock(&contextpp
->lock
);
1329 nss_endent(nss_db_root_t
*rootp
, nss_db_initf_t initf
, nss_getent_t
*contextpp
)
1331 if (contextpp
== 0) {
1334 cancel_safe_mutex_lock(&contextpp
->lock
);
1335 nss_endent_u(rootp
, initf
, contextpp
);
1336 cancel_safe_mutex_unlock(&contextpp
->lock
);
1340 * Each of the _u versions of the nss interfaces assume that the context
1341 * lock is held. No need to divert to nscd. Private to local sequencing.
1345 end_iter_u(nss_db_root_t
*rootp
, struct nss_getent_context
*contextp
)
1347 struct nss_db_state
*s
;
1352 n_src
= contextp
->n_src
;
1356 if (n_src
< s
->max_src
&& be
!= 0) {
1357 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_ENDENT
, 0);
1358 NSS_RELOCK(&rootp
, s
);
1359 nss_put_backend_u(s
, n_src
, be
);
1360 contextp
->be
= 0; /* Should be unnecessary, but hey */
1361 NSS_UNREF_UNLOCK(rootp
, s
);
1368 nss_setent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
1369 nss_getent_t
*contextpp
)
1371 nss_status_t status
;
1372 struct nss_db_state
*s
;
1373 struct nss_getent_context
*contextp
;
1377 /* setup process wide context while locked */
1378 if ((contextp
= contextpp
->ctx
) == 0) {
1379 if ((contextp
= libc_malloc(sizeof (*contextp
))) == 0) {
1382 contextpp
->ctx
= contextp
;
1383 contextp
->cookie
= NSCD_NEW_COOKIE
; /* cookie init */
1384 contextp
->seq_num
= 0; /* seq_num init */
1388 if (contextp
->cookie
!= NSCD_LOCAL_COOKIE
)
1389 contextp
->cookie
= NSCD_NEW_COOKIE
;
1392 /* name service cache daemon divert */
1393 if (contextp
->cookie
== NSCD_NEW_COOKIE
) {
1394 status
= _nsc_setent_u(rootp
, initf
, contextpp
);
1395 if (status
!= NSS_TRYLOCAL
)
1399 /* fall through - process locally */
1401 NSS_LOCK_CHECK(rootp
, initf
, &s
);
1403 /* Couldn't set up state, so quit */
1405 /* ==== is there any danger of not having done an */
1406 /* end_iter() here, and hence of losing backends? */
1408 libc_free(contextp
);
1415 n_src
= contextp
->n_src
;
1417 if (n_src
== 0 && be
!= 0) {
1419 * Optimization: don't do endent, don't change
1420 * backends, just do the setent. Look Ma, no locks
1421 * (nor any context that needs updating).
1423 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_SETENT
, 0);
1426 if (n_src
< s
->max_src
&& be
!= 0) {
1427 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_ENDENT
, 0);
1428 NSS_RELOCK(&rootp
, s
);
1429 nss_put_backend_u(s
, n_src
, be
);
1430 contextp
->be
= 0; /* Play it safe */
1432 NSS_RELOCK(&rootp
, s
);
1435 for (n_src
= 0, be
= 0; n_src
< s
->max_src
&&
1436 (be
= nss_get_backend_u(&rootp
, s
, n_src
)) == 0; n_src
++) {
1441 contextp
->n_src
= n_src
;
1445 /* Things are broken enough that we can't do setent/getent */
1446 nss_endent_u(rootp
, initf
, contextpp
);
1449 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_SETENT
, 0);
1453 nss_getent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
1454 nss_getent_t
*contextpp
, void *args
)
1456 nss_status_t status
;
1457 struct nss_db_state
*s
;
1458 struct nss_getent_context
*contextp
;
1462 if ((contextp
= contextpp
->ctx
) == 0) {
1463 nss_setent_u(rootp
, initf
, contextpp
);
1464 if ((contextp
= contextpp
->ctx
) == 0) {
1466 return (NSS_UNAVAIL
);
1469 /* name service cache daemon divert */
1470 status
= _nsc_getent_u(rootp
, initf
, contextpp
, args
);
1471 if (status
!= NSS_TRYLOCAL
)
1474 /* fall through - process locally */
1476 n_src
= contextp
->n_src
;
1481 * We've done an end_iter() and haven't done nss_setent()
1482 * or nss_endent() since; we should stick in this state
1483 * until the caller invokes one of those two routines.
1485 return (NSS_SUCCESS
);
1488 while (n_src
< s
->max_src
) {
1492 /* If it's null it's a bug, but let's play safe */
1495 res
= NSS_INVOKE_DBOP(be
, NSS_DBOP_GETENT
, args
);
1498 if (__NSW_ACTION_V1(s
->src
[n_src
].lkp
, res
) == __NSW_RETURN
) {
1499 if (res
!= __NSW_SUCCESS
) {
1500 end_iter_u(rootp
, contextp
);
1504 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_ENDENT
, 0);
1505 NSS_RELOCK(&rootp
, s
);
1506 nss_put_backend_u(s
, n_src
, be
);
1509 } while (n_src
< s
->max_src
&&
1510 (be
= nss_get_backend_u(&rootp
, s
, n_src
)) == 0);
1514 * This is the case where we failed to get the backend
1515 * for the last source. We exhausted all sources.
1517 * We need to do cleanup ourselves because end_iter_u()
1518 * does not do it for be == 0.
1520 NSS_UNREF_UNLOCK(rootp
, s
);
1525 contextp
->n_src
= n_src
;
1526 (void) NSS_INVOKE_DBOP(be
, NSS_DBOP_SETENT
, 0);
1529 /* Got to the end of the sources without finding another entry */
1530 end_iter_u(rootp
, contextp
);
1531 return (NSS_SUCCESS
);
1532 /* success is either a successful entry or end of the sources */
1537 nss_endent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
1538 nss_getent_t
*contextpp
)
1540 nss_status_t status
;
1541 struct nss_getent_context
*contextp
;
1543 if ((contextp
= contextpp
->ctx
) == 0) {
1544 /* nss_endent() on an unused context is a no-op */
1548 /* notify name service cache daemon */
1549 status
= _nsc_endent_u(rootp
, initf
, contextpp
);
1550 if (status
!= NSS_TRYLOCAL
) {
1552 libc_free(contextp
);
1557 /* fall through - process locally */
1560 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1561 * following an endXXXent() behaves as though the user had invoked
1562 * setXXXent(), i.e. it iterates properly from the beginning.
1563 * We'd better not break this, so our choices are
1564 * (1) leave the context structure around, and do nss_setent or
1565 * something equivalent,
1566 * or (2) free the context completely, and rely on the code in
1567 * nss_getent() that makes getXXXent() do the right thing
1568 * even without a preceding setXXXent().
1569 * The code below does (2), which frees up resources nicely but will
1570 * cost more if the user then does more getXXXent() operations.
1571 * Moral: for efficiency, don't call endXXXent() prematurely.
1573 end_iter_u(rootp
, contextp
);
1574 libc_free(contextp
);
1579 * pack dbd data into header
1580 * Argment pointers assumed valid.
1581 * poff offset position pointer
1582 * IN = starting offset for dbd header
1583 * OUT = starting offset for next section
1587 nss_pack_dbd(void *buffer
, size_t bufsize
, nss_db_params_t
*p
, size_t *poff
)
1589 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buffer
;
1596 pbuf
->dbd_off
= (nssuint_t
)off
;
1597 bptr
= (char *)buffer
+ off
;
1598 blen
= bufsize
- off
;
1599 len
= sizeof (nss_dbd_t
);
1602 if (p
->name
== NULL
) {
1603 errno
= ERANGE
; /* actually EINVAL */
1607 /* if default config not specified, the flag should be reset */
1608 if (p
->default_config
== NULL
) {
1609 p
->default_config
= "<NULL>";
1610 p
->flags
= p
->flags
& ~NSS_USE_DEFAULT_CONFIG
;
1613 n
= strlen(p
->name
) + 1;
1614 dc
= strlen(p
->default_config
) + 1;
1615 if (n
< 2 || dc
< 2) { /* What no DB? */
1616 errno
= ERANGE
; /* actually EINVAL */
1619 if (p
->config_name
!= NULL
) {
1620 nc
= strlen(p
->config_name
) + 1;
1622 if ((len
+ n
+ nc
+ dc
) >= blen
) {
1623 errno
= ERANGE
; /* actually EINVAL */
1627 pdbd
= (nss_dbd_t
*)((void *)bptr
);
1629 pdbd
->flags
= p
->flags
;
1631 (void) strlcpy(bptr
, p
->name
, n
);
1635 pdbd
->o_config_name
= 0;
1637 pdbd
->o_config_name
= len
;
1638 (void) strlcpy(bptr
, p
->config_name
, nc
);
1642 pdbd
->o_default_config
= len
;
1643 (void) strlcpy(bptr
, p
->default_config
, dc
);
1645 pbuf
->dbd_len
= (nssuint_t
)len
;
1646 off
+= ROUND_UP(len
, sizeof (nssuint_t
));
1648 return (NSS_SUCCESS
);
1652 * Switch packed and _nsc (switch->nscd) interfaces
1653 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1658 nss_pack(void *buffer
, size_t bufsize
, nss_db_root_t
*rootp
,
1659 nss_db_initf_t initf
, int search_fnum
, void *search_args
)
1661 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buffer
;
1662 nss_XbyY_args_t
*in
= (nss_XbyY_args_t
*)search_args
;
1663 nss_db_params_t tparam
= { 0 };
1664 nss_status_t ret
= NSS_ERROR
;
1666 size_t blen
, len
, off
= 0;
1668 struct nss_groupsbymem
*gbm
;
1670 if (pbuf
== NULL
|| in
== NULL
|| initf
== (nss_db_initf_t
)NULL
) {
1671 errno
= ERANGE
; /* actually EINVAL */
1674 tparam
.cleanup
= NULL
;
1676 if ((dbn
= tparam
.name
) == 0) {
1677 if (tparam
.cleanup
!= 0)
1678 (tparam
.cleanup
)(&tparam
);
1679 errno
= ERANGE
; /* actually EINVAL */
1683 /* init buffer header */
1684 pbuf
->pbufsiz
= (nssuint_t
)bufsize
;
1685 pbuf
->p_ruid
= (uint32_t)getuid();
1686 pbuf
->p_euid
= (uint32_t)geteuid();
1687 pbuf
->p_version
= NSCD_HEADER_REV
;
1692 /* possible audituser init */
1693 if (strcmp(dbn
, NSS_DBNAM_AUTHATTR
) == 0 && in
->h_errno
!= 0)
1694 pbuf
->p_herrno
= (uint32_t)in
->h_errno
;
1698 off
= sizeof (nss_pheader_t
);
1700 /* setup getXbyY operation - database and sub function */
1701 pbuf
->nss_dbop
= (uint32_t)search_fnum
;
1702 ret
= nss_pack_dbd(buffer
, bufsize
, &tparam
, &off
);
1703 if (ret
!= NSS_SUCCESS
) {
1704 errno
= ERANGE
; /* actually EINVAL */
1708 /* setup request key */
1709 pbuf
->key_off
= (nssuint_t
)off
;
1710 bptr
= (char *)buffer
+ off
;
1711 blen
= bufsize
- off
;
1712 /* use key2str if provided, else call default getXbyY packer */
1713 if (strcmp(dbn
, NSS_DBNAM_NETGROUP
) == 0) {
1714 /* This has to run locally due to backend knowledge */
1715 if (search_fnum
== NSS_DBOP_NETGROUP_SET
) {
1717 return (NSS_TRYLOCAL
);
1719 /* use default packer for known getXbyY ops */
1720 ret
= nss_default_key2str(bptr
, blen
, in
, dbn
,
1722 } else if (in
->key2str
== NULL
||
1723 (search_fnum
== NSS_DBOP_GROUP_BYMEMBER
&&
1724 strcmp(dbn
, NSS_DBNAM_GROUP
) == 0)) {
1725 /* use default packer for known getXbyY ops */
1726 ret
= nss_default_key2str(bptr
, blen
, in
, dbn
,
1729 ret
= (*in
->key2str
)(bptr
, blen
, &in
->key
, &len
);
1731 if (tparam
.cleanup
!= 0)
1732 (tparam
.cleanup
)(&tparam
);
1733 if (ret
!= NSS_SUCCESS
) {
1734 errno
= ERANGE
; /* actually ENOMEM */
1737 pbuf
->key_len
= (nssuint_t
)len
;
1738 off
+= ROUND_UP(len
, sizeof (nssuint_t
));
1740 pbuf
->data_off
= (nssuint_t
)off
;
1741 pbuf
->data_len
= (nssuint_t
)(bufsize
- off
);
1743 * Prime data return with first result if
1744 * the first result is passed in
1745 * [_getgroupsbymember oddness]
1747 gbm
= (struct nss_groupsbymem
*)search_args
;
1748 if (search_fnum
== NSS_DBOP_GROUP_BYMEMBER
&&
1749 strcmp(dbn
, NSS_DBNAM_GROUP
) == 0 && gbm
->numgids
== 1) {
1751 gidp
= (gid_t
*)((void *)((char *)buffer
+ off
));
1752 *gidp
= gbm
->gid_array
[0];
1755 errno
= 0; /* just in case ... */
1756 return (NSS_SUCCESS
);
1760 * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1761 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1766 nss_pack_ent(void *buffer
, size_t bufsize
, nss_db_root_t
*rootp
,
1767 nss_db_initf_t initf
, nss_getent_t
*contextpp
)
1769 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buffer
;
1770 struct nss_getent_context
*contextp
= contextpp
->ctx
;
1771 nss_status_t ret
= NSS_ERROR
;
1772 size_t blen
, len
= 0, off
= 0;
1776 if (pbuf
== NULL
|| initf
== (nss_db_initf_t
)NULL
) {
1777 errno
= ERANGE
; /* actually EINVAL */
1781 /* init buffer header */
1782 pbuf
->pbufsiz
= (nssuint_t
)bufsize
;
1783 pbuf
->p_ruid
= (uint32_t)getuid();
1784 pbuf
->p_euid
= (uint32_t)geteuid();
1785 pbuf
->p_version
= NSCD_HEADER_REV
;
1791 off
= sizeof (nss_pheader_t
);
1793 /* setup getXXXent operation - database and sub function */
1794 pbuf
->nss_dbop
= (uint32_t)0; /* iterators have no dbop */
1795 ret
= nss_pack_dbd(buffer
, bufsize
, &contextp
->param
, &off
);
1796 if (ret
!= NSS_SUCCESS
) {
1797 errno
= ERANGE
; /* actually EINVAL */
1801 off
+= ROUND_UP(len
, sizeof (nssuint_t
));
1803 pbuf
->key_off
= (nssuint_t
)off
;
1804 bptr
= (char *)buffer
+ off
;
1805 blen
= bufsize
- off
;
1806 len
= (size_t)(sizeof (nssuint_t
) * 2);
1808 errno
= ERANGE
; /* actually EINVAL */
1811 nptr
= (nssuint_t
*)((void *)bptr
);
1812 *nptr
++ = contextp
->cookie
;
1813 *nptr
= contextp
->seq_num
;
1814 pbuf
->key_len
= (nssuint_t
)len
;
1817 pbuf
->data_off
= (nssuint_t
)off
;
1818 pbuf
->data_len
= (nssuint_t
)(bufsize
- off
);
1819 return (NSS_SUCCESS
);
1823 * Unpack packed arguments buffer
1824 * Return: status, errnos and results from requested operation.
1826 * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1827 * or via the backwards compatibility interfaces then the standard
1828 * str2group API is used in conjunction with process_cstr. When,
1829 * processing a returned buffer, in NSS2 the return results are the
1830 * already digested groups array. Therefore, unpack the digested results
1831 * back to the return buffer.
1833 * Note: the digested results are nssuint_t quantities. _getgroupsbymember
1834 * digests int quantities. Therefore convert. Assume input is in nssuint_t
1835 * quantities. Store in an int array... Assume gid's are <= 32 bits...
1840 nss_unpack(void *buffer
, size_t bufsize
, nss_db_root_t
*rootp
,
1841 nss_db_initf_t initf
, int search_fnum
, void *search_args
)
1843 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buffer
;
1844 nss_XbyY_args_t
*in
= (nss_XbyY_args_t
*)search_args
;
1847 nss_status_t status
;
1855 struct nss_groupsbymem
*arg
;
1858 if (pbuf
== NULL
|| in
== NULL
)
1860 status
= pbuf
->p_status
;
1861 /* Identify odd cases */
1862 pdbd
= (nss_dbd_t
*)((void *)((char *)buffer
+ pbuf
->dbd_off
));
1863 dbn
= (char *)pdbd
+ pdbd
->o_name
;
1864 fmt_type
= 0; /* nss_XbyY_args_t */
1865 if (search_fnum
== NSS_DBOP_GROUP_BYMEMBER
&&
1866 strcmp(dbn
, NSS_DBNAM_GROUP
) == 0)
1867 fmt_type
= 1; /* struct nss_groupsbymem */
1868 else if (search_fnum
== NSS_DBOP_NETGROUP_IN
&&
1869 strcmp(dbn
, NSS_DBNAM_NETGROUP
) == 0)
1870 fmt_type
= 2; /* struct nss_innetgr_args */
1872 /* if error - door's switch error */
1873 /* extended data could contain additional information? */
1874 if (status
!= NSS_SUCCESS
) {
1875 if (fmt_type
== 0) {
1876 in
->h_errno
= (int)pbuf
->p_herrno
;
1877 if (pbuf
->p_errno
== ERANGE
)
1883 if (pbuf
->data_off
== 0 || pbuf
->data_len
== 0)
1884 return (NSS_NOTFOUND
);
1886 buf
= (char *)buffer
+ pbuf
->data_off
;
1887 len
= pbuf
->data_len
;
1889 /* sidestep odd cases */
1890 if (fmt_type
== 1) {
1891 arg
= (struct nss_groupsbymem
*)in
;
1892 /* copy returned gid array from returned nscd buffer */
1893 i
= len
/ sizeof (gid_t
);
1894 /* not enough buffer */
1895 if (i
> arg
->maxgids
) {
1899 gidp
= arg
->gid_array
;
1900 gptr
= (gid_t
*)((void *)buf
);
1901 (void) memcpy(gidp
, gptr
, len
);
1902 return (NSS_SUCCESS
);
1904 if (fmt_type
== 2) {
1905 struct nss_innetgr_args
*arg
= (struct nss_innetgr_args
*)in
;
1907 if (pbuf
->p_status
== NSS_SUCCESS
) {
1908 arg
->status
= NSS_NETGR_FOUND
;
1909 return (NSS_SUCCESS
);
1911 arg
->status
= NSS_NETGR_NO
;
1912 return (NSS_NOTFOUND
);
1916 /* process the normal cases */
1917 /* marshall data directly into users buffer */
1918 ret
= (*in
->str2ent
)(buf
, len
, in
->buf
.result
, in
->buf
.buffer
,
1920 if (ret
== NSS_STR_PARSE_ERANGE
) {
1925 } else if (ret
== NSS_STR_PARSE_SUCCESS
) {
1926 in
->returnval
= in
->buf
.result
;
1927 in
->returnlen
= len
;
1930 in
->h_errno
= (int)pbuf
->p_herrno
;
1931 return ((nss_status_t
)ret
);
1935 * Unpack a returned packed {set,get,end}ent arguments buffer
1936 * Return: status, errnos, cookie info and results from requested operation.
1941 nss_unpack_ent(void *buffer
, size_t bufsize
, nss_db_root_t
*rootp
,
1942 nss_db_initf_t initf
, nss_getent_t
*contextpp
, void *args
)
1944 nss_pheader_t
*pbuf
= (nss_pheader_t
*)buffer
;
1945 nss_XbyY_args_t
*in
= (nss_XbyY_args_t
*)args
;
1946 struct nss_getent_context
*contextp
= contextpp
->ctx
;
1949 nss_status_t status
;
1956 status
= pbuf
->p_status
;
1957 /* if error - door's switch error */
1958 /* extended data could contain additional information? */
1959 if (status
!= NSS_SUCCESS
)
1962 /* unpack assigned cookie from SET/GET/END request */
1963 if (pbuf
->key_off
== 0 ||
1964 pbuf
->key_len
!= (sizeof (nssuint_t
) * 2))
1965 return (NSS_NOTFOUND
);
1967 nptr
= (nssuint_t
*)((void *)((char *)buffer
+ pbuf
->key_off
));
1968 cookie
= contextp
->cookie
;
1969 if (cookie
!= NSCD_NEW_COOKIE
&& cookie
!= contextp
->cookie_setent
&&
1972 * Should either be new, or the cookie returned by the last
1973 * setent (i.e., this is the first getent after the setent)
1974 * or a match, else error
1976 return (NSS_NOTFOUND
);
1978 /* save away for the next ent request */
1979 contextp
->cookie
= *nptr
++;
1980 contextp
->seq_num
= *nptr
;
1982 /* All done if no marshalling is expected {set,end}ent */
1984 return (NSS_SUCCESS
);
1986 /* unmarshall the data */
1987 if (pbuf
->data_off
== 0 || pbuf
->data_len
== 0)
1988 return (NSS_NOTFOUND
);
1989 buf
= (char *)buffer
+ pbuf
->data_off
;
1991 len
= pbuf
->data_len
;
1993 /* marshall data directly into users buffer */
1994 ret
= (*in
->str2ent
)(buf
, len
, in
->buf
.result
, in
->buf
.buffer
,
1996 if (ret
== NSS_STR_PARSE_ERANGE
) {
2000 } else if (ret
== NSS_STR_PARSE_SUCCESS
) {
2001 in
->returnval
= in
->buf
.result
;
2002 in
->returnlen
= len
;
2004 in
->h_errno
= (int)pbuf
->p_herrno
;
2005 return ((nss_status_t
)ret
);
2009 * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
2013 _nsc_search(nss_db_root_t
*rootp
, nss_db_initf_t initf
, int search_fnum
,
2016 nss_pheader_t
*pbuf
;
2017 void *doorptr
= NULL
;
2019 size_t datasize
= 0;
2020 nss_status_t status
;
2022 if (_nsc_proc_is_cache() > 0) {
2023 /* internal nscd call - don't use the door */
2024 return (NSS_TRYLOCAL
);
2027 /* standard client calls nscd code */
2028 if (search_args
== NULL
)
2029 return (NSS_NOTFOUND
);
2031 /* get the door buffer & configured size */
2032 bufsize
= ((nss_XbyY_args_t
*)search_args
)->buf
.buflen
;
2033 if (_nsc_getdoorbuf(&doorptr
, &bufsize
) != 0)
2034 return (NSS_TRYLOCAL
);
2035 if (doorptr
== NULL
|| bufsize
== 0)
2036 return (NSS_TRYLOCAL
);
2038 pbuf
= (nss_pheader_t
*)doorptr
;
2039 /* pack argument and request into door buffer */
2040 pbuf
->nsc_callnumber
= NSCD_SEARCH
;
2041 /* copy relevant door request info into door buffer */
2042 status
= nss_pack((void *)pbuf
, bufsize
, rootp
,
2043 initf
, search_fnum
, search_args
);
2045 /* Packing error return error results */
2046 if (status
!= NSS_SUCCESS
)
2049 /* transfer packed switch request to nscd via door */
2050 /* data_off can be used because it is header+dbd_len+key_len */
2051 datasize
= pbuf
->data_off
;
2052 status
= _nsc_trydoorcall_ext(&doorptr
, &bufsize
, &datasize
);
2054 /* If unsuccessful fallback to standard nss logic */
2055 if (status
!= NSS_SUCCESS
) {
2057 * check if doors reallocated the memory underneath us
2058 * if they did munmap it or suffer a memory leak
2060 if (doorptr
!= (void *)pbuf
) {
2061 _nsc_resizedoorbuf(bufsize
);
2062 (void) munmap((void *)doorptr
, bufsize
);
2064 return (NSS_TRYLOCAL
);
2067 /* unpack and marshall data/errors to user structure */
2068 /* set any error conditions */
2069 status
= nss_unpack((void *)doorptr
, bufsize
, rootp
, initf
,
2070 search_fnum
, search_args
);
2072 * check if doors reallocated the memory underneath us
2073 * if they did munmap it or suffer a memory leak
2075 if (doorptr
!= (void *)pbuf
) {
2076 _nsc_resizedoorbuf(bufsize
);
2077 (void) munmap((void *)doorptr
, bufsize
);
2083 * contact nscd for a cookie or to reset an existing cookie
2084 * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2085 * continue diverting to local
2088 _nsc_setent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
2089 nss_getent_t
*contextpp
)
2091 nss_status_t status
= NSS_TRYLOCAL
;
2092 struct nss_getent_context
*contextp
= contextpp
->ctx
;
2093 nss_pheader_t
*pbuf
;
2094 void *doorptr
= NULL
;
2096 size_t datasize
= 0;
2098 /* return if already in local mode */
2099 if (contextp
->cookie
== NSCD_LOCAL_COOKIE
)
2100 return (NSS_TRYLOCAL
);
2102 if (_nsc_proc_is_cache() > 0) {
2103 /* internal nscd call - don't try to use the door */
2104 contextp
->cookie
= NSCD_LOCAL_COOKIE
;
2105 return (NSS_TRYLOCAL
);
2108 /* get the door buffer & configured size */
2109 if (_nsc_getdoorbuf(&doorptr
, &bufsize
) != 0) {
2110 contextp
->cookie
= NSCD_LOCAL_COOKIE
;
2111 return (NSS_TRYLOCAL
);
2113 if (doorptr
== NULL
|| bufsize
== 0) {
2114 contextp
->cookie
= NSCD_LOCAL_COOKIE
;
2115 return (NSS_TRYLOCAL
);
2118 pbuf
= (nss_pheader_t
*)doorptr
;
2119 pbuf
->nsc_callnumber
= NSCD_SETENT
;
2121 contextp
->param
.cleanup
= NULL
;
2122 (*initf
)(&contextp
->param
);
2123 if (contextp
->param
.name
== 0) {
2124 if (contextp
->param
.cleanup
!= 0)
2125 (contextp
->param
.cleanup
)(&contextp
->param
);
2126 errno
= ERANGE
; /* actually EINVAL */
2130 /* pack relevant setent request info into door buffer */
2131 status
= nss_pack_ent((void *)pbuf
, bufsize
, rootp
, initf
, contextpp
);
2132 if (status
!= NSS_SUCCESS
)
2135 /* transfer packed switch request to nscd via door */
2136 /* data_off can be used because it is header+dbd_len+key_len */
2137 datasize
= pbuf
->data_off
;
2138 status
= _nsc_trydoorcall_ext(&doorptr
, &bufsize
, &datasize
);
2140 /* If fallback to standard nss logic (door failure) if possible */
2141 if (status
!= NSS_SUCCESS
) {
2142 if (contextp
->cookie
== NSCD_NEW_COOKIE
) {
2143 contextp
->cookie
= NSCD_LOCAL_COOKIE
;
2144 return (NSS_TRYLOCAL
);
2146 return (NSS_UNAVAIL
);
2148 /* unpack returned cookie stash it away */
2149 status
= nss_unpack_ent((void *)doorptr
, bufsize
, rootp
,
2150 initf
, contextpp
, NULL
);
2151 /* save the setent cookie for later use */
2152 contextp
->cookie_setent
= contextp
->cookie
;
2154 * check if doors reallocated the memory underneath us
2155 * if they did munmap it or suffer a memory leak
2157 if (doorptr
!= (void *)pbuf
) {
2158 _nsc_resizedoorbuf(bufsize
);
2159 (void) munmap((void *)doorptr
, bufsize
);
2165 _nsc_getent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
2166 nss_getent_t
*contextpp
, void *args
)
2168 nss_status_t status
= NSS_TRYLOCAL
;
2169 struct nss_getent_context
*contextp
= contextpp
->ctx
;
2170 nss_pheader_t
*pbuf
;
2171 void *doorptr
= NULL
;
2173 size_t datasize
= 0;
2175 /* return if already in local mode */
2176 if (contextp
->cookie
== NSCD_LOCAL_COOKIE
)
2177 return (NSS_TRYLOCAL
);
2179 /* _nsc_setent_u already checked for nscd local case ... proceed */
2181 return (NSS_NOTFOUND
);
2183 /* get the door buffer & configured size */
2184 bufsize
= ((nss_XbyY_args_t
*)args
)->buf
.buflen
;
2185 if (_nsc_getdoorbuf(&doorptr
, &bufsize
) != 0)
2186 return (NSS_UNAVAIL
);
2187 if (doorptr
== NULL
|| bufsize
== 0)
2188 return (NSS_UNAVAIL
);
2190 pbuf
= (nss_pheader_t
*)doorptr
;
2191 pbuf
->nsc_callnumber
= NSCD_GETENT
;
2193 /* pack relevant setent request info into door buffer */
2194 status
= nss_pack_ent((void *)pbuf
, bufsize
, rootp
, initf
, contextpp
);
2195 if (status
!= NSS_SUCCESS
)
2198 /* transfer packed switch request to nscd via door */
2199 /* data_off can be used because it is header+dbd_len+key_len */
2200 datasize
= pbuf
->data_off
;
2201 status
= _nsc_trydoorcall_ext(&doorptr
, &bufsize
, &datasize
);
2203 /* If fallback to standard nss logic (door failure) if possible */
2204 if (status
!= NSS_SUCCESS
) {
2205 if (status
== NSS_TRYLOCAL
||
2206 contextp
->cookie
== NSCD_NEW_COOKIE
) {
2207 contextp
->cookie
= NSCD_LOCAL_COOKIE
;
2209 /* init the local cookie */
2210 nss_setent_u(rootp
, initf
, contextpp
);
2211 if (contextpp
->ctx
== 0)
2212 return (NSS_UNAVAIL
);
2213 return (NSS_TRYLOCAL
);
2215 return (NSS_UNAVAIL
);
2217 /* check error, unpack and process results */
2218 status
= nss_unpack_ent((void *)doorptr
, bufsize
, rootp
,
2219 initf
, contextpp
, args
);
2221 * check if doors reallocated the memory underneath us
2222 * if they did munmap it or suffer a memory leak
2224 if (doorptr
!= (void *)pbuf
) {
2225 _nsc_resizedoorbuf(bufsize
);
2226 (void) munmap((void *)doorptr
, bufsize
);
2232 _nsc_endent_u(nss_db_root_t
*rootp
, nss_db_initf_t initf
,
2233 nss_getent_t
*contextpp
)
2235 nss_status_t status
= NSS_TRYLOCAL
;
2236 struct nss_getent_context
*contextp
= contextpp
->ctx
;
2237 nss_pheader_t
*pbuf
;
2238 void *doorptr
= NULL
;
2240 size_t datasize
= 0;
2242 /* return if already in local mode */
2243 if (contextp
->cookie
== NSCD_LOCAL_COOKIE
)
2244 return (NSS_TRYLOCAL
);
2246 /* _nsc_setent_u already checked for nscd local case ... proceed */
2248 /* get the door buffer & configured size */
2249 if (_nsc_getdoorbuf(&doorptr
, &bufsize
) != 0)
2250 return (NSS_UNAVAIL
);
2251 if (doorptr
== NULL
|| bufsize
== 0)
2252 return (NSS_UNAVAIL
);
2254 /* pack up a NSCD_ENDGET request passing in the cookie */
2255 pbuf
= (nss_pheader_t
*)doorptr
;
2256 pbuf
->nsc_callnumber
= NSCD_ENDENT
;
2258 /* pack relevant setent request info into door buffer */
2259 status
= nss_pack_ent((void *)pbuf
, bufsize
, rootp
, initf
, contextpp
);
2260 if (status
!= NSS_SUCCESS
)
2263 /* transfer packed switch request to nscd via door */
2264 /* data_off can be used because it is header+dbd_len+key_len */
2265 datasize
= pbuf
->data_off
;
2266 (void) _nsc_trydoorcall_ext(&doorptr
, &bufsize
, &datasize
);
2268 /* error codes & unpacking ret values don't matter. We're done */
2271 * check if doors reallocated the memory underneath us
2272 * if they did munmap it or suffer a memory leak
2274 if (doorptr
!= (void *)pbuf
) {
2275 _nsc_resizedoorbuf(bufsize
);
2276 (void) munmap((void *)doorptr
, bufsize
);
2279 /* clean up initf setup */
2280 if (contextp
->param
.cleanup
!= 0)
2281 (contextp
->param
.cleanup
)(&contextp
->param
);
2282 contextp
->param
.cleanup
= NULL
;
2285 contextp
->cookie
= NSCD_NEW_COOKIE
;
2286 return (NSS_SUCCESS
);
2290 * Internal private API to return default suggested buffer sizes
2291 * for nsswitch API requests.
2295 _nss_get_bufsizes(int arg
)
2298 case _SC_GETGR_R_SIZE_MAX
:
2299 return (__nss_buflen_group
);
2301 return (__nss_buflen_default
);
2305 _nss_XbyY_fini(nss_XbyY_args_t
*args
)
2307 if ((args
->returnval
== NULL
) && (args
->erange
!= 0))
2309 return (args
->returnval
);