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