xref: /illumos-gate/usr/src/lib/libnsl/yp/yp_match.c (revision e8031f0a)
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