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
freenode(struct cache * n)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 *
makenode(char * domain,char * map,int keylen,int vallen)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
in_cache(char * domain,char * map,char * key,int keylen,char ** val,int * vallen)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
__yp_match_cflookup(char * domain,char * map,char * key,int keylen,char ** val,int * vallen,int hardlookup)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
yp_match(char * domain,char * map,char * key,int keylen,char ** val,int * vallen)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
__empty_yp_cache(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
__yp_match_rsvdport_cflookup(char * domain,char * map,char * key,int keylen,char ** val,int * vallen,int hardlookup)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
yp_match_rsvdport(char * domain,char * map,char * key,int keylen,char ** val,int * vallen)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
domatch(char * domain,char * map,char * key,int keylen,struct dom_binding * pdomb,struct timeval * timeoutp,char ** val,int * vallen)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