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 * Copyright 2012 Milan Jurik. All rights reserved.
26 */
27
28 #include <stdlib.h> /* getenv() */
29 #include <assert.h>
30 #include <unistd.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <dlfcn.h>
34 #include <nss_dbdefs.h>
35 #include <exec_attr.h>
36 #include <gssapi/gssapi.h>
37 #include "nscd_door.h"
38 #include "nscd_switch.h"
39 #include "nscd_log.h"
40 #include "nscd_frontend.h"
41
42 #pragma weak nss_search = _nss_search
43 #define nss_search _nss_search
44
45 extern rwlock_t nscd_smf_service_state_lock;
46
47 /* nscd id: main, forker, or child */
48 extern int _whoami;
49
50 static int
51 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp)
52 {
53 if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) {
54 if (res == NSS_SUCCESS) {
55 __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]);
56 __NSW_UNPAUSE_ACTION(
57 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]);
58 }
59 return (0);
60 }
61
62 if ((res == NSS_TRYAGAIN &&
63 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) ||
64 (res == NSS_NISSERVDNS_TRYAGAIN &&
65 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER))
66 return (1);
67
68 if (res == NSS_TRYAGAIN &&
69 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
70 if (n <= lkp->max_retries)
71 return (1);
72 else {
73 lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED;
74 return (0);
75 }
76
77 if (res == NSS_NISSERVDNS_TRYAGAIN &&
78 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
79 if (n <= lkp->max_retries)
80 return (1);
81 else {
82 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] =
83 __NSW_TRYAGAIN_PAUSED;
84 return (0);
85 }
86
87 return (0);
88 }
89
90 static thread_key_t loopback_key = THR_ONCE_KEY;
91 typedef struct lb_key {
92 int srci;
93 int dbi;
94 int fnum;
95 int *lb_flagp;
96 } lb_key_t;
97
98 static int
99 set_loopback_key(lb_key_t *key) {
100
101 int rc;
102
103 rc = thr_keycreate_once(&loopback_key, NULL);
104 /* set key if not already set */
105 if (rc == 0 && pthread_getspecific(loopback_key) == NULL)
106 rc = thr_setspecific(loopback_key, key);
107
108 return (rc);
109 }
110
111 static lb_key_t *
112 get_loopback_key(void) {
113
114 char *me = "get_loopback_key";
115 lb_key_t *k = NULL;
116
117 k = pthread_getspecific(loopback_key);
118
119 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
120 (me, "get loopback key, key = %p\n", k);
121
122 return (k);
123 }
124
125 static void
126 clear_loopback_key(lb_key_t *key) {
127
128 char *me = "clear_loopback_key";
129
130 if (loopback_key != THR_ONCE_KEY && key != NULL) {
131 /*
132 * key->lb_flagp points to the location of the
133 * flag, check_flag, in the stack where it was
134 * first set; clearing the flag tells that
135 * stack the loopback error has been resolved
136 */
137 *key->lb_flagp = 0;
138 (void) thr_setspecific(loopback_key, NULL);
139 }
140
141 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
142 (me, "key %p cleared\n", key);
143 }
144
145 static thread_key_t initf_key = THR_ONCE_KEY;
146
147 static int
148 set_initf_key(void *pbuf) {
149
150 int rc;
151
152 rc = thr_keycreate_once(&initf_key, NULL);
153 if (rc == 0)
154 rc = thr_setspecific(initf_key, pbuf);
155
156 return (rc);
157 }
158
159 static void *
160 get_initf_key(void) {
161
162 char *me = "get_initf_key";
163 void *pbuf;
164
165 pbuf = pthread_getspecific(initf_key);
166
167 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
168 (me, "got initf pbuf, key = %p\n", pbuf);
169
170 return (pbuf);
171 }
172
173 static void
174 clear_initf_key(void) {
175
176 char *me = "clear_initf_key";
177
178 (void) thr_setspecific(initf_key, NULL);
179
180 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
181 (me, "initf pbuf cleared\n");
182 }
183
184 /*
185 * Call the input initf function to extract the
186 * NSS front end parameters and examine them to
187 * determine if an NSS lookup is to be performed
188 * on a regular or a pseudo (called from compat
189 * backend) database. Then set the necessary
190 * parameters for later data structures creation
191 * and processing.
192 */
193 static nscd_rc_t
194 getparams(
195 int search_fnum,
196 nss_db_initf_t initf,
197 nscd_nsw_params_t *params)
198 {
199
200 nscd_rc_t rc = NSCD_SUCCESS;
201 nss_db_params_t *p;
202 int j;
203 char *dbn;
204 const char *n;
205 char *me = "getparams";
206
207 p = ¶ms->p;
208 (void) memset(p, 0, sizeof (*p));
209 (*initf)(p);
210 params->dbi = -1;
211 params->cfgdbi = -1;
212 params->compati = -1;
213 params->dnsi = -1;
214
215 /* map database name to index */
216 n = p->name;
217 for (j = 0; j < NSCD_NUM_DB; j++) {
218 dbn = NSCD_NSW_DB_NAME(j);
219 if (*n != *dbn)
220 continue;
221 if (strcmp(n, dbn) == 0) {
222 params->dbi = j;
223 if (*n != 'h' && *n != 'i' && *n != 's' && *n != 'a')
224 break;
225 if (strcmp(n, NSS_DBNAM_HOSTS) == 0 &&
226 search_fnum == NSS_DBOP_HOSTS_BYNAME)
227 params->dnsi = 0;
228 else if (strcmp(n, NSS_DBNAM_IPNODES) == 0 &&
229 search_fnum == NSS_DBOP_IPNODES_BYNAME)
230 params->dnsi = 1;
231 else if (strcmp(n, NSS_DBNAM_SHADOW) == 0)
232 params->privdb = 1;
233 break;
234 }
235 }
236
237 /*
238 * use the switch policy for passwd_compat or
239 * group_compat?
240 */
241 if (p->config_name != NULL) {
242
243 n = p->config_name;
244 for (j = 0; j < NSCD_NUM_DB; j++) {
245 dbn = NSCD_NSW_DB_NAME(j);
246 if (*n == *dbn) {
247 if (strcmp(n, dbn) == 0) {
248 params->cfgdbi = j;
249 break;
250 }
251 }
252 }
253 }
254
255 /* map the database name to the pseudo database index */
256 if (params->cfgdbi != -1) {
257 if (strstr(p->config_name, "_compat") != NULL) {
258 n = p->name;
259 for (j = params->cfgdbi; j < NSCD_NUM_DB; j++) {
260 dbn = NSCD_NSW_DB_NAME(j);
261 if (*n == *dbn) {
262 if (strcmp(n, dbn) == 0) {
263 params->compati = j;
264 break;
265 }
266 }
267 }
268 }
269 }
270
271 /*
272 * if unsupported database, let caller determine what to do next
273 */
274 if (params->dbi == -1) {
275 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
276 (me, "unsupported database: %s\n", p->name);
277 return (NSCD_CFG_UNSUPPORTED_SWITCH_DB);
278 }
279
280 return (rc);
281 }
282
283 static void
284 nscd_initf(nss_db_params_t *p)
285 {
286 nss_pheader_t *pbuf;
287 nssuint_t off;
288 nss_dbd_t *pdbd;
289 char *me = "nscd_initf";
290
291 pbuf = (nss_pheader_t *)get_initf_key();
292 if (pbuf == NULL) {
293 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
294 (me, "ERROR: initf key not set\n");
295 return;
296 }
297
298 if (pbuf->dbd_len <= sizeof (nss_dbd_t)) {
299 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
300 (me, "invalid db front params data ? dbd_len = %d\n",
301 pbuf->dbd_len);
302 return;
303 }
304
305 off = pbuf->dbd_off;
306 pdbd = (nss_dbd_t *)((void *)((char *)pbuf + off));
307
308 p->name = (char *)pdbd + pdbd->o_name;
309 p->config_name = (char *)pdbd + pdbd->o_config_name;
310 p->default_config = (char *)pdbd + pdbd->o_default_config;
311 p->flags = (enum nss_dbp_flags)pdbd->flags;
312 (void) memcpy(&p->private, &pbuf->nscdpriv, sizeof (p->private));
313
314 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
315 (me, "db frontend params: name =%s, config_name = %s, "
316 "default_config = %s, flags = %x\n", p->name,
317 (p->config_name && *p->config_name != '\0' ?
318 p->config_name : "<NOT SPECIFIED>"),
319 (p->default_config && *p->default_config != '\0' ?
320 p->default_config : "<NOT SPECIFIED>"),
321 p->flags);
322 }
323
324
325 static void
326 trace_result(
327 int dbi,
328 int srci,
329 int op,
330 nss_status_t res,
331 nss_XbyY_args_t *arg)
332 {
333 char *res_str;
334 char *src = "?";
335 char *db = "?";
336 char *data_str = "<NOT STRING FORMAT>";
337 int data_len = 0;
338 char *me = "trace_result";
339
340 switch (res) {
341 case NSS_SUCCESS:
342 res_str = "NSS_SUCCESS";
343 break;
344 case NSS_NOTFOUND:
345 res_str = "NSS_NOTFOUND";
346 break;
347 case NSS_UNAVAIL:
348 res_str = "NSS_UNAVAIL";
349 break;
350 case NSS_TRYAGAIN:
351 res_str = "NSS_TRYAGAIN";
352 break;
353 case NSS_NISSERVDNS_TRYAGAIN:
354 res_str = "NSS_NISSERVDNS_TRYAGAIN";
355 break;
356 default:
357 res_str = "UNKNOWN STATUS";
358 break;
359 }
360
361 if (dbi != -1)
362 db = NSCD_NSW_DB_NAME(dbi);
363 if (srci != -1)
364 src = NSCD_NSW_SRC_NAME(srci);
365
366 if (arg->buf.result == NULL) {
367 data_str = arg->buf.buffer;
368 data_len = arg->returnlen;
369 }
370
371 if (res == NSS_SUCCESS) {
372 _nscd_logit(me, "%s: database: %s, operation: %d, "
373 "source: %s returned >>%s<<, length = %d\n",
374 res_str, db, op, src, data_str, data_len);
375 return;
376 }
377
378 _nscd_logit(me, "%s: database: %s, operation: %d, source: %s, "
379 "erange= %d, herrno: %s (%d)\n",
380 res_str, db, op, src, arg->erange, hstrerror(arg->h_errno),
381 arg->h_errno);
382 }
383
384 /*
385 * Determine if a request should be done locally in the getXbyY caller's
386 * process. Return none zero if yes, 0 otherwise. This should be called
387 * before the switch engine steps through the backends/sources.
388 * This function returns 1 if:
389 * -- the database is exec_attr and the search_flag is GET_ALL
390 */
391 static int
392 try_local(
393 int dbi,
394 void *arg)
395 {
396 struct nss_XbyY_args *ap = (struct nss_XbyY_args *)arg;
397 _priv_execattr *ep;
398 int rc = 0;
399 char *me = "try_local";
400
401 if (strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_EXECATTR) == 0) {
402 if ((ep = ap->key.attrp) != NULL && IS_GET_ALL(ep->search_flag))
403 rc = 1;
404 }
405
406 if (rc != 0) {
407
408 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
409 (me, "TRYLOCAL: exec_attr:GET_ALL\n");
410 }
411
412 return (rc);
413 }
414
415 /*
416 * Determine if a request should be done locally in the getXbyY caller's
417 * process. Return none zero if yes, 0 otherwise. This should be called
418 * before the switch engine invokes any backend.
419 * This function returns 1 if:
420 * -- the database is shadow and the source is compat
421 */
422 static int
423 try_local2(
424 int dbi,
425 int srci)
426 {
427 int rc = 0;
428 char *me = "try_local2";
429
430 if (*NSCD_NSW_DB_NAME(dbi) == 's' &&
431 strcmp(NSCD_NSW_DB_NAME(dbi), NSS_DBNAM_SHADOW) == 0) {
432 if (strcmp(NSCD_NSW_SRC_NAME(srci), "compat") == 0)
433 rc = 1;
434 }
435
436 if (rc != 0) {
437 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
438 (me, "TRYLOCAL: database: shadow, source: %s\n",
439 NSCD_NSW_SRC_NAME(srci));
440 }
441
442 return (rc);
443 }
444
445 static nscd_rc_t
446 get_lib_func(void **handle, void **func, mutex_t *lock,
447 char *lib, char *name, void **func_p)
448 {
449 char *me = "get_lib_func";
450 void *sym;
451
452 if (func_p != NULL && *handle != NULL && *func != NULL) {
453 *func_p = *func;
454 return (NSCD_SUCCESS);
455 }
456
457 (void) mutex_lock(lock);
458
459 /* close the handle if requested */
460 if (func_p == NULL) {
461 if (*handle != NULL) {
462 (void) dlclose(*handle);
463 *handle = NULL;
464 *func = NULL;
465 }
466 (void) mutex_unlock(lock);
467 return (NSCD_SUCCESS);
468 }
469
470 if (*handle != NULL && *func != NULL) {
471 *func_p = *func;
472 (void) mutex_unlock(lock);
473 return (NSCD_SUCCESS);
474 }
475
476 if (*handle == NULL) {
477 *handle = dlopen(lib, RTLD_LAZY);
478 if (*handle == NULL) {
479 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
480 (me, "unable to dlopen %s\n", lib);
481 (void) mutex_unlock(lock);
482 return (NSCD_CFG_DLOPEN_ERROR);
483 }
484 }
485
486 if ((sym = dlsym(*handle, name)) == NULL) {
487
488 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
489 (me, "unable to find symbol %s:%s\n", lib, name);
490 (void) mutex_unlock(lock);
491 return (NSCD_CFG_DLSYM_ERROR);
492 } else {
493 *func_p = sym;
494 *func = sym;
495 }
496
497 (void) mutex_unlock(lock);
498 return (NSCD_SUCCESS);
499 }
500
501 static nscd_rc_t
502 get_libc_nss_search(void **func_p)
503 {
504 static void *handle = NULL;
505 static void *func = NULL;
506 static mutex_t lock = DEFAULTMUTEX;
507
508 return (get_lib_func(&handle, &func, &lock,
509 "libc.so", "nss_search", func_p));
510 }
511
512 static nscd_rc_t
513 get_gss_func(void **func_p)
514 {
515 static void *handle = NULL;
516 static void *func = NULL;
517 static mutex_t lock = DEFAULTMUTEX;
518
519 return (get_lib_func(&handle, &func, &lock,
520 "libgss.so", "gss_inquire_cred", func_p));
521 }
522
523 static nscd_rc_t
524 get_sldap_shadow_func(void **func_p)
525 {
526 static void *handle = NULL;
527 static void *func = NULL;
528 static mutex_t lock = DEFAULTMUTEX;
529
530 return (get_lib_func(&handle, &func, &lock,
531 "libsldap.so", "__ns_ldap_is_shadow_update_enabled",
532 func_p));
533 }
534
535 /*
536 * get_dns_funcs returns pointers to gethostbyname functions in the
537 * dynamically loaded nss_dns & nss_mdns modules that return host
538 * lookup results along with the TTL value in the DNS resource
539 * records. The dnsi parameter indicates whether the lookup database
540 * is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS
541 * module: dns/mdns and the function returns the address of the specific
542 * gethostbyname function in func_p variable.
543 */
544 static nscd_rc_t
545 get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
546 {
547 int si;
548 void **funcpp;
549 static void *handle[2] = { NULL, NULL };
550 static mutex_t func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX };
551 static void *func[2][2] = {{NULL, NULL}, {NULL, NULL}};
552 static const char *lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" };
553 static const char *func_name[2][2] =
554 {{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
555 { "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
556
557 /* source index: 0 = dns, 1 = mdns */
558 if (strcmp(srcname, "dns") == 0)
559 si = 0;
560 else
561 si = 1;
562
563 /*
564 * function index (func[si][dnsi]):
565 * [0,0] = dns/hosts, [0,1] = dns/ipnodes,
566 * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes
567 */
568
569 if (dnsi < 0) { /* close handle */
570 funcpp = NULL;
571 (void) mutex_lock(&func_lock[si]);
572 func[si][0] = NULL;
573 func[si][1] = NULL;
574 (void) mutex_unlock(&func_lock[si]);
575 } else
576 funcpp = (void **)func_p;
577
578 return (get_lib_func(&handle[si], &func[si][dnsi], &func_lock[si],
579 (char *)lib[si], (char *)func_name[si][dnsi], funcpp));
580 }
581
582 static nss_status_t
583 search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
584 {
585 nss_status_t (*func)();
586 nss_status_t res = NSS_UNAVAIL;
587 nscd_rc_t rc;
588
589 swret->noarg = 0;
590 if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
591 return (NSS_ERROR);
592
593 rc = get_dns_funcs(dnsi, &func, srcname);
594 if (rc == NSCD_SUCCESS) {
595 /*
596 * data_len in the packed buf header may be changed
597 * by the dns or mdns backend, reset it just in
598 * case
599 */
600 ((nss_pheader_t *)swret->pbuf)->data_len =
601 swret->datalen;
602 res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
603 }
604 return (res);
605 }
606
607 /*
608 * Returns a flag to indicate if needs to fall back to the
609 * main nscd when a per-user lookup failed with rc NSS_NOTFOUND.
610 */
611 static int
612 set_fallback_flag(char *srcname, nss_status_t rc)
613 {
614 char *me = "set_fallback_flag";
615 if (strcmp(srcname, "ldap") == 0 && rc == NSS_NOTFOUND) {
616 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
617 (me, "NSS_NOTFOUND (ldap): fallback to main nscd "
618 "may be needed\n");
619 return (1);
620 }
621 return (0);
622 }
623
624 nss_status_t
625 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum,
626 void *search_args)
627 {
628 char *me = "nss_search";
629 nss_status_t res = NSS_UNAVAIL;
630 nscd_nsw_state_t *s = NULL;
631 int n_src;
632 unsigned int status_vec = 0;
633 int dbi, srci = -1;
634 int check_loopback = 0;
635 int state_thr = 0;
636 lb_key_t key, *k = NULL;
637 nss_db_root_t root_db;
638 nscd_nsw_params_t params;
639 nscd_sw_return_t *swret;
640
641 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
642 (me, "rootp = %p, initf = %p, search_fnum = %d, "
643 "search_args = %p\n", rootp, initf,
644 search_fnum, search_args);
645
646 NSCD_SW_STATS_G.lookup_request_received_g++;
647 NSCD_SW_STATS_G.lookup_request_in_progress_g++;
648 NSCD_SW_STATS_G.lookup_request_queued_g++;
649
650 /* determine db index, cfg db index, etc */
651 if (getparams(search_fnum, initf, ¶ms) ==
652 NSCD_CFG_UNSUPPORTED_SWITCH_DB) {
653 /*
654 * if unsupported database and the request is from the
655 * the door, tell the door client to try it locally
656 */
657 if (initf == nscd_initf) {
658 res = NSS_TRYLOCAL;
659 goto error_exit;
660 } else { /* otherwise, let libc:nss_search() handle it */
661 nss_status_t (*func)();
662
663 if (get_libc_nss_search((void **)&func) ==
664 NSCD_SUCCESS)
665 return ((func)(rootp, initf, search_fnum,
666 search_args));
667 else
668 goto error_exit;
669 }
670 }
671 dbi = params.dbi;
672
673 /* get address of the switch engine return data area */
674 if (initf == nscd_initf) {
675 swret = (nscd_sw_return_t *)params.p.private;
676 swret->srci = -1;
677 } else {
678 swret = NULL;
679 params.dnsi = -1;
680 }
681
682 /*
683 * for door request that should be processed by the client,
684 * send it back with status NSS_TRYLOCAL
685 */
686 if (initf == nscd_initf && try_local(dbi, search_args) == 1) {
687 res = NSS_TRYLOCAL;
688 goto error_exit;
689 }
690
691 NSCD_SW_STATS(dbi).lookup_request_received++;
692 NSCD_SW_STATS(dbi).lookup_request_in_progress++;
693 NSCD_SW_STATS(dbi).lookup_request_queued++;
694
695 /* if lookup not enabled, return NSS_UNAVAIL */
696 if (!(NSCD_SW_CFG_G.enable_lookup_g == nscd_true &&
697 NSCD_SW_CFG(dbi).enable_lookup == nscd_true)) {
698
699 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
700 (me, "lookup not enabled for %s\n", NSCD_NSW_DB_NAME(dbi));
701
702 goto error_exit;
703 }
704
705 /* determine if loopback checking is configured */
706 if (NSCD_SW_CFG_G.enable_loopback_checking_g == nscd_true &&
707 NSCD_SW_CFG(dbi).enable_loopback_checking == nscd_true) {
708 check_loopback = 1;
709
710 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
711 (me, "loopback checking enabled for %s\n",
712 NSCD_NSW_DB_NAME(dbi));
713 }
714
715 if (check_loopback) {
716 k = get_loopback_key();
717 if (k != NULL) {
718 if (k->dbi != dbi || k->fnum != search_fnum) {
719 clear_loopback_key(k);
720 k = NULL;
721 }
722 }
723 }
724
725 if (s == 0) {
726 nscd_rc_t rc;
727
728 if (check_loopback) {
729 rc = _nscd_get_nsw_state_thread(&root_db, ¶ms);
730 state_thr = 1;
731 } else
732 rc = _nscd_get_nsw_state(&root_db, ¶ms);
733
734 NSCD_SW_STATS_G.lookup_request_queued_g--;
735 NSCD_SW_STATS(dbi).lookup_request_queued--;
736
737 if (rc != NSCD_SUCCESS)
738 goto error_exit;
739
740 s = (nscd_nsw_state_t *)root_db.s;
741 }
742
743 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
744 (me, "database = %s, config = >>%s<<\n", NSCD_NSW_DB_NAME(dbi),
745 (*s->nsw_cfg_p)->nsw_cfg_str);
746
747 for (n_src = 0; n_src < s->max_src; n_src++) {
748 nss_backend_t *be = NULL;
749 nss_backend_op_t funcp = NULL;
750 struct __nsw_lookup_v1 *lkp;
751 int smf_state;
752 int n_loop = 0;
753 int max_retry = 10;
754
755 res = NSS_UNAVAIL;
756
757 if (n_src == 0)
758 lkp = s->config->lookups;
759 else
760 lkp = lkp->next;
761
762 /* set the number of max. retries */
763 if (lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES)
764 max_retry = lkp->max_retries;
765
766 srci = (*s->nsw_cfg_p)->src_idx[n_src];
767 if (swret != NULL)
768 swret->srci = srci;
769
770 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
771 (me, "nsw source = %s\n", NSCD_NSW_SRC_NAME(srci));
772
773 /*
774 * If no privilege to look up, skip.
775 * 'files' requires PRIV_FILE_DAC_READ to read shadow(4) data,
776 * 'ldap' requires all zones privilege.
777 */
778 if (params.privdb == 1 && swret != NULL) {
779 boolean_t (*is_shadow_update_enabled)();
780 boolean_t check_ldap_priv = B_FALSE;
781
782 if (strcmp(NSCD_NSW_SRC_NAME(srci), "ldap") == 0) {
783 if (get_sldap_shadow_func(
784 (void **)&is_shadow_update_enabled) ==
785 NSCD_SUCCESS &&
786 is_shadow_update_enabled()) {
787 check_ldap_priv = B_TRUE;
788
789 /*
790 * A peruser nscd doesn't have
791 * the privileges to lookup a
792 * private database, such as shadow,
793 * returns NSS_ALTRETRY to have the
794 * main nscd do the job.
795 */
796 if (_whoami == NSCD_CHILD) {
797 res = NSS_ALTRETRY;
798 goto free_nsw_state;
799 }
800 }
801 }
802
803 if ((strcmp(NSCD_NSW_SRC_NAME(srci), "files") == 0 &&
804 _nscd_check_client_priv(NSCD_READ_PRIV) != 0) ||
805 (check_ldap_priv &&
806 _nscd_check_client_priv(NSCD_ALL_PRIV) != 0)) {
807 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
808 NSCD_LOG_LEVEL_DEBUG)
809 (me, "no privilege to look up, skip source\n");
810
811 goto next_src;
812 }
813 }
814
815 /* get state of the (backend) client service */
816 smf_state = _nscd_get_smf_state(srci, dbi, 0);
817
818 /* stop if the source is one that should be TRYLOCAL */
819 if (initf == nscd_initf && /* request is from the door */
820 (smf_state == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
821 (smf_state == NSCD_SVC_STATE_FOREIGN_SRC &&
822 s->be_version_p[n_src] == NULL) ||
823 (params.privdb && try_local2(dbi, srci) == 1))) {
824 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
825 (me, "returning TRYLOCAL ... \n");
826 res = NSS_TRYLOCAL;
827 goto free_nsw_state;
828 }
829
830 if (check_loopback && k != NULL) {
831
832 if (k->srci == srci && k->dbi == dbi)
833 if (k->fnum == search_fnum) {
834
835 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
836 NSCD_LOG_LEVEL_DEBUG)
837 (me, "loopback detected: "
838 "source = %s, database = %s "
839 "search fnum = %d\n",
840 NSCD_NSW_SRC_NAME(srci),
841 NSCD_NSW_DB_NAME(dbi), search_fnum);
842
843 NSCD_SW_STATS_G.loopback_nsw_db_skipped_g++;
844 NSCD_SW_STATS(dbi).loopback_nsw_db_skipped++;
845 continue;
846 }
847 }
848
849 be = s->be[n_src];
850 if (be != NULL)
851 funcp = NSS_LOOKUP_DBOP(be, search_fnum);
852
853 /* request could be from within nscd so check states again */
854 if (be == NULL || (params.dnsi < 0 && (funcp == NULL ||
855 (smf_state != NSCD_SVC_STATE_UNINITED &&
856 smf_state != NSCD_SVC_STATE_UNSUPPORTED_SRC &&
857 smf_state != NSCD_SVC_STATE_FOREIGN_SRC &&
858 smf_state < SCF_STATE_ONLINE)))) {
859
860 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
861 NSCD_LOG_LEVEL_DEBUG)
862 (me, "unable to look up source %s: be = %p, "
863 "smf state = %d, funcp = %p\n",
864 NSCD_NSW_SRC_NAME(srci), be, smf_state, funcp);
865
866 goto next_src;
867 }
868
869 do {
870 /*
871 * we can only retry max_retry times,
872 * otherwise threads may get stuck in this
873 * do-while loop forever
874 */
875 if (n_loop > max_retry) {
876 if (swret != NULL)
877 res = NSS_TRYLOCAL;
878 goto free_nsw_state;
879 }
880
881 /*
882 * set up to prevent loopback
883 */
884 if (check_loopback && k == NULL) {
885 key.srci = srci;
886 key.dbi = dbi;
887 key.fnum = search_fnum;
888 key.lb_flagp = &check_loopback;
889 (void) set_loopback_key(&key);
890 k = &key;
891 }
892
893 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
894 NSCD_LOG_LEVEL_DEBUG)
895 (me, "looking up source = %s, loop# = %d \n",
896 NSCD_NSW_SRC_NAME(srci), n_loop);
897
898 /*
899 * search the backend, if hosts lookups,
900 * try to get the hosts data with ttl first
901 */
902 if (params.dnsi >= 0) {
903 res = search_dns_withttl(swret,
904 NSCD_NSW_SRC_NAME(srci), params.dnsi);
905 /*
906 * if not able to get ttl, fall back
907 * to the regular backend call
908 */
909 if (res == NSS_ERROR)
910 res = (*funcp)(be, search_args);
911 else {
912 /*
913 * status/result are in the
914 * packed buffer, not
915 * search_args
916 */
917 swret->noarg = 1;
918 }
919 } else
920 res = (*funcp)(be, search_args);
921 if (swret != NULL)
922 swret->errnum = errno;
923
924 /*
925 * backend is not up, check and update the
926 * smf state table
927 */
928 if (res == NSS_UNAVAIL)
929 (void) _nscd_get_smf_state(srci, dbi, 1);
930
931 /*
932 * may need to fall back to use the main nscd
933 * if per-user lookup
934 */
935 if (_whoami == NSCD_CHILD && swret != NULL)
936 swret->fallback = set_fallback_flag(
937 NSCD_NSW_SRC_NAME(srci), res);
938
939 _NSCD_LOG_IF(NSCD_LOG_SWITCH_ENGINE,
940 NSCD_LOG_LEVEL_DEBUG) {
941
942 /*
943 * set up to trace the result/status
944 * of the dns/ttl lookup
945 */
946 if (swret != NULL && swret->noarg == 1) {
947 nss_pheader_t *phdr;
948 struct nss_XbyY_args *arg;
949 arg = (struct nss_XbyY_args *)
950 search_args;
951 phdr = (nss_pheader_t *)swret->pbuf;
952 arg->buf.buffer = (char *)phdr +
953 phdr->data_off;
954 arg->returnlen = phdr->data_len;
955 if (phdr->p_errno == ERANGE)
956 arg->erange = 1;
957 arg->h_errno = phdr->p_herrno;
958 }
959
960 trace_result(dbi, srci, search_fnum, res,
961 (nss_XbyY_args_t *)search_args);
962 }
963
964 n_loop++;
965 } while (retry_test(res, n_loop, lkp));
966
967 next_src:
968
969 status_vec |= (1 << res);
970
971 if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
972 break;
973 }
974 }
975
976 free_nsw_state:
977
978 if (state_thr == 1)
979 _nscd_put_nsw_state_thread(s);
980 else
981 _nscd_put_nsw_state(s);
982 if (check_loopback && k != NULL)
983 clear_loopback_key(k);
984
985 if (res != NSS_SUCCESS)
986 goto error_exit;
987
988 NSCD_SW_STATS_G.lookup_request_succeeded_g++;
989 NSCD_SW_STATS(dbi).lookup_request_succeeded++;
990 NSCD_SW_STATS_G.lookup_request_in_progress_g--;
991 NSCD_SW_STATS(dbi).lookup_request_in_progress--;
992
993 return (NSS_SUCCESS);
994
995 error_exit:
996
997 NSCD_SW_STATS_G.lookup_request_failed_g++;
998 NSCD_SW_STATS_G.lookup_request_in_progress_g--;
999 NSCD_SW_STATS(dbi).lookup_request_failed++;
1000 NSCD_SW_STATS(dbi).lookup_request_in_progress--;
1001
1002 return (res);
1003 }
1004
1005
1006 /* ===> get/set/endent */
1007
1008 static void nss_setent_u(nss_db_root_t *,
1009 nss_db_initf_t,
1010 nss_getent_t *);
1011 static nss_status_t nss_getent_u(nss_db_root_t *,
1012 nss_db_initf_t,
1013 nss_getent_t *,
1014 void *);
1015 static void nss_endent_u(nss_db_root_t *,
1016 nss_db_initf_t,
1017 nss_getent_t *);
1018
1019 void
1020 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf,
1021 nss_getent_t *contextpp)
1022 {
1023 if (contextpp == 0)
1024 return;
1025 nss_setent_u(rootp, initf, contextpp);
1026 }
1027
1028 nss_status_t
1029 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp,
1030 void *args)
1031 {
1032 nss_status_t status;
1033
1034 if (contextpp == 0) {
1035 return (NSS_UNAVAIL);
1036 }
1037 status = nss_getent_u(rootp, initf, contextpp, args);
1038 return (status);
1039 }
1040
1041 void
1042 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf,
1043 nss_getent_t *contextpp)
1044 {
1045 if (contextpp == 0)
1046 return;
1047 nss_endent_u(rootp, initf, contextpp);
1048 }
1049
1050 /*ARGSUSED*/
1051 static void
1052 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp)
1053 {
1054 nscd_getent_context_t *ctx;
1055 nscd_nsw_state_t *s;
1056 nss_backend_t *be;
1057 int n_src;
1058
1059 ctx = (nscd_getent_context_t *)contextp;
1060 s = ctx->nsw_state;
1061 n_src = ctx->n_src;
1062 be = ctx->be;
1063
1064 if (s != 0) {
1065 if (n_src < s->max_src && be != 0) {
1066 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1067 ctx->be = 0; /* Should be unnecessary, but hey */
1068 }
1069 }
1070 ctx->n_src = 0;
1071 }
1072
1073 static void
1074 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1075 nss_getent_t *contextpp)
1076 {
1077 char *me = "nss_setent_u";
1078 nscd_nsw_state_t *s;
1079 nscd_getent_context_t *contextp;
1080 nscd_nsw_params_t params;
1081 nss_db_root_t root;
1082 nss_backend_t *be;
1083 int n_src, i;
1084 nscd_sw_return_t *swret = NULL;
1085
1086 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1087 (me, "rootp = %p, initf = %p, contextpp = %p \n",
1088 rootp, initf, contextpp);
1089
1090 /*
1091 * Get the nsw db index via the initf function. If unsupported
1092 * database, no need to continue
1093 */
1094 if (getparams(-1, initf, ¶ms) == NSCD_CFG_UNSUPPORTED_SWITCH_DB)
1095 return;
1096
1097 /* get address of the switch engine return data area */
1098 if (initf == nscd_initf)
1099 swret = (nscd_sw_return_t *)params.p.private;
1100
1101 /* if no privilege to look up, return */
1102 if (params.privdb == 1 && swret != NULL &&
1103 _nscd_check_client_priv(NSCD_READ_PRIV) != 0) {
1104
1105 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1106 (me, "no privilege \n");
1107 return;
1108 }
1109
1110 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1111 if ((_nscd_get_getent_ctx(contextpp, ¶ms)) !=
1112 NSCD_SUCCESS) {
1113 return;
1114 }
1115 contextp = (nscd_getent_context_t *)contextpp->ctx;
1116 }
1117 s = contextp->nsw_state;
1118
1119 if (s == 0) {
1120 if (_nscd_get_nsw_state(&root, ¶ms) !=
1121 NSCD_SUCCESS) {
1122 return;
1123 }
1124 s = (nscd_nsw_state_t *)root.s;
1125 contextp->nsw_state = s;
1126
1127 } else {
1128 s = contextp->nsw_state;
1129 n_src = contextp->n_src;
1130 be = contextp->be;
1131 if (n_src == 0 && be != 0) {
1132 /*
1133 * Optimization: don't do endent, don't change
1134 * backends, just do the setent. Look Ma, no locks
1135 * (nor any context that needs updating).
1136 */
1137 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1138 return;
1139 }
1140 if (n_src < s->max_src && be != 0) {
1141 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1142 contextp->be = 0; /* Play it safe */
1143 }
1144 }
1145 for (n_src = 0, be = 0; n_src < s->max_src &&
1146 (be = s->be[n_src]) == 0; n_src++) {
1147 ;
1148 }
1149
1150 contextp->n_src = n_src;
1151 contextp->be = be;
1152
1153 if (be == 0) {
1154 /* Things are broken enough that we can't do setent/getent */
1155 nss_endent_u(rootp, initf, contextpp);
1156 return;
1157 }
1158
1159 /*
1160 * make sure all the backends are supported
1161 */
1162 for (i = 0; i < s->max_src; i++) {
1163 int st, srci;
1164
1165 if (s->be[i] == NULL)
1166 continue;
1167
1168 srci = (*s->nsw_cfg_p)->src_idx[i];
1169 st = _nscd_get_smf_state(srci, params.dbi, 1);
1170 if (st == NSCD_SVC_STATE_UNSUPPORTED_SRC ||
1171 (st == NSCD_SVC_STATE_FOREIGN_SRC &&
1172 s->be_version_p[i] == NULL && initf == nscd_initf) ||
1173 st == NSCD_SVC_STATE_UNINITED ||
1174 (params.privdb &&
1175 try_local2(params.dbi, srci) == 1)) {
1176 nss_endent_u(rootp, initf, contextpp);
1177
1178 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1179 NSCD_LOG_LEVEL_DEBUG)
1180 (me, "backend (%s) not available (state = %d)\n",
1181 NSCD_NSW_SRC_NAME(srci), st);
1182
1183 return;
1184 }
1185 }
1186
1187 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1188 }
1189
1190 nss_status_t
1191 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1192 nss_getent_t *contextpp, void *args)
1193 {
1194 char *me = "nss_getent_u";
1195 nscd_nsw_state_t *s;
1196 nscd_getent_context_t *contextp;
1197 int n_src;
1198 nss_backend_t *be;
1199
1200 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1201 (me, "rootp = %p, initf = %p, contextpp = %p, args = %p\n",
1202 rootp, initf, contextpp, args);
1203
1204 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1205 nss_setent_u(rootp, initf, contextpp);
1206 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1207 /* Give up */
1208 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1209 NSCD_LOG_LEVEL_ERROR)
1210 (me, "not able to obtain getent context ... give up\n");
1211
1212 return (NSS_UNAVAIL);
1213 }
1214 }
1215
1216 s = contextp->nsw_state;
1217 n_src = contextp->n_src;
1218 be = contextp->be;
1219
1220 if (s == 0) {
1221 /*
1222 * We've done an end_iter() and haven't done nss_setent()
1223 * or nss_endent() since; we should stick in this state
1224 * until the caller invokes one of those two routines.
1225 */
1226 return (NSS_SUCCESS);
1227 }
1228
1229 while (n_src < s->max_src) {
1230 nss_status_t res;
1231 struct __nsw_lookup_v1 *lkp = NULL;
1232 int n;
1233
1234 /* get the nsw config for the current source */
1235 lkp = s->config->lookups;
1236 for (n = 0; n < n_src; n++)
1237 lkp = lkp->next;
1238
1239 if (be == 0) {
1240 /* If it's null it's a bug, but let's play safe */
1241 res = NSS_UNAVAIL;
1242 } else {
1243 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1244 NSCD_LOG_LEVEL_DEBUG)
1245 (me, "database: %s, backend: %s, nsswitch config: %s\n",
1246 NSCD_NSW_DB_NAME(s->dbi),
1247 lkp->service_name,
1248 (*s->nsw_cfg_p)->nsw_cfg_str);
1249
1250 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args);
1251 }
1252
1253 if (__NSW_ACTION_V1(lkp, res) == __NSW_RETURN) {
1254 if (res != __NSW_SUCCESS) {
1255 end_iter_u(rootp,
1256 (struct nss_getent_context *)contextp);
1257 }
1258 return (res);
1259 }
1260 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0);
1261 do {
1262 n_src++;
1263 } while (n_src < s->max_src &&
1264 (be = s->be[n_src]) == 0);
1265 if (be == 0) {
1266 /*
1267 * This is the case where we failed to get the backend
1268 * for the last source. We exhausted all sources.
1269 */
1270 nss_endent_u(rootp, initf, contextpp);
1271 return (NSS_NOTFOUND);
1272 }
1273 contextp->n_src = n_src;
1274 contextp->be = be;
1275 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0);
1276 }
1277 /* Got to the end of the sources without finding another entry */
1278 end_iter_u(rootp, (struct nss_getent_context *)contextp);
1279 return (NSS_SUCCESS);
1280 /* success is either a successful entry or end of the sources */
1281 }
1282
1283 /*ARGSUSED*/
1284 void
1285 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf,
1286 nss_getent_t *contextpp)
1287 {
1288 char *me = "nss_endent_u";
1289 nscd_getent_context_t *contextp;
1290
1291 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1292 (me, "rootp = %p, initf = %p, contextpp = %p \n",
1293 rootp, initf, contextpp);
1294
1295 if ((contextp = (nscd_getent_context_t *)contextpp->ctx) == 0) {
1296 /* nss_endent() on an unused context is a no-op */
1297 return;
1298 }
1299
1300 if (_nscd_is_getent_ctx_in_use(contextp) == 0) {
1301 end_iter_u(rootp, (struct nss_getent_context *)contextp);
1302 _nscd_put_getent_ctx(contextp);
1303 contextpp->ctx = NULL;
1304 }
1305 }
1306
1307 /*
1308 * _nss_db_state_destr() and nss_delete() do nothing in nscd
1309 * but is needed to make the caller (below nscd) happy
1310 */
1311 /*ARGSUSED*/
1312 void
1313 _nss_db_state_destr(struct nss_db_state *s)
1314 {
1315 /* nsw state in nscd is always reused, so do nothing here */
1316 }
1317
1318 /*ARGSUSED*/
1319 void
1320 nss_delete(nss_db_root_t *rootp)
1321 {
1322 /*
1323 * the only resource kept tracked by the nss_db_root_t
1324 * is the nsw state which is always reused and no need
1325 * to be freed. So just return.
1326 */
1327 }
1328
1329 /*
1330 * Start of nss_psearch/nss_psetent()/nss_pgetent()/nss_pendent()
1331 * buffers switch entry points
1332 */
1333
1334 /*
1335 * nss_psearch opens a packed structure header, assembles a local
1336 * nss_XbyY_args_t structure and calls the local copy of nss_search.
1337 * The return data is assembled in "files native format" in the
1338 * return buffer location. Status if packed back up with the buffer
1339 * and the whole wad is returned to the cache or the client.
1340 */
1341
1342 void
1343 nss_psearch(void *buffer, size_t length)
1344 {
1345 /* inputs */
1346 nss_db_initf_t initf;
1347 int dbop;
1348 int rc;
1349 nss_XbyY_args_t arg;
1350 nss_status_t status;
1351 nscd_sw_return_t swret = { 0 }, *swrp = &swret;
1352 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1353 char *me = "nss_psearch";
1354
1355 if (buffer == NULL || length == 0) {
1356 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1357 return;
1358 }
1359
1360 status = nss_packed_arg_init(buffer, length,
1361 NULL, &initf, &dbop, &arg);
1362 if (status != NSS_SUCCESS) {
1363 NSCD_SET_STATUS(pbuf, status, -1);
1364 return;
1365 }
1366
1367 /*
1368 * pass the address of the return data area
1369 * for the switch engine to return its own data
1370 */
1371 (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1372 swret.pbuf = buffer;
1373 swret.pbufsiz = length;
1374 swret.datalen = pbuf->data_len;
1375
1376 /*
1377 * use the generic nscd_initf for all database lookups
1378 * (the TSD key is the pointer to the packed header)
1379 */
1380 rc = set_initf_key(pbuf);
1381 if (rc != 0) {
1382 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1383 return;
1384 }
1385 initf = nscd_initf;
1386
1387 /* Perform local search and pack results into return buffer */
1388 /* nscd's search ignores db_root */
1389 status = nss_search(NULL, initf, dbop, &arg);
1390
1391 /*
1392 * If status is NSS_NOTFOUND and ldap also returned
1393 * NSS_NOTFOUND, it is possible that the user does
1394 * not have a credential, so check and see if
1395 * needs to return NSS_ALTRETRY to let the main
1396 * nscd get a chance to process the lookup
1397 */
1398 if (swret.fallback == 1 && status == NSS_NOTFOUND) {
1399 OM_uint32 (*func)();
1400 OM_uint32 stat;
1401 nscd_rc_t rc;
1402
1403 rc = get_gss_func((void **)&func);
1404 if (rc == NSCD_SUCCESS) {
1405 if (func(&stat, GSS_C_NO_CREDENTIAL,
1406 NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1407
1408 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1409 NSCD_LOG_LEVEL_DEBUG)
1410 (me, "NSS_ALTRETRY: fallback to main nscd needed\n");
1411
1412 status = NSS_ALTRETRY;
1413 }
1414 }
1415 }
1416
1417 NSCD_SET_STATUS(pbuf, status, -1);
1418 errno = swret.errnum;
1419
1420 /*
1421 * Move result/status from args to packed buffer only if
1422 * arg was being used and rc from the switch engine is not
1423 * NSS_TRYLOCAL.
1424 */
1425 if (!swret.noarg && status != NSS_TRYLOCAL)
1426 nss_packed_set_status(buffer, length, status, &arg);
1427
1428 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1429 (me, "switch engine result: source is %s, status %d, "
1430 "herrno is %d, errno is %s\n",
1431 (swret.srci != -1) ? NSCD_NSW_SRC_NAME(swret.srci) : "<NOTSET>",
1432 pbuf->p_status, pbuf->p_herrno, strerror(pbuf->p_errno));
1433
1434 /* clear the TSD key used by the generic initf */
1435 clear_initf_key();
1436 pbuf->nscdpriv = 0;
1437 }
1438
1439 static void
1440 nscd_map_contextp(void *buffer, nss_getent_t *contextp,
1441 nssuint_t **cookie_num_p, nssuint_t **seqnum_p, int setent)
1442 {
1443 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1444 nssuint_t off;
1445 nscd_getent_context_t *ctx;
1446 char *me = "nscd_map_contextp";
1447 nscd_getent_p1_cookie_t *cookie;
1448
1449 if (buffer == NULL) {
1450 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1451 return;
1452 }
1453
1454 off = pbuf->key_off;
1455 cookie = (nscd_getent_p1_cookie_t *)((void *)((char *)buffer + off));
1456 if (seqnum_p != NULL)
1457 *seqnum_p = &cookie->p1_seqnum;
1458
1459 /*
1460 * if called by nss_psetent, and the passed in cookie number
1461 * is NSCD_NEW_COOKIE, then there is no cookie yet, return a
1462 * pointer pointing to where the cookie number will be stored.
1463 * Also because there is no cookie to validate, just return
1464 * success.
1465 *
1466 * On the other hand, if a cookie number is passed in, we need
1467 * to validate the cookie number before returning.
1468 */
1469 if (cookie_num_p != NULL)
1470 *cookie_num_p = &cookie->p1_cookie_num;
1471 if (setent == 1 && cookie->p1_cookie_num == NSCD_NEW_COOKIE) {
1472 NSCD_SET_STATUS_SUCCESS(pbuf);
1473 return;
1474 }
1475
1476 /*
1477 * If the sequence number and start time match nscd's p0 cookie,
1478 * then either setent was done twice in a row or this is the
1479 * first getent after the setent, return success as well.
1480 */
1481 if (cookie->p1_seqnum == NSCD_P0_COOKIE_SEQNUM) {
1482 nscd_getent_p0_cookie_t *p0c =
1483 (nscd_getent_p0_cookie_t *)cookie;
1484 if (p0c->p0_time == _nscd_get_start_time()) {
1485 NSCD_SET_STATUS_SUCCESS(pbuf);
1486 return;
1487 }
1488 }
1489
1490 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1491 (me, "cookie # = %lld, sequence # = %lld\n",
1492 cookie->p1_cookie_num, cookie->p1_seqnum);
1493
1494 ctx = _nscd_is_getent_ctx(cookie->p1_cookie_num);
1495
1496 if (ctx == NULL) {
1497 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1498 (me, "No matching context found (cookie number: %lld)\n",
1499 cookie->p1_cookie_num);
1500
1501 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1502 return;
1503 }
1504
1505 /* if not called by nss_psetent, verify sequence number */
1506 if (setent != 1 && ctx->seq_num !=
1507 (nscd_seq_num_t)cookie->p1_seqnum) {
1508 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1509 (me, "invalid sequence # (%lld)\n", cookie->p1_seqnum);
1510
1511 _nscd_free_ctx_if_aborted(ctx);
1512 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1513 return;
1514 }
1515
1516 contextp->ctx = (struct nss_getent_context *)ctx;
1517
1518 NSCD_SET_STATUS_SUCCESS(pbuf);
1519 }
1520
1521 void
1522 nss_psetent(void *buffer, size_t length, pid_t pid)
1523 {
1524 nss_getent_t context = { 0 };
1525 nss_getent_t *contextp = &context;
1526 nssuint_t *cookie_num_p;
1527 nssuint_t *seqnum_p;
1528 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1529 nscd_getent_p0_cookie_t *p0c;
1530 char *me = "nss_psetent";
1531
1532 if (buffer == NULL || length == 0) {
1533 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1534 return;
1535 }
1536
1537 /*
1538 * If this is a per-user nscd, and the user does not have
1539 * the necessary credential, return NSS_TRYLOCAL, so the
1540 * setent/getent can be done locally in the process of the
1541 * setent call
1542 */
1543 if (_whoami == NSCD_CHILD) {
1544 OM_uint32 (*func)();
1545 OM_uint32 stat;
1546 nscd_rc_t rc;
1547
1548 rc = get_gss_func((void **)&func);
1549 if (rc == NSCD_SUCCESS) {
1550 if (func(&stat, GSS_C_NO_CREDENTIAL,
1551 NULL, NULL, NULL, NULL) != GSS_S_COMPLETE) {
1552
1553 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE,
1554 NSCD_LOG_LEVEL_DEBUG)
1555 (me, "NSS_TRYLOCAL: fallback to caller process\n");
1556 NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
1557 return;
1558 }
1559 }
1560 }
1561
1562 /* check cookie number */
1563 nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 1);
1564 if (NSCD_STATUS_IS_NOT_OK(pbuf))
1565 return;
1566
1567 /* set cookie number and sequence number */
1568 p0c = (nscd_getent_p0_cookie_t *)cookie_num_p;
1569 if (contextp->ctx == NULL) {
1570 /*
1571 * first setent (no existing getent context),
1572 * return a p0 cookie
1573 */
1574 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1575 (me, "first setent, no getent context yet\n");
1576 } else {
1577 /*
1578 * doing setent on an existing getent context,
1579 * release resources allocated and return a
1580 * p0 cookie
1581 */
1582 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1583 (me, "setent resetting sequence number = %lld\n", *seqnum_p);
1584
1585 if (_nscd_is_getent_ctx_in_use((nscd_getent_context_t *)
1586 contextp->ctx) == 0) {
1587 /*
1588 * context not in use, release the backend and
1589 * return the context to the pool
1590 */
1591 end_iter_u(NULL, contextp->ctx);
1592 _nscd_put_getent_ctx(
1593 (nscd_getent_context_t *)contextp->ctx);
1594 contextp->ctx = NULL;
1595 }
1596 }
1597
1598 p0c->p0_pid = pid;
1599 p0c->p0_time = _nscd_get_start_time();
1600 p0c->p0_seqnum = NSCD_P0_COOKIE_SEQNUM;
1601 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1602 (me, "returning a p0 cookie: pid = %ld, time = %ld, seq #= %llx\n",
1603 p0c->p0_pid, p0c->p0_time, p0c->p0_seqnum);
1604
1605 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1606 }
1607
1608 static void
1609 delayed_setent(nss_pheader_t *pbuf, nss_db_initf_t initf,
1610 nss_getent_t *contextp, nssuint_t *cookie_num_p,
1611 nssuint_t *seqnum_p, pid_t pid)
1612 {
1613 nscd_getent_context_t *ctx;
1614 nscd_sw_return_t swret = { 0 }, *swrp = &swret;
1615 char *me = "delayed_setent";
1616
1617 /*
1618 * check credential
1619 */
1620 _nscd_APP_check_cred(pbuf, &pid, "NSCD_DELAYED_SETENT",
1621 NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR);
1622 if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1623 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1624 (me, "invalid credential\n");
1625 return;
1626 }
1627
1628 /*
1629 * pass the packed header buffer pointer to nss_setent
1630 */
1631 (void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
1632 swret.pbuf = pbuf;
1633
1634 /* Perform local setent and set context */
1635 nss_setent(NULL, initf, contextp);
1636
1637 /* insert cookie info into packed buffer header */
1638 ctx = (nscd_getent_context_t *)contextp->ctx;
1639 if (ctx != NULL) {
1640 *cookie_num_p = ctx->cookie_num;
1641 *seqnum_p = ctx->seq_num;
1642 ctx->pid = pid;
1643 } else {
1644 /*
1645 * not able to allocate a getent context, the
1646 * client should try the enumeration locally
1647 */
1648 *cookie_num_p = NSCD_LOCAL_COOKIE;
1649 *seqnum_p = 0;
1650
1651 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1652 (me, "NSS_TRYLOCAL: cookie # = %lld, sequence # = %lld\n",
1653 *cookie_num_p, *seqnum_p);
1654 NSCD_SET_STATUS(pbuf, NSS_TRYLOCAL, 0);
1655 return;
1656 }
1657
1658 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1659 (me, "NSS_SUCCESS: cookie # = %lld, sequence # = %lld\n",
1660 ctx->cookie_num, ctx->seq_num);
1661
1662 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1663 }
1664
1665 void
1666 nss_pgetent(void *buffer, size_t length)
1667 {
1668 /* inputs */
1669 nss_db_initf_t initf;
1670 nss_getent_t context = { 0 };
1671 nss_getent_t *contextp = &context;
1672 nss_XbyY_args_t arg = { 0};
1673 nss_status_t status;
1674 nssuint_t *cookie_num_p;
1675 nssuint_t *seqnum_p;
1676 nscd_getent_context_t *ctx;
1677 int rc;
1678 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1679 char *me = "nss_pgetent";
1680
1681 if (buffer == NULL || length == 0) {
1682 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1683 return;
1684 }
1685
1686 /* verify the cookie passed in */
1687 nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1688 if (NSCD_STATUS_IS_NOT_OK(pbuf))
1689 return;
1690
1691 /*
1692 * use the generic nscd_initf for all the getent requests
1693 * (the TSD key is the pointer to the packed header)
1694 */
1695 rc = set_initf_key(pbuf);
1696 if (rc != 0) {
1697 NSCD_SET_STATUS(pbuf, NSS_UNAVAIL, EINVAL);
1698 return;
1699 }
1700 initf = nscd_initf;
1701
1702 /* if no context yet, get one */
1703 if (contextp->ctx == NULL) {
1704 nscd_getent_p0_cookie_t *p0c =
1705 (nscd_getent_p0_cookie_t *)cookie_num_p;
1706
1707 delayed_setent(pbuf, initf, contextp, cookie_num_p,
1708 seqnum_p, p0c->p0_pid);
1709 if (NSCD_STATUS_IS_NOT_OK(pbuf)) {
1710 clear_initf_key();
1711 return;
1712 }
1713 }
1714
1715 status = nss_packed_context_init(buffer, length,
1716 NULL, &initf, &contextp, &arg);
1717 if (status != NSS_SUCCESS) {
1718 clear_initf_key();
1719 _nscd_free_ctx_if_aborted(
1720 (nscd_getent_context_t *)contextp->ctx);
1721 NSCD_SET_STATUS(pbuf, status, -1);
1722 return;
1723 }
1724
1725 /* Perform local search and pack results into return buffer */
1726 status = nss_getent(NULL, initf, contextp, &arg);
1727 NSCD_SET_STATUS(pbuf, status, -1);
1728 nss_packed_set_status(buffer, length, status, &arg);
1729
1730 /* increment sequence number in the buffer and nscd context */
1731 if (status == NSS_SUCCESS) {
1732 ctx = (nscd_getent_context_t *)contextp->ctx;
1733 ctx->seq_num++;
1734 *seqnum_p = ctx->seq_num;
1735 *cookie_num_p = ctx->cookie_num;
1736
1737 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1738 (me, "getent OK, new sequence # = %lld, len = %lld,"
1739 " data = >>%s<<\n", *seqnum_p,
1740 pbuf->data_len, (char *)buffer + pbuf->data_off);
1741
1742 _nscd_free_ctx_if_aborted(ctx);
1743 } else {
1744 /* release the resources used */
1745 ctx = (nscd_getent_context_t *)contextp->ctx;
1746 if (ctx != NULL && _nscd_is_getent_ctx_in_use(ctx) == 0) {
1747 _nscd_put_getent_ctx(ctx);
1748 contextp->ctx = NULL;
1749 }
1750 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1751 (me, "getent failed, status = %d, sequence # = %lld\n",
1752 status, *seqnum_p);
1753 }
1754
1755 /* clear the TSD key used by the generic initf */
1756 clear_initf_key();
1757 }
1758
1759 void
1760 nss_pendent(void *buffer, size_t length)
1761 {
1762 nss_getent_t context = { 0 };
1763 nss_getent_t *contextp = &context;
1764 nssuint_t *seqnum_p;
1765 nssuint_t *cookie_num_p;
1766 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1767 char *me = "nss_pendent";
1768
1769 if (buffer == NULL || length == 0) {
1770 NSCD_SET_STATUS(pbuf, NSS_ERROR, EFAULT);
1771 return;
1772 }
1773
1774 /* map the contextp from the cookie information */
1775 nscd_map_contextp(buffer, contextp, &cookie_num_p, &seqnum_p, 0);
1776 if (NSCD_STATUS_IS_NOT_OK(pbuf))
1777 return;
1778
1779 if (contextp->ctx == NULL)
1780 return;
1781
1782 _NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_DEBUG)
1783 (me, "endent, cookie = %lld, sequence # = %lld\n",
1784 *cookie_num_p, *seqnum_p);
1785
1786 /* Perform local endent and reset context */
1787 nss_endent(NULL, NULL, contextp);
1788
1789 NSCD_SET_STATUS(pbuf, NSS_SUCCESS, 0);
1790 }
1791
1792 /*ARGSUSED*/
1793 void
1794 nss_pdelete(void *buffer, size_t length)
1795 {
1796 nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
1797
1798 /* unnecessary, kept for completeness */
1799 NSCD_SET_STATUS_SUCCESS(pbuf);
1800 }