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