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 NSS_DEBUG_INSECURE
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 NSS_DEBUG_INSECURE
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 NSS_DEBUG_INSECURE
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
nss_cfgcn_cmp(const char * cfgname,const char * compname)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
nss_cfg_init()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 *
nss_cfgcomp_get(char * name,int add)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 *
nss_cfgparam_get(char * name,int add)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
nss_cfg_del(nss_config_t * cfgp)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
nss_cfg_get(nss_config_t * next)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
nss_cfg_put(nss_config_t * next,int add)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
nss_config(nss_config_t ** plist,int cnt)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
nss_cfg_policy_init()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
set_option(struct option * opt,char * name,char * val)641 set_option(struct option *opt, char *name, char *val)
642 {
643 int n;
644 char *p;
645 #ifdef NSS_DEBUG_INSECURE
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 NSS_DEBUG_INSECURE
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
__parse_environment(struct option * opt,char * p)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
nss_get_environment()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
__nis_get_environment()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 *
nss_get_backend_u(nss_db_root_t ** rootpp,struct nss_db_state * s,int n_src)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
nss_put_backend_u(struct nss_db_state * s,int n_src,nss_backend_t * be)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 *
_nss_db_state_constr(nss_db_initf_t initf)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
_nss_src_state_destr(struct nss_src_state * src,int max_dormant)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
_nss_db_state_destr(struct nss_db_state * s)974 _nss_db_state_destr(struct nss_db_state *s)
975 {
976 if (s == NULL)
977 return;
978
979 /* === mutex_destroy(&s->orphan_root.lock); */
980 if (s->p.cleanup != 0) {
981 (*s->p.cleanup)(&s->p);
982 }
983 if (s->config != 0) {
984 (void) __nsw_freeconfig_v1(s->config);
985 }
986 if (s->src != 0) {
987 int n_src;
988
989 for (n_src = 0; n_src < s->max_src; n_src++) {
990 _nss_src_state_destr(&s->src[n_src],
991 s->p.max_dormant_per_src);
992 }
993 libc_free(s->src);
994 }
995 libc_free(s);
996 }
997
998
999 /*
1000 * _nss_status_vec() returns a bit vector of all status codes returned during
1001 * the most recent call to nss_search().
1002 * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on
1003 * failure.
1004 * These functions are private. Don't use them externally without discussing
1005 * it with the switch maintainers.
1006 */
1007 static uint_t *
_nss_status_vec_p()1008 _nss_status_vec_p()
1009 {
1010 return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL));
1011 }
1012
1013 unsigned int
_nss_status_vec(void)1014 _nss_status_vec(void)
1015 {
1016 unsigned int *status_vec_p = _nss_status_vec_p();
1017
1018 return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL));
1019 }
1020
1021 static void
output_loop_diag_a(int n,char * dbase,struct __nsw_lookup_v1 * lkp)1022 output_loop_diag_a(int n,
1023 char *dbase,
1024 struct __nsw_lookup_v1 *lkp)
1025 {
1026 (void) fprintf(__nss_debug_file,
1027 "NSS_retry(%d): '%s': trying '%s' ... ",
1028 n, dbase, lkp->service_name);
1029 (void) fflush(__nss_debug_file);
1030
1031 }
1032
1033 static void
output_loop_diag_b(nss_status_t res,struct __nsw_lookup_v1 * lkp)1034 output_loop_diag_b(nss_status_t res,
1035 struct __nsw_lookup_v1 *lkp)
1036 {
1037 (void) fprintf(__nss_debug_file, "result=");
1038 switch (res) {
1039 case NSS_SUCCESS:
1040 (void) fprintf(__nss_debug_file, "SUCCESS");
1041 break;
1042 case NSS_NOTFOUND:
1043 (void) fprintf(__nss_debug_file, "NOTFOUND");
1044 break;
1045 case NSS_UNAVAIL:
1046 (void) fprintf(__nss_debug_file, "UNAVAIL");
1047 break;
1048 case NSS_TRYAGAIN:
1049 (void) fprintf(__nss_debug_file, "TRYAGAIN");
1050 break;
1051 case NSS_NISSERVDNS_TRYAGAIN:
1052 (void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN");
1053 break;
1054 default:
1055 (void) fprintf(__nss_debug_file, "undefined");
1056 }
1057 (void) fprintf(__nss_debug_file, ", action=");
1058 switch (lkp->actions[res]) {
1059 case __NSW_CONTINUE:
1060 (void) fprintf(__nss_debug_file, "CONTINUE");
1061 break;
1062 case __NSW_RETURN:
1063 (void) fprintf(__nss_debug_file, "RETURN");
1064 break;
1065 case __NSW_TRYAGAIN_FOREVER:
1066 (void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER");
1067 break;
1068 case __NSW_TRYAGAIN_NTIMES:
1069 (void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)",
1070 lkp->max_retries);
1071 break;
1072 case __NSW_TRYAGAIN_PAUSED:
1073 (void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED");
1074 break;
1075 default:
1076 (void) fprintf(__nss_debug_file, "undefined");
1077 }
1078 (void) fprintf(__nss_debug_file, "\n");
1079 }
1080
1081 #define NSS_BACKOFF(n, b, t) \
1082 ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1))))
1083
1084 static int
retry_test(nss_status_t res,int n,struct __nsw_lookup_v1 * lkp)1085 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
1086 {
1087 if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) {
1088 if (res == NSS_SUCCESS) {
1089 __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
1090 __NSW_UNPAUSE_ACTION(
1091 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
1092 }
1093 return (0);
1094 }
1095
1096 if ((res == NSS_TRYAGAIN &&
1097 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
1098 (res == NSS_NISSERVDNS_TRYAGAIN &&
1099 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
1100 return (1);
1101
1102 if (res == NSS_TRYAGAIN &&
1103 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1104 if (n <= lkp->max_retries)
1105 return (1);
1106 else {
1107 lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
1108 return (0);
1109 }
1110
1111 if (res == NSS_NISSERVDNS_TRYAGAIN &&
1112 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
1113 if (n <= lkp->max_retries)
1114 return (1);
1115 else {
1116 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
1117 __NSW_TRYAGAIN_PAUSED;
1118 return (0);
1119 }
1120
1121 return (0);
1122 }
1123
1124 /*
1125 * Switch policy component functional interfaces
1126 */
1127
1128 void
nss_delete(nss_db_root_t * rootp)1129 nss_delete(nss_db_root_t *rootp)
1130 {
1131 struct nss_db_state *s;
1132
1133 /* no name service cache daemon divert here */
1134 /* local nss_delete decrements state reference counts */
1135 /* and may free up opened switch resources. */
1136
1137 NSS_ROOTLOCK(rootp, &s);
1138 if (s == 0) {
1139 NSS_UNLOCK(rootp);
1140 } else {
1141 rootp->s = 0;
1142 NSS_UNREF_UNLOCK(rootp, s);
1143 }
1144 }
1145
1146 nss_status_t
nss_search(nss_db_root_t * rootp,nss_db_initf_t initf,int search_fnum,void * search_args)1147 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1148 void *search_args)
1149 {
1150 nss_status_t res = NSS_UNAVAIL;
1151 struct nss_db_state *s;
1152 int n_src;
1153 unsigned int *status_vec_p;
1154
1155 /* name service cache daemon divert */
1156 res = _nsc_search(rootp, initf, search_fnum, search_args);
1157 if (res != NSS_TRYLOCAL)
1158 return (res);
1159
1160 /* fall through - process locally */
1161 errno = 0; /* just in case ... */
1162 res = NSS_UNAVAIL;
1163 status_vec_p = _nss_status_vec_p();
1164
1165 if (status_vec_p == NULL) {
1166 return (NSS_UNAVAIL);
1167 }
1168 *status_vec_p = 0;
1169
1170 NSS_LOCK_CHECK(rootp, initf, &s);
1171 if (s == 0) {
1172 NSS_UNLOCK(rootp);
1173 return (res);
1174 }
1175 NSS_STATE_REF_u(s);
1176
1177 for (n_src = 0; n_src < s->max_src; n_src++) {
1178 nss_backend_t *be;
1179 nss_backend_op_t funcp;
1180
1181 res = NSS_UNAVAIL;
1182 if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) {
1183 if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) {
1184 int n_loop = 0;
1185 int no_backoff = 19;
1186 int max_backoff = 5; /* seconds */
1187
1188 do {
1189 /*
1190 * Backend operation may take a while;
1191 * drop the lock so we don't serialize
1192 * more than necessary.
1193 */
1194 NSS_UNLOCK(rootp);
1195
1196 /* After several tries, backoff... */
1197 if (n_loop > no_backoff) {
1198 if (__nss_debug_eng_loop > 1)
1199 (void) fprintf(
1200 __nss_debug_file,
1201 "NSS: loop: "
1202 "sleeping %d ...\n",
1203 NSS_BACKOFF(n_loop,
1204 no_backoff,
1205 max_backoff));
1206
1207 (void) sleep(NSS_BACKOFF(n_loop,
1208 no_backoff, max_backoff));
1209 }
1210
1211 if (__nss_debug_eng_loop)
1212 output_loop_diag_a(n_loop,
1213 s->config->dbase,
1214 s->src[n_src].lkp);
1215
1216
1217 res = (*funcp)(be, search_args);
1218 NSS_RELOCK(&rootp, s);
1219 n_loop++;
1220 if (__nss_debug_eng_loop)
1221 output_loop_diag_b(res,
1222 s->src[n_src].lkp);
1223 } while (retry_test(res, n_loop,
1224 s->src[n_src].lkp));
1225 }
1226 nss_put_backend_u(s, n_src, be);
1227 }
1228 *status_vec_p |= (1 << res);
1229 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1230 if (__nss_debug_eng_loop)
1231 (void) fprintf(__nss_debug_file,
1232 "NSS: '%s': return.\n",
1233 s->config->dbase);
1234 break;
1235 } else
1236 if (__nss_debug_eng_loop)
1237 (void) fprintf(__nss_debug_file,
1238 "NSS: '%s': continue ...\n",
1239 s->config->dbase);
1240 }
1241 NSS_UNREF_UNLOCK(rootp, s);
1242 return (res);
1243 }
1244
1245
1246 /*
1247 * Start of nss_{setent|getent|endent}
1248 */
1249
1250 /*
1251 * State (here called "context") for one setent/getent.../endent sequence.
1252 * In principle there could be multiple contexts active for a single
1253 * database; in practice, since Posix and UI have helpfully said that
1254 * getent() state is global rather than, say, per-thread or user-supplied,
1255 * we have at most one of these per nss_db_state.
1256 * XXX ? Is this statement still true?
1257 *
1258 * NSS2 - a client's context is maintained as a cookie delivered by and
1259 * passed to nscd. The cookie is a 64 bit (nssuint_t) unique opaque value
1260 * created by nscd.
1261 * cookie states:
1262 * NSCD_NEW_COOKIE - cookie value uninitialized
1263 * NSCD_LOCAL_COOKIE - setent is a local setent
1264 * all other - NSCD unique opaque id for this setent
1265 * A client's context is also associated with a seq_num. This is a nscd
1266 * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd
1267 * to validate the sequencing of the context. The client treats this as
1268 * a pass through value.
1269 *
1270 * XXX ?? Use Cookie as cross-check info so that we can detect an
1271 * nss_context that missed an nss_delete() or similar.
1272 */
1273
1274 struct nss_getent_context {
1275 int n_src; /* >= max_src ==> end of sequence */
1276 nss_backend_t *be;
1277 struct nss_db_state *s;
1278 nssuint_t cookie;
1279 nssuint_t seq_num;
1280 nssuint_t cookie_setent;
1281 nss_db_params_t param;
1282 };
1283
1284 static void nss_setent_u(nss_db_root_t *, nss_db_initf_t, nss_getent_t *);
1285 static nss_status_t nss_getent_u(nss_db_root_t *, nss_db_initf_t,
1286 nss_getent_t *, void *);
1287 static void nss_endent_u(nss_db_root_t *, nss_db_initf_t, nss_getent_t *);
1288
1289 void
nss_setent(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)1290 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1291 {
1292 if (contextpp == 0) {
1293 return;
1294 }
1295 cancel_safe_mutex_lock(&contextpp->lock);
1296 nss_setent_u(rootp, initf, contextpp);
1297 cancel_safe_mutex_unlock(&contextpp->lock);
1298 }
1299
1300 nss_status_t
nss_getent(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp,void * args)1301 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1302 void *args)
1303 {
1304 nss_status_t status;
1305
1306 if (contextpp == 0) {
1307 return (NSS_UNAVAIL);
1308 }
1309 cancel_safe_mutex_lock(&contextpp->lock);
1310 status = nss_getent_u(rootp, initf, contextpp, args);
1311 cancel_safe_mutex_unlock(&contextpp->lock);
1312 return (status);
1313 }
1314
1315 void
nss_endent(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)1316 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp)
1317 {
1318 if (contextpp == 0) {
1319 return;
1320 }
1321 cancel_safe_mutex_lock(&contextpp->lock);
1322 nss_endent_u(rootp, initf, contextpp);
1323 cancel_safe_mutex_unlock(&contextpp->lock);
1324 }
1325
1326 /*
1327 * Each of the _u versions of the nss interfaces assume that the context
1328 * lock is held. No need to divert to nscd. Private to local sequencing.
1329 */
1330
1331 static void
end_iter_u(nss_db_root_t * rootp,struct nss_getent_context * contextp)1332 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1333 {
1334 struct nss_db_state *s;
1335 nss_backend_t *be;
1336 int n_src;
1337
1338 s = contextp->s;
1339 n_src = contextp->n_src;
1340 be = contextp->be;
1341
1342 if (s != 0) {
1343 if (n_src < s->max_src && be != 0) {
1344 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1345 NSS_RELOCK(&rootp, s);
1346 nss_put_backend_u(s, n_src, be);
1347 contextp->be = 0; /* Should be unnecessary, but hey */
1348 NSS_UNREF_UNLOCK(rootp, s);
1349 }
1350 contextp->s = 0;
1351 }
1352 }
1353
1354 static void
nss_setent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)1355 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1356 nss_getent_t *contextpp)
1357 {
1358 nss_status_t status;
1359 struct nss_db_state *s;
1360 struct nss_getent_context *contextp;
1361 nss_backend_t *be;
1362 int n_src;
1363
1364 /* setup process wide context while locked */
1365 if ((contextp = contextpp->ctx) == 0) {
1366 if ((contextp = libc_malloc(sizeof (*contextp))) == 0) {
1367 return;
1368 }
1369 contextpp->ctx = contextp;
1370 contextp->cookie = NSCD_NEW_COOKIE; /* cookie init */
1371 contextp->seq_num = 0; /* seq_num init */
1372 s = 0;
1373 } else {
1374 s = contextp->s;
1375 if (contextp->cookie != NSCD_LOCAL_COOKIE)
1376 contextp->cookie = NSCD_NEW_COOKIE;
1377 }
1378
1379 /* name service cache daemon divert */
1380 if (contextp->cookie == NSCD_NEW_COOKIE) {
1381 status = _nsc_setent_u(rootp, initf, contextpp);
1382 if (status != NSS_TRYLOCAL)
1383 return;
1384 }
1385
1386 /* fall through - process locally */
1387 if (s == 0) {
1388 NSS_LOCK_CHECK(rootp, initf, &s);
1389 if (s == 0) {
1390 /* Couldn't set up state, so quit */
1391 NSS_UNLOCK(rootp);
1392 /* ==== is there any danger of not having done an */
1393 /* end_iter() here, and hence of losing backends? */
1394 contextpp->ctx = 0;
1395 libc_free(contextp);
1396 return;
1397 }
1398 NSS_STATE_REF_u(s);
1399 contextp->s = s;
1400 } else {
1401 s = contextp->s;
1402 n_src = contextp->n_src;
1403 be = contextp->be;
1404 if (n_src == 0 && be != 0) {
1405 /*
1406 * Optimization: don't do endent, don't change
1407 * backends, just do the setent. Look Ma, no locks
1408 * (nor any context that needs updating).
1409 */
1410 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1411 return;
1412 }
1413 if (n_src < s->max_src && be != 0) {
1414 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1415 NSS_RELOCK(&rootp, s);
1416 nss_put_backend_u(s, n_src, be);
1417 contextp->be = 0; /* Play it safe */
1418 } else {
1419 NSS_RELOCK(&rootp, s);
1420 }
1421 }
1422 for (n_src = 0, be = 0; n_src < s->max_src &&
1423 (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) {
1424 ;
1425 }
1426 NSS_UNLOCK(rootp);
1427
1428 contextp->n_src = n_src;
1429 contextp->be = be;
1430
1431 if (be == 0) {
1432 /* Things are broken enough that we can't do setent/getent */
1433 nss_endent_u(rootp, initf, contextpp);
1434 return;
1435 }
1436 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1437 }
1438
1439 static nss_status_t
nss_getent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp,void * args)1440 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1441 nss_getent_t *contextpp, void *args)
1442 {
1443 nss_status_t status;
1444 struct nss_db_state *s;
1445 struct nss_getent_context *contextp;
1446 int n_src;
1447 nss_backend_t *be;
1448
1449 if ((contextp = contextpp->ctx) == 0) {
1450 nss_setent_u(rootp, initf, contextpp);
1451 if ((contextp = contextpp->ctx) == 0) {
1452 /* Give up */
1453 return (NSS_UNAVAIL);
1454 }
1455 }
1456 /* name service cache daemon divert */
1457 status = _nsc_getent_u(rootp, initf, contextpp, args);
1458 if (status != NSS_TRYLOCAL)
1459 return (status);
1460
1461 /* fall through - process locally */
1462 s = contextp->s;
1463 n_src = contextp->n_src;
1464 be = contextp->be;
1465
1466 if (s == 0) {
1467 /*
1468 * We've done an end_iter() and haven't done nss_setent()
1469 * or nss_endent() since; we should stick in this state
1470 * until the caller invokes one of those two routines.
1471 */
1472 return (NSS_SUCCESS);
1473 }
1474
1475 while (n_src < s->max_src) {
1476 nss_status_t res;
1477
1478 if (be == 0) {
1479 /* If it's null it's a bug, but let's play safe */
1480 res = NSS_UNAVAIL;
1481 } else {
1482 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1483 }
1484
1485 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) {
1486 if (res != __NSW_SUCCESS) {
1487 end_iter_u(rootp, contextp);
1488 }
1489 return (res);
1490 }
1491 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1492 NSS_RELOCK(&rootp, s);
1493 nss_put_backend_u(s, n_src, be);
1494 do {
1495 n_src++;
1496 } while (n_src < s->max_src &&
1497 (be = nss_get_backend_u(&rootp, s, n_src)) == 0);
1498 contextp->be = be;
1499 if (be == 0) {
1500 /*
1501 * This is the case where we failed to get the backend
1502 * for the last source. We exhausted all sources.
1503 *
1504 * We need to do cleanup ourselves because end_iter_u()
1505 * does not do it for be == 0.
1506 */
1507 NSS_UNREF_UNLOCK(rootp, s);
1508 contextp->s = 0;
1509 break;
1510 } else {
1511 NSS_UNLOCK(rootp);
1512 contextp->n_src = n_src;
1513 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1514 }
1515 }
1516 /* Got to the end of the sources without finding another entry */
1517 end_iter_u(rootp, contextp);
1518 return (NSS_SUCCESS);
1519 /* success is either a successful entry or end of the sources */
1520 }
1521
1522 /*ARGSUSED*/
1523 static void
nss_endent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)1524 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1525 nss_getent_t *contextpp)
1526 {
1527 nss_status_t status;
1528 struct nss_getent_context *contextp;
1529
1530 if ((contextp = contextpp->ctx) == 0) {
1531 /* nss_endent() on an unused context is a no-op */
1532 return;
1533 }
1534
1535 /* notify name service cache daemon */
1536 status = _nsc_endent_u(rootp, initf, contextpp);
1537 if (status != NSS_TRYLOCAL) {
1538 /* clean up */
1539 libc_free(contextp);
1540 contextpp->ctx = 0;
1541 return;
1542 }
1543
1544 /* fall through - process locally */
1545
1546 /*
1547 * Existing code (BSD, SunOS) works in such a way that getXXXent()
1548 * following an endXXXent() behaves as though the user had invoked
1549 * setXXXent(), i.e. it iterates properly from the beginning.
1550 * We'd better not break this, so our choices are
1551 * (1) leave the context structure around, and do nss_setent or
1552 * something equivalent,
1553 * or (2) free the context completely, and rely on the code in
1554 * nss_getent() that makes getXXXent() do the right thing
1555 * even without a preceding setXXXent().
1556 * The code below does (2), which frees up resources nicely but will
1557 * cost more if the user then does more getXXXent() operations.
1558 * Moral: for efficiency, don't call endXXXent() prematurely.
1559 */
1560 end_iter_u(rootp, contextp);
1561 libc_free(contextp);
1562 contextpp->ctx = 0;
1563 }
1564
1565 /*
1566 * pack dbd data into header
1567 * Argment pointers assumed valid.
1568 * poff offset position pointer
1569 * IN = starting offset for dbd header
1570 * OUT = starting offset for next section
1571 */
1572
1573 static nss_status_t
nss_pack_dbd(void * buffer,size_t bufsize,nss_db_params_t * p,size_t * poff)1574 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff)
1575 {
1576 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1577 nss_dbd_t *pdbd;
1578 size_t off = *poff;
1579 size_t len, blen;
1580 size_t n, nc, dc;
1581 char *bptr;
1582
1583 pbuf->dbd_off = (nssuint_t)off;
1584 bptr = (char *)buffer + off;
1585 blen = bufsize - off;
1586 len = sizeof (nss_dbd_t);
1587
1588 n = nc = dc = 0;
1589 if (p->name == NULL) {
1590 errno = ERANGE; /* actually EINVAL */
1591 return (NSS_ERROR);
1592 }
1593
1594 /* if default config not specified, the flag should be reset */
1595 if (p->default_config == NULL) {
1596 p->default_config = "<NULL>";
1597 p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG;
1598 }
1599
1600 n = strlen(p->name) + 1;
1601 dc = strlen(p->default_config) + 1;
1602 if (n < 2 || dc < 2) { /* What no DB? */
1603 errno = ERANGE; /* actually EINVAL */
1604 return (NSS_ERROR);
1605 }
1606 if (p->config_name != NULL) {
1607 nc = strlen(p->config_name) + 1;
1608 }
1609 if ((len + n + nc + dc) >= blen) {
1610 errno = ERANGE; /* actually EINVAL */
1611 return (NSS_ERROR);
1612 }
1613
1614 pdbd = (nss_dbd_t *)((void *)bptr);
1615 bptr += len;
1616 pdbd->flags = p->flags;
1617 pdbd->o_name = len;
1618 (void) strlcpy(bptr, p->name, n);
1619 len += n;
1620 bptr += n;
1621 if (nc == 0) {
1622 pdbd->o_config_name = 0;
1623 } else {
1624 pdbd->o_config_name = len;
1625 (void) strlcpy(bptr, p->config_name, nc);
1626 bptr += nc;
1627 len += nc;
1628 }
1629 pdbd->o_default_config = len;
1630 (void) strlcpy(bptr, p->default_config, dc);
1631 len += dc;
1632 pbuf->dbd_len = (nssuint_t)len;
1633 off += ROUND_UP(len, sizeof (nssuint_t));
1634 *poff = off;
1635 return (NSS_SUCCESS);
1636 }
1637
1638 /*
1639 * Switch packed and _nsc (switch->nscd) interfaces
1640 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1641 */
1642
1643 nss_status_t
nss_pack(void * buffer,size_t bufsize,nss_db_root_t * rootp __unused,nss_db_initf_t initf,int search_fnum,void * search_args)1644 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp __unused,
1645 nss_db_initf_t initf, int search_fnum, void *search_args)
1646 {
1647 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1648 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args;
1649 nss_db_params_t tparam = { 0 };
1650 nss_status_t ret = NSS_ERROR;
1651 const char *dbn;
1652 size_t blen, len, off = 0;
1653 char *bptr;
1654 struct nss_groupsbymem *gbm;
1655
1656 if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) {
1657 errno = ERANGE; /* actually EINVAL */
1658 return (ret);
1659 }
1660 tparam.cleanup = NULL;
1661 (*initf)(&tparam);
1662 if ((dbn = tparam.name) == 0) {
1663 if (tparam.cleanup != 0)
1664 (tparam.cleanup)(&tparam);
1665 errno = ERANGE; /* actually EINVAL */
1666 return (ret);
1667 }
1668
1669 /* init buffer header */
1670 pbuf->pbufsiz = (nssuint_t)bufsize;
1671 pbuf->p_ruid = (uint32_t)getuid();
1672 pbuf->p_euid = (uint32_t)geteuid();
1673 pbuf->p_version = NSCD_HEADER_REV;
1674 pbuf->p_status = 0;
1675 pbuf->p_errno = 0;
1676 pbuf->p_herrno = 0;
1677
1678 /* possible audituser init */
1679 if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0)
1680 pbuf->p_herrno = (uint32_t)in->h_errno;
1681
1682 pbuf->libpriv = 0;
1683
1684 off = sizeof (nss_pheader_t);
1685
1686 /* setup getXbyY operation - database and sub function */
1687 pbuf->nss_dbop = (uint32_t)search_fnum;
1688 ret = nss_pack_dbd(buffer, bufsize, &tparam, &off);
1689 if (ret != NSS_SUCCESS) {
1690 errno = ERANGE; /* actually EINVAL */
1691 return (ret);
1692 }
1693 ret = NSS_ERROR;
1694 /* setup request key */
1695 pbuf->key_off = (nssuint_t)off;
1696 bptr = (char *)buffer + off;
1697 blen = bufsize - off;
1698 /* use key2str if provided, else call default getXbyY packer */
1699 if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) {
1700 /* This has to run locally due to backend knowledge */
1701 if (search_fnum == NSS_DBOP_NETGROUP_SET) {
1702 errno = 0;
1703 return (NSS_TRYLOCAL);
1704 }
1705 /* use default packer for known getXbyY ops */
1706 ret = nss_default_key2str(bptr, blen, in, dbn,
1707 search_fnum, &len);
1708 } else if (in->key2str == NULL ||
1709 (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1710 strcmp(dbn, NSS_DBNAM_GROUP) == 0)) {
1711 /* use default packer for known getXbyY ops */
1712 ret = nss_default_key2str(bptr, blen, in, dbn,
1713 search_fnum, &len);
1714 } else {
1715 ret = (*in->key2str)(bptr, blen, &in->key, &len);
1716 }
1717 if (tparam.cleanup != 0)
1718 (tparam.cleanup)(&tparam);
1719 if (ret != NSS_SUCCESS) {
1720 errno = ERANGE; /* actually ENOMEM */
1721 return (ret);
1722 }
1723 pbuf->key_len = (nssuint_t)len;
1724 off += ROUND_UP(len, sizeof (nssuint_t));
1725
1726 pbuf->data_off = (nssuint_t)off;
1727 pbuf->data_len = (nssuint_t)(bufsize - off);
1728 /*
1729 * Prime data return with first result if
1730 * the first result is passed in
1731 * [_getgroupsbymember oddness]
1732 */
1733 gbm = (struct nss_groupsbymem *)search_args;
1734 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1735 strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) {
1736 gid_t *gidp;
1737 gidp = (gid_t *)((void *)((char *)buffer + off));
1738 *gidp = gbm->gid_array[0];
1739 }
1740
1741 errno = 0; /* just in case ... */
1742 return (NSS_SUCCESS);
1743 }
1744
1745 /*
1746 * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces
1747 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND
1748 */
1749
1750 nss_status_t
nss_pack_ent(void * buffer,size_t bufsize,nss_db_root_t * rootp __unused,nss_db_initf_t initf,nss_getent_t * contextpp)1751 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp __unused,
1752 nss_db_initf_t initf, nss_getent_t *contextpp)
1753 {
1754 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1755 struct nss_getent_context *contextp = contextpp->ctx;
1756 nss_status_t ret = NSS_ERROR;
1757 size_t blen, len = 0, off = 0;
1758 char *bptr;
1759 nssuint_t *nptr;
1760
1761 if (pbuf == NULL || initf == (nss_db_initf_t)NULL) {
1762 errno = ERANGE; /* actually EINVAL */
1763 return (ret);
1764 }
1765
1766 /* init buffer header */
1767 pbuf->pbufsiz = (nssuint_t)bufsize;
1768 pbuf->p_ruid = (uint32_t)getuid();
1769 pbuf->p_euid = (uint32_t)geteuid();
1770 pbuf->p_version = NSCD_HEADER_REV;
1771 pbuf->p_status = 0;
1772 pbuf->p_errno = 0;
1773 pbuf->p_herrno = 0;
1774 pbuf->libpriv = 0;
1775
1776 off = sizeof (nss_pheader_t);
1777
1778 /* setup getXXXent operation - database and sub function */
1779 pbuf->nss_dbop = (uint32_t)0; /* iterators have no dbop */
1780 ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off);
1781 if (ret != NSS_SUCCESS) {
1782 errno = ERANGE; /* actually EINVAL */
1783 return (ret);
1784 }
1785 ret = NSS_ERROR;
1786 off += ROUND_UP(len, sizeof (nssuint_t));
1787
1788 pbuf->key_off = (nssuint_t)off;
1789 bptr = (char *)buffer + off;
1790 blen = bufsize - off;
1791 len = (size_t)(sizeof (nssuint_t) * 2);
1792 if (len >= blen) {
1793 errno = ERANGE; /* actually EINVAL */
1794 return (ret);
1795 }
1796 nptr = (nssuint_t *)((void *)bptr);
1797 *nptr++ = contextp->cookie;
1798 *nptr = contextp->seq_num;
1799 pbuf->key_len = (nssuint_t)len;
1800
1801 off += len;
1802 pbuf->data_off = (nssuint_t)off;
1803 pbuf->data_len = (nssuint_t)(bufsize - off);
1804 return (NSS_SUCCESS);
1805 }
1806
1807 /*
1808 * Unpack packed arguments buffer
1809 * Return: status, errnos and results from requested operation.
1810 *
1811 * NOTES: When getgroupsbymember is being processed in the NSCD backend,
1812 * or via the backwards compatibility interfaces then the standard
1813 * str2group API is used in conjunction with process_cstr. When,
1814 * processing a returned buffer, in NSS2 the return results are the
1815 * already digested groups array. Therefore, unpack the digested results
1816 * back to the return buffer.
1817 *
1818 * Note: the digested results are nssuint_t quantities. _getgroupsbymember
1819 * digests int quantities. Therefore convert. Assume input is in nssuint_t
1820 * quantities. Store in an int array... Assume gid's are <= 32 bits...
1821 */
1822
1823 nss_status_t
nss_unpack(void * buffer,size_t bufsize __unused,nss_db_root_t * rootp __unused,nss_db_initf_t initf __unused,int search_fnum,void * search_args)1824 nss_unpack(void *buffer, size_t bufsize __unused, nss_db_root_t *rootp __unused,
1825 nss_db_initf_t initf __unused, int search_fnum, void *search_args)
1826 {
1827 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1828 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args;
1829 nss_dbd_t *pdbd;
1830 char *dbn;
1831 nss_status_t status;
1832 char *buf;
1833 int len;
1834 int ret;
1835 int i;
1836 int fmt_type;
1837 gid_t *gidp;
1838 gid_t *gptr;
1839 struct nss_groupsbymem *arg;
1840
1841
1842 if (pbuf == NULL || in == NULL)
1843 return (-1);
1844 status = pbuf->p_status;
1845 /* Identify odd cases */
1846 pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off));
1847 dbn = (char *)pdbd + pdbd->o_name;
1848 fmt_type = 0; /* nss_XbyY_args_t */
1849 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER &&
1850 strcmp(dbn, NSS_DBNAM_GROUP) == 0)
1851 fmt_type = 1; /* struct nss_groupsbymem */
1852 else if (search_fnum == NSS_DBOP_NETGROUP_IN &&
1853 strcmp(dbn, NSS_DBNAM_NETGROUP) == 0)
1854 fmt_type = 2; /* struct nss_innetgr_args */
1855
1856 /* if error - door's switch error */
1857 /* extended data could contain additional information? */
1858 if (status != NSS_SUCCESS) {
1859 if (fmt_type == 0) {
1860 in->h_errno = (int)pbuf->p_herrno;
1861 if (pbuf->p_errno == ERANGE)
1862 in->erange = 1;
1863 }
1864 return (status);
1865 }
1866
1867 if (pbuf->data_off == 0 || pbuf->data_len == 0)
1868 return (NSS_NOTFOUND);
1869
1870 buf = (char *)buffer + pbuf->data_off;
1871 len = pbuf->data_len;
1872
1873 /* sidestep odd cases */
1874 if (fmt_type == 1) {
1875 arg = (struct nss_groupsbymem *)in;
1876 /* copy returned gid array from returned nscd buffer */
1877 i = len / sizeof (gid_t);
1878 /* not enough buffer */
1879 if (i > arg->maxgids) {
1880 i = arg->maxgids;
1881 }
1882 arg->numgids = i;
1883 gidp = arg->gid_array;
1884 gptr = (gid_t *)((void *)buf);
1885 (void) memcpy(gidp, gptr, len);
1886 return (NSS_SUCCESS);
1887 }
1888 if (fmt_type == 2) {
1889 struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in;
1890
1891 if (pbuf->p_status == NSS_SUCCESS) {
1892 arg->status = NSS_NETGR_FOUND;
1893 return (NSS_SUCCESS);
1894 } else {
1895 arg->status = NSS_NETGR_NO;
1896 return (NSS_NOTFOUND);
1897 }
1898 }
1899
1900 /* process the normal cases */
1901 /* marshall data directly into users buffer */
1902 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1903 in->buf.buflen);
1904 if (ret == NSS_STR_PARSE_ERANGE) {
1905 in->returnval = 0;
1906 in->returnlen = 0;
1907 in->erange = 1;
1908 ret = NSS_NOTFOUND;
1909 } else if (ret == NSS_STR_PARSE_SUCCESS) {
1910 in->returnval = in->buf.result;
1911 in->returnlen = len;
1912 ret = NSS_SUCCESS;
1913 }
1914 in->h_errno = (int)pbuf->p_herrno;
1915 return ((nss_status_t)ret);
1916 }
1917
1918 /*
1919 * Unpack a returned packed {set,get,end}ent arguments buffer
1920 * Return: status, errnos, cookie info and results from requested operation.
1921 */
1922
1923 nss_status_t
nss_unpack_ent(void * buffer,size_t bufsize __unused,nss_db_root_t * rootp __unused,nss_db_initf_t initf __unused,nss_getent_t * contextpp,void * args)1924 nss_unpack_ent(void *buffer, size_t bufsize __unused,
1925 nss_db_root_t *rootp __unused, nss_db_initf_t initf __unused,
1926 nss_getent_t *contextpp, void *args)
1927 {
1928 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1929 nss_XbyY_args_t *in = (nss_XbyY_args_t *)args;
1930 struct nss_getent_context *contextp = contextpp->ctx;
1931 nssuint_t *nptr;
1932 nssuint_t cookie;
1933 nss_status_t status;
1934 char *buf;
1935 int len;
1936 int ret;
1937
1938 if (pbuf == NULL)
1939 return (-1);
1940 status = pbuf->p_status;
1941 /* if error - door's switch error */
1942 /* extended data could contain additional information? */
1943 if (status != NSS_SUCCESS)
1944 return (status);
1945
1946 /* unpack assigned cookie from SET/GET/END request */
1947 if (pbuf->key_off == 0 ||
1948 pbuf->key_len != (sizeof (nssuint_t) * 2))
1949 return (NSS_NOTFOUND);
1950
1951 nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off));
1952 cookie = contextp->cookie;
1953 if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent &&
1954 cookie != *nptr) {
1955 /*
1956 * Should either be new, or the cookie returned by the last
1957 * setent (i.e., this is the first getent after the setent)
1958 * or a match, else error
1959 */
1960 return (NSS_NOTFOUND);
1961 }
1962 /* save away for the next ent request */
1963 contextp->cookie = *nptr++;
1964 contextp->seq_num = *nptr;
1965
1966 /* All done if no marshalling is expected {set,end}ent */
1967 if (args == NULL)
1968 return (NSS_SUCCESS);
1969
1970 /* unmarshall the data */
1971 if (pbuf->data_off == 0 || pbuf->data_len == 0)
1972 return (NSS_NOTFOUND);
1973 buf = (char *)buffer + pbuf->data_off;
1974
1975 len = pbuf->data_len;
1976
1977 /* marshall data directly into users buffer */
1978 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer,
1979 in->buf.buflen);
1980 if (ret == NSS_STR_PARSE_ERANGE) {
1981 in->returnval = 0;
1982 in->returnlen = 0;
1983 in->erange = 1;
1984 } else if (ret == NSS_STR_PARSE_SUCCESS) {
1985 in->returnval = in->buf.result;
1986 in->returnlen = len;
1987 }
1988 in->h_errno = (int)pbuf->p_herrno;
1989 return ((nss_status_t)ret);
1990 }
1991
1992 /*
1993 * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs
1994 */
1995
1996 nss_status_t
_nsc_search(nss_db_root_t * rootp,nss_db_initf_t initf,int search_fnum,void * search_args)1997 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
1998 void *search_args)
1999 {
2000 nss_pheader_t *pbuf;
2001 void *doorptr = NULL;
2002 size_t bufsize = 0;
2003 size_t datasize = 0;
2004 nss_status_t status;
2005
2006 if (_nsc_proc_is_cache() > 0) {
2007 /* internal nscd call - don't use the door */
2008 return (NSS_TRYLOCAL);
2009 }
2010
2011 /* standard client calls nscd code */
2012 if (search_args == NULL)
2013 return (NSS_NOTFOUND);
2014
2015 /* get the door buffer & configured size */
2016 bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen;
2017 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2018 return (NSS_TRYLOCAL);
2019 if (doorptr == NULL || bufsize == 0)
2020 return (NSS_TRYLOCAL);
2021
2022 pbuf = (nss_pheader_t *)doorptr;
2023 /* pack argument and request into door buffer */
2024 pbuf->nsc_callnumber = NSCD_SEARCH;
2025 /* copy relevant door request info into door buffer */
2026 status = nss_pack((void *)pbuf, bufsize, rootp,
2027 initf, search_fnum, search_args);
2028
2029 /* Packing error return error results */
2030 if (status != NSS_SUCCESS)
2031 return (status);
2032
2033 /* transfer packed switch request to nscd via door */
2034 /* data_off can be used because it is header+dbd_len+key_len */
2035 datasize = pbuf->data_off;
2036 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2037
2038 /* If unsuccessful fallback to standard nss logic */
2039 if (status != NSS_SUCCESS) {
2040 /*
2041 * check if doors reallocated the memory underneath us
2042 * if they did munmap it or suffer a memory leak
2043 */
2044 if (doorptr != (void *)pbuf) {
2045 _nsc_resizedoorbuf(bufsize);
2046 (void) munmap((void *)doorptr, bufsize);
2047 }
2048 return (NSS_TRYLOCAL);
2049 }
2050
2051 /* unpack and marshall data/errors to user structure */
2052 /* set any error conditions */
2053 status = nss_unpack((void *)doorptr, bufsize, rootp, initf,
2054 search_fnum, search_args);
2055 /*
2056 * check if doors reallocated the memory underneath us
2057 * if they did munmap it or suffer a memory leak
2058 */
2059 if (doorptr != (void *)pbuf) {
2060 _nsc_resizedoorbuf(bufsize);
2061 (void) munmap((void *)doorptr, bufsize);
2062 }
2063 return (status);
2064 }
2065
2066 /*
2067 * contact nscd for a cookie or to reset an existing cookie
2068 * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and
2069 * continue diverting to local
2070 */
2071 nss_status_t
_nsc_setent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)2072 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2073 nss_getent_t *contextpp)
2074 {
2075 nss_status_t status = NSS_TRYLOCAL;
2076 struct nss_getent_context *contextp = contextpp->ctx;
2077 nss_pheader_t *pbuf;
2078 void *doorptr = NULL;
2079 size_t bufsize = 0;
2080 size_t datasize = 0;
2081
2082 /* return if already in local mode */
2083 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2084 return (NSS_TRYLOCAL);
2085
2086 if (_nsc_proc_is_cache() > 0) {
2087 /* internal nscd call - don't try to use the door */
2088 contextp->cookie = NSCD_LOCAL_COOKIE;
2089 return (NSS_TRYLOCAL);
2090 }
2091
2092 /* get the door buffer & configured size */
2093 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) {
2094 contextp->cookie = NSCD_LOCAL_COOKIE;
2095 return (NSS_TRYLOCAL);
2096 }
2097 if (doorptr == NULL || bufsize == 0) {
2098 contextp->cookie = NSCD_LOCAL_COOKIE;
2099 return (NSS_TRYLOCAL);
2100 }
2101
2102 pbuf = (nss_pheader_t *)doorptr;
2103 pbuf->nsc_callnumber = NSCD_SETENT;
2104
2105 contextp->param.cleanup = NULL;
2106 (*initf)(&contextp->param);
2107 if (contextp->param.name == 0) {
2108 if (contextp->param.cleanup != 0)
2109 (contextp->param.cleanup)(&contextp->param);
2110 errno = ERANGE; /* actually EINVAL */
2111 return (NSS_ERROR);
2112 }
2113
2114 /* pack relevant setent request info into door buffer */
2115 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2116 if (status != NSS_SUCCESS)
2117 return (status);
2118
2119 /* transfer packed switch request to nscd via door */
2120 /* data_off can be used because it is header+dbd_len+key_len */
2121 datasize = pbuf->data_off;
2122 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2123
2124 /* If fallback to standard nss logic (door failure) if possible */
2125 if (status != NSS_SUCCESS) {
2126 if (contextp->cookie == NSCD_NEW_COOKIE) {
2127 contextp->cookie = NSCD_LOCAL_COOKIE;
2128 return (NSS_TRYLOCAL);
2129 }
2130 return (NSS_UNAVAIL);
2131 }
2132 /* unpack returned cookie stash it away */
2133 status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2134 initf, contextpp, NULL);
2135 /* save the setent cookie for later use */
2136 contextp->cookie_setent = contextp->cookie;
2137 /*
2138 * check if doors reallocated the memory underneath us
2139 * if they did munmap it or suffer a memory leak
2140 */
2141 if (doorptr != (void *)pbuf) {
2142 _nsc_resizedoorbuf(bufsize);
2143 (void) munmap((void *)doorptr, bufsize);
2144 }
2145 return (status);
2146 }
2147
2148 nss_status_t
_nsc_getent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp,void * args)2149 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2150 nss_getent_t *contextpp, void *args)
2151 {
2152 nss_status_t status = NSS_TRYLOCAL;
2153 struct nss_getent_context *contextp = contextpp->ctx;
2154 nss_pheader_t *pbuf;
2155 void *doorptr = NULL;
2156 size_t bufsize = 0;
2157 size_t datasize = 0;
2158
2159 /* return if already in local mode */
2160 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2161 return (NSS_TRYLOCAL);
2162
2163 /* _nsc_setent_u already checked for nscd local case ... proceed */
2164 if (args == NULL)
2165 return (NSS_NOTFOUND);
2166
2167 /* get the door buffer & configured size */
2168 bufsize = ((nss_XbyY_args_t *)args)->buf.buflen;
2169 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2170 return (NSS_UNAVAIL);
2171 if (doorptr == NULL || bufsize == 0)
2172 return (NSS_UNAVAIL);
2173
2174 pbuf = (nss_pheader_t *)doorptr;
2175 pbuf->nsc_callnumber = NSCD_GETENT;
2176
2177 /* pack relevant setent request info into door buffer */
2178 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2179 if (status != NSS_SUCCESS)
2180 return (status);
2181
2182 /* transfer packed switch request to nscd via door */
2183 /* data_off can be used because it is header+dbd_len+key_len */
2184 datasize = pbuf->data_off;
2185 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2186
2187 /* If fallback to standard nss logic (door failure) if possible */
2188 if (status != NSS_SUCCESS) {
2189 if (status == NSS_TRYLOCAL ||
2190 contextp->cookie == NSCD_NEW_COOKIE) {
2191 contextp->cookie = NSCD_LOCAL_COOKIE;
2192
2193 /* init the local cookie */
2194 nss_setent_u(rootp, initf, contextpp);
2195 if (contextpp->ctx == 0)
2196 return (NSS_UNAVAIL);
2197 return (NSS_TRYLOCAL);
2198 }
2199 return (NSS_UNAVAIL);
2200 }
2201 /* check error, unpack and process results */
2202 status = nss_unpack_ent((void *)doorptr, bufsize, rootp,
2203 initf, contextpp, args);
2204 /*
2205 * check if doors reallocated the memory underneath us
2206 * if they did munmap it or suffer a memory leak
2207 */
2208 if (doorptr != (void *)pbuf) {
2209 _nsc_resizedoorbuf(bufsize);
2210 (void) munmap((void *)doorptr, bufsize);
2211 }
2212 return (status);
2213 }
2214
2215 nss_status_t
_nsc_endent_u(nss_db_root_t * rootp,nss_db_initf_t initf,nss_getent_t * contextpp)2216 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
2217 nss_getent_t *contextpp)
2218 {
2219 nss_status_t status = NSS_TRYLOCAL;
2220 struct nss_getent_context *contextp = contextpp->ctx;
2221 nss_pheader_t *pbuf;
2222 void *doorptr = NULL;
2223 size_t bufsize = 0;
2224 size_t datasize = 0;
2225
2226 /* return if already in local mode */
2227 if (contextp->cookie == NSCD_LOCAL_COOKIE)
2228 return (NSS_TRYLOCAL);
2229
2230 /* _nsc_setent_u already checked for nscd local case ... proceed */
2231
2232 /* get the door buffer & configured size */
2233 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0)
2234 return (NSS_UNAVAIL);
2235 if (doorptr == NULL || bufsize == 0)
2236 return (NSS_UNAVAIL);
2237
2238 /* pack up a NSCD_ENDGET request passing in the cookie */
2239 pbuf = (nss_pheader_t *)doorptr;
2240 pbuf->nsc_callnumber = NSCD_ENDENT;
2241
2242 /* pack relevant setent request info into door buffer */
2243 status = nss_pack_ent((void *)pbuf, bufsize, rootp, initf, contextpp);
2244 if (status != NSS_SUCCESS)
2245 return (status);
2246
2247 /* transfer packed switch request to nscd via door */
2248 /* data_off can be used because it is header+dbd_len+key_len */
2249 datasize = pbuf->data_off;
2250 (void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize);
2251
2252 /* error codes & unpacking ret values don't matter. We're done */
2253
2254 /*
2255 * check if doors reallocated the memory underneath us
2256 * if they did munmap it or suffer a memory leak
2257 */
2258 if (doorptr != (void *)pbuf) {
2259 _nsc_resizedoorbuf(bufsize);
2260 (void) munmap((void *)doorptr, bufsize);
2261 }
2262
2263 /* clean up initf setup */
2264 if (contextp->param.cleanup != 0)
2265 (contextp->param.cleanup)(&contextp->param);
2266 contextp->param.cleanup = NULL;
2267
2268 /* clear cookie */
2269 contextp->cookie = NSCD_NEW_COOKIE;
2270 return (NSS_SUCCESS);
2271 }
2272
2273 /*
2274 * Internal private API to return default suggested buffer sizes
2275 * for nsswitch API requests.
2276 */
2277
2278 size_t
_nss_get_bufsizes(int arg)2279 _nss_get_bufsizes(int arg)
2280 {
2281 switch (arg) {
2282 case _SC_GETGR_R_SIZE_MAX:
2283 return (__nss_buflen_group);
2284 }
2285 return (__nss_buflen_default);
2286 }
2287
2288 void *
_nss_XbyY_fini(nss_XbyY_args_t * args)2289 _nss_XbyY_fini(nss_XbyY_args_t *args)
2290 {
2291 if ((args->returnval == NULL) && (args->erange != 0))
2292 errno = ERANGE;
2293 return (args->returnval);
2294 }
2295