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