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