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 /* 30 * Shared code used by the name-service-switch frontends (e.g. getpwnam_r()) 31 */ 32 33 #pragma weak nss_delete = _nss_delete 34 #pragma weak nss_endent = _nss_endent 35 #pragma weak nss_getent = _nss_getent 36 #pragma weak nss_search = _nss_search 37 #pragma weak nss_setent = _nss_setent 38 39 #include "synonyms.h" 40 #include <mtlib.h> 41 #include <dlfcn.h> 42 #include <atomic.h> 43 44 #define __NSS_PRIVATE_INTERFACE 45 #include "nsswitch_priv.h" 46 #undef __NSS_PRIVATE_INTERFACE 47 48 #include <nss_common.h> 49 #include <nss_dbdefs.h> 50 #include <unistd.h> 51 #include <stdlib.h> 52 #include <stdio.h> 53 #include <string.h> 54 #include <thread.h> 55 #include <sys/types.h> 56 #include <sys/mman.h> 57 #include <errno.h> 58 #include "libc.h" 59 #include "tsd.h" 60 61 #include <getxby_door.h> 62 63 /* 64 * policy component function interposing definitions: 65 * nscd if so desired can interpose it's own switch functions over 66 * the internal unlocked counterparts. This will allow nscd to replace 67 * the switch policy state engine with one that uses it's internal 68 * components. 69 * Only nscd can change this through it's use of nss_config. 70 * The golden rule is: ptr == NULL checking is used in the switch to 71 * see if a function was interposed. But nscd is responsible for seeing 72 * that mutex locking to change the values are observed when the data is 73 * changed. Especially if it happens > once. The switch does not lock 74 * the pointer with mutexs. 75 */ 76 77 typedef struct { 78 void *p; 79 #if 0 80 void (*nss_delete_fp)(nss_db_root_t *rootp); 81 nss_status_t (*nss_search_fp)(nss_db_root_t *rootp, 82 nss_db_initf_t initf, int search_fnum, 83 void *search_args); 84 void (*nss_setent_u_fp)(nss_db_root_t *, 85 nss_db_initf_t, nss_getent_t *); 86 nss_status_t (*nss_getent_u_fp)(nss_db_root_t *, 87 nss_db_initf_t, nss_getent_t *, void *); 88 void (*nss_endent_u_fp)(nss_db_root_t *, 89 nss_db_initf_t, nss_getent_t *); 90 void (*end_iter_u_fp)(nss_db_root_t *rootp, 91 struct nss_getent_context *contextp); 92 #endif 93 } nss_policyf_t; 94 95 static mutex_t nss_policyf_lock = DEFAULTMUTEX; 96 static nss_policyf_t nss_policyf_ptrs = 97 { (void *)NULL }; 98 99 /* 100 * nsswitch db_root state machine definitions: 101 * The golden rule is: if you hold a pointer to an nss_db_state struct and 102 * you don't hold the lock, you'd better have incremented the refcount 103 * while you held the lock; otherwise, it may vanish or change 104 * significantly when you least expect it. 105 * 106 * The pointer in nss_db_root_t is one such, so the reference count >= 1. 107 * Ditto the pointer in struct nss_getent_context. 108 */ 109 110 /* 111 * State for one nsswitch database (e.g. "passwd", "hosts") 112 */ 113 struct nss_db_state { 114 nss_db_root_t orphan_root; /* XXX explain */ 115 unsigned refcount; /* One for the pointer in */ 116 /* nss_db_root_t, plus one */ 117 /* for each active thread. */ 118 nss_db_params_t p; 119 struct __nsw_switchconfig_v1 *config; 120 int max_src; /* is == config->num_lookups */ 121 struct nss_src_state *src; /* Pointer to array[max_src] */ 122 }; 123 124 /* 125 * State for one of the sources (e.g. "nis", "compat") for a database 126 */ 127 struct nss_src_state { 128 struct __nsw_lookup_v1 *lkp; 129 int n_active; 130 int n_dormant; 131 int n_waiting; /* ... on wanna_be */ 132 cond_t wanna_be; 133 union { 134 nss_backend_t *single; /* Efficiency hack for common case */ 135 /* when limit_dead_backends == 1 */ 136 nss_backend_t **multi; /* array[limit_dead_backends] of */ 137 } dormant; /* pointers to dormant backends */ 138 nss_backend_constr_t be_constr; 139 nss_backend_finder_t *finder; 140 void *finder_priv; 141 }; 142 143 static struct nss_db_state *_nss_db_state_constr(nss_db_initf_t); 144 void _nss_db_state_destr(struct nss_db_state *); 145 146 /* ==== null definitions if !MTSAFE? Ditto lock field in nss_db_root_t */ 147 148 #define NSS_ROOTLOCK(r, sp) ((void) _private_mutex_lock(&(r)->lock), \ 149 *(sp) = (r)->s) 150 151 #define NSS_UNLOCK(r) ((void) _private_mutex_unlock(&(r)->lock)) 152 153 #define NSS_CHECKROOT(rp, s) ((s) != (*(rp))->s && \ 154 ((void) _private_mutex_unlock(&(*(rp))->lock), \ 155 (void) _private_mutex_lock(&(s)->orphan_root.lock), \ 156 *(rp) = &(s)->orphan_root)) 157 158 #define NSS_RELOCK(rp, s) ((void) _private_mutex_lock(&(*(rp))->lock), \ 159 NSS_CHECKROOT(rp, s)) 160 161 #define NSS_STATE_REF_u(s) (++(s)->refcount) 162 163 #define NSS_UNREF_UNLOCK(r, s) (--(s)->refcount != 0 \ 164 ? ((void)NSS_UNLOCK(r)) \ 165 : (NSS_UNLOCK(r), (void)_nss_db_state_destr(s))) 166 167 #define NSS_LOCK_CHECK(r, f, sp) (NSS_ROOTLOCK((r), (sp)), \ 168 *(sp) == 0 && \ 169 (r->s = *(sp) = _nss_db_state_constr(f))) 170 /* === In the future, NSS_LOCK_CHECK() may also have to check that */ 171 /* === the config info hasn't changed (by comparing version numbers) */ 172 173 174 /* 175 * NSS_OPTIONS/NIS_OPTIONS environment varibles data definitions: 176 * This remains for backwards compatibility. But generally nscd will 177 * decide if/how this gets used. 178 */ 179 static int checked_env = 0; /* protected by "rootlock" */ 180 181 /* allowing __nss_debug_file to be set could be a security hole. */ 182 FILE *__nss_debug_file = stdout; 183 int __nss_debug_eng_loop; 184 185 /* NIS_OPTIONS infrastructure (from linbsl/nis/cache/cache_api.cc) */ 186 /* allowing __nis_debug_file to be set could be a security hole. */ 187 FILE *__nis_debug_file = stdout; 188 int __nis_debug_bind; 189 int __nis_debug_rpc; 190 int __nis_debug_calls; 191 char *__nis_prefsrv; 192 char *__nis_preftype; 193 char *__nis_server; /* if set, use only this server for binding */ 194 195 #define OPT_INT 1 196 #define OPT_STRING 2 197 #ifdef DEBUG 198 #define OPT_FILE 3 199 #endif 200 201 struct option { 202 char *name; 203 int type; 204 void *address; 205 }; 206 207 static struct option nss_options[] = { 208 #ifdef DEBUG 209 /* allowing __nss_debug_file to be set could be a security hole. */ 210 { "debug_file", OPT_FILE, &__nss_debug_file }, 211 #endif 212 { "debug_eng_loop", OPT_INT, &__nss_debug_eng_loop }, 213 { 0, 0, 0 }, 214 }; 215 216 static struct option nis_options[] = { 217 #ifdef DEBUG 218 /* allowing __nis_debug_file to be set could be a security hole. */ 219 { "debug_file", OPT_FILE, &__nis_debug_file }, 220 #endif 221 { "debug_bind", OPT_INT, &__nis_debug_bind }, 222 { "debug_rpc", OPT_INT, &__nis_debug_rpc }, 223 { "debug_calls", OPT_INT, &__nis_debug_calls }, 224 { "server", OPT_STRING, &__nis_server }, 225 { "pref_srvr", OPT_STRING, &__nis_prefsrv }, 226 { "pref_type", OPT_STRING, &__nis_preftype }, 227 { 0, 0, 0 }, 228 }; 229 230 /* 231 * switch configuration parameter "database" definitions: 232 * The switch maintains a simmple read/write parameter database 233 * that nscd and the switch components can use to communicate 234 * nscd data to other components for configuration or out of band 235 * [IE no in the context of a getXbyY or putXbyY operation] data. 236 * The data passed are pointers to a lock data buffer and a length. 237 * Use of this is treated as SunwPrivate between nscd and the switch 238 * unless other wise stated. 239 */ 240 241 typedef struct nss_cfgparam { 242 char *name; 243 mutex_t *lock; 244 void *buffer; 245 size_t length; 246 } nss_cfgparam_t; 247 248 typedef struct nss_cfglist { 249 char *name; 250 nss_cfgparam_t *list; 251 int count; 252 int max; 253 } nss_cfglist_t; 254 255 #define NSS_CFG_INCR 16 256 257 static nss_cfglist_t *nss_cfg = NULL; 258 static int nss_cfgcount = 0; 259 static int nss_cfgmax = 0; 260 static mutex_t nss_cfglock = DEFAULTMUTEX; 261 262 static int nss_cfg_policy_init(); 263 264 /* 265 * A config parameters are in the form component:parameter 266 * as in: nss:parameter - switch (internal FE/policy/BE) parameter 267 * nscd:param - nscd application parameter 268 * ldap:param - nss_ldap BE parameter 269 * passwd:param - get/put passwd FE parameter 270 */ 271 272 #define NSS_CONFIG_BRK ':' 273 274 /* 275 * The policy components initial parameter list 276 */ 277 static nss_config_t nss_policy_params[] = { 278 { "nss:policyfunc", NSS_CONFIG_ADD, &nss_policyf_lock, 279 (void *)&nss_policyf_ptrs, (size_t)sizeof (nss_policyf_t) }, 280 { NULL, NSS_CONFIG_ADD, (mutex_t *)NULL, (void *)NULL, (size_t)0 }, 281 }; 282 283 /* 284 * NSS parameter configuration routines 285 */ 286 287 /* compare config name (component:parameter) to a component name */ 288 static int 289 nss_cfgcn_cmp(const char *cfgname, const char *compname) 290 { 291 char *c; 292 size_t len, len2; 293 294 /* this code assumes valid pointers */ 295 if ((c = strchr(cfgname, NSS_CONFIG_BRK)) == NULL) 296 return (-1); 297 len = (size_t)(c - cfgname); 298 len2 = strlen(compname); 299 if (len2 != len) 300 return (-1); 301 return (strncmp(cfgname, compname, len)); 302 } 303 304 /* init configuration arena */ 305 static int 306 nss_cfg_init() 307 { 308 nss_cfglist_t *cfg; 309 int i; 310 311 /* First time caller? */ 312 if (nss_cfg != NULL) { 313 membar_consumer(); 314 return (0); 315 } 316 317 /* Initialize internal tables */ 318 lmutex_lock(&nss_cfglock); 319 if (nss_cfg != NULL) { 320 lmutex_unlock(&nss_cfglock); 321 membar_consumer(); 322 return (0); 323 } 324 cfg = libc_malloc(NSS_CFG_INCR * sizeof (nss_cfglist_t)); 325 if (cfg == NULL) { 326 errno = ENOMEM; 327 lmutex_unlock(&nss_cfglock); 328 return (-1); 329 } 330 for (i = 0; i < NSS_CFG_INCR; i++) { 331 cfg[i].list = libc_malloc( 332 NSS_CFG_INCR * sizeof (nss_cfgparam_t)); 333 if (cfg[i].list == NULL) { 334 while (--i >= 0) 335 libc_free(cfg[i].list); 336 libc_free(cfg); 337 errno = ENOMEM; 338 lmutex_unlock(&nss_cfglock); 339 return (-1); 340 } 341 cfg[i].max = NSS_CFG_INCR; 342 } 343 nss_cfgmax = NSS_CFG_INCR; 344 membar_producer(); 345 nss_cfg = cfg; 346 lmutex_unlock(&nss_cfglock); 347 348 /* Initialize Policy Engine values */ 349 if (nss_cfg_policy_init() < 0) { 350 return (-1); 351 } 352 return (0); 353 } 354 355 /* find the name'd component list - create it if non-existent */ 356 static nss_cfglist_t * 357 nss_cfgcomp_get(char *name, int add) 358 { 359 nss_cfglist_t *next; 360 char *c; 361 int i, len; 362 size_t nsize; 363 364 /* Make sure system is init'd */ 365 if (nss_cfg_init() < 0) 366 return ((nss_cfglist_t *)NULL); 367 368 /* and check component:name validity */ 369 if (name == NULL || (c = strchr(name, NSS_CONFIG_BRK)) == NULL) 370 return ((nss_cfglist_t *)NULL); 371 372 lmutex_lock(&nss_cfglock); 373 next = nss_cfg; 374 for (i = 0; i < nss_cfgcount; i++) { 375 if (next->name && nss_cfgcn_cmp(name, next->name) == 0) { 376 lmutex_unlock(&nss_cfglock); 377 return (next); 378 } 379 next++; 380 } 381 if (!add) { 382 lmutex_unlock(&nss_cfglock); 383 return (NULL); 384 } 385 386 /* not found, create a fresh one */ 387 if (nss_cfgcount >= nss_cfgmax) { 388 /* realloc first */ 389 nsize = (nss_cfgmax + NSS_CFG_INCR) * sizeof (nss_cfgparam_t); 390 next = (nss_cfglist_t *)libc_realloc(nss_cfg, nsize); 391 if (next == NULL) { 392 errno = ENOMEM; 393 lmutex_unlock(&nss_cfglock); 394 return ((nss_cfglist_t *)NULL); 395 } 396 (void) memset((void *)(next + nss_cfgcount), '\0', 397 NSS_CFG_INCR * sizeof (nss_cfglist_t)); 398 nss_cfgmax += NSS_CFG_INCR; 399 nss_cfg = next; 400 } 401 next = nss_cfg + nss_cfgcount; 402 len = (size_t)(c - name) + 1; 403 if ((next->name = libc_malloc(len)) == NULL) { 404 errno = ENOMEM; 405 lmutex_unlock(&nss_cfglock); 406 return ((nss_cfglist_t *)NULL); 407 } 408 nss_cfgcount++; 409 (void) strlcpy(next->name, name, len); 410 lmutex_unlock(&nss_cfglock); 411 return (next); 412 } 413 414 /* find the name'd parameter - create it if non-existent */ 415 static nss_cfgparam_t * 416 nss_cfgparam_get(char *name, int add) 417 { 418 nss_cfglist_t *comp; 419 nss_cfgparam_t *next; 420 int count, i; 421 size_t nsize; 422 423 if ((comp = nss_cfgcomp_get(name, add)) == NULL) 424 return ((nss_cfgparam_t *)NULL); 425 lmutex_lock(&nss_cfglock); 426 count = comp->count; 427 next = comp->list; 428 for (i = 0; i < count; i++) { 429 if (next->name && strcmp(name, next->name) == 0) { 430 lmutex_unlock(&nss_cfglock); 431 return (next); 432 } 433 next++; 434 } 435 if (!add) { 436 lmutex_unlock(&nss_cfglock); 437 return (NULL); 438 } 439 440 /* not found, create a fresh one */ 441 if (count >= comp->max) { 442 /* realloc first */ 443 nsize = (comp->max + NSS_CFG_INCR) * sizeof (nss_cfgparam_t); 444 next = (nss_cfgparam_t *)libc_realloc(comp->list, nsize); 445 if (next == NULL) { 446 errno = ENOMEM; 447 lmutex_unlock(&nss_cfglock); 448 return ((nss_cfgparam_t *)NULL); 449 } 450 comp->max += NSS_CFG_INCR; 451 comp->list = next; 452 } 453 next = comp->list + comp->count; 454 if ((next->name = libc_strdup(name)) == NULL) { 455 errno = ENOMEM; 456 lmutex_unlock(&nss_cfglock); 457 return ((nss_cfgparam_t *)NULL); 458 } 459 comp->count++; 460 lmutex_unlock(&nss_cfglock); 461 return (next); 462 } 463 464 /* find the name'd parameter - delete it if it exists */ 465 static void 466 nss_cfg_del(nss_config_t *cfgp) 467 { 468 char *name; 469 nss_cfglist_t *comp; 470 nss_cfgparam_t *next, *cur; 471 int count, i, j; 472 473 /* exit if component name does not already exist */ 474 if ((name = cfgp->name) == NULL || 475 (comp = nss_cfgcomp_get(name, 0)) == NULL) 476 return; 477 478 /* find it */ 479 lmutex_lock(&nss_cfglock); 480 count = comp->count; 481 next = comp->list; 482 for (i = 0; i < count; i++) { 483 if (next->name && strcmp(name, next->name) == 0) { 484 break; /* found it... */ 485 } 486 next++; 487 } 488 if (i >= count) { 489 /* not found, already deleted */ 490 lmutex_unlock(&nss_cfglock); 491 return; 492 } 493 494 /* copy down the remaining parameters, and clean up */ 495 /* don't try to clean up component tables */ 496 cur = next; 497 next++; 498 for (j = i+1; j < count; j++) { 499 *cur = *next; 500 cur++; 501 next++; 502 } 503 /* erase the last one */ 504 if (cur->name) { 505 libc_free(cur->name); 506 cur->name = (char *)NULL; 507 } 508 cur->lock = (mutex_t *)NULL; 509 cur->buffer = (void *)NULL; 510 cur->length = 0; 511 comp->count--; 512 lmutex_unlock(&nss_cfglock); 513 } 514 515 static int 516 nss_cfg_get(nss_config_t *next) 517 { 518 nss_cfgparam_t *param; 519 520 errno = 0; 521 if ((param = nss_cfgparam_get(next->name, 0)) == NULL) 522 return (-1); 523 next->lock = param->lock; 524 next->buffer = param->buffer; 525 next->length = param->length; 526 return (0); 527 } 528 529 static int 530 nss_cfg_put(nss_config_t *next, int add) 531 { 532 nss_cfgparam_t *param; 533 534 errno = 0; 535 if ((param = nss_cfgparam_get(next->name, add)) == NULL) 536 return (-1); 537 param->lock = next->lock; 538 param->buffer = next->buffer; 539 param->length = next->length; 540 return (0); 541 } 542 543 /* 544 * Policy engine configurator - set and get interface 545 * argument is a NULL terminated list of set/get requests 546 * with input/result buffers and lengths. nss_cname is the 547 * specifier of a set or get operation and the property being 548 * managed. The intent is limited functions and expandability. 549 */ 550 551 nss_status_t 552 nss_config(nss_config_t **plist, int cnt) 553 { 554 nss_config_t *next; 555 int i; 556 557 /* interface is only available to nscd */ 558 if (_nsc_proc_is_cache() <= 0) { 559 return (NSS_UNAVAIL); 560 } 561 if (plist == NULL || cnt <= 0) 562 return (NSS_SUCCESS); 563 for (i = 0; i < cnt; i++) { 564 next = plist[i]; 565 if (next == NULL) 566 break; 567 if (next->name == NULL) { 568 errno = EFAULT; 569 return (NSS_ERROR); 570 } 571 switch (next->cop) { 572 case NSS_CONFIG_GET: 573 /* get current lock/buffer/length fields */ 574 if (nss_cfg_get(next) < 0) { 575 return (NSS_ERROR); 576 } 577 break; 578 case NSS_CONFIG_PUT: 579 /* set new lock/buffer/length fields */ 580 if (nss_cfg_put(next, 0) < 0) { 581 return (NSS_ERROR); 582 } 583 break; 584 case NSS_CONFIG_ADD: 585 /* add parameter & set new lock/buffer/length fields */ 586 if (nss_cfg_put(next, 1) < 0) { 587 return (NSS_ERROR); 588 } 589 break; 590 case NSS_CONFIG_DELETE: 591 /* delete parameter - should always work... */ 592 nss_cfg_del(next); 593 break; 594 case NSS_CONFIG_LIST: 595 break; 596 default: 597 continue; 598 } 599 } 600 return (NSS_SUCCESS); 601 } 602 603 /* 604 * This routine is called immediately after nss_cfg_init but prior to 605 * any commands from nscd being processed. The intent here is to 606 * initialize the nss:* parameters allowed by the policy component 607 * so that nscd can then proceed and modify them if so desired. 608 * 609 * We know we can only get here if we are nscd so we can skip the 610 * preliminaries. 611 */ 612 613 static int 614 nss_cfg_policy_init() 615 { 616 nss_config_t *next = &nss_policy_params[0]; 617 618 for (; next && next->name != NULL; next++) { 619 if (nss_cfg_put(next, 1) < 0) 620 return (-1); 621 } 622 return (0); 623 } 624 625 /* 626 * NSS_OPTION & NIS_OPTION environment variable functions 627 */ 628 629 static 630 void 631 set_option(struct option *opt, char *name, char *val) 632 { 633 int n; 634 char *p; 635 #ifdef DEBUG 636 FILE *fp; 637 #endif 638 639 for (; opt->name; opt++) { 640 if (strcmp(name, opt->name) == 0) { 641 switch (opt->type) { 642 case OPT_STRING: 643 p = libc_strdup(val); 644 *((char **)opt->address) = p; 645 break; 646 647 case OPT_INT: 648 if (strcmp(val, "") == 0) 649 n = 1; 650 else 651 n = atoi(val); 652 *((int *)opt->address) = n; 653 break; 654 #ifdef DEBUG 655 case OPT_FILE: 656 fp = fopen(val, "wF"); 657 *((FILE **)opt->address) = fp; 658 break; 659 #endif 660 } 661 break; 662 } 663 } 664 } 665 666 static 667 void 668 __parse_environment(struct option *opt, char *p) 669 { 670 char *base; 671 char optname[100]; 672 char optval[100]; 673 674 while (*p) { 675 while (isspace(*p)) 676 p++; 677 if (*p == '\0') 678 break; 679 680 base = p; 681 while (*p && *p != '=' && !isspace(*p)) 682 p++; 683 /* 684 * play it safe and keep it simple, bail if an opt name 685 * is too long. 686 */ 687 if ((p-base) >= sizeof (optname)) 688 return; 689 690 (void) strncpy(optname, base, p-base); 691 optname[p-base] = '\0'; 692 693 if (*p == '=') { 694 p++; 695 base = p; 696 while (*p && !isspace(*p)) 697 p++; 698 /* 699 * play it safe and keep it simple, bail if an opt 700 * value is too long. 701 */ 702 if ((p-base) >= sizeof (optval)) 703 return; 704 705 (void) strncpy(optval, base, p-base); 706 optval[p-base] = '\0'; 707 } else { 708 optval[0] = '\0'; 709 } 710 711 set_option(opt, optname, optval); 712 } 713 } 714 715 static 716 void 717 nss_get_environment() 718 { 719 char *p; 720 721 /* NSS_OPTIONS is undocumented and should be used without nscd running. */ 722 p = getenv("NSS_OPTIONS"); 723 if (p == NULL) 724 return; 725 __parse_environment(nss_options, p); 726 } 727 728 /* 729 * sole external routine called from libnsl/nis/cache/cache_api.cc in the 730 * routines _nis_CacheInit/__nis_CacheLocalInit/__nis_CacheMgrInit_discard 731 * Only after checking "checked_env" (which must be done with mutex 732 * "cur_cache_lock" held) and is done once, (then "checked_env" is set) 733 */ 734 void 735 __nis_get_environment() 736 { 737 char *p; 738 739 p = getenv("NIS_OPTIONS"); 740 if (p == NULL) 741 return; 742 __parse_environment(nis_options, p); 743 } 744 745 746 /* 747 * Switch policy component backend state machine functions 748 */ 749 750 static nss_backend_t * 751 nss_get_backend_u(nss_db_root_t **rootpp, struct nss_db_state *s, int n_src) 752 { 753 struct nss_src_state *src = &s->src[n_src]; 754 nss_backend_t *be; 755 756 for (;;) { 757 if (src->n_dormant > 0) { 758 src->n_dormant--; 759 src->n_active++; 760 if (s->p.max_dormant_per_src == 1) { 761 be = src->dormant.single; 762 } else { 763 be = src->dormant.multi[src->n_dormant]; 764 } 765 break; 766 } 767 768 if (src->be_constr == 0) { 769 nss_backend_finder_t *bf; 770 771 for (bf = s->p.finders; bf != 0; bf = bf->next) { 772 nss_backend_constr_t c; 773 774 c = (*bf->lookup) 775 (bf->lookup_priv, 776 s->p.name, 777 src->lkp->service_name, 778 &src->finder_priv); 779 if (c != 0) { 780 src->be_constr = c; 781 src->finder = bf; 782 break; 783 } 784 } 785 if (src->be_constr == 0) { 786 /* Couldn't find the backend anywhere */ 787 be = 0; 788 break; 789 } 790 } 791 792 if (src->n_active < s->p.max_active_per_src) { 793 be = (*src->be_constr)(s->p.name, 794 src->lkp->service_name, 795 0 /* === unimplemented */); 796 if (be != 0) { 797 src->n_active++; 798 break; 799 } else if (src->n_active == 0) { 800 /* Something's wrong; we should be */ 801 /* able to create at least one */ 802 /* instance of the backend */ 803 break; 804 } 805 /* 806 * Else it's odd that we can't create another backend 807 * instance, but don't sweat it; instead, queue for 808 * an existing backend instance. 809 */ 810 } 811 812 src->n_waiting++; 813 (void) cond_wait(&src->wanna_be, &(*rootpp)->lock); 814 NSS_CHECKROOT(rootpp, s); 815 src->n_waiting--; 816 817 /* 818 * Loop and see whether things got better for us, or whether 819 * someone else got scheduled first and we have to try 820 * this again. 821 * 822 * === ?? Should count iterations, assume bug if many ?? 823 */ 824 } 825 return (be); 826 } 827 828 static void 829 nss_put_backend_u(struct nss_db_state *s, int n_src, nss_backend_t *be) 830 { 831 struct nss_src_state *src = &s->src[n_src]; 832 833 if (be == 0) { 834 return; 835 } 836 837 src->n_active--; 838 839 if (src->n_dormant < s->p.max_dormant_per_src) { 840 if (s->p.max_dormant_per_src == 1) { 841 src->dormant.single = be; 842 src->n_dormant++; 843 } else if (src->dormant.multi != 0 || 844 (src->dormant.multi = 845 libc_malloc(s->p.max_dormant_per_src * 846 sizeof (nss_backend_t *))) != NULL) { 847 src->dormant.multi[src->n_dormant] = be; 848 src->n_dormant++; 849 } else { 850 /* Can't store it, so toss it */ 851 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0); 852 } 853 } else { 854 /* We've stored as many as we want, so toss it */ 855 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_DESTRUCTOR, 0); 856 } 857 if (src->n_waiting > 0) { 858 (void) cond_signal(&src->wanna_be); 859 } 860 } 861 862 static struct nss_db_state * 863 _nss_db_state_constr(nss_db_initf_t initf) 864 { 865 struct nss_db_state *s; 866 struct __nsw_switchconfig_v1 *config = 0; 867 struct __nsw_lookup_v1 *lkp; 868 enum __nsw_parse_err err; 869 const char *config_name; 870 int n_src; 871 872 if ((s = libc_malloc(sizeof (*s))) == 0) { 873 return (0); 874 } 875 (void) _private_mutex_init(&s->orphan_root.lock, USYNC_THREAD, 0); 876 877 s->p.max_active_per_src = 10; 878 s->p.max_dormant_per_src = 1; 879 s->p.finders = nss_default_finders; 880 (*initf)(&s->p); 881 if (s->p.name == 0) { 882 _nss_db_state_destr(s); 883 return (0); 884 } 885 886 if (!checked_env) { 887 /* NSS_OPTIONS is undocumented and should be used without nscd running. */ 888 nss_get_environment(); 889 checked_env = 1; 890 } 891 892 config_name = s->p.config_name ? s->p.config_name : s->p.name; 893 if (! (s->p.flags & NSS_USE_DEFAULT_CONFIG)) { 894 config = __nsw_getconfig_v1(config_name, &err); 895 /* === ? test err ? */ 896 } 897 if (config == 0) { 898 /* getconfig failed, or frontend demanded default config */ 899 900 char *str; /* _nsw_getoneconfig() clobbers its argument */ 901 902 if ((str = libc_strdup(s->p.default_config)) != 0) { 903 config = _nsw_getoneconfig_v1(config_name, str, &err); 904 libc_free(str); 905 } 906 if (config == 0) { 907 _nss_db_state_destr(s); 908 return (0); 909 } 910 } 911 s->config = config; 912 if ((s->max_src = config->num_lookups) <= 0 || 913 (s->src = libc_malloc(s->max_src * sizeof (*s->src))) == 0) { 914 _nss_db_state_destr(s); 915 return (0); 916 } 917 for (n_src = 0, lkp = config->lookups; 918 n_src < s->max_src; n_src++, lkp = lkp->next) { 919 s->src[n_src].lkp = lkp; 920 (void) cond_init(&s->src[n_src].wanna_be, USYNC_THREAD, 0); 921 } 922 s->refcount = 1; 923 return (s); 924 } 925 926 void 927 _nss_src_state_destr(struct nss_src_state *src, int max_dormant) 928 { 929 if (max_dormant == 1) { 930 if (src->n_dormant != 0) { 931 (void) NSS_INVOKE_DBOP(src->dormant.single, 932 NSS_DBOP_DESTRUCTOR, 0); 933 }; 934 } else if (src->dormant.multi != 0) { 935 int n; 936 937 for (n = 0; n < src->n_dormant; n++) { 938 (void) NSS_INVOKE_DBOP(src->dormant.multi[n], 939 NSS_DBOP_DESTRUCTOR, 0); 940 } 941 libc_free(src->dormant.multi); 942 } 943 944 /* cond_destroy(&src->wanna_be); */ 945 946 if (src->finder != 0) { 947 (*src->finder->delete)(src->finder_priv, src->be_constr); 948 } 949 } 950 951 /* 952 * _nss_db_state_destr() -- used by NSS_UNREF_UNLOCK() to free the entire 953 * nss_db_state structure. 954 * Assumes that s has been ref-counted down to zero (in particular, 955 * rootp->s has already been dealt with). 956 * 957 * Nobody else holds a pointer to *s (if they did, refcount != 0), 958 * so we can clean up state *after* we drop the lock (also, by the 959 * time we finish freeing the state structures, the lock may have 960 * ceased to exist -- if we were using the orphan_root). 961 */ 962 963 void 964 _nss_db_state_destr(struct nss_db_state *s) 965 { 966 967 if (s == NULL) 968 return; 969 970 /* === _private_mutex_destroy(&s->orphan_root.lock); */ 971 if (s->p.cleanup != 0) { 972 (*s->p.cleanup)(&s->p); 973 } 974 if (s->config != 0) { 975 (void) __nsw_freeconfig_v1(s->config); 976 } 977 if (s->src != 0) { 978 int n_src; 979 980 for (n_src = 0; n_src < s->max_src; n_src++) { 981 _nss_src_state_destr(&s->src[n_src], 982 s->p.max_dormant_per_src); 983 } 984 libc_free(s->src); 985 } 986 libc_free(s); 987 } 988 989 990 /* 991 * _nss_status_vec() returns a bit vector of all status codes returned during 992 * the most recent call to nss_search(). 993 * _nss_status_vec_p() returns a pointer to this bit vector, or NULL on 994 * failure. 995 * These functions are private. Don't use them externally without discussing 996 * it with the switch maintainers. 997 */ 998 static uint_t * 999 _nss_status_vec_p() 1000 { 1001 return (tsdalloc(_T_NSS_STATUS_VEC, sizeof (uint_t), NULL)); 1002 } 1003 1004 unsigned int 1005 _nss_status_vec(void) 1006 { 1007 unsigned int *status_vec_p = _nss_status_vec_p(); 1008 1009 return ((status_vec_p != NULL) ? *status_vec_p : (1 << NSS_UNAVAIL)); 1010 } 1011 1012 static void 1013 output_loop_diag_a( 1014 int n, 1015 char *dbase, 1016 struct __nsw_lookup_v1 *lkp) 1017 1018 { 1019 (void) fprintf(__nss_debug_file, 1020 "NSS_retry(%d): '%s': trying '%s' ... ", 1021 n, dbase, lkp->service_name); 1022 (void) fflush(__nss_debug_file); 1023 1024 } 1025 1026 static void 1027 output_loop_diag_b( 1028 nss_status_t res, 1029 struct __nsw_lookup_v1 *lkp) 1030 1031 { 1032 (void) fprintf(__nss_debug_file, "result="); 1033 switch (res) { 1034 case NSS_SUCCESS: 1035 (void) fprintf(__nss_debug_file, "SUCCESS"); 1036 break; 1037 case NSS_NOTFOUND: 1038 (void) fprintf(__nss_debug_file, "NOTFOUND"); 1039 break; 1040 case NSS_UNAVAIL: 1041 (void) fprintf(__nss_debug_file, "UNAVAIL"); 1042 break; 1043 case NSS_TRYAGAIN: 1044 (void) fprintf(__nss_debug_file, "TRYAGAIN"); 1045 break; 1046 case NSS_NISSERVDNS_TRYAGAIN: 1047 (void) fprintf(__nss_debug_file, "NISSERVDNS_TRYAGAIN"); 1048 break; 1049 default: 1050 (void) fprintf(__nss_debug_file, "undefined"); 1051 } 1052 (void) fprintf(__nss_debug_file, ", action="); 1053 switch (lkp->actions[res]) { 1054 case __NSW_CONTINUE: 1055 (void) fprintf(__nss_debug_file, "CONTINUE"); 1056 break; 1057 case __NSW_RETURN: 1058 (void) fprintf(__nss_debug_file, "RETURN"); 1059 break; 1060 case __NSW_TRYAGAIN_FOREVER: 1061 (void) fprintf(__nss_debug_file, "TRYAGAIN_FOREVER"); 1062 break; 1063 case __NSW_TRYAGAIN_NTIMES: 1064 (void) fprintf(__nss_debug_file, "TRYAGAIN_NTIMES (N=%d)", 1065 lkp->max_retries); 1066 break; 1067 case __NSW_TRYAGAIN_PAUSED: 1068 (void) fprintf(__nss_debug_file, "TRYAGAIN_PAUSED"); 1069 break; 1070 default: 1071 (void) fprintf(__nss_debug_file, "undefined"); 1072 } 1073 (void) fprintf(__nss_debug_file, "\n"); 1074 } 1075 1076 #define NSS_BACKOFF(n, b, t) \ 1077 ((n) > ((b) + 3) ? t : (1 << ((n) - ((b) + 1)))) 1078 1079 static int 1080 retry_test(nss_status_t res, int n, struct __nsw_lookup_v1 *lkp) 1081 { 1082 if (res != NSS_TRYAGAIN && res != NSS_NISSERVDNS_TRYAGAIN) { 1083 if (res == NSS_SUCCESS) { 1084 __NSW_UNPAUSE_ACTION(lkp->actions[__NSW_TRYAGAIN]); 1085 __NSW_UNPAUSE_ACTION( 1086 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN]); 1087 } 1088 return (0); 1089 } 1090 1091 if ((res == NSS_TRYAGAIN && 1092 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER) || 1093 (res == NSS_NISSERVDNS_TRYAGAIN && 1094 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_FOREVER)) 1095 return (1); 1096 1097 if (res == NSS_TRYAGAIN && 1098 lkp->actions[__NSW_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES) 1099 if (n <= lkp->max_retries) 1100 return (1); 1101 else { 1102 lkp->actions[__NSW_TRYAGAIN] = __NSW_TRYAGAIN_PAUSED; 1103 return (0); 1104 } 1105 1106 if (res == NSS_NISSERVDNS_TRYAGAIN && 1107 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] == __NSW_TRYAGAIN_NTIMES) 1108 if (n <= lkp->max_retries) 1109 return (1); 1110 else { 1111 lkp->actions[__NSW_NISSERVDNS_TRYAGAIN] = 1112 __NSW_TRYAGAIN_PAUSED; 1113 return (0); 1114 } 1115 1116 return (0); 1117 } 1118 1119 /* 1120 * Switch policy component functional interfaces 1121 */ 1122 1123 void 1124 nss_delete(nss_db_root_t *rootp) 1125 { 1126 struct nss_db_state *s; 1127 1128 /* no name service cache daemon divert here */ 1129 /* local nss_delete decrements state reference counts */ 1130 /* and may free up opened switch resources. */ 1131 1132 NSS_ROOTLOCK(rootp, &s); 1133 if (s == 0) { 1134 NSS_UNLOCK(rootp); 1135 } else { 1136 rootp->s = 0; 1137 NSS_UNREF_UNLOCK(rootp, s); 1138 } 1139 } 1140 1141 nss_status_t 1142 nss_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum, 1143 void *search_args) 1144 { 1145 nss_status_t res = NSS_UNAVAIL; 1146 struct nss_db_state *s; 1147 int n_src; 1148 unsigned int *status_vec_p; 1149 1150 /* name service cache daemon divert */ 1151 res = _nsc_search(rootp, initf, search_fnum, search_args); 1152 if (res != NSS_TRYLOCAL) 1153 return (res); 1154 1155 /* fall through - process locally */ 1156 errno = 0; /* just in case ... */ 1157 res = NSS_UNAVAIL; 1158 status_vec_p = _nss_status_vec_p(); 1159 1160 if (status_vec_p == NULL) { 1161 return (NSS_UNAVAIL); 1162 } 1163 *status_vec_p = 0; 1164 1165 NSS_LOCK_CHECK(rootp, initf, &s); 1166 if (s == 0) { 1167 NSS_UNLOCK(rootp); 1168 return (res); 1169 } 1170 NSS_STATE_REF_u(s); 1171 1172 for (n_src = 0; n_src < s->max_src; n_src++) { 1173 nss_backend_t *be; 1174 nss_backend_op_t funcp; 1175 1176 res = NSS_UNAVAIL; 1177 if ((be = nss_get_backend_u(&rootp, s, n_src)) != 0) { 1178 if ((funcp = NSS_LOOKUP_DBOP(be, search_fnum)) != 0) { 1179 int n_loop = 0; 1180 int no_backoff = 19; 1181 int max_backoff = 5; /* seconds */ 1182 1183 do { 1184 /* 1185 * Backend operation may take a while; 1186 * drop the lock so we don't serialize 1187 * more than necessary. 1188 */ 1189 NSS_UNLOCK(rootp); 1190 1191 /* After several tries, backoff... */ 1192 if (n_loop > no_backoff) { 1193 if (__nss_debug_eng_loop > 1) 1194 (void) fprintf(__nss_debug_file, 1195 "NSS: loop: sleeping %d ...\n", 1196 NSS_BACKOFF(n_loop, 1197 no_backoff, max_backoff)); 1198 1199 (void) sleep(NSS_BACKOFF(n_loop, 1200 no_backoff, max_backoff)); 1201 } 1202 1203 if (__nss_debug_eng_loop) 1204 output_loop_diag_a(n_loop, 1205 s->config->dbase, 1206 s->src[n_src].lkp); 1207 1208 1209 res = (*funcp)(be, search_args); 1210 NSS_RELOCK(&rootp, s); 1211 n_loop++; 1212 if (__nss_debug_eng_loop) 1213 output_loop_diag_b(res, 1214 s->src[n_src].lkp); 1215 } while (retry_test(res, n_loop, 1216 s->src[n_src].lkp)); 1217 } 1218 nss_put_backend_u(s, n_src, be); 1219 } 1220 *status_vec_p |= (1 << res); 1221 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) { 1222 if (__nss_debug_eng_loop) 1223 (void) fprintf(__nss_debug_file, 1224 "NSS: '%s': return.\n", 1225 s->config->dbase); 1226 break; 1227 } else 1228 if (__nss_debug_eng_loop) 1229 (void) fprintf(__nss_debug_file, 1230 "NSS: '%s': continue ...\n", 1231 s->config->dbase); 1232 } 1233 NSS_UNREF_UNLOCK(rootp, s); 1234 return (res); 1235 } 1236 1237 1238 /* 1239 * Start of nss_{setent|getent|endent} 1240 */ 1241 1242 /* 1243 * State (here called "context") for one setent/getent.../endent sequence. 1244 * In principle there could be multiple contexts active for a single 1245 * database; in practice, since Posix and UI have helpfully said that 1246 * getent() state is global rather than, say, per-thread or user-supplied, 1247 * we have at most one of these per nss_db_state. 1248 * XXX ? Is this statement still true? 1249 * 1250 * NSS2 - a client's context is maintained as a cookie delivered by and 1251 * passed to nscd. The cookie is a 64 bit (nssuint_t) unique opaque value 1252 * created by nscd. 1253 * cookie states: 1254 * NSCD_NEW_COOKIE - cookie value uninitialized 1255 * NSCD_LOCAL_COOKIE - setent is a local setent 1256 * all other - NSCD unique opaque id for this setent 1257 * A client's context is also associated with a seq_num. This is a nscd 1258 * opaque 64 bit (nssuint_t) value passed with a cookie, and used to by nscd 1259 * to validate the sequencing of the context. The client treats this as 1260 * a pass through value. 1261 * 1262 * XXX ?? Use Cookie as cross-check info so that we can detect an 1263 * nss_context that missed an nss_delete() or similar. 1264 */ 1265 1266 struct nss_getent_context { 1267 int n_src; /* >= max_src ==> end of sequence */ 1268 nss_backend_t *be; 1269 struct nss_db_state *s; 1270 nssuint_t cookie; 1271 nssuint_t seq_num; 1272 nssuint_t cookie_setent; 1273 nss_db_params_t param; 1274 }; 1275 1276 static void nss_setent_u(nss_db_root_t *, 1277 nss_db_initf_t, 1278 nss_getent_t *); 1279 static nss_status_t nss_getent_u(nss_db_root_t *, 1280 nss_db_initf_t, 1281 nss_getent_t *, 1282 void *); 1283 static void nss_endent_u(nss_db_root_t *, 1284 nss_db_initf_t, 1285 nss_getent_t *); 1286 1287 void 1288 nss_setent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp) 1289 { 1290 if (contextpp == 0) { 1291 return; 1292 } 1293 (void) _private_mutex_lock(&contextpp->lock); 1294 nss_setent_u(rootp, initf, contextpp); 1295 (void) _private_mutex_unlock(&contextpp->lock); 1296 } 1297 1298 nss_status_t 1299 nss_getent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp, 1300 void *args) 1301 { 1302 nss_status_t status; 1303 1304 if (contextpp == 0) { 1305 return (NSS_UNAVAIL); 1306 } 1307 (void) _private_mutex_lock(&contextpp->lock); 1308 status = nss_getent_u(rootp, initf, contextpp, args); 1309 (void) _private_mutex_unlock(&contextpp->lock); 1310 return (status); 1311 } 1312 1313 void 1314 nss_endent(nss_db_root_t *rootp, nss_db_initf_t initf, nss_getent_t *contextpp) 1315 { 1316 if (contextpp == 0) { 1317 return; 1318 } 1319 (void) _private_mutex_lock(&contextpp->lock); 1320 nss_endent_u(rootp, initf, contextpp); 1321 (void) _private_mutex_unlock(&contextpp->lock); 1322 } 1323 1324 /* 1325 * Each of the _u versions of the nss interfaces assume that the context 1326 * lock is held. No need to divert to nscd. Private to local sequencing. 1327 */ 1328 1329 static void 1330 end_iter_u(nss_db_root_t *rootp, struct nss_getent_context *contextp) 1331 { 1332 struct nss_db_state *s; 1333 nss_backend_t *be; 1334 int n_src; 1335 1336 s = contextp->s; 1337 n_src = contextp->n_src; 1338 be = contextp->be; 1339 1340 if (s != 0) { 1341 if (n_src < s->max_src && be != 0) { 1342 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 1343 NSS_RELOCK(&rootp, s); 1344 nss_put_backend_u(s, n_src, be); 1345 contextp->be = 0; /* Should be unnecessary, but hey */ 1346 NSS_UNREF_UNLOCK(rootp, s); 1347 } 1348 contextp->s = 0; 1349 } 1350 } 1351 1352 static void 1353 nss_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 1354 nss_getent_t *contextpp) 1355 { 1356 nss_status_t status; 1357 struct nss_db_state *s; 1358 struct nss_getent_context *contextp; 1359 nss_backend_t *be; 1360 int n_src; 1361 1362 /* setup process wide context while locked */ 1363 if ((contextp = contextpp->ctx) == 0) { 1364 if ((contextp = libc_malloc(sizeof (*contextp))) == 0) { 1365 return; 1366 } 1367 contextpp->ctx = contextp; 1368 contextp->cookie = NSCD_NEW_COOKIE; /* cookie init */ 1369 contextp->seq_num = 0; /* seq_num init */ 1370 s = 0; 1371 } else { 1372 s = contextp->s; 1373 if (contextp->cookie != NSCD_LOCAL_COOKIE) 1374 contextp->cookie = NSCD_NEW_COOKIE; 1375 } 1376 1377 /* name service cache daemon divert */ 1378 if (contextp->cookie == NSCD_NEW_COOKIE) { 1379 status = _nsc_setent_u(rootp, initf, contextpp); 1380 if (status != NSS_TRYLOCAL) 1381 return; 1382 } 1383 1384 /* fall through - process locally */ 1385 if (s == 0) { 1386 NSS_LOCK_CHECK(rootp, initf, &s); 1387 if (s == 0) { 1388 /* Couldn't set up state, so quit */ 1389 NSS_UNLOCK(rootp); 1390 /* ==== is there any danger of not having done an */ 1391 /* end_iter() here, and hence of losing backends? */ 1392 contextpp->ctx = 0; 1393 libc_free(contextp); 1394 return; 1395 } 1396 NSS_STATE_REF_u(s); 1397 contextp->s = s; 1398 } else { 1399 s = contextp->s; 1400 n_src = contextp->n_src; 1401 be = contextp->be; 1402 if (n_src == 0 && be != 0) { 1403 /* 1404 * Optimization: don't do endent, don't change 1405 * backends, just do the setent. Look Ma, no locks 1406 * (nor any context that needs updating). 1407 */ 1408 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 1409 return; 1410 } 1411 if (n_src < s->max_src && be != 0) { 1412 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 1413 NSS_RELOCK(&rootp, s); 1414 nss_put_backend_u(s, n_src, be); 1415 contextp->be = 0; /* Play it safe */ 1416 } else { 1417 NSS_RELOCK(&rootp, s); 1418 } 1419 } 1420 for (n_src = 0, be = 0; n_src < s->max_src && 1421 (be = nss_get_backend_u(&rootp, s, n_src)) == 0; n_src++) { 1422 ; 1423 } 1424 NSS_UNLOCK(rootp); 1425 1426 contextp->n_src = n_src; 1427 contextp->be = be; 1428 1429 if (be == 0) { 1430 /* Things are broken enough that we can't do setent/getent */ 1431 nss_endent_u(rootp, initf, contextpp); 1432 return; 1433 } 1434 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 1435 } 1436 1437 static nss_status_t 1438 nss_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 1439 nss_getent_t *contextpp, void *args) 1440 { 1441 nss_status_t status; 1442 struct nss_db_state *s; 1443 struct nss_getent_context *contextp; 1444 int n_src; 1445 nss_backend_t *be; 1446 1447 if ((contextp = contextpp->ctx) == 0) { 1448 nss_setent_u(rootp, initf, contextpp); 1449 if ((contextp = contextpp->ctx) == 0) { 1450 /* Give up */ 1451 return (NSS_UNAVAIL); 1452 } 1453 } 1454 /* name service cache daemon divert */ 1455 status = _nsc_getent_u(rootp, initf, contextpp, args); 1456 if (status != NSS_TRYLOCAL) 1457 return (status); 1458 1459 /* fall through - process locally */ 1460 s = contextp->s; 1461 n_src = contextp->n_src; 1462 be = contextp->be; 1463 1464 if (s == 0) { 1465 /* 1466 * We've done an end_iter() and haven't done nss_setent() 1467 * or nss_endent() since; we should stick in this state 1468 * until the caller invokes one of those two routines. 1469 */ 1470 return (NSS_SUCCESS); 1471 } 1472 1473 while (n_src < s->max_src) { 1474 nss_status_t res; 1475 1476 if (be == 0) { 1477 /* If it's null it's a bug, but let's play safe */ 1478 res = NSS_UNAVAIL; 1479 } else { 1480 res = NSS_INVOKE_DBOP(be, NSS_DBOP_GETENT, args); 1481 } 1482 1483 if (__NSW_ACTION_V1(s->src[n_src].lkp, res) == __NSW_RETURN) { 1484 if (res != __NSW_SUCCESS) { 1485 end_iter_u(rootp, contextp); 1486 } 1487 return (res); 1488 } 1489 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_ENDENT, 0); 1490 NSS_RELOCK(&rootp, s); 1491 nss_put_backend_u(s, n_src, be); 1492 do { 1493 n_src++; 1494 } while (n_src < s->max_src && 1495 (be = nss_get_backend_u(&rootp, s, n_src)) == 0); 1496 if (be == 0) { 1497 /* 1498 * This is the case where we failed to get the backend 1499 * for the last source. We exhausted all sources. 1500 */ 1501 NSS_UNLOCK(rootp); 1502 nss_endent_u(rootp, initf, contextpp); 1503 nss_delete(rootp); 1504 return (NSS_SUCCESS); 1505 } 1506 NSS_UNLOCK(rootp); 1507 contextp->n_src = n_src; 1508 contextp->be = be; 1509 (void) NSS_INVOKE_DBOP(be, NSS_DBOP_SETENT, 0); 1510 } 1511 /* Got to the end of the sources without finding another entry */ 1512 end_iter_u(rootp, contextp); 1513 return (NSS_SUCCESS); 1514 /* success is either a successful entry or end of the sources */ 1515 } 1516 1517 /*ARGSUSED*/ 1518 static void 1519 nss_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 1520 nss_getent_t *contextpp) 1521 { 1522 nss_status_t status; 1523 struct nss_getent_context *contextp; 1524 1525 if ((contextp = contextpp->ctx) == 0) { 1526 /* nss_endent() on an unused context is a no-op */ 1527 return; 1528 } 1529 1530 /* notify name service cache daemon */ 1531 status = _nsc_endent_u(rootp, initf, contextpp); 1532 if (status != NSS_TRYLOCAL) { 1533 /* clean up */ 1534 libc_free(contextp); 1535 contextpp->ctx = 0; 1536 return; 1537 } 1538 1539 /* fall through - process locally */ 1540 1541 /* 1542 * Existing code (BSD, SunOS) works in such a way that getXXXent() 1543 * following an endXXXent() behaves as though the user had invoked 1544 * setXXXent(), i.e. it iterates properly from the beginning. 1545 * We'd better not break this, so our choices are 1546 * (1) leave the context structure around, and do nss_setent or 1547 * something equivalent, 1548 * or (2) free the context completely, and rely on the code in 1549 * nss_getent() that makes getXXXent() do the right thing 1550 * even without a preceding setXXXent(). 1551 * The code below does (2), which frees up resources nicely but will 1552 * cost more if the user then does more getXXXent() operations. 1553 * Moral: for efficiency, don't call endXXXent() prematurely. 1554 */ 1555 end_iter_u(rootp, contextp); 1556 libc_free(contextp); 1557 contextpp->ctx = 0; 1558 } 1559 1560 /* 1561 * pack dbd data into header 1562 * Argment pointers assumed valid. 1563 * poff offset position pointer 1564 * IN = starting offset for dbd header 1565 * OUT = starting offset for next section 1566 */ 1567 1568 static nss_status_t 1569 nss_pack_dbd(void *buffer, size_t bufsize, nss_db_params_t *p, size_t *poff) 1570 { 1571 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 1572 nss_dbd_t *pdbd; 1573 size_t off = *poff; 1574 size_t len, blen; 1575 size_t n, nc, dc; 1576 char *bptr; 1577 1578 pbuf->dbd_off = (nssuint_t)off; 1579 bptr = (char *)buffer + off; 1580 blen = bufsize - off; 1581 len = sizeof (nss_dbd_t); 1582 1583 n = nc = dc = 0; 1584 if (p->name == NULL) { 1585 errno = ERANGE; /* actually EINVAL */ 1586 return (NSS_ERROR); 1587 } 1588 1589 /* if default config not specified, the flag should be reset */ 1590 if (p->default_config == NULL) { 1591 p->default_config = "<NULL>"; 1592 p->flags = p->flags & ~NSS_USE_DEFAULT_CONFIG; 1593 } 1594 1595 n = strlen(p->name) + 1; 1596 dc = strlen(p->default_config) + 1; 1597 if (n < 2 || dc < 2) { /* What no DB? */ 1598 errno = ERANGE; /* actually EINVAL */ 1599 return (NSS_ERROR); 1600 } 1601 if (p->config_name != NULL) { 1602 nc = strlen(p->config_name) + 1; 1603 } 1604 if ((len + n + nc + dc) >= blen) { 1605 errno = ERANGE; /* actually EINVAL */ 1606 return (NSS_ERROR); 1607 } 1608 1609 pdbd = (nss_dbd_t *)((void *)bptr); 1610 bptr += len; 1611 pdbd->flags = p->flags; 1612 pdbd->o_name = len; 1613 (void) strlcpy(bptr, p->name, n); 1614 len += n; 1615 bptr += n; 1616 if (nc == 0) { 1617 pdbd->o_config_name = 0; 1618 } else { 1619 pdbd->o_config_name = len; 1620 (void) strlcpy(bptr, p->config_name, nc); 1621 bptr += nc; 1622 len += nc; 1623 } 1624 pdbd->o_default_config = len; 1625 (void) strlcpy(bptr, p->default_config, dc); 1626 len += dc; 1627 pbuf->dbd_len = (nssuint_t)len; 1628 off += ROUND_UP(len, sizeof (nssuint_t)); 1629 *poff = off; 1630 return (NSS_SUCCESS); 1631 } 1632 1633 /* 1634 * Switch packed and _nsc (switch->nscd) interfaces 1635 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND 1636 */ 1637 1638 /*ARGSUSED*/ 1639 nss_status_t 1640 nss_pack(void *buffer, size_t bufsize, nss_db_root_t *rootp, 1641 nss_db_initf_t initf, int search_fnum, void *search_args) 1642 { 1643 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 1644 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args; 1645 nss_db_params_t tparam = { 0 }; 1646 nss_status_t ret = NSS_ERROR; 1647 const char *dbn; 1648 size_t blen, len, off = 0; 1649 char *bptr; 1650 struct nss_groupsbymem *gbm; 1651 1652 if (pbuf == NULL || in == NULL || initf == (nss_db_initf_t)NULL) { 1653 errno = ERANGE; /* actually EINVAL */ 1654 return (ret); 1655 } 1656 tparam.cleanup = NULL; 1657 (*initf)(&tparam); 1658 if ((dbn = tparam.name) == 0) { 1659 if (tparam.cleanup != 0) 1660 (tparam.cleanup)(&tparam); 1661 errno = ERANGE; /* actually EINVAL */ 1662 return (ret); 1663 } 1664 1665 /* init buffer header */ 1666 pbuf->pbufsiz = (nssuint_t)bufsize; 1667 pbuf->p_ruid = (uint32_t)getuid(); 1668 pbuf->p_euid = (uint32_t)geteuid(); 1669 pbuf->p_version = NSCD_HEADER_REV; 1670 pbuf->p_status = 0; 1671 pbuf->p_errno = 0; 1672 pbuf->p_herrno = 0; 1673 1674 /* possible audituser init */ 1675 if (strcmp(dbn, NSS_DBNAM_AUTHATTR) == 0 && in->h_errno != 0) 1676 pbuf->p_herrno = (uint32_t)in->h_errno; 1677 1678 pbuf->libpriv = 0; 1679 1680 off = sizeof (nss_pheader_t); 1681 1682 /* setup getXbyY operation - database and sub function */ 1683 pbuf->nss_dbop = (uint32_t)search_fnum; 1684 ret = nss_pack_dbd(buffer, bufsize, &tparam, &off); 1685 if (ret != NSS_SUCCESS) { 1686 errno = ERANGE; /* actually EINVAL */ 1687 return (ret); 1688 } 1689 ret = NSS_ERROR; 1690 /* setup request key */ 1691 pbuf->key_off = (nssuint_t)off; 1692 bptr = (char *)buffer + off; 1693 blen = bufsize - off; 1694 /* use key2str if provided, else call default getXbyY packer */ 1695 if (strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) { 1696 /* This has to run locally due to backend knowledge */ 1697 if (search_fnum == NSS_DBOP_NETGROUP_SET) { 1698 errno = 0; 1699 return (NSS_TRYLOCAL); 1700 } 1701 /* use default packer for known getXbyY ops */ 1702 ret = nss_default_key2str(bptr, blen, in, dbn, 1703 search_fnum, &len); 1704 } else if (in->key2str == NULL || 1705 (search_fnum == NSS_DBOP_GROUP_BYMEMBER && 1706 strcmp(dbn, NSS_DBNAM_GROUP) == 0)) { 1707 /* use default packer for known getXbyY ops */ 1708 ret = nss_default_key2str(bptr, blen, in, dbn, 1709 search_fnum, &len); 1710 } else { 1711 ret = (*in->key2str)(bptr, blen, &in->key, &len); 1712 } 1713 if (tparam.cleanup != 0) 1714 (tparam.cleanup)(&tparam); 1715 if (ret != NSS_SUCCESS) { 1716 errno = ERANGE; /* actually ENOMEM */ 1717 return (ret); 1718 } 1719 pbuf->key_len = (nssuint_t)len; 1720 off += ROUND_UP(len, sizeof (nssuint_t)); 1721 1722 pbuf->data_off = (nssuint_t)off; 1723 pbuf->data_len = (nssuint_t)(bufsize - off); 1724 /* 1725 * Prime data return with first result if 1726 * the first result is passed in 1727 * [_getgroupsbymember oddness] 1728 */ 1729 gbm = (struct nss_groupsbymem *)search_args; 1730 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER && 1731 strcmp(dbn, NSS_DBNAM_GROUP) == 0 && gbm->numgids == 1) { 1732 gid_t *gidp; 1733 gidp = (gid_t *)((void *)((char *)buffer + off)); 1734 *gidp = gbm->gid_array[0]; 1735 } 1736 1737 errno = 0; /* just in case ... */ 1738 return (NSS_SUCCESS); 1739 } 1740 1741 /* 1742 * Switch packed and _nsc (switch->nscd) {set/get/end}ent interfaces 1743 * Return: NSS_SUCCESS (OK to proceed), NSS_ERROR, NSS_NOTFOUND 1744 */ 1745 1746 /*ARGSUSED*/ 1747 nss_status_t 1748 nss_pack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp, 1749 nss_db_initf_t initf, nss_getent_t *contextpp) 1750 { 1751 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 1752 struct nss_getent_context *contextp = contextpp->ctx; 1753 nss_status_t ret = NSS_ERROR; 1754 size_t blen, len = 0, off = 0; 1755 char *bptr; 1756 nssuint_t *nptr; 1757 1758 if (pbuf == NULL || initf == (nss_db_initf_t)NULL) { 1759 errno = ERANGE; /* actually EINVAL */ 1760 return (ret); 1761 } 1762 1763 /* init buffer header */ 1764 pbuf->pbufsiz = (nssuint_t)bufsize; 1765 pbuf->p_ruid = (uint32_t)getuid(); 1766 pbuf->p_euid = (uint32_t)geteuid(); 1767 pbuf->p_version = NSCD_HEADER_REV; 1768 pbuf->p_status = 0; 1769 pbuf->p_errno = 0; 1770 pbuf->p_herrno = 0; 1771 pbuf->libpriv = 0; 1772 1773 off = sizeof (nss_pheader_t); 1774 1775 /* setup getXXXent operation - database and sub function */ 1776 pbuf->nss_dbop = (uint32_t)0; /* iterators have no dbop */ 1777 ret = nss_pack_dbd(buffer, bufsize, &contextp->param, &off); 1778 if (ret != NSS_SUCCESS) { 1779 errno = ERANGE; /* actually EINVAL */ 1780 return (ret); 1781 } 1782 ret = NSS_ERROR; 1783 off += ROUND_UP(len, sizeof (nssuint_t)); 1784 1785 pbuf->key_off = (nssuint_t)off; 1786 bptr = (char *)buffer + off; 1787 blen = bufsize - off; 1788 len = (size_t)(sizeof (nssuint_t) * 2); 1789 if (len >= blen) { 1790 errno = ERANGE; /* actually EINVAL */ 1791 return (ret); 1792 } 1793 nptr = (nssuint_t *)((void *)bptr); 1794 *nptr++ = contextp->cookie; 1795 *nptr = contextp->seq_num; 1796 pbuf->key_len = (nssuint_t)len; 1797 1798 off += len; 1799 pbuf->data_off = (nssuint_t)off; 1800 pbuf->data_len = (nssuint_t)(bufsize - off); 1801 return (NSS_SUCCESS); 1802 } 1803 1804 /* 1805 * Unpack packed arguments buffer 1806 * Return: status, errnos and results from requested operation. 1807 * 1808 * NOTES: When getgroupsbymember is being processed in the NSCD backend, 1809 * or via the backwards compatibility interfaces then the standard 1810 * str2group API is used in conjunction with process_cstr. When, 1811 * processing a returned buffer, in NSS2 the return results are the 1812 * already digested groups array. Therefore, unpack the digested results 1813 * back to the return buffer. 1814 * 1815 * Note: the digested results are nssuint_t quantities. _getgroupsbymember 1816 * digests int quantities. Therefore convert. Assume input is in nssuint_t 1817 * quantities. Store in an int array... Assume gid's are <= 32 bits... 1818 */ 1819 1820 /*ARGSUSED*/ 1821 nss_status_t 1822 nss_unpack(void *buffer, size_t bufsize, nss_db_root_t *rootp, 1823 nss_db_initf_t initf, int search_fnum, void *search_args) 1824 { 1825 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 1826 nss_XbyY_args_t *in = (nss_XbyY_args_t *)search_args; 1827 nss_dbd_t *pdbd; 1828 char *dbn; 1829 nss_status_t status; 1830 char *buf; 1831 int len; 1832 int ret; 1833 int i; 1834 int fmt_type; 1835 gid_t *gidp; 1836 gid_t *gptr; 1837 struct nss_groupsbymem *arg; 1838 1839 1840 if (pbuf == NULL || in == NULL) 1841 return (-1); 1842 status = pbuf->p_status; 1843 /* Identify odd cases */ 1844 pdbd = (nss_dbd_t *)((void *)((char *)buffer + pbuf->dbd_off)); 1845 dbn = (char *)pdbd + pdbd->o_name; 1846 fmt_type = 0; /* nss_XbyY_args_t */ 1847 if (search_fnum == NSS_DBOP_GROUP_BYMEMBER && 1848 strcmp(dbn, NSS_DBNAM_GROUP) == 0) 1849 fmt_type = 1; /* struct nss_groupsbymem */ 1850 else if (search_fnum == NSS_DBOP_NETGROUP_IN && 1851 strcmp(dbn, NSS_DBNAM_NETGROUP) == 0) 1852 fmt_type = 2; /* struct nss_innetgr_args */ 1853 1854 /* if error - door's switch error */ 1855 /* extended data could contain additional information? */ 1856 if (status != NSS_SUCCESS) { 1857 if (fmt_type == 0) { 1858 in->h_errno = (int)pbuf->p_herrno; 1859 if (pbuf->p_errno == ERANGE) 1860 in->erange = 1; 1861 } 1862 return (status); 1863 } 1864 1865 if (pbuf->data_off == 0 || pbuf->data_len == 0) 1866 return (NSS_NOTFOUND); 1867 1868 buf = (char *)buffer + pbuf->data_off; 1869 len = pbuf->data_len; 1870 1871 /* sidestep odd cases */ 1872 if (fmt_type == 1) { 1873 arg = (struct nss_groupsbymem *)in; 1874 /* copy returned gid array from returned nscd buffer */ 1875 i = len / sizeof (gid_t); 1876 /* not enough buffer */ 1877 if (i > arg->maxgids) { 1878 i = arg->maxgids; 1879 } 1880 arg->numgids = i; 1881 gidp = arg->gid_array; 1882 gptr = (gid_t *)((void *)buf); 1883 memcpy(gidp, gptr, len); 1884 return (NSS_SUCCESS); 1885 } 1886 if (fmt_type == 2) { 1887 struct nss_innetgr_args *arg = (struct nss_innetgr_args *)in; 1888 1889 if (pbuf->p_status == NSS_SUCCESS) { 1890 arg->status = NSS_NETGR_FOUND; 1891 return (NSS_SUCCESS); 1892 } else { 1893 arg->status = NSS_NETGR_NO; 1894 return (NSS_NOTFOUND); 1895 } 1896 } 1897 1898 /* process the normal cases */ 1899 /* marshall data directly into users buffer */ 1900 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer, 1901 in->buf.buflen); 1902 if (ret == NSS_STR_PARSE_ERANGE) { 1903 in->returnval = 0; 1904 in->returnlen = 0; 1905 in->erange = 1; 1906 ret = NSS_NOTFOUND; 1907 } else if (ret == NSS_STR_PARSE_SUCCESS) { 1908 in->returnval = in->buf.result; 1909 in->returnlen = len; 1910 ret = NSS_SUCCESS; 1911 } 1912 in->h_errno = (int)pbuf->p_herrno; 1913 return ((nss_status_t)ret); 1914 } 1915 1916 /* 1917 * Unpack a returned packed {set,get,end}ent arguments buffer 1918 * Return: status, errnos, cookie info and results from requested operation. 1919 */ 1920 1921 /*ARGSUSED*/ 1922 nss_status_t 1923 nss_unpack_ent(void *buffer, size_t bufsize, nss_db_root_t *rootp, 1924 nss_db_initf_t initf, nss_getent_t *contextpp, void *args) 1925 { 1926 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 1927 nss_XbyY_args_t *in = (nss_XbyY_args_t *)args; 1928 struct nss_getent_context *contextp = contextpp->ctx; 1929 nssuint_t *nptr; 1930 nssuint_t cookie; 1931 nss_status_t status; 1932 char *buf; 1933 int len; 1934 int ret; 1935 1936 if (pbuf == NULL) 1937 return (-1); 1938 status = pbuf->p_status; 1939 /* if error - door's switch error */ 1940 /* extended data could contain additional information? */ 1941 if (status != NSS_SUCCESS) 1942 return (status); 1943 1944 /* unpack assigned cookie from SET/GET/END request */ 1945 if (pbuf->key_off == 0 || 1946 pbuf->key_len != (sizeof (nssuint_t) * 2)) 1947 return (NSS_NOTFOUND); 1948 1949 nptr = (nssuint_t *)((void *)((char *)buffer + pbuf->key_off)); 1950 cookie = contextp->cookie; 1951 if (cookie != NSCD_NEW_COOKIE && cookie != contextp->cookie_setent && 1952 cookie != *nptr) { 1953 /* 1954 * Should either be new, or the cookie returned by the last 1955 * setent (i.e., this is the first getent after the setent) 1956 * or a match, else error 1957 */ 1958 return (NSS_NOTFOUND); 1959 } 1960 /* save away for the next ent request */ 1961 contextp->cookie = *nptr++; 1962 contextp->seq_num = *nptr; 1963 1964 /* All done if no marshalling is expected {set,end}ent */ 1965 if (args == NULL) 1966 return (NSS_SUCCESS); 1967 1968 /* unmarshall the data */ 1969 if (pbuf->data_off == 0 || pbuf->data_len == 0) 1970 return (NSS_NOTFOUND); 1971 buf = (char *)buffer + pbuf->data_off; 1972 1973 len = pbuf->data_len; 1974 1975 /* marshall data directly into users buffer */ 1976 ret = (*in->str2ent)(buf, len, in->buf.result, in->buf.buffer, 1977 in->buf.buflen); 1978 if (ret == NSS_STR_PARSE_ERANGE) { 1979 in->returnval = 0; 1980 in->returnlen = 0; 1981 in->erange = 1; 1982 } else if (ret == NSS_STR_PARSE_SUCCESS) { 1983 in->returnval = in->buf.result; 1984 in->returnlen = len; 1985 } 1986 in->h_errno = (int)pbuf->p_herrno; 1987 return ((nss_status_t)ret); 1988 } 1989 1990 /* 1991 * Start of _nsc_{search|setent_u|getent_u|endent_u} NSCD interposition funcs 1992 */ 1993 1994 nss_status_t 1995 _nsc_search(nss_db_root_t *rootp, nss_db_initf_t initf, int search_fnum, 1996 void *search_args) 1997 { 1998 nss_pheader_t *pbuf; 1999 void *doorptr = NULL; 2000 size_t bufsize = 0; 2001 size_t datasize = 0; 2002 nss_status_t status; 2003 2004 if (_nsc_proc_is_cache() > 0) { 2005 /* internal nscd call - don't use the door */ 2006 return (NSS_TRYLOCAL); 2007 } 2008 2009 /* standard client calls nscd code */ 2010 if (search_args == NULL) 2011 return (NSS_NOTFOUND); 2012 2013 /* get the door buffer & configured size */ 2014 bufsize = ((nss_XbyY_args_t *)search_args)->buf.buflen; 2015 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) 2016 return (NSS_TRYLOCAL); 2017 if (doorptr == NULL || bufsize == 0) 2018 return (NSS_TRYLOCAL); 2019 2020 pbuf = (nss_pheader_t *)doorptr; 2021 /* pack argument and request into door buffer */ 2022 pbuf->nsc_callnumber = NSCD_SEARCH; 2023 /* copy relevant door request info into door buffer */ 2024 status = nss_pack((void *)pbuf, bufsize, rootp, 2025 initf, search_fnum, search_args); 2026 2027 /* Packing error return error results */ 2028 if (status != NSS_SUCCESS) 2029 return (status); 2030 2031 /* transfer packed switch request to nscd via door */ 2032 /* data_off can be used because it is header+dbd_len+key_len */ 2033 datasize = pbuf->data_off; 2034 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize); 2035 2036 /* If unsuccessful fallback to standard nss logic */ 2037 if (status != NSS_SUCCESS) { 2038 /* 2039 * check if doors reallocated the memory underneath us 2040 * if they did munmap it or suffer a memory leak 2041 */ 2042 if (doorptr != (void *)pbuf) { 2043 _nsc_resizedoorbuf(bufsize); 2044 munmap((void *)doorptr, bufsize); 2045 } 2046 return (NSS_TRYLOCAL); 2047 } 2048 2049 /* unpack and marshall data/errors to user structure */ 2050 /* set any error conditions */ 2051 status = nss_unpack((void *)doorptr, bufsize, rootp, initf, 2052 search_fnum, search_args); 2053 /* 2054 * check if doors reallocated the memory underneath us 2055 * if they did munmap it or suffer a memory leak 2056 */ 2057 if (doorptr != (void *)pbuf) { 2058 _nsc_resizedoorbuf(bufsize); 2059 munmap((void *)doorptr, bufsize); 2060 } 2061 return (status); 2062 } 2063 2064 /* 2065 * contact nscd for a cookie or to reset an existing cookie 2066 * if nscd fails (NSS_TRYLOCAL) then set cookie to -1 and 2067 * continue diverting to local 2068 */ 2069 nss_status_t 2070 _nsc_setent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 2071 nss_getent_t *contextpp) 2072 { 2073 nss_status_t status = NSS_TRYLOCAL; 2074 struct nss_getent_context *contextp = contextpp->ctx; 2075 nss_pheader_t *pbuf; 2076 void *doorptr = NULL; 2077 size_t bufsize = 0; 2078 size_t datasize = 0; 2079 2080 /* return if already in local mode */ 2081 if (contextp->cookie == NSCD_LOCAL_COOKIE) 2082 return (NSS_TRYLOCAL); 2083 2084 if (_nsc_proc_is_cache() > 0) { 2085 /* internal nscd call - don't try to use the door */ 2086 contextp->cookie = NSCD_LOCAL_COOKIE; 2087 return (NSS_TRYLOCAL); 2088 } 2089 2090 /* get the door buffer & configured size */ 2091 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) { 2092 contextp->cookie = NSCD_LOCAL_COOKIE; 2093 return (NSS_TRYLOCAL); 2094 } 2095 if (doorptr == NULL || bufsize == 0) { 2096 contextp->cookie = NSCD_LOCAL_COOKIE; 2097 return (NSS_TRYLOCAL); 2098 } 2099 2100 pbuf = (nss_pheader_t *)doorptr; 2101 pbuf->nsc_callnumber = NSCD_SETENT; 2102 2103 contextp->param.cleanup = NULL; 2104 (*initf)(&contextp->param); 2105 if (contextp->param.name == 0) { 2106 if (contextp->param.cleanup != 0) 2107 (contextp->param.cleanup)(&contextp->param); 2108 errno = ERANGE; /* actually EINVAL */ 2109 return (NSS_ERROR); 2110 } 2111 2112 /* pack relevant setent request info into door buffer */ 2113 status = nss_pack_ent((void *)pbuf, bufsize, rootp, 2114 initf, contextpp); 2115 if (status != NSS_SUCCESS) 2116 return (status); 2117 2118 /* transfer packed switch request to nscd via door */ 2119 /* data_off can be used because it is header+dbd_len+key_len */ 2120 datasize = pbuf->data_off; 2121 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize); 2122 2123 /* If fallback to standard nss logic (door failure) if possible */ 2124 if (status != NSS_SUCCESS) { 2125 if (contextp->cookie == NSCD_NEW_COOKIE) { 2126 contextp->cookie = NSCD_LOCAL_COOKIE; 2127 return (NSS_TRYLOCAL); 2128 } 2129 return (NSS_UNAVAIL); 2130 } 2131 /* unpack returned cookie stash it away */ 2132 status = nss_unpack_ent((void *)doorptr, bufsize, rootp, 2133 initf, contextpp, NULL); 2134 /* save the setent cookie for later use */ 2135 contextp->cookie_setent = contextp->cookie; 2136 /* 2137 * check if doors reallocated the memory underneath us 2138 * if they did munmap it or suffer a memory leak 2139 */ 2140 if (doorptr != (void *)pbuf) { 2141 _nsc_resizedoorbuf(bufsize); 2142 munmap((void *)doorptr, bufsize); 2143 } 2144 return (status); 2145 } 2146 2147 nss_status_t 2148 _nsc_getent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 2149 nss_getent_t *contextpp, void *args) 2150 { 2151 nss_status_t status = NSS_TRYLOCAL; 2152 struct nss_getent_context *contextp = contextpp->ctx; 2153 nss_pheader_t *pbuf; 2154 void *doorptr = NULL; 2155 size_t bufsize = 0; 2156 size_t datasize = 0; 2157 2158 /* return if already in local mode */ 2159 if (contextp->cookie == NSCD_LOCAL_COOKIE) 2160 return (NSS_TRYLOCAL); 2161 2162 /* _nsc_setent_u already checked for nscd local case ... proceed */ 2163 if (args == NULL) 2164 return (NSS_NOTFOUND); 2165 2166 /* get the door buffer & configured size */ 2167 bufsize = ((nss_XbyY_args_t *)args)->buf.buflen; 2168 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) 2169 return (NSS_UNAVAIL); 2170 if (doorptr == NULL || bufsize == 0) 2171 return (NSS_UNAVAIL); 2172 2173 pbuf = (nss_pheader_t *)doorptr; 2174 pbuf->nsc_callnumber = NSCD_GETENT; 2175 2176 /* pack relevant setent request info into door buffer */ 2177 status = nss_pack_ent((void *)pbuf, bufsize, rootp, 2178 initf, contextpp); 2179 if (status != NSS_SUCCESS) 2180 return (status); 2181 2182 /* transfer packed switch request to nscd via door */ 2183 /* data_off can be used because it is header+dbd_len+key_len */ 2184 datasize = pbuf->data_off; 2185 status = _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize); 2186 2187 /* If fallback to standard nss logic (door failure) if possible */ 2188 if (status != NSS_SUCCESS) { 2189 if (status == NSS_TRYLOCAL || 2190 contextp->cookie == NSCD_NEW_COOKIE) { 2191 contextp->cookie = NSCD_LOCAL_COOKIE; 2192 2193 /* init the local cookie */ 2194 nss_setent_u(rootp, initf, contextpp); 2195 if (contextpp->ctx == 0) 2196 return (NSS_UNAVAIL); 2197 return (NSS_TRYLOCAL); 2198 } 2199 return (NSS_UNAVAIL); 2200 } 2201 /* check error, unpack and process results */ 2202 status = nss_unpack_ent((void *)doorptr, bufsize, rootp, 2203 initf, contextpp, args); 2204 /* 2205 * check if doors reallocated the memory underneath us 2206 * if they did munmap it or suffer a memory leak 2207 */ 2208 if (doorptr != (void *)pbuf) { 2209 _nsc_resizedoorbuf(bufsize); 2210 munmap((void *)doorptr, bufsize); 2211 } 2212 return (status); 2213 } 2214 2215 nss_status_t 2216 _nsc_endent_u(nss_db_root_t *rootp, nss_db_initf_t initf, 2217 nss_getent_t *contextpp) 2218 { 2219 nss_status_t status = NSS_TRYLOCAL; 2220 struct nss_getent_context *contextp = contextpp->ctx; 2221 nss_pheader_t *pbuf; 2222 void *doorptr = NULL; 2223 size_t bufsize = 0; 2224 size_t datasize = 0; 2225 2226 /* return if already in local mode */ 2227 if (contextp->cookie == NSCD_LOCAL_COOKIE) 2228 return (NSS_TRYLOCAL); 2229 2230 /* _nsc_setent_u already checked for nscd local case ... proceed */ 2231 2232 /* get the door buffer & configured size */ 2233 if (_nsc_getdoorbuf(&doorptr, &bufsize) != 0) 2234 return (NSS_UNAVAIL); 2235 if (doorptr == NULL || bufsize == 0) 2236 return (NSS_UNAVAIL); 2237 2238 /* pack up a NSCD_ENDGET request passing in the cookie */ 2239 pbuf = (nss_pheader_t *)doorptr; 2240 pbuf->nsc_callnumber = NSCD_ENDENT; 2241 2242 /* pack relevant setent request info into door buffer */ 2243 status = nss_pack_ent((void *)pbuf, bufsize, rootp, 2244 initf, contextpp); 2245 if (status != NSS_SUCCESS) 2246 return (status); 2247 2248 /* transfer packed switch request to nscd via door */ 2249 /* data_off can be used because it is header+dbd_len+key_len */ 2250 datasize = pbuf->data_off; 2251 (void) _nsc_trydoorcall_ext(&doorptr, &bufsize, &datasize); 2252 2253 /* error codes & unpacking ret values don't matter. We're done */ 2254 2255 /* 2256 * check if doors reallocated the memory underneath us 2257 * if they did munmap it or suffer a memory leak 2258 */ 2259 if (doorptr != (void *)pbuf) { 2260 _nsc_resizedoorbuf(bufsize); 2261 munmap((void *)doorptr, bufsize); 2262 } 2263 2264 /* clean up initf setup */ 2265 if (contextp->param.cleanup != 0) 2266 (contextp->param.cleanup)(&contextp->param); 2267 contextp->param.cleanup = NULL; 2268 2269 /* clear cookie */ 2270 contextp->cookie = NSCD_NEW_COOKIE; 2271 return (NSS_SUCCESS); 2272 } 2273