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