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