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