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