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