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