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 /*
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Shared code used by the name-service-switch frontends (e.g. getpwnam_r())
29 */
30
31 #include "lint.h"
32 #include <mtlib.h>
33 #include <dlfcn.h>
34 #include <atomic.h>
35
36 #define __NSS_PRIVATE_INTERFACE
37 #include "nsswitch_priv.h"
38 #undef __NSS_PRIVATE_INTERFACE
39
40 #include <nss_common.h>
41 #include <nss_dbdefs.h>
42 #include <unistd.h>
43 #include <stdlib.h>
44 #include <stdio.h>
45 #include <string.h>
46 #include <thread.h>
47 #include <synch.h>
48 #include <pthread.h>
49 #include <sys/types.h>
50 #include <sys/mman.h>
51 #include <errno.h>
52 #include "libc.h"
53 #include "tsd.h"
54
55 #include <getxby_door.h>
56
57 /*
58 * configurable values for default buffer sizes
59 */
60
61 /*
62 * PSARC/2005/133 updated the buffering mechanisms to handle
63 * up to 2^64 buffering. But sets a practical limit of 512*1024.
64 * The expectation is the practical limit will be dynamic from
65 * nscd. For now, set the group limit to this value.
66 */
67
68 #define NSS_BUFLEN_PRACTICAL (512*1024)
69
70 static size_t __nss_buflen_group = NSS_BUFLEN_PRACTICAL;
71 static size_t __nss_buflen_default = NSS_BUFLEN_DOOR;
72
73 /*
74 * policy component function interposing definitions:
75 * nscd if so desired can interpose it's own switch functions over
76 * the internal unlocked counterparts. This will allow nscd to replace
77 * the switch policy state engine with one that uses it's internal
78 * components.
79 * Only nscd can change this through it's use of nss_config.
80 * The golden rule is: ptr == NULL checking is used in the switch to
81 * see if a function was interposed. But nscd is responsible for seeing
82 * that mutex locking to change the values are observed when the data is
83 * changed. Especially if it happens > once. The switch does not lock
84 * the pointer with mutexs.
85 */
86
87 typedef struct {
88 void *p;
89 #if 0
90 void (*nss_delete_fp)(nss_db_root_t *rootp);
91 nss_status_t (*nss_search_fp)(nss_db_root_t *rootp,
92 nss_db_initf_t initf, int search_fnum,
93 void *search_args);
94 void (*nss_setent_u_fp)(nss_db_root_t *,
95 nss_db_initf_t, nss_getent_t *);
96 nss_status_t (*nss_getent_u_fp)(nss_db_root_t *,
97 nss_db_initf_t, nss_getent_t *, void *);
98 void (*nss_endent_u_fp)(nss_db_root_t *,
99 nss_db_initf_t, nss_getent_t *);
100 void (*end_iter_u_fp)(nss_db_root_t *rootp,
101 struct nss_getent_context *contextp);
102 #endif
103 } nss_policyf_t;
104
105 static mutex_t nss_policyf_lock = DEFAULTMUTEX;
106 static nss_policyf_t nss_policyf_ptrs =
107 { (void *)NULL };
108
109 /*
110 * nsswitch db_root state machine definitions:
111 * The golden rule is: if you hold a pointer to an nss_db_state struct and
112 * you don't hold the lock, you'd better have incremented the refcount
113 * while you held the lock; otherwise, it may vanish or change
114 * significantly when you least expect it.
115 *
116 * The pointer in nss_db_root_t is one such, so the reference count >= 1.
117 * Ditto the pointer in struct nss_getent_context.
118 */
119
120 /*
121 * State for one nsswitch database (e.g. "passwd", "hosts")
122 */
123 struct nss_db_state {
124 nss_db_root_t orphan_root; /* XXX explain */
125 unsigned refcount; /* One for the pointer in */
126 /* nss_db_root_t, plus one */
127 /* for each active thread. */
128 nss_db_params_t p;
129 struct __nsw_switchconfig_v1 *config;
130 int max_src; /* is == config->num_lookups */
131 struct nss_src_state *src; /* Pointer to array[max_src] */
132 };
133
134 /*
135 * State for one of the sources (e.g. "nis", "compat") for a database
136 */
137 struct nss_src_state {
138 struct __nsw_lookup_v1 *lkp;
139 int n_active;
140 int n_dormant;
141 int n_waiting; /* ... on wanna_be */
142 cond_t wanna_be;
143 union {
144 nss_backend_t *single; /* Efficiency hack for common case */
145 /* when limit_dead_backends == 1 */
146 nss_backend_t **multi; /* array[limit_dead_backends] of */
147 } dormant; /* pointers to dormant backends */
148 nss_backend_constr_t be_constr;
149 nss_backend_finder_t *finder;
150 void *finder_priv;
151 };
152
153 static struct nss_db_state *_nss_db_state_constr(nss_db_initf_t);
154 void _nss_db_state_destr(struct nss_db_state *);
155
156 /* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */
157
158 #define NSS_ROOTLOCK(r, sp) (cancel_safe_mutex_lock(&(r)->lock), \
159 *(sp) = (r)->s)
160
161 #define NSS_UNLOCK(r) (cancel_safe_mutex_unlock(&(r)->lock))
162
163 #define NSS_CHECKROOT(rp, s) ((s) != (*(rp))->s && \
164 (cancel_safe_mutex_unlock(&(*(rp))->lock), \
165 cancel_safe_mutex_lock(&(s)->orphan_root.lock), \
166 *(rp) = &(s)->orphan_root))
167
168 #define NSS_RELOCK(rp, s) (cancel_safe_mutex_lock(&(*(rp))->lock), \
169 NSS_CHECKROOT(rp, s))
170
171 #define NSS_STATE_REF_u(s) (++(s)->refcount)
172
173 #define NSS_UNREF_UNLOCK(r, s) (--(s)->refcount != 0 \
174 ? ((void)NSS_UNLOCK(r)) \
175 : ((void)NSS_UNLOCK(r), (void)_nss_db_state_destr(s)))
176
177 #define NSS_LOCK_CHECK(r, f, sp) (NSS_ROOTLOCK((r), (sp)), \
178 *(sp) == 0 && \
179 (r->s = *(sp) = _nss_db_state_constr(f)))
180 /* === In the future, NSS_LOCK_CHECK() may also have to check that */
181 /* === the config info hasn't changed (by comparing version numbers) */
182
183
184 /*
185 * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions:
186 * This remains for backwards compatibility. But generally nscd will
187 * decide if/how this gets used.
188 */
189 static int checked_env = 0; /* protected by "rootlock" */
190
191 /* allowing __nss_debug_file to be set could be a security hole. */
192 FILE *__nss_debug_file = stdout;
193 int __nss_debug_eng_loop;
194
195 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */
196 /* allowing __nis_debug_file to be set could be a security hole. */
197 FILE *__nis_debug_file = stdout;
198 int __nis_debug_bind;
199 int __nis_debug_rpc;
200 int __nis_debug_calls;
201 char *__nis_prefsrv;
202 char *__nis_preftype;
203 char *__nis_server; /* if set, use only this server for binding */
204
205 #define OPT_INT 1
206 #define OPT_STRING 2
207 #ifdef DEBUG
208 #define OPT_FILE 3
209 #endif
210
211 struct option {
212 char *name;
213 int type;
214 void *address;
215 };
216
217 static struct option nss_options[] = {
218 #ifdef DEBUG
219 /* allowing __nss_debug_file to be set could be a security hole. */
220 { "debug_file", OPT_FILE, &__nss_debug_file },
221 #endif
222 { "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop },
223 { 0, 0, 0 },
224 };
225
226 static struct option nis_options[] = {
227 #ifdef DEBUG
228 /* allowing __nis_debug_file to be set could be a security hole. */
229 { "debug_file", OPT_FILE, &__nis_debug_file },
230 #endif
231 { "debug_bind", OPT_INT, &__nis_debug_bind },
232 { "debug_rpc", OPT_INT, &__nis_debug_rpc },
233 { "debug_calls", OPT_INT, &__nis_debug_calls },
234 { "server", OPT_STRING, &__nis_server },
235 { "pref_srvr", OPT_STRING, &__nis_prefsrv },
236 { "pref_type", OPT_STRING, &__nis_preftype },
237 { 0, 0, 0 },
238 };
239
240 /*
241 * switch configuration parameter "database" definitions:
242 * The switch maintains a simmple read/write parameter database
243 * that nscd and the switch components can use to communicate
244 * nscd data to other components for configuration or out of band
245 * [IE no in the context of a getXbyY or putXbyY operation] data.
246 * The data passed are pointers to a lock data buffer and a length.
247 * Use of this is treated as SunwPrivate between nscd and the switch
248 * unless other wise stated.
249 */
250
251 typedef struct nss_cfgparam {
252 char *name;
253 mutex_t *lock;
254 void *buffer;
255 size_t length;
256 } nss_cfgparam_t;
257
258 typedef struct nss_cfglist {
259 char *name;
260 nss_cfgparam_t *list;
261 int count;
262 int max;
263 } nss_cfglist_t;
264
265 #define NSS_CFG_INCR 16
266
267 static nss_cfglist_t *nss_cfg = NULL;
268 static int nss_cfgcount = 0;
269 static int nss_cfgmax = 0;
270 static mutex_t nss_cfglock = DEFAULTMUTEX;
271
272 static int nss_cfg_policy_init();
273
274 /*
275 * A config parameters are in the form component:parameter
276 * as in: nss:parameter - switch (internal FE/policy/BE) parameter
277 * nscd:param - nscd application parameter
278 * ldap:param - nss_ldap BE parameter
279 * passwd:param - get/put passwd FE parameter
280 */
281
282 #define NSS_CONFIG_BRK ':'
283
284 /*
285 * The policy components initial parameter list
286 */
287 static nss_config_t nss_policy_params[] = {
288 { "nss:policyfunc", NSS_CONFIG_ADD, &nss_policyf_lock,
289 (void *)&nss_policyf_ptrs, (size_t)sizeof (nss_policyf_t) },
290 { NULL, NSS_CONFIG_ADD, (mutex_t *)NULL, (void *)NULL, (size_t)0 },
291 };
292
293 /*
294 * NSS parameter configuration routines
295 */
296
297 /* compare config name (component:parameter) to a component name */
298 static int
299 nss_cfgcn_cmp(const char *cfgname, const char *compname)
300 {
301 char *c;
302 size_t len, len2;
303
304 /* this code assumes valid pointers */
305 if ((c = strchr(cfgname, NSS_CONFIG_BRK)) == NULL)
306 return (-1);
307 len = (size_t)(c - cfgname);
308 len2 = strlen(compname);
309 if (len2 != len)
310 return (-1);
311 return (strncmp(cfgname, compname, len));
312 }
313
314 /* init configuration arena */
315 static int
316 nss_cfg_init()
317 {
318 nss_cfglist_t *cfg;
319 int i;
320
321 /* First time caller? */
322 if (nss_cfg != NULL) {
323 membar_consumer();
324 return (0);
325 }
326
327 /* Initialize internal tables */
328 lmutex_lock(&nss_cfglock);
329 if (nss_cfg != NULL) {
330 lmutex_unlock(&nss_cfglock);
331 membar_consumer();
332 return (0);
333 }
334 cfg = libc_malloc(NSS_CFG_INCR * sizeof (nss_cfglist_t));
335 if (cfg == NULL) {
336 errno = ENOMEM;
337 lmutex_unlock(&nss_cfglock);
338 return (-1);
339 }
340 for (i = 0; i < NSS_CFG_INCR; i++) {
341 cfg[i].list = libc_malloc(
342 NSS_CFG_INCR * sizeof (nss_cfgparam_t));
343 if (cfg[i].list == NULL) {
344 while (--i >= 0)
345 libc_free(cfg[i].list);
346 libc_free(cfg);
347 errno = ENOMEM;
348 lmutex_unlock(&nss_cfglock);
349 return (-1);
350 }
351 cfg[i].max = NSS_CFG_INCR;
352 }
353 nss_cfgmax = NSS_CFG_INCR;
354 membar_producer();
355 nss_cfg = cfg;
356 lmutex_unlock(&nss_cfglock);
357
358 /* Initialize Policy Engine values */
359 if (nss_cfg_policy_init() < 0) {
360 return (-1);
361 }
362 return (0);
363 }
364
365 /* find the name'd component list - create it if non-existent */
366 static nss_cfglist_t *
367 nss_cfgcomp_get(char *name, int add)
368 {
369 nss_cfglist_t *next;
370 char *c;
371 int i, len;
372 size_t nsize;
373
374 /* Make sure system is init'd */
375 if (nss_cfg_init() < 0)
376 return ((nss_cfglist_t *)NULL);
377
378 /* and check component:name validity */
379 if (name == NULL || (c = strchr(name, NSS_CONFIG_BRK)) == NULL)
380 return ((nss_cfglist_t *)NULL);
381
382 lmutex_lock(&nss_cfglock);
383 next = nss_cfg;
384 for (i = 0; i < nss_cfgcount; i++) {
385 if (next->name && nss_cfgcn_cmp(name, next->name) == 0) {
386 lmutex_unlock(&nss_cfglock);
387 return (next);
388 }
389 next++;
390 }
391 if (!add) {
392 lmutex_unlock(&nss_cfglock);
393 return (NULL);
394 }
395
396 /* not found, create a fresh one */
397 if (nss_cfgcount >= nss_cfgmax) {
398 /* realloc first */
399 nsize = (nss_cfgmax + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
400 next = (nss_cfglist_t *)libc_realloc(nss_cfg, nsize);
401 if (next == NULL) {
402 errno = ENOMEM;
403 lmutex_unlock(&nss_cfglock);
404 return ((nss_cfglist_t *)NULL);
405 }
406 (void) memset((void *)(next + nss_cfgcount), '\0',
407 NSS_CFG_INCR * sizeof (nss_cfglist_t));
408 nss_cfgmax += NSS_CFG_INCR;
409 nss_cfg = next;
410 }
411 next = nss_cfg + nss_cfgcount;
412 len = (size_t)(c - name) + 1;
413 if ((next->name = libc_malloc(len)) == NULL) {
414 errno = ENOMEM;
415 lmutex_unlock(&nss_cfglock);
416 return ((nss_cfglist_t *)NULL);
417 }
418 nss_cfgcount++;
419 (void) strlcpy(next->name, name, len);
420 lmutex_unlock(&nss_cfglock);
421 return (next);
422 }
423
424 /* find the name'd parameter - create it if non-existent */
425 static nss_cfgparam_t *
426 nss_cfgparam_get(char *name, int add)
427 {
428 nss_cfglist_t *comp;
429 nss_cfgparam_t *next;
430 int count, i;
431 size_t nsize;
432
433 if ((comp = nss_cfgcomp_get(name, add)) == NULL)
434 return ((nss_cfgparam_t *)NULL);
435 lmutex_lock(&nss_cfglock);
436 count = comp->count;
437 next = comp->list;
438 for (i = 0; i < count; i++) {
439 if (next->name && strcmp(name, next->name) == 0) {
440 lmutex_unlock(&nss_cfglock);
441 return (next);
442 }
443 next++;
444 }
445 if (!add) {
446 lmutex_unlock(&nss_cfglock);
447 return (NULL);
448 }
449
450 /* not found, create a fresh one */
451 if (count >= comp->max) {
452 /* realloc first */
453 nsize = (comp->max + NSS_CFG_INCR) * sizeof (nss_cfgparam_t);
454 next = (nss_cfgparam_t *)libc_realloc(comp->list, nsize);
455 if (next == NULL) {
456 errno = ENOMEM;
457 lmutex_unlock(&nss_cfglock);
458 return ((nss_cfgparam_t *)NULL);
459 }
460 comp->max += NSS_CFG_INCR;
461 comp->list = next;
462 }
463 next = comp->list + comp->count;
464 if ((next->name = libc_strdup(name)) == NULL) {
465 errno = ENOMEM;
466 lmutex_unlock(&nss_cfglock);
467 return ((nss_cfgparam_t *)NULL);
468 }
469 comp->count++;
470 lmutex_unlock(&nss_cfglock);
471 return (next);
472 }
473
474 /* find the name'd parameter - delete it if it exists */
475 static void
476 nss_cfg_del(nss_config_t *cfgp)
477 {
478 char *name;
479 nss_cfglist_t *comp;
480 nss_cfgparam_t *next, *cur;
481 int count, i, j;
482
483 /* exit if component name does not already exist */
484 if ((name = cfgp->name) == NULL ||
485 (comp = nss_cfgcomp_get(name, 0)) == NULL)
486 return;
487
488 /* find it */
489 lmutex_lock(&nss_cfglock);
490 count = comp->count;
491 next = comp->list;
492 for (i = 0; i < count; i++) {
493 if (next->name && strcmp(name, next->name) == 0) {
494 break; /* found it... */
495 }
496 next++;
497 }
498 if (i >= count) {
499 /* not found, already deleted */
500 lmutex_unlock(&nss_cfglock);
501 return;
502 }
503
504 /* copy down the remaining parameters, and clean up */
505 /* don't try to clean up component tables */
506 cur = next;
507 next++;
508 for (j = i+1; j < count; j++) {
509 *cur = *next;
510 cur++;
511 next++;
512 }
513 /* erase the last one */
514 if (cur->name) {
515 libc_free(cur->name);
516 cur->name = (char *)NULL;
517 }
518 cur->lock = (mutex_t *)NULL;
519 cur->buffer = (void *)NULL;
520 cur->length = 0;
521 comp->count--;
522 lmutex_unlock(&nss_cfglock);
523 }
524
525 static int
526 nss_cfg_get(nss_config_t *next)
527 {
528 nss_cfgparam_t *param;
529
530 errno = 0;
531 if ((param = nss_cfgparam_get(next->name, 0)) == NULL)
532 return (-1);
533 next->lock = param->lock;
534 next->buffer = param->buffer;
535 next->length = param->length;
536 return (0);
537 }
538
539 static int
540 nss_cfg_put(nss_config_t *next, int add)
541 {
542 nss_cfgparam_t *param;
543
544 errno = 0;
545 if ((param = nss_cfgparam_get(next->name, add)) == NULL)
546 return (-1);
547 param->lock = next->lock;
548 param->buffer = next->buffer;
549 param->length = next->length;
550 return (0);
551 }
552
553 /*
554 * Policy engine configurator - set and get interface
555 * argument is a NULL terminated list of set/get requests
556 * with input/result buffers and lengths. nss_cname is the
557 * specifier of a set or get operation and the property being
558 * managed. The intent is limited functions and expandability.
559 */
560
561 nss_status_t
562 nss_config(nss_config_t **plist, int cnt)
563 {
564 nss_config_t *next;
565 int i;
566
567 /* interface is only available to nscd */
568 if (_nsc_proc_is_cache() <= 0) {
569 return (NSS_UNAVAIL);
570 }
571 if (plist == NULL || cnt <= 0)
572 return (NSS_SUCCESS);
573 for (i = 0; i < cnt; i++) {
574 next = plist[i];
575 if (next == NULL)
576 break;
577 if (next->name == NULL) {
578 errno = EFAULT;
579 return (NSS_ERROR);
580 }
581 switch (next->cop) {
582 case NSS_CONFIG_GET:
583 /* get current lock/buffer/length fields */
584 if (nss_cfg_get(next) < 0) {
585 return (NSS_ERROR);
586 }
587 break;
588 case NSS_CONFIG_PUT:
589 /* set new lock/buffer/length fields */
590 if (nss_cfg_put(next, 0) < 0) {
591 return (NSS_ERROR);
592 }
593 break;
594 case NSS_CONFIG_ADD:
595 /* add parameter & set new lock/buffer/length fields */
596 if (nss_cfg_put(next, 1) < 0) {
597 return (NSS_ERROR);
598 }
599 break;
600 case NSS_CONFIG_DELETE:
601 /* delete parameter - should always work... */
602 nss_cfg_del(next);
603 break;
604 case NSS_CONFIG_LIST:
605 break;
606 default:
607 continue;
608 }
609 }
610 return (NSS_SUCCESS);
611 }
612
613 /*
614 * This routine is called immediately after nss_cfg_init but prior to
615 * any commands from nscd being processed. The intent here is to
616 * initialize the nss:* parameters allowed by the policy component
617 * so that nscd can then proceed and modify them if so desired.
618 *
619 * We know we can only get here if we are nscd so we can skip the
620 * preliminaries.
621 */
622
623 static int
624 nss_cfg_policy_init()
625 {
626 nss_config_t *next = &nss_policy_params[0];
627
628 for (; next && next->name != NULL; next++) {
629 if (nss_cfg_put(next, 1) < 0)
630 return (-1);
631 }
632 return (0);
633 }
634
635 /*
636 * NSS_OPTION & NIS_OPTION environment variable functions
637 */
638
639 static
640 void
641 set_option(struct option *opt, char *name, char *val)
642 {
643 int n;
644 char *p;
645 #ifdef DEBUG
646 FILE *fp;
647 #endif
648
649 for (; opt->name; opt++) {
650 if (strcmp(name, opt->name) == 0) {
651 switch (opt->type) {
652 case OPT_STRING:
653 p = libc_strdup(val);
654 *((char **)opt->address) = p;
655 break;
656
657 case OPT_INT:
658 if (strcmp(val, "") == 0)
659 n = 1;
660 else
661 n = atoi(val);
662 *((int *)opt->address) = n;
663 break;
664 #ifdef DEBUG
665 case OPT_FILE:
666 fp = fopen(val, "wF");
667 *((FILE **)opt->address) = fp;
668 break;
669 #endif
670 }
671 break;
672 }
673 }
674 }
675
676 static
677 void
678 __parse_environment(struct option *opt, char *p)
679 {
680 char *base;
681 char optname[100];
682 char optval[100];
683
684 while (*p) {
685 while (isspace(*p))
686 p++;
687 if (*p == '\0')
688 break;
689
690 base = p;
691 while (*p && *p != '=' && !isspace(*p))
692 p++;
693 /*
694 * play it safe and keep it simple, bail if an opt name
695 * is too long.
696 */
697 if ((p-base) >= sizeof (optname))
698 return;
699
700 (void) strncpy(optname, base, p-base);
701 optname[p-base] = '\0';
702
703 if (*p == '=') {
704 p++;
705 base = p;
706 while (*p && !isspace(*p))
707 p++;
708 /*
709 * play it safe and keep it simple, bail if an opt
710 * value is too long.
711 */
712 if ((p-base) >= sizeof (optval))
713 return;
714
715 (void) strncpy(optval, base, p-base);
716 optval[p-base] = '\0';
717 } else {
718 optval[0] = '\0';
719 }
720
721 set_option(opt, optname, optval);
722 }
723 }
724
725 static
726 void
727 nss_get_environment()
728 {
729 char *p;
730
731 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
732 p = getenv("NSS_OPTIONS");
733 if (p == NULL)
734 return;
735 __parse_environment(nss_options, p);
736 }
737
738 /*
739 * sole external routine called from libnsl/nis/cache/cache_api.cc in the
740 * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard
741 * Only after checking "checked_env" (which must be done with mutex
742 * "cur_cache_lock" held) and is done once, (then "checked_env" is set)
743 */
744 void
745 __nis_get_environment()
746 {
747 char *p;
748
749 p = getenv("NIS_OPTIONS");
750 if (p == NULL)
751 return;
752 __parse_environment(nis_options, p);
753 }
754
755
756 /*
757 * Switch policy component backend state machine functions
758 */
759
760 static nss_backend_t *
761 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src)
762 {
763 struct nss_src_state *src = &s->src[n_src];
764 nss_backend_t *be;
765 int cancel_state;
766
767 for (;;) {
768 if (src->n_dormant > 0) {
769 src->n_dormant--;
770 src->n_active++;
771 if (s->p.max_dormant_per_src == 1) {
772 be = src->dormant.single;
773 } else {
774 be = src->dormant.multi[src->n_dormant];
775 }
776 break;
777 }
778
779 if (src->be_constr == 0) {
780 nss_backend_finder_t *bf;
781
782 for (bf = s->p.finders; bf != 0; bf = bf->next) {
783 nss_backend_constr_t c;
784
785 c = (*bf->lookup) (bf->lookup_priv, s->p.name,
786 src->lkp->service_name, &src->finder_priv);
787 if (c != 0) {
788 src->be_constr = c;
789 src->finder = bf;
790 break;
791 }
792 }
793 if (src->be_constr == 0) {
794 /* Couldn't find the backend anywhere */
795 be = 0;
796 break;
797 }
798 }
799
800 if (src->n_active < s->p.max_active_per_src) {
801 be = (*src->be_constr)(s->p.name,
802 src->lkp->service_name, 0 /* === unimplemented */);
803 if (be != 0) {
804 src->n_active++;
805 break;
806 } else if (src->n_active == 0) {
807 /* Something's wrong; we should be */
808 /* able to create at least one */
809 /* instance of the backend */
810 break;
811 }
812 /*
813 * Else it's odd that we can't create another backend
814 * instance, but don't sweat it; instead, queue for
815 * an existing backend instance.
816 */
817 }
818
819 src->n_waiting++;
820 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,
821 &cancel_state);
822 (void) cond_wait(&src->wanna_be, &(*rootpp)->lock);
823 (void) pthread_setcancelstate(cancel_state, NULL);
824 NSS_CHECKROOT(rootpp, s);
825 src->n_waiting--;
826
827 /*
828 * Loop and see whether things got better for us, or whether
829 * someone else got scheduled first and we have to try
830 * this again.
831 *
832 * === ?? Should count iterations, assume bug if many ??
833 */
834 }
835 return (be);
836 }
837
838 static void
839 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be)
840 {
841 struct nss_src_state *src = &s->src[n_src];
842
843 if (be == 0) {
844 return;
845 }
846
847 src->n_active--;
848
849 if (src->n_dormant < s->p.max_dormant_per_src) {
850 if (s->p.max_dormant_per_src == 1) {
851 src->dormant.single = be;
852 src->n_dormant++;
853 } else if (src->dormant.multi != 0 ||
854 (src->dormant.multi =
855 libc_malloc(s->p.max_dormant_per_src *
856 sizeof (nss_backend_t *))) != NULL) {
857 src->dormant.multi[src->n_dormant] = be;
858 src->n_dormant++;
859 } else {
860 /* Can't store it, so toss it */
861 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
862 }
863 } else {
864 /* We've stored as many as we want, so toss it */
865 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0);
866 }
867 if (src->n_waiting > 0) {
868 (void) cond_signal(&src->wanna_be);
869 }
870 }
871
872 static struct nss_db_state *
873 _nss_db_state_constr(nss_db_initf_t initf)
874 {
875 struct nss_db_state *s;
876 struct __nsw_switchconfig_v1 *config = 0;
877 struct __nsw_lookup_v1 *lkp;
878 enum __nsw_parse_err err;
879 const char *config_name;
880 int n_src;
881
882 if ((s = libc_malloc(sizeof (*s))) == 0) {
883 return (0);
884 }
885 (void) mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0);
886
887 s->p.max_active_per_src = 10;
888 s->p.max_dormant_per_src = 1;
889 s->p.finders = nss_default_finders;
890 (*initf)(&s->p);
891 if (s->p.name == 0) {
892 _nss_db_state_destr(s);
893 return (0);
894 }
895
896 if (!checked_env) {
897 /* NSS_OPTIONS is undocumented and should be used without nscd running. */
898 nss_get_environment();
899 checked_env = 1;
900 }
901
902 config_name = s->p.config_name ? s->p.config_name : s->p.name;
903 if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) {
904 config = __nsw_getconfig_v1(config_name, &err);
905 /* === ? test err ? */
906 }
907 if (config == 0) {
908 /* getconfig failed, or frontend demanded default config */
909
910 char *str; /* _nsw_getoneconfig() clobbers its argument */
911
912 if ((str = libc_strdup(s->p.default_config)) != 0) {
913 config = _nsw_getoneconfig_v1(config_name, str, &err);
914 libc_free(str);
915 }
916 if (config == 0) {
917 _nss_db_state_destr(s);
918 return (0);
919 }
920 }
921 s->config = config;
922 if ((s->max_src = config->num_lookups) <= 0 ||
923 (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) {
924 _nss_db_state_destr(s);
925 return (0);
926 }
927 for (n_src = 0, lkp = config->lookups;
928 n_src < s->max_src; n_src++, lkp = lkp->next) {
929 s->src[n_src].lkp = lkp;
930 (void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0);
931 }
932 s->refcount = 1;
933 return (s);
934 }
935
936 void
937 _nss_src_state_destr(struct nss_src_state *src, int max_dormant)
938 {
939 if (max_dormant == 1) {
940 if (src->n_dormant != 0) {
941 (void) NSS_INVOKE_DBOP(src->dormant.single,
942 NSS_DBOP_DESTRUCTOR, 0);
943 };
944 } else if (src->dormant.multi != 0) {
945 int n;
946
947 for (n = 0; n < src->n_dormant; n++) {
948 (void) NSS_INVOKE_DBOP(src->dormant.multi[n],
949 NSS_DBOP_DESTRUCTOR, 0);
950 }
951 libc_free(src->dormant.multi);
952 }
953
954 /* cond_destroy(&src->wanna_be); */
955
956 if (src->finder != 0) {
957 (*src->finder->delete)(src->finder_priv, src->be_constr);
958 }
959 }
960
961 /*
962 * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire
963 * nss_db_state structure.
964 * Assumes that s has been ref-counted down to zero (in particular,
965 * rootp->s has already been dealt with).
966 *
967 * Nobody else holds a pointer to *s (if they did, refcount != 0),
968 * so we can clean up state *after* we drop the lock (also, by the
969 * time we finish freeing the state structures, the lock may have
970 * ceased to exist -- if we were using the orphan_root).
971 */
972
973 void
974 _nss_db_state_destr(struct nss_db_state *s)
975 {
976
977 if (s == NULL)
978 return;
979
980 /* === mutex_destroy(&s->orphan_root.lock); */
981 if (s->p.cleanup != 0) {
982 (*s->p.cleanup)(&s->p);
983 }
984 if (s->config != 0) {
985 (void) __nsw_freeconfig_v1(s->config);
986 }
987 if (s->src != 0) {
988 int n_src;
989
990 for (n_src = 0; n_src < s->max_src; n_src++) {
991 _nss_src_state_destr(&s->src[n_src],
992 s->p.max_dormant_per_src);
993 }
994 libc_free(s->src);
995 }
996 libc_free(s);
997 }
998
999
1000 /*
1001 * _nss_status_vec() returns a bit vector of all status codes returned during
1002 * the most recent call to nss_search().
1003 * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1004 * failure.
1005 * These functions are private. Don't use them externally without discussing
1006 * it with the switch maintainers.
1007 */
1008 static uint_t *
1009 _nss_status_vec_p()
1010 {
1011 return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
1012 }
1013
1014 unsigned int
1015 _nss_status_vec(void)
1016 {
1017 unsigned int *status_vec_p = _nss_status_vec_p();
1018
1019 return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
1020 }
1021
1022 static void
1023 output_loop_diag_a(
1024 int n,
1025 char *dbase,
1026 struct __nsw_lookup_v1 *lkp)
1027 {
1028 (void) fprintf(__nss_debug_file,
1029 "NSS_retry(%d): '%s': trying '%s' ... ",
1030 n, dbase, lkp->service_name);
1031 (void) fflush(__nss_debug_file);
1032
1033 }
1034
1035 static void
1036 output_loop_diag_b(
1037 nss_status_t res,
1038 struct __nsw_lookup_v1 *lkp)
1039 {
1040 (void) fprintf(__nss_debug_file, "result=");
1041 switch (res) {
1042 case NSS_SUCCESS:
1043 (void) fprintf(__nss_debug_file, "SUCCESS");
1044 break;
1045 case NSS_NOTFOUND:
1046 (void) fprintf(__nss_debug_file, "NOTFOUND");
1047 break;
1048 case NSS_UNAVAIL:
1049 (void) fprintf(__nss_debug_file, "UNAVAIL");
1050 break;
1051 case NSS_TRYAGAIN:
1052 (void) fprintf(__nss_debug_file, "TRYAGAIN");
1053 break;
1054 case NSS_NISSERVDNS_TRYAGAIN:
1055 (void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
1056 break;
1057 default:
1058 (void) fprintf(__nss_debug_file, "undefined");
1059 }
1060 (void) fprintf(__nss_debug_file, ", action=");
1061 switch (lkp->actions[res]) {
1062 case __NSW_CONTINUE:
1063 (void) fprintf(__nss_debug_file, "CONTINUE");
1064 break;
1065 case __NSW_RETURN:
1066 (void) fprintf(__nss_debug_file, "RETURN");
1067 break;
1068 case __NSW_TRYAGAIN_FOREVER:
1069 (void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
1070 break;
1071 case __NSW_TRYAGAIN_NTIMES:
1072 (void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
1073 lkp->max_retries);
1074 break;
1075 case __NSW_TRYAGAIN_PAUSED:
1076 (void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
1077 break;
1078 default:
1079 (void) fprintf(__nss_debug_file, "undefined");
1080 }
1081 (void) fprintf(__nss_debug_file, "\n");
1082 }
1083
1084 #define NSS_BACKOFF(n, b, t) \
1085 ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1086
1087 static int
1088 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
1089 {
1090 if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) {
1091 if (res == NSS_SUCCESS) {
1092 __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
1093 __NSW_UNPAUSE_ACTION(
1094 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
1095 }
1096 return (0);
1097 }
1098
1099 if ((res == NSS_TRYAGAIN &&
1100 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
1101 (res == NSS_NISSERVDNS_TRYAGAIN &&
1102 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
1103 return (1);
1104
1105 if (res == NSS_TRYAGAIN &&
1106 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1107 if (n <= lkp->max_retries)
1108 return (1);
1109 else {
1110 lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
1111 return (0);
1112 }
1113
1114 if (res == NSS_NISSERVDNS_TRYAGAIN &&
1115 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1116 if (n <= lkp->max_retries)
1117 return (1);
1118 else {
1119 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
1120 __NSW_TRYAGAIN_PAUSED;
1121 return (0);
1122 }
1123
1124 return (0);
1125 }
1126
1127 /*
1128 * Switch policy component functional interfaces
1129 */
1130
1131 void
1132 nss_delete(nss_db_root_t *rootp)
1133 {
1134 struct nss_db_state *s;
1135
1136 /* no name service cache daemon divert here */
1137 /* local nss_delete decrements state reference counts */
1138 /* and may free up opened switch resources. */
1139
1140 NSS_ROOTLOCK(rootp, &s);
1141 if (s == 0) {
1142 NSS_UNLOCK(rootp);
1143 } else {
1144 rootp->s = 0;
1145 NSS_UNREF_UNLOCK(rootp, s);
1146 }
1147 }
1148
1149 nss_status_t
1150 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1151 void *search_args)
1152 {
1153 nss_status_t res = NSS_UNAVAIL;
1154 struct nss_db_state *s;
1155 int n_src;
1156 unsigned int *status_vec_p;
1157
1158 /* name service cache daemon divert */
1159 res = _nsc_search(rootp, initf, search_fnum, search_args);
1160 if (res != NSS_TRYLOCAL)
1161 return (res);
1162
1163 /* fall through - process locally */
1164 errno = 0; /* just in case ... */
1165 res = NSS_UNAVAIL;
1166 status_vec_p = _nss_status_vec_p();
1167
1168 if (status_vec_p == NULL) {
1169 return (NSS_UNAVAIL);
1170 }
1171 *status_vec_p = 0;
1172
1173 NSS_LOCK_CHECK(rootp, initf, &s);
1174 if (s == 0) {
1175 NSS_UNLOCK(rootp);
1176 return (res);
1177 }
1178 NSS_STATE_REF_u(s);
1179
1180 for (n_src = 0; n_src < s->max_src; n_src++) {
1181 nss_backend_t *be;
1182 nss_backend_op_t funcp;
1183
1184 res = NSS_UNAVAIL;
1185 if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
1186 if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
1187 int n_loop = 0;
1188 int no_backoff = 19;
1189 int max_backoff = 5; /* seconds */
1190
1191 do {
1192 /*
1193 * Backend operation may take a while;
1194 * drop the lock so we don't serialize
1195 * more than necessary.
1196 */
1197 NSS_UNLOCK(rootp);
1198
1199 /* After several tries, backoff... */
1200 if (n_loop > no_backoff) {
1201 if (__nss_debug_eng_loop > 1)
1202 (void) fprintf(
1203 __nss_debug_file,
1204 "NSS: loop: "
1205 "sleeping %d ...\n",
1206 NSS_BACKOFF(n_loop,
1207 no_backoff,
1208 max_backoff));
1209
1210 (void) sleep(NSS_BACKOFF(n_loop,
1211 no_backoff, max_backoff));
1212 }
1213
1214 if (__nss_debug_eng_loop)
1215 output_loop_diag_a(n_loop,
1216 s->config->dbase,
1217 s->src[n_src].lkp);
1218
1219
1220 res = (*funcp)(be, search_args);
1221 NSS_RELOCK(&rootp, s);
1222 n_loop++;
1223 if (__nss_debug_eng_loop)
1224 output_loop_diag_b(res,
1225 s->src[n_src].lkp);
1226 } while (retry_test(res, n_loop,
1227 s->src[n_src].lkp));
1228 }
1229 nss_put_backend_u(s, n_src, be);
1230 }
1231 *status_vec_p |= (1 << res);
1232 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1233 if (__nss_debug_eng_loop)
1234 (void) fprintf(__nss_debug_file,
1235 "NSS: '%s': return.\n",
1236 s->config->dbase);
1237 break;
1238 } else
1239 if (__nss_debug_eng_loop)
1240 (void) fprintf(__nss_debug_file,
1241 "NSS: '%s': continue ...\n",
1242 s->config->dbase);
1243 }
1244 NSS_UNREF_UNLOCK(rootp, s);
1245 return (res);
1246 }
1247
1248
1249 /*
1250 * Start of nss_{setent|getent|endent}
1251 */
1252
1253 /*
1254 * State (here called "context") for one setent/getent.../endent sequence.
1255 * In principle there could be multiple contexts active for a single
1256 * database; in practice, since Posix and UI have helpfully said that
1257 * getent() state is global rather than, say, per-thread or user-supplied,
1258 * we have at most one of these per nss_db_state.
1259 * XXX ? Is this statement still true?
1260 *
1261 * NSS2 - a client's context is maintained as a cookie delivered by and
1262 * passed to nscd. The cookie is a 64 bit (nssuint_t) unique opaque value
1263 * created by nscd.
1264 * cookie states:
1265 * NSCD_NEW_COOKIE - cookie value uninitialized
1266 * NSCD_LOCAL_COOKIE - setent is a local setent
1267 * all other - NSCD unique opaque id for this setent
1268 * A client's context is also associated with a seq_num. This is a nscd
1269 * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1270 * to validate the sequencing of the context. The client treats this as
1271 * a pass through value.
1272 *
1273 * XXX ?? Use Cookie as cross-check info so that we can detect an
1274 * nss_context that missed an nss_delete() or similar.
1275 */
1276
1277 struct nss_getent_context {
1278 int n_src; /* >= max_src ==> end of sequence */
1279 nss_backend_t *be;
1280 struct nss_db_state *s;
1281 nssuint_t cookie;
1282 nssuint_t seq_num;
1283 nssuint_t cookie_setent;
1284 nss_db_params_t param;
1285 };
1286
1287 static void nss_setent_u(nss_db_root_t *,
1288 nss_db_initf_t,
1289 nss_getent_t *);
1290 static nss_status_t nss_getent_u(nss_db_root_t *,
1291 nss_db_initf_t,
1292 nss_getent_t *,
1293 void *);
1294 static void nss_endent_u(nss_db_root_t *,
1295 nss_db_initf_t,
1296 nss_getent_t *);
1297
1298 void
1299 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1300 {
1301 if (contextpp == 0) {
1302 return;
1303 }
1304 cancel_safe_mutex_lock(&contextpp->lock);
1305 nss_setent_u(rootp, initf, contextpp);
1306 cancel_safe_mutex_unlock(&contextpp->lock);
1307 }
1308
1309 nss_status_t
1310 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1311 void *args)
1312 {
1313 nss_status_t status;
1314
1315 if (contextpp == 0) {
1316 return (NSS_UNAVAIL);
1317 }
1318 cancel_safe_mutex_lock(&contextpp->lock);
1319 status = nss_getent_u(rootp, initf, contextpp, args);
1320 cancel_safe_mutex_unlock(&contextpp->lock);
1321 return (status);
1322 }
1323
1324 void
1325 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1326 {
1327 if (contextpp == 0) {
1328 return;
1329 }
1330 cancel_safe_mutex_lock(&contextpp->lock);
1331 nss_endent_u(rootp, initf, contextpp);
1332 cancel_safe_mutex_unlock(&contextpp->lock);
1333 }
1334
1335 /*
1336 * Each of the _u versions of the nss interfaces assume that the context
1337 * lock is held. No need to divert to nscd. Private to local sequencing.
1338 */
1339
1340 static void
1341 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1342 {
1343 struct nss_db_state *s;
1344 nss_backend_t *be;
1345 int n_src;
1346
1347 s = contextp->s;
1348 n_src = contextp->n_src;
1349 be = contextp->be;
1350
1351 if (s != 0) {
1352 if (n_src < s->max_src && be != 0) {
1353 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1354 NSS_RELOCK(&rootp, s);
1355 nss_put_backend_u(s, n_src, be);
1356 contextp->be = 0; /* Should be unnecessary, but hey */
1357 NSS_UNREF_UNLOCK(rootp, s);
1358 }
1359 contextp->s = 0;
1360 }
1361 }
1362
1363 static void
1364 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1365 nss_getent_t *contextpp)
1366 {
1367 nss_status_t status;
1368 struct nss_db_state *s;
1369 struct nss_getent_context *contextp;
1370 nss_backend_t *be;
1371 int n_src;
1372
1373 /* setup process wide context while locked */
1374 if ((contextp = contextpp->ctx) == 0) {
1375 if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1376 return;
1377 }
1378 contextpp->ctx = contextp;
1379 contextp->cookie = NSCD_NEW_COOKIE; /* cookie init */
1380 contextp->seq_num = 0; /* seq_num init */
1381 s = 0;
1382 } else {
1383 s = contextp->s;
1384 if (contextp->cookie != NSCD_LOCAL_COOKIE)
1385 contextp->cookie = NSCD_NEW_COOKIE;
1386 }
1387
1388 /* name service cache daemon divert */
1389 if (contextp->cookie == NSCD_NEW_COOKIE) {
1390 status = _nsc_setent_u(rootp, initf, contextpp);
1391 if (status != NSS_TRYLOCAL)
1392 return;
1393 }
1394
1395 /* fall through - process locally */
1396 if (s == 0) {
1397 NSS_LOCK_CHECK(rootp, initf, &s);
1398 if (s == 0) {
1399 /* Couldn't set up state, so quit */
1400 NSS_UNLOCK(rootp);
1401 /* ==== is there any danger of not having done an */
1402 /* end_iter() here, and hence of losing backends? */
1403 contextpp->ctx = 0;
1404 libc_free(contextp);
1405 return;
1406 }
1407 NSS_STATE_REF_u(s);
1408 contextp->s = s;
1409 } else {
1410 s = contextp->s;
1411 n_src = contextp->n_src;
1412 be = contextp->be;
1413 if (n_src == 0 && be != 0) {
1414 /*
1415 * Optimization: don't do endent, don't change
1416 * backends, just do the setent. Look Ma, no locks
1417 * (nor any context that needs updating).
1418 */
1419 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1420 return;
1421 }
1422 if (n_src < s->max_src && be != 0) {
1423 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1424 NSS_RELOCK(&rootp, s);
1425 nss_put_backend_u(s, n_src, be);
1426 contextp->be = 0; /* Play it safe */
1427 } else {
1428 NSS_RELOCK(&rootp, s);
1429 }
1430 }
1431 for (n_src = 0, be = 0; n_src < s->max_src &&
1432 (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1433 ;
1434 }
1435 NSS_UNLOCK(rootp);
1436
1437 contextp->n_src = n_src;
1438 contextp->be = be;
1439
1440 if (be == 0) {
1441 /* Things are broken enough that we can't do setent/getent */
1442 nss_endent_u(rootp, initf, contextpp);
1443 return;
1444 }
1445 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1446 }
1447
1448 static nss_status_t
1449 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1450 nss_getent_t *contextpp, void *args)
1451 {
1452 nss_status_t status;
1453 struct nss_db_state *s;
1454 struct nss_getent_context *contextp;
1455 int n_src;
1456 nss_backend_t *be;
1457
1458 if ((contextp = contextpp->ctx) == 0) {
1459 nss_setent_u(rootp, initf, contextpp);
1460 if ((contextp = contextpp->ctx) == 0) {
1461 /* Give up */
1462 return (NSS_UNAVAIL);
1463 }
1464 }
1465 /* name service cache daemon divert */
1466 status = _nsc_getent_u(rootp, initf, contextpp, args);
1467 if (status != NSS_TRYLOCAL)
1468 return (status);
1469
1470 /* fall through - process locally */
1471 s = contextp->s;
1472 n_src = contextp->n_src;
1473 be = contextp->be;
1474
1475 if (s == 0) {
1476 /*
1477 * We've done an end_iter() and haven't done nss_setent()
1478 * or nss_endent() since; we should stick in this state
1479 * until the caller invokes one of those two routines.
1480 */
1481 return (NSS_SUCCESS);
1482 }
1483
1484 while (n_src < s->max_src) {
1485 nss_status_t res;
1486
1487 if (be == 0) {
1488 /* If it's null it's a bug, but let's play safe */
1489 res = NSS_UNAVAIL;
1490 } else {
1491 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1492 }
1493
1494 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1495 if (res != __NSW_SUCCESS) {
1496 end_iter_u(rootp, contextp);
1497 }
1498 return (res);
1499 }
1500 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1501 NSS_RELOCK(&rootp, s);
1502 nss_put_backend_u(s, n_src, be);
1503 do {
1504 n_src++;
1505 } while (n_src < s->max_src &&
1506 (be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1507 contextp->be = be;
1508 if (be == 0) {
1509 /*
1510 * This is the case where we failed to get the backend
1511 * for the last source. We exhausted all sources.
1512 *
1513 * We need to do cleanup ourselves because end_iter_u()
1514 * does not do it for be == 0.
1515 */
1516 NSS_UNREF_UNLOCK(rootp, s);
1517 contextp->s = 0;
1518 break;
1519 } else {
1520 NSS_UNLOCK(rootp);
1521 contextp->n_src = n_src;
1522 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1523 }
1524 }
1525 /* Got to the end of the sources without finding another entry */
1526 end_iter_u(rootp, contextp);
1527 return (NSS_SUCCESS);
1528 /* success is either a successful entry or end of the sources */
1529 }
1530
1531 /*ARGSUSED*/
1532 static void
1533 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1534 nss_getent_t *contextpp)
1535 {
1536 nss_status_t status;
1537 struct nss_getent_context *contextp;
1538
1539 if ((contextp = contextpp->ctx) == 0) {
1540 /* nss_endent() on an unused context is a no-op */
1541 return;
1542 }
1543
1544 /* notify name service cache daemon */
1545 status = _nsc_endent_u(rootp, initf, contextpp);
1546 if (status != NSS_TRYLOCAL) {
1547 /* clean up */
1548 libc_free(contextp);
1549 contextpp->ctx = 0;
1550 return;
1551 }
1552
1553 /* fall through - process locally */
1554
1555 /*
1556 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1557 * following an endXXXent() behaves as though the user had invoked
1558 * setXXXent(), i.e. it iterates properly from the beginning.
1559 * We'd better not break this, so our choices are
1560 * (1) leave the context structure around, and do nss_setent or
1561 * something equivalent,
1562 * or (2) free the context completely, and rely on the code in
1563 * nss_getent() that makes getXXXent() do the right thing
1564 * even without a preceding setXXXent().
1565 * The code below does (2), which frees up resources nicely but will
1566 * cost more if the user then does more getXXXent() operations.
1567 * Moral: for efficiency, don't call endXXXent() prematurely.
1568 */
1569 end_iter_u(rootp, contextp);
1570 libc_free(contextp);
1571 contextpp->ctx = 0;
1572 }
1573
1574 /*
1575 * pack dbd data into header
1576 * Argment pointers assumed valid.
1577 * poff offset position pointer
1578 * IN = starting offset for dbd header
1579 * OUT = starting offset for next section
1580 */
1581
1582 static nss_status_t
1583 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1584 {
1585 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1586 nss_dbd_t *pdbd;
1587 size_t off = *poff;
1588 size_t len, blen;
1589 size_t n, nc, dc;
1590 char *bptr;
1591
1592 pbuf->dbd_off = (nssuint_t)off;
1593 bptr = (char *)buffer + off;
1594 blen = bufsize - off;
1595 len = sizeof (nss_dbd_t);
1596
1597 n = nc = dc = 0;
1598 if (p->name == NULL) {
1599 errno = ERANGE; /* actually EINVAL */
1600 return (NSS_ERROR);
1601 }
1602
1603 /* if default config not specified, the flag should be reset */
1604 if (p->default_config == NULL) {
1605 p->default_config = "<NULL>";
1606 p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1607 }
1608
1609 n = strlen(p->name) + 1;
1610 dc = strlen(p->default_config) + 1;
1611 if (n < 2 || dc < 2) { /* What no DB? */
1612 errno = ERANGE; /* actually EINVAL */
1613 return (NSS_ERROR);
1614 }
1615 if (p->config_name != NULL) {
1616 nc = strlen(p->config_name) + 1;
1617 }
1618 if ((len + n + nc + dc) >= blen) {
1619 errno = ERANGE; /* actually EINVAL */
1620 return (NSS_ERROR);
1621 }
1622
1623 pdbd = (nss_dbd_t *)((void *)bptr);
1624 bptr += len;
1625 pdbd->flags = p->flags;
1626 pdbd->o_name = len;
1627 (void) strlcpy(bptr, p->name, n);
1628 len += n;
1629 bptr += n;
1630 if (nc == 0) {
1631 pdbd->o_config_name = 0;
1632 } else {
1633 pdbd->o_config_name = len;
1634 (void) strlcpy(bptr, p->config_name, nc);
1635 bptr += nc;
1636 len += nc;
1637 }
1638 pdbd->o_default_config = len;
1639 (void) strlcpy(bptr, p->default_config, dc);
1640 len += dc;
1641 pbuf->dbd_len = (nssuint_t)len;
1642 off += ROUND_UP(len, sizeof (nssuint_t));
1643 *poff = off;
1644 return (NSS_SUCCESS);
1645 }
1646
1647 /*
1648 * Switch packed and _nsc (switch->nscd) interfaces
1649 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1650 */
1651
1652 /*ARGSUSED*/
1653 nss_status_t
1654 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1655 nss_db_initf_t initf, int search_fnum, void *search_args)
1656 {
1657 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1658 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args;
1659 nss_db_params_t tparam = { 0 };
1660 nss_status_t ret = NSS_ERROR;
1661 const char *dbn;
1662 size_t blen, len, off = 0;
1663 char *bptr;
1664 struct nss_groupsbymem *gbm;
1665
1666 if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1667 errno = ERANGE; /* actually EINVAL */
1668 return (ret);
1669 }
1670 tparam.cleanup = NULL;
1671 (*initf)(&tparam);
1672 if ((dbn = tparam.name) == 0) {
1673 if (tparam.cleanup != 0)
1674 (tparam.cleanup)(&tparam);
1675 errno = ERANGE; /* actually EINVAL */
1676 return (ret);
1677 }
1678
1679 /* init buffer header */
1680 pbuf->pbufsiz = (nssuint_t)bufsize;
1681 pbuf->p_ruid = (uint32_t)getuid();
1682 pbuf->p_euid = (uint32_t)geteuid();
1683 pbuf->p_version = NSCD_HEADER_REV;
1684 pbuf->p_status = 0;
1685 pbuf->p_errno = 0;
1686 pbuf->p_herrno = 0;
1687
1688 /* possible audituser init */
1689 if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1690 pbuf->p_herrno = (uint32_t)in->h_errno;
1691
1692 pbuf->libpriv = 0;
1693
1694 off = sizeof (nss_pheader_t);
1695
1696 /* setup getXbyY operation - database and sub function */
1697 pbuf->nss_dbop = (uint32_t)search_fnum;
1698 ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1699 if (ret != NSS_SUCCESS) {
1700 errno = ERANGE; /* actually EINVAL */
1701 return (ret);
1702 }
1703 ret = NSS_ERROR;
1704 /* setup request key */
1705 pbuf->key_off = (nssuint_t)off;
1706 bptr = (char *)buffer + off;
1707 blen = bufsize - off;
1708 /* use key2str if provided, else call default getXbyY packer */
1709 if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1710 /* This has to run locally due to backend knowledge */
1711 if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1712 errno = 0;
1713 return (NSS_TRYLOCAL);
1714 }
1715 /* use default packer for known getXbyY ops */
1716 ret = nss_default_key2str(bptr, blen, in, dbn,
1717 search_fnum, &len);
1718 } else if (in->key2str == NULL ||
1719 (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1720 strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1721 /* use default packer for known getXbyY ops */
1722 ret = nss_default_key2str(bptr, blen, in, dbn,
1723 search_fnum, &len);
1724 } else {
1725 ret = (*in->key2str)(bptr, blen, &in->key, &len);
1726 }
1727 if (tparam.cleanup != 0)
1728 (tparam.cleanup)(&tparam);
1729 if (ret != NSS_SUCCESS) {
1730 errno = ERANGE; /* actually ENOMEM */
1731 return (ret);
1732 }
1733 pbuf->key_len = (nssuint_t)len;
1734 off += ROUND_UP(len, sizeof (nssuint_t));
1735
1736 pbuf->data_off = (nssuint_t)off;
1737 pbuf->data_len = (nssuint_t)(bufsize - off);
1738 /*
1739 * Prime data return with first result if
1740 * the first result is passed in
1741 * [_getgroupsbymember oddness]
1742 */
1743 gbm = (struct nss_groupsbymem *)search_args;
1744 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1745 strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1746 gid_t *gidp;
1747 gidp = (gid_t *)((void *)((char *)buffer + off));
1748 *gidp = gbm->gid_array[0];
1749 }
1750
1751 errno = 0; /* just in case ... */
1752 return (NSS_SUCCESS);
1753 }
1754
1755 /*
1756 * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1757 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1758 */
1759
1760 /*ARGSUSED*/
1761 nss_status_t
1762 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1763 nss_db_initf_t initf, nss_getent_t *contextpp)
1764 {
1765 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1766 struct nss_getent_context *contextp = contextpp->ctx;
1767 nss_status_t ret = NSS_ERROR;
1768 size_t blen, len = 0, off = 0;
1769 char *bptr;
1770 nssuint_t *nptr;
1771
1772 if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1773 errno = ERANGE; /* actually EINVAL */
1774 return (ret);
1775 }
1776
1777 /* init buffer header */
1778 pbuf->pbufsiz = (nssuint_t)bufsize;
1779 pbuf->p_ruid = (uint32_t)getuid();
1780 pbuf->p_euid = (uint32_t)geteuid();
1781 pbuf->p_version = NSCD_HEADER_REV;
1782 pbuf->p_status = 0;
1783 pbuf->p_errno = 0;
1784 pbuf->p_herrno = 0;
1785 pbuf->libpriv = 0;
1786
1787 off = sizeof (nss_pheader_t);
1788
1789 /* setup getXXXent operation - database and sub function */
1790 pbuf->nss_dbop = (uint32_t)0; /* iterators have no dbop */
1791 ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1792 if (ret != NSS_SUCCESS) {
1793 errno = ERANGE; /* actually EINVAL */
1794 return (ret);
1795 }
1796 ret = NSS_ERROR;
1797 off += ROUND_UP(len, sizeof (nssuint_t));
1798
1799 pbuf->key_off = (nssuint_t)off;
1800 bptr = (char *)buffer + off;
1801 blen = bufsize - off;
1802 len = (size_t)(sizeof (nssuint_t) * 2);
1803 if (len >= blen) {
1804 errno = ERANGE; /* actually EINVAL */
1805 return (ret);
1806 }
1807 nptr = (nssuint_t *)((void *)bptr);
1808 *nptr++ = contextp->cookie;
1809 *nptr = contextp->seq_num;
1810 pbuf->key_len = (nssuint_t)len;
1811
1812 off += len;
1813 pbuf->data_off = (nssuint_t)off;
1814 pbuf->data_len = (nssuint_t)(bufsize - off);
1815 return (NSS_SUCCESS);
1816 }
1817
1818 /*
1819 * Unpack packed arguments buffer
1820 * Return: status, errnos and results from requested operation.
1821 *
1822 * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1823 * or via the backwards compatibility interfaces then the standard
1824 * str2group API is used in conjunction with process_cstr. When,
1825 * processing a returned buffer, in NSS2 the return results are the
1826 * already digested groups array. Therefore, unpack the digested results
1827 * back to the return buffer.
1828 *
1829 * Note: the digested results are nssuint_t quantities. _getgroupsbymember
1830 * digests int quantities. Therefore convert. Assume input is in nssuint_t
1831 * quantities. Store in an int array... Assume gid's are <= 32 bits...
1832 */
1833
1834 /*ARGSUSED*/
1835 nss_status_t
1836 nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1837 nss_db_initf_t initf, int search_fnum, void *search_args)
1838 {
1839 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1840 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args;
1841 nss_dbd_t *pdbd;
1842 char *dbn;
1843 nss_status_t status;
1844 char *buf;
1845 int len;
1846 int ret;
1847 int i;
1848 int fmt_type;
1849 gid_t *gidp;
1850 gid_t *gptr;
1851 struct nss_groupsbymem *arg;
1852
1853
1854 if (pbuf == NULL || in == NULL)
1855 return (-1);
1856 status = pbuf->p_status;
1857 /* Identify odd cases */
1858 pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1859 dbn = (char *)pdbd + pdbd->o_name;
1860 fmt_type = 0; /* nss_XbyY_args_t */
1861 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1862 strcmp(dbn, NSS_DBNAM_GROUP) == 0)
1863 fmt_type = 1; /* struct nss_groupsbymem */
1864 else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
1865 strcmp(dbn, NSS_DBNAM_NETGROUP) == 0)
1866 fmt_type = 2; /* struct nss_innetgr_args */
1867
1868 /* if error - door's switch error */
1869 /* extended data could contain additional information? */
1870 if (status != NSS_SUCCESS) {
1871 if (fmt_type == 0) {
1872 in->h_errno = (int)pbuf->p_herrno;
1873 if (pbuf->p_errno == ERANGE)
1874 in->erange = 1;
1875 }
1876 return (status);
1877 }
1878
1879 if (pbuf->data_off == 0 || pbuf->data_len == 0)
1880 return (NSS_NOTFOUND);
1881
1882 buf = (char *)buffer + pbuf->data_off;
1883 len = pbuf->data_len;
1884
1885 /* sidestep odd cases */
1886 if (fmt_type == 1) {
1887 arg = (struct nss_groupsbymem *)in;
1888 /* copy returned gid array from returned nscd buffer */
1889 i = len / sizeof (gid_t);
1890 /* not enough buffer */
1891 if (i > arg->maxgids) {
1892 i = arg->maxgids;
1893 }
1894 arg->numgids = i;
1895 gidp = arg->gid_array;
1896 gptr = (gid_t *)((void *)buf);
1897 (void) memcpy(gidp, gptr, len);
1898 return (NSS_SUCCESS);
1899 }
1900 if (fmt_type == 2) {
1901 struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in;
1902
1903 if (pbuf->p_status == NSS_SUCCESS) {
1904 arg->status = NSS_NETGR_FOUND;
1905 return (NSS_SUCCESS);
1906 } else {
1907 arg->status = NSS_NETGR_NO;
1908 return (NSS_NOTFOUND);
1909 }
1910 }
1911
1912 /* process the normal cases */
1913 /* marshall data directly into users buffer */
1914 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1915 in->buf.buflen);
1916 if (ret == NSS_STR_PARSE_ERANGE) {
1917 in->returnval = 0;
1918 in->returnlen = 0;
1919 in->erange = 1;
1920 ret = NSS_NOTFOUND;
1921 } else if (ret == NSS_STR_PARSE_SUCCESS) {
1922 in->returnval = in->buf.result;
1923 in->returnlen = len;
1924 ret = NSS_SUCCESS;
1925 }
1926 in->h_errno = (int)pbuf->p_herrno;
1927 return ((nss_status_t)ret);
1928 }
1929
1930 /*
1931 * Unpack a returned packed {set,get,end}ent arguments buffer
1932 * Return: status, errnos, cookie info and results from requested operation.
1933 */
1934
1935 /*ARGSUSED*/
1936 nss_status_t
1937 nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp,
1938 nss_db_initf_t initf, nss_getent_t *contextpp, void *args)
1939 {
1940 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1941 nss_XbyY_args_t *in = (nss_XbyY_args_t *)args;
1942 struct nss_getent_context *contextp = contextpp->ctx;
1943 nssuint_t *nptr;
1944 nssuint_t cookie;
1945 nss_status_t status;
1946 char *buf;
1947 int len;
1948 int ret;
1949
1950 if (pbuf == NULL)
1951 return (-1);
1952 status = pbuf->p_status;
1953 /* if error - door's switch error */
1954 /* extended data could contain additional information? */
1955 if (status != NSS_SUCCESS)
1956 return (status);
1957
1958 /* unpack assigned cookie from SET/GET/END request */
1959 if (pbuf->key_off == 0 ||
1960 pbuf->key_len != (sizeof (nssuint_t) * 2))
1961 return (NSS_NOTFOUND);
1962
1963 nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1964 cookie = contextp->cookie;
1965 if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent &&
1966 cookie != *nptr) {
1967 /*
1968 * Should either be new, or the cookie returned by the last
1969 * setent (i.e., this is the first getent after the setent)
1970 * or a match, else error
1971 */
1972 return (NSS_NOTFOUND);
1973 }
1974 /* save away for the next ent request */
1975 contextp->cookie = *nptr++;
1976 contextp->seq_num = *nptr;
1977
1978 /* All done if no marshalling is expected {set,end}ent */
1979 if (args == NULL)
1980 return (NSS_SUCCESS);
1981
1982 /* unmarshall the data */
1983 if (pbuf->data_off == 0 || pbuf->data_len == 0)
1984 return (NSS_NOTFOUND);
1985 buf = (char *)buffer + pbuf->data_off;
1986
1987 len = pbuf->data_len;
1988
1989 /* marshall data directly into users buffer */
1990 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1991 in->buf.buflen);
1992 if (ret == NSS_STR_PARSE_ERANGE) {
1993 in->returnval = 0;
1994 in->returnlen = 0;
1995 in->erange = 1;
1996 } else if (ret == NSS_STR_PARSE_SUCCESS) {
1997 in->returnval = in->buf.result;
1998 in->returnlen = len;
1999 }
2000 in->h_errno = (int)pbuf->p_herrno;
2001 return ((nss_status_t)ret);
2002 }
2003
2004 /*
2005 * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
2006 */
2007
2008 nss_status_t
2009 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
2010 void *search_args)
2011 {
2012 nss_pheader_t *pbuf;
2013 void *doorptr = NULL;
2014 size_t bufsize = 0;
2015 size_t datasize = 0;
2016 nss_status_t status;
2017
2018 if (_nsc_proc_is_cache() > 0) {
2019 /* internal nscd call - don't use the door */
2020 return (NSS_TRYLOCAL);
2021 }
2022
2023 /* standard client calls nscd code */
2024 if (search_args == NULL)
2025 return (NSS_NOTFOUND);
2026
2027 /* get the door buffer & configured size */
2028 bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
2029 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2030 return (NSS_TRYLOCAL);
2031 if (doorptr == NULL || bufsize == 0)
2032 return (NSS_TRYLOCAL);
2033
2034 pbuf = (nss_pheader_t *)doorptr;
2035 /* pack argument and request into door buffer */
2036 pbuf->nsc_callnumber = NSCD_SEARCH;
2037 /* copy relevant door request info into door buffer */
2038 status = nss_pack((void *)pbuf, bufsize, rootp,
2039 initf, search_fnum, search_args);
2040
2041 /* Packing error return error results */
2042 if (status != NSS_SUCCESS)
2043 return (status);
2044
2045 /* transfer packed switch request to nscd via door */
2046 /* data_off can be used because it is header+dbd_len+key_len */
2047 datasize = pbuf->data_off;
2048 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2049
2050 /* If unsuccessful fallback to standard nss logic */
2051 if (status != NSS_SUCCESS) {
2052 /*
2053 * check if doors reallocated the memory underneath us
2054 * if they did munmap it or suffer a memory leak
2055 */
2056 if (doorptr != (void *)pbuf) {
2057 _nsc_resizedoorbuf(bufsize);
2058 (void) munmap((void *)doorptr, bufsize);
2059 }
2060 return (NSS_TRYLOCAL);
2061 }
2062
2063 /* unpack and marshall data/errors to user structure */
2064 /* set any error conditions */
2065 status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2066 search_fnum, search_args);
2067 /*
2068 * check if doors reallocated the memory underneath us
2069 * if they did munmap it or suffer a memory leak
2070 */
2071 if (doorptr != (void *)pbuf) {
2072 _nsc_resizedoorbuf(bufsize);
2073 (void) munmap((void *)doorptr, bufsize);
2074 }
2075 return (status);
2076 }
2077
2078 /*
2079 * contact nscd for a cookie or to reset an existing cookie
2080 * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2081 * continue diverting to local
2082 */
2083 nss_status_t
2084 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2085 nss_getent_t *contextpp)
2086 {
2087 nss_status_t status = NSS_TRYLOCAL;
2088 struct nss_getent_context *contextp = contextpp->ctx;
2089 nss_pheader_t *pbuf;
2090 void *doorptr = NULL;
2091 size_t bufsize = 0;
2092 size_t datasize = 0;
2093
2094 /* return if already in local mode */
2095 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2096 return (NSS_TRYLOCAL);
2097
2098 if (_nsc_proc_is_cache() > 0) {
2099 /* internal nscd call - don't try to use the door */
2100 contextp->cookie = NSCD_LOCAL_COOKIE;
2101 return (NSS_TRYLOCAL);
2102 }
2103
2104 /* get the door buffer & configured size */
2105 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2106 contextp->cookie = NSCD_LOCAL_COOKIE;
2107 return (NSS_TRYLOCAL);
2108 }
2109 if (doorptr == NULL || bufsize == 0) {
2110 contextp->cookie = NSCD_LOCAL_COOKIE;
2111 return (NSS_TRYLOCAL);
2112 }
2113
2114 pbuf = (nss_pheader_t *)doorptr;
2115 pbuf->nsc_callnumber = NSCD_SETENT;
2116
2117 contextp->param.cleanup = NULL;
2118 (*initf)(&contextp->param);
2119 if (contextp->param.name == 0) {
2120 if (contextp->param.cleanup != 0)
2121 (contextp->param.cleanup)(&contextp->param);
2122 errno = ERANGE; /* actually EINVAL */
2123 return (NSS_ERROR);
2124 }
2125
2126 /* pack relevant setent request info into door buffer */
2127 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2128 if (status != NSS_SUCCESS)
2129 return (status);
2130
2131 /* transfer packed switch request to nscd via door */
2132 /* data_off can be used because it is header+dbd_len+key_len */
2133 datasize = pbuf->data_off;
2134 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2135
2136 /* If fallback to standard nss logic (door failure) if possible */
2137 if (status != NSS_SUCCESS) {
2138 if (contextp->cookie == NSCD_NEW_COOKIE) {
2139 contextp->cookie = NSCD_LOCAL_COOKIE;
2140 return (NSS_TRYLOCAL);
2141 }
2142 return (NSS_UNAVAIL);
2143 }
2144 /* unpack returned cookie stash it away */
2145 status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2146 initf, contextpp, NULL);
2147 /* save the setent cookie for later use */
2148 contextp->cookie_setent = contextp->cookie;
2149 /*
2150 * check if doors reallocated the memory underneath us
2151 * if they did munmap it or suffer a memory leak
2152 */
2153 if (doorptr != (void *)pbuf) {
2154 _nsc_resizedoorbuf(bufsize);
2155 (void) munmap((void *)doorptr, bufsize);
2156 }
2157 return (status);
2158 }
2159
2160 nss_status_t
2161 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2162 nss_getent_t *contextpp, void *args)
2163 {
2164 nss_status_t status = NSS_TRYLOCAL;
2165 struct nss_getent_context *contextp = contextpp->ctx;
2166 nss_pheader_t *pbuf;
2167 void *doorptr = NULL;
2168 size_t bufsize = 0;
2169 size_t datasize = 0;
2170
2171 /* return if already in local mode */
2172 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2173 return (NSS_TRYLOCAL);
2174
2175 /* _nsc_setent_u already checked for nscd local case ... proceed */
2176 if (args == NULL)
2177 return (NSS_NOTFOUND);
2178
2179 /* get the door buffer & configured size */
2180 bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2181 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2182 return (NSS_UNAVAIL);
2183 if (doorptr == NULL || bufsize == 0)
2184 return (NSS_UNAVAIL);
2185
2186 pbuf = (nss_pheader_t *)doorptr;
2187 pbuf->nsc_callnumber = NSCD_GETENT;
2188
2189 /* pack relevant setent request info into door buffer */
2190 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2191 if (status != NSS_SUCCESS)
2192 return (status);
2193
2194 /* transfer packed switch request to nscd via door */
2195 /* data_off can be used because it is header+dbd_len+key_len */
2196 datasize = pbuf->data_off;
2197 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2198
2199 /* If fallback to standard nss logic (door failure) if possible */
2200 if (status != NSS_SUCCESS) {
2201 if (status == NSS_TRYLOCAL ||
2202 contextp->cookie == NSCD_NEW_COOKIE) {
2203 contextp->cookie = NSCD_LOCAL_COOKIE;
2204
2205 /* init the local cookie */
2206 nss_setent_u(rootp, initf, contextpp);
2207 if (contextpp->ctx == 0)
2208 return (NSS_UNAVAIL);
2209 return (NSS_TRYLOCAL);
2210 }
2211 return (NSS_UNAVAIL);
2212 }
2213 /* check error, unpack and process results */
2214 status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2215 initf, contextpp, args);
2216 /*
2217 * check if doors reallocated the memory underneath us
2218 * if they did munmap it or suffer a memory leak
2219 */
2220 if (doorptr != (void *)pbuf) {
2221 _nsc_resizedoorbuf(bufsize);
2222 (void) munmap((void *)doorptr, bufsize);
2223 }
2224 return (status);
2225 }
2226
2227 nss_status_t
2228 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2229 nss_getent_t *contextpp)
2230 {
2231 nss_status_t status = NSS_TRYLOCAL;
2232 struct nss_getent_context *contextp = contextpp->ctx;
2233 nss_pheader_t *pbuf;
2234 void *doorptr = NULL;
2235 size_t bufsize = 0;
2236 size_t datasize = 0;
2237
2238 /* return if already in local mode */
2239 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2240 return (NSS_TRYLOCAL);
2241
2242 /* _nsc_setent_u already checked for nscd local case ... proceed */
2243
2244 /* get the door buffer & configured size */
2245 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2246 return (NSS_UNAVAIL);
2247 if (doorptr == NULL || bufsize == 0)
2248 return (NSS_UNAVAIL);
2249
2250 /* pack up a NSCD_ENDGET request passing in the cookie */
2251 pbuf = (nss_pheader_t *)doorptr;
2252 pbuf->nsc_callnumber = NSCD_ENDENT;
2253
2254 /* pack relevant setent request info into door buffer */
2255 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2256 if (status != NSS_SUCCESS)
2257 return (status);
2258
2259 /* transfer packed switch request to nscd via door */
2260 /* data_off can be used because it is header+dbd_len+key_len */
2261 datasize = pbuf->data_off;
2262 (void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2263
2264 /* error codes & unpacking ret values don't matter. We're done */
2265
2266 /*
2267 * check if doors reallocated the memory underneath us
2268 * if they did munmap it or suffer a memory leak
2269 */
2270 if (doorptr != (void *)pbuf) {
2271 _nsc_resizedoorbuf(bufsize);
2272 (void) munmap((void *)doorptr, bufsize);
2273 }
2274
2275 /* clean up initf setup */
2276 if (contextp->param.cleanup != 0)
2277 (contextp->param.cleanup)(&contextp->param);
2278 contextp->param.cleanup = NULL;
2279
2280 /* clear cookie */
2281 contextp->cookie = NSCD_NEW_COOKIE;
2282 return (NSS_SUCCESS);
2283 }
2284
2285 /*
2286 * Internal private API to return default suggested buffer sizes
2287 * for nsswitch API requests.
2288 */
2289
2290 size_t
2291 _nss_get_bufsizes(int arg)
2292 {
2293 switch (arg) {
2294 case _SC_GETGR_R_SIZE_MAX:
2295 return (__nss_buflen_group);
2296 }
2297 return (__nss_buflen_default);
2298 }
2299
2300 void *
2301 _nss_XbyY_fini(nss_XbyY_args_t *args)
2302 {
2303 if ((args->returnval == NULL) && (args->erange != 0))
2304 errno = ERANGE;
2305 return (args->returnval);
2306 }