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