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