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