Home | History | Annotate | Download | only in yp
      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  */
     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 "mt.h"
     40 #include <stdlib.h>
     41 #include <unistd.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