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