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