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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 23 /* 24 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 25 * Use is subject to license terms. 26 */ 27 28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 29 /* All Rights Reserved */ 30 31 /* 32 * Portions of this source code were derived from Berkeley 33 * under license from the Regents of the University of 34 * California. 35 */ 36 37 #pragma ident "%Z%%M% %I% %E% SMI" 38 39 #include <stdlib.h> 40 #include <unistd.h> 41 #include "mt.h" 42 #include "../rpc/rpc_mt.h" 43 #include <rpc/rpc.h> 44 #include <sys/types.h> 45 #include "yp_b.h" 46 #include <rpcsvc/yp_prot.h> 47 #include <rpcsvc/ypclnt.h> 48 #include <malloc.h> 49 #include <string.h> 50 #include <sys/time.h> 51 52 extern int __yp_dobind_cflookup(char *, struct dom_binding **, int); 53 extern int __yp_dobind_rsvdport_cflookup(char *, struct dom_binding **, int); 54 55 static int domatch(char *, char *, char *, int, struct dom_binding *, 56 struct timeval *, char **, int *); 57 int yp_match_rsvdport(); 58 int yp_match_rsvdport_cflookup(); 59 60 struct cache { 61 struct cache *next; 62 unsigned int birth; 63 char *domain; 64 char *map; 65 char *key; 66 int keylen; 67 char *val; 68 int vallen; 69 }; 70 71 static mutex_t cache_lock = DEFAULTMUTEX; 72 static int generation; /* Incremented when we add to cache */ 73 static struct cache *head; 74 75 #define CACHESZ 16 76 #define CACHETO 600 77 78 static void 79 freenode(struct cache *n) 80 { 81 if (n->val != 0) 82 free(n->val); 83 if (n->key != 0) 84 free(n->key); 85 if (n->map != 0) 86 free(n->map); 87 if (n->domain != 0) 88 free(n->domain); 89 free(n); 90 } 91 92 /* 93 * Attempt to Add item to cache 94 */ 95 static struct cache * 96 makenode(char *domain, char *map, int keylen, int vallen) 97 { 98 struct cache *n; 99 100 /* Do not cache 'passwd' values i.e. passwd.byname or passwd.byuid. */ 101 if (strncmp(map, "passwd", 6) == 0) 102 return (0); 103 104 if ((n = calloc(1, sizeof (*n))) == 0) 105 return (0); 106 if (((n->domain = strdup(domain)) == 0) || 107 ((n->map = strdup(map)) == 0) || 108 ((n->key = malloc(keylen)) == 0) || 109 ((n->val = malloc(vallen)) == 0)) { 110 freenode(n); 111 return (0); 112 } 113 return (n); 114 } 115 116 /* 117 * Look for a matching result in the per-process cache. 118 * Upon finding a match set the passed in 'val' and 'vallen' 119 * parameters and return 1. Otherwise return 0. 120 */ 121 static int 122 in_cache(char *domain, char *map, char *key, int keylen, char **val, 123 int *vallen) 124 { 125 struct cache *c, **pp; 126 int cnt; 127 struct timeval now; 128 struct timezone tz; 129 130 /* The 'passwd' data is not cached. */ 131 if (strncmp(map, "passwd", 6) == 0) 132 return (0); 133 134 /* 135 * Assumes that caller (yp_match) has locked the cache 136 */ 137 for (pp = &head, cnt = 0; (c = *pp) != 0; pp = &c->next, cnt++) { 138 if ((c->keylen == keylen) && 139 (memcmp(key, c->key, (size_t)keylen) == 0) && 140 (strcmp(map, c->map) == 0) && 141 (strcmp(domain, c->domain) == 0)) { 142 /* cache hit */ 143 (void) gettimeofday(&now, &tz); 144 if ((now.tv_sec - c->birth) > CACHETO) { 145 /* rats. it is too old to use */ 146 *pp = c->next; 147 freenode(c); 148 break; 149 } else { 150 *val = c->val; 151 *vallen = c->vallen; 152 153 /* Ersatz LRU: Move this entry to the front */ 154 *pp = c->next; 155 c->next = head; 156 head = c; 157 return (1); 158 } 159 } 160 if (cnt >= CACHESZ) { 161 *pp = c->next; 162 freenode(c); 163 break; 164 } 165 } 166 return (0); 167 } 168 169 /* 170 * Requests the yp server associated with a given domain to attempt to match 171 * the passed key datum in the named map, and to return the associated value 172 * datum. This part does parameter checking, and implements the "infinite" 173 * (until success) sleep loop if 'hardlookup' parameter is set. 174 */ 175 int 176 __yp_match_cflookup(char *domain, char *map, char *key, int keylen, char **val, 177 int *vallen, int hardlookup) 178 { 179 size_t domlen; 180 size_t maplen; 181 int reason; 182 struct dom_binding *pdomb; 183 int savesize; 184 struct timeval now; 185 struct timezone tz; 186 char *my_val; 187 int my_vallen; 188 int found_it; 189 int cachegen; 190 191 if ((map == NULL) || (domain == NULL)) 192 return (YPERR_BADARGS); 193 194 domlen = strlen(domain); 195 maplen = strlen(map); 196 197 if ((domlen == 0) || (domlen > YPMAXDOMAIN) || 198 (maplen == 0) || (maplen > YPMAXMAP) || 199 (key == NULL) || (keylen == 0)) 200 return (YPERR_BADARGS); 201 202 (void) mutex_lock(&cache_lock); 203 found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen); 204 cachegen = generation; 205 206 if (found_it) { 207 /* NB: Copy two extra bytes; see below */ 208 savesize = my_vallen + 2; 209 if ((*val = malloc((size_t)savesize)) == 0) { 210 (void) mutex_unlock(&cache_lock); 211 return (YPERR_RESRC); 212 } 213 (void) memcpy(*val, my_val, (size_t)savesize); 214 *vallen = my_vallen; 215 (void) mutex_unlock(&cache_lock); 216 return (0); /* Success */ 217 } 218 (void) mutex_unlock(&cache_lock); 219 220 for (;;) { 221 222 if (reason = __yp_dobind_cflookup(domain, &pdomb, hardlookup)) 223 return (reason); 224 225 if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) { 226 227 reason = domatch(domain, map, key, keylen, pdomb, 228 &_ypserv_timeout, val, vallen); 229 230 __yp_rel_binding(pdomb); 231 if (reason == YPERR_RPC || reason == YPERR_YPSERV || 232 reason == YPERR_BUSY /* as if */) { 233 yp_unbind(domain); 234 if (hardlookup) 235 (void) _sleep(_ypsleeptime); /* retry */ 236 else 237 return (reason); 238 } else 239 break; 240 } else { 241 __yp_rel_binding(pdomb); 242 return (YPERR_VERS); 243 } 244 } 245 246 /* add to our cache */ 247 if (reason == 0) { 248 (void) mutex_lock(&cache_lock); 249 /* 250 * Check whether some other annoying thread did the same 251 * thing in parallel with us. I hate it when that happens... 252 */ 253 if (generation != cachegen && 254 in_cache(domain, map, key, keylen, &my_val, &my_vallen)) { 255 /* 256 * Could get cute and update the birth time, but it's 257 * not worth the bother. 258 * It looks strange that we return one val[] array 259 * to the caller and have a different copy of the 260 * val[] array in the cache (presumably with the 261 * same contents), but it should work just fine. 262 * So, do absolutely nothing... 263 */ 264 /* EMPTY */ 265 } else { 266 struct cache *c; 267 /* 268 * NB: allocate and copy extract two bytes of the 269 * value; these are mandatory CR and NULL bytes. 270 */ 271 savesize = *vallen + 2; 272 c = makenode(domain, map, keylen, savesize); 273 if (c != 0) { 274 (void) gettimeofday(&now, &tz); 275 c->birth = now.tv_sec; 276 c->keylen = keylen; 277 c->vallen = *vallen; 278 (void) memcpy(c->key, key, (size_t)keylen); 279 (void) memcpy(c->val, *val, (size_t)savesize); 280 281 c->next = head; 282 head = c; 283 ++generation; 284 } 285 } 286 (void) mutex_unlock(&cache_lock); 287 } else if (reason == YPERR_MAP && geteuid() == 0) { 288 /* 289 * Lookup could be for a secure map; fail over to retry 290 * from a reserved port. Only useful to try this if we're 291 * the super user. 292 */ 293 int rsvdreason; 294 rsvdreason = yp_match_rsvdport(domain, map, key, keylen, val, 295 vallen); 296 if (rsvdreason == 0) 297 reason = rsvdreason; 298 } 299 return (reason); 300 } 301 302 int 303 yp_match( 304 char *domain, 305 char *map, 306 char *key, 307 int keylen, 308 char **val, /* returns value array */ 309 int *vallen) /* returns bytes in val */ 310 311 { 312 /* the traditional yp_match loops forever thus hardlookup is set */ 313 return (__yp_match_cflookup(domain, map, key, keylen, val, vallen, 1)); 314 } 315 316 extern void 317 __empty_yp_cache(void) 318 { 319 struct cache *p, *n; 320 321 /* Copy the cache pointer and make it ZERO */ 322 (void) mutex_lock(&cache_lock); 323 p = head; 324 head = 0; 325 (void) mutex_unlock(&cache_lock); 326 327 if (p == 0) 328 return; 329 330 /* Empty the cache */ 331 n = p->next; 332 while (p) { 333 freenode(p); 334 p = n; 335 if (p) 336 n = p->next; 337 } 338 } 339 340 /* 341 * Requests the yp server associated with a given domain to attempt to match 342 * the passed key datum in the named map, and to return the associated value 343 * datum. This part does parameter checking, and implements the "infinite" 344 * (until success) sleep loop. 345 * 346 * XXX special version for handling C2 (passwd.adjunct) lookups when we need 347 * a reserved port. 348 * Only difference against yp_match is that this function uses 349 * __yp_dobind_rsvdport(). 350 * 351 * Only called from NIS switch backend. 352 */ 353 int 354 __yp_match_rsvdport_cflookup( 355 char *domain, 356 char *map, 357 char *key, 358 int keylen, 359 char **val, /* returns value array */ 360 int *vallen, /* returns bytes in val */ 361 int hardlookup) /* retry until we can an answer */ 362 { 363 size_t domlen; 364 size_t maplen; 365 int reason; 366 struct dom_binding *pdomb; 367 int savesize; 368 struct timeval now; 369 struct timezone tz; 370 char *my_val; 371 int my_vallen; 372 int found_it; 373 int cachegen; 374 375 if ((map == NULL) || (domain == NULL)) 376 return (YPERR_BADARGS); 377 378 domlen = strlen(domain); 379 maplen = strlen(map); 380 381 if ((domlen == 0) || (domlen > YPMAXDOMAIN) || 382 (maplen == 0) || (maplen > YPMAXMAP) || 383 (key == NULL) || (keylen == 0)) 384 return (YPERR_BADARGS); 385 386 (void) mutex_lock(&cache_lock); 387 found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen); 388 cachegen = generation; 389 if (found_it) { 390 /* NB: Copy two extra bytes; see below */ 391 savesize = my_vallen + 2; 392 if ((*val = malloc((size_t)savesize)) == 0) { 393 (void) mutex_unlock(&cache_lock); 394 return (YPERR_RESRC); 395 } 396 (void) memcpy(*val, my_val, (size_t)savesize); 397 *vallen = my_vallen; 398 (void) mutex_unlock(&cache_lock); 399 return (0); /* Success */ 400 } 401 (void) mutex_unlock(&cache_lock); 402 403 for (;;) { 404 405 if (reason = __yp_dobind_rsvdport_cflookup(domain, &pdomb, 406 hardlookup)) 407 return (reason); 408 409 if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) { 410 411 reason = domatch(domain, map, key, keylen, 412 pdomb, &_ypserv_timeout, val, vallen); 413 414 /* 415 * Have to free the binding since the reserved 416 * port bindings are not cached. 417 */ 418 __yp_rel_binding(pdomb); 419 free_dom_binding(pdomb); 420 421 if (reason == YPERR_RPC || reason == YPERR_YPSERV || 422 reason == YPERR_BUSY /* as if */) { 423 yp_unbind(domain); 424 if (hardlookup) 425 (void) _sleep(_ypsleeptime); /* retry */ 426 else 427 return (reason); 428 } else 429 break; 430 } else { 431 /* 432 * Have to free the binding since the reserved 433 * port bindings are not cached. 434 */ 435 __yp_rel_binding(pdomb); 436 free_dom_binding(pdomb); 437 return (YPERR_VERS); 438 } 439 } 440 441 /* add to our cache */ 442 if (reason == 0) { 443 (void) mutex_lock(&cache_lock); 444 /* 445 * Check whether some other annoying thread did the same 446 * thing in parallel with us. I hate it when that happens... 447 */ 448 if (generation != cachegen && 449 in_cache(domain, map, key, keylen, &my_val, &my_vallen)) { 450 /* 451 * Could get cute and update the birth time, but it's 452 * not worth the bother. 453 * It looks strange that we return one val[] array 454 * to the caller and have a different copy of the 455 * val[] array in the cache (presumably with the 456 * same contents), but it should work just fine. 457 * So, do absolutely nothing... 458 */ 459 /* EMPTY */ 460 } else { 461 struct cache *c; 462 /* 463 * NB: allocate and copy extract two bytes of the 464 * value; these are mandatory CR and NULL bytes. 465 */ 466 savesize = *vallen + 2; 467 c = makenode(domain, map, keylen, savesize); 468 if (c != 0) { 469 (void) gettimeofday(&now, &tz); 470 c->birth = now.tv_sec; 471 c->keylen = keylen; 472 c->vallen = *vallen; 473 (void) memcpy(c->key, key, (size_t)keylen); 474 (void) memcpy(c->val, *val, (size_t)savesize); 475 476 c->next = head; 477 head = c; 478 ++generation; 479 } 480 } 481 (void) mutex_unlock(&cache_lock); 482 } 483 return (reason); 484 } 485 486 487 int 488 yp_match_rsvdport( 489 char *domain, 490 char *map, 491 char *key, 492 int keylen, 493 char **val, /* returns value array */ 494 int *vallen) /* returns bytes in val */ 495 { 496 /* traditional yp_match retries forever so set hardlookup */ 497 return (__yp_match_rsvdport_cflookup(domain, map, key, keylen, val, 498 vallen, 1)); 499 } 500 501 502 /* 503 * This talks v3 protocol to ypserv 504 */ 505 static int 506 domatch(char *domain, char *map, char *key, int keylen, 507 struct dom_binding *pdomb, struct timeval *timeoutp, char **val, 508 int *vallen) 509 { 510 struct ypreq_key req; 511 struct ypresp_val resp; 512 unsigned int retval = 0; 513 514 req.domain = domain; 515 req.map = map; 516 req.keydat.dptr = key; 517 req.keydat.dsize = keylen; 518 519 resp.valdat.dptr = NULL; 520 resp.valdat.dsize = 0; 521 (void) memset((char *)&resp, 0, sizeof (struct ypresp_val)); 522 523 /* 524 * Do the match request. If the rpc call failed, return with status 525 * from this point. 526 */ 527 528 switch (clnt_call(pdomb->dom_client, YPPROC_MATCH, 529 (xdrproc_t)xdr_ypreq_key, (char *)&req, 530 (xdrproc_t)xdr_ypresp_val, (char *)&resp, 531 *timeoutp)) { 532 case RPC_SUCCESS: 533 break; 534 case RPC_TIMEDOUT: 535 return (YPERR_YPSERV); 536 default: 537 return (YPERR_RPC); 538 } 539 540 /* See if the request succeeded */ 541 542 if (resp.status != YP_TRUE) { 543 retval = ypprot_err(resp.status); 544 } 545 546 /* Get some memory which the user can get rid of as he likes */ 547 548 if (!retval && ((*val = malloc((size_t) 549 resp.valdat.dsize + 2)) == NULL)) { 550 retval = YPERR_RESRC; 551 } 552 553 /* Copy the returned value byte string into the new memory */ 554 555 if (!retval) { 556 *vallen = (int)resp.valdat.dsize; 557 (void) memcpy(*val, resp.valdat.dptr, 558 (size_t)resp.valdat.dsize); 559 (*val)[resp.valdat.dsize] = '\n'; 560 (*val)[resp.valdat.dsize + 1] = '\0'; 561 } 562 563 CLNT_FREERES(pdomb->dom_client, 564 (xdrproc_t)xdr_ypresp_val, (char *)&resp); 565 return (retval); 566 } 567