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