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