xref: /illumos-gate/usr/src/lib/libnsl/yp/yp_bind.c (revision 17074b3a)
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 (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
27 /*	  All Rights Reserved   */
28 
29 /*
30  * Portions of this source code were derived from Berkeley
31  * under license from the Regents of the University of
32  * California.
33  */
34 #include "mt.h"
35 #include "../rpc/rpc_mt.h"
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <errno.h>
42 #include <unistd.h>
43 #include <rpc/rpc.h>
44 #include <netconfig.h>
45 #include <netdir.h>
46 #include <syslog.h>
47 #include "yp_b.h"
48 #include <rpcsvc/yp_prot.h>
49 #include <rpcsvc/ypclnt.h>
50 #include <sys/tiuser.h>
51 
52 #define	BFSIZE	(YPMAXDOMAIN + 32)	/* size of binding file */
53 int	 __ypipbufsize = 8192;		/* size used for clnt_tli_create */
54 
55 /* This should match the one in ypbind.c */
56 
57 extern int getdomainname(char *, int);
58 
59 static CLIENT *getclnt(rpcprog_t, rpcvers_t, struct netconfig *, int *);
60 static struct dom_binding *load_dom_binding(struct ypbind_resp *, char *,
61     int *);
62 static ypbind_resp *get_cached_domain(char *);
63 static int get_cached_transport(struct netconfig *, int, char *, int);
64 static int ypbind_running(int, int);
65 static void set_rdev(struct dom_binding *);
66 static int check_rdev(struct dom_binding *);
67 
68 static char nullstring[] = "";
69 /*
70  * Time parameters when talking to the ypbind and pmap processes
71  */
72 
73 #define	YPSLEEPTIME	5		/* Time to sleep between tries */
74 unsigned int _ypsleeptime = YPSLEEPTIME;
75 
76 /*
77  * Time parameters when talking to the ypserv process
78  */
79 
80 #ifdef  DEBUG
81 #define	YPTIMEOUT	120		/* Total seconds for timeout */
82 #define	YPINTER_TRY	60		/* Seconds between tries */
83 #else
84 #define	YPTIMEOUT	20		/* Total seconds for timeout */
85 #define	YPINTER_TRY	5		/* Seconds between tries */
86 #endif
87 
88 #define	MAX_TRIES_FOR_NEW_YP	1	/* Number of times we'll try to */
89 					/* get a new YP server before   */
90 					/* we'll settle for an old one. */
91 struct timeval _ypserv_timeout = {
92 	YPTIMEOUT,			/* Seconds */
93 	0				/* Microseconds */
94 	};
95 
96 static mutex_t			default_domain_lock = DEFAULTMUTEX;
97 static char			*default_domain;
98 
99 /*
100  * The bound_domains_lock serializes all action in yp_unbind(), __yp_dobind(),
101  *   newborn(), check_binding() and laod_dom_binding(), not just the direct
102  *   manipulation of the bound_domains list.
103  * It also protects all of the fields within a domain binding except
104  *   the server_name field (which is protected by the server_name_lock).
105  * A better implementation might try to serialize each domain separately,
106  *   but normally we're only dealing with one domain (the default) anyway.
107  * To avoid one thread freeing a domain binding while another is using
108  *   the binding, we maintain a reference count for each binding.  The
109  *   reference count is incremented in __yp_dobind.  The thread calls
110  *   __yp_rel_binding() when it has finished using the binding (which
111  *   decrements the reference count).  If the reference count is non-zero
112  *   when a thread tries to free a binding, the need_free flag is set and
113  *   the free is delayed.  The __yp_rel_binding() routine checks the flag
114  *   and calls the free routine if the flag is set and the reference
115  *   count is zero.
116  */
117 static mutex_t			bound_domains_lock = DEFAULTMUTEX;
118 static struct dom_binding	*bound_domains; /* List of bound domains */
119 
120 
121 /*
122  *  Must be called with bound_domains_lock held or with a dom_binding
123  *  that cannot be referenced by another thread.
124  */
125 void
free_dom_binding(struct dom_binding * p)126 free_dom_binding(struct dom_binding *p)
127 {
128 	if (p->ref_count != 0) {
129 		p->need_free = 1;
130 		return;
131 	}
132 	(void) check_rdev(p);
133 	clnt_destroy(p->dom_client);
134 	free(p->dom_domain);
135 	free(p);
136 }
137 
138 /*
139  * Attempts to find a dom_binding in the list at bound_domains having the
140  * domain name field equal to the passed domain name, and removes it if found.
141  * The domain-server binding will not exist after the call to this function.
142  * All resources associated with the binding will be freed.
143  *
144  * yp_unbind is MT-safe because it serializes on bound_domains_lock.
145  */
146 
147 static void
__yp_unbind_nolock(char * domain)148 __yp_unbind_nolock(char *domain)
149 {
150 	struct dom_binding *p;
151 	struct dom_binding **prev;
152 
153 	if ((domain == NULL) || (strlen(domain) == 0)) {
154 		return;
155 	}
156 
157 	/*
158 	 *  If we used a cache file to bind, then we will mark the
159 	 *  cache bad.  This will cause a subsequent call to __yp_dobind
160 	 *  to ignore the cache and talk to ypbind.  Otherwise, we
161 	 *  have already gotten a binding by talking to ypbind and
162 	 *  the binding is not good.
163 	 *
164 	 *  An optimization could be to check to see if the cache
165 	 *  file has changed (ypbind is pointing at a new server) and
166 	 *  reload the binding from it.  But that is too much work
167 	 *  for now.
168 	 */
169 	for (prev = &bound_domains;  (p = *prev) != 0;  prev = &p->dom_pnext) {
170 
171 		if (strcmp(domain, p->dom_domain) == 0) {
172 			if (!p->cache_bad) {
173 				p->cache_bad = 1;
174 				break;
175 			}
176 			*prev = p->dom_pnext;
177 			free_dom_binding(p);
178 			break;
179 		}
180 
181 	}
182 }
183 
184 
185 void
yp_unbind(char * domain)186 yp_unbind(char *domain)
187 {
188 	(void) mutex_lock(&bound_domains_lock);
189 	__yp_unbind_nolock(domain);
190 	(void) mutex_unlock(&bound_domains_lock);
191 }
192 
193 
194 /*
195  * This checks to see if this is a new process incarnation which has
196  * inherited bindings from a parent, and unbinds the world if so.
197  *
198  * MT-safe because it is only invoked from __yp_dobind(), which serializes
199  * all requests.
200  */
201 static void
newborn(void)202 newborn(void)
203 {
204 	static pid_t mypid;	/* Cached to detect forks */
205 	pid_t testpid;
206 	struct dom_binding *p, *q;
207 
208 	if ((testpid = getpid()) != mypid) {
209 
210 		mypid = testpid;
211 
212 		for (p = bound_domains;  p != 0;  p = q) {
213 			q = p->dom_pnext;
214 			free_dom_binding(p);
215 		}
216 		bound_domains = 0;
217 	}
218 }
219 
220 /*
221  * This checks that the socket for a domain which has already been bound
222  * hasn't been closed or changed under us.  If it has, unbind the domain
223  * without closing the socket, which may be in use by some higher level
224  * code.  This returns TRUE and points the binding parameter at the found
225  * dom_binding if the binding is found and the socket looks OK, and FALSE
226  * otherwise.
227  *
228  * MT-safe because it is only invoked from __yp_dobind(), which serializes
229  * all requests.
230  */
231 static bool
check_binding(char * domain,struct dom_binding ** binding)232 check_binding(char *domain, struct dom_binding **binding)
233 {
234 	struct dom_binding *pdomb;
235 	struct ypbind_resp *ypbind_resp;
236 	int status;
237 
238 	for (pdomb = bound_domains; pdomb != NULL; pdomb = pdomb->dom_pnext) {
239 
240 		if (strcmp(domain, pdomb->dom_domain) == 0) {
241 		/*
242 		 * XXX How do we really make sure the udp connection hasn't
243 		 * changes under us ? If it happens and we can't detect it,
244 		 * the appliction is doomed !
245 		 * POLICY: Let nobody do a yp_bind or __yp_dobind explicitly
246 		 * and forget to to yp_unbind it. All apps should go
247 		 * through the standard yp_match/first etc. functions.
248 		 */
249 
250 			*binding = pdomb;
251 			return (TRUE);
252 		}
253 	}
254 
255 	/*
256 	 *  We check to see if we can do a quick bind to ypserv.
257 	 *  If we can, then we load the binding (i.e., add it to our
258 	 *  cache of bindings) and then return it.
259 	 */
260 	if ((ypbind_resp = get_cached_domain(domain)) != 0) {
261 		pdomb = load_dom_binding(ypbind_resp, domain, &status);
262 		if (pdomb == 0)
263 			return (FALSE);
264 		*binding = pdomb;
265 		return (TRUE);
266 	}
267 	return (FALSE);
268 }
269 
270 /*
271  *  This routine adds a binding for a particular server to our
272  *  list of bound domains.  We check to see if there is actually
273  *  a yp server at the given address.  If not, or if there is
274  *  any other error, we return 0.  We have to malloc the binding
275  *  structure because that is what a call to ypbind returns and
276  *  we are basically doing what a call to ypbind would do.
277  */
278 
279 #define	SOCKADDR_SIZE (sizeof (struct sockaddr_in6))
280 static int
__yp_add_binding_netid(char * domain,char * addr,char * netid)281 __yp_add_binding_netid(char *domain, char *addr, char *netid)
282 {
283 	struct netconfig *nconf = 0;
284 	struct netbuf *svcaddr = 0;
285 	struct ypbind_binding *binding = 0;
286 	int status;
287 	struct ypbind_resp resp;
288 	struct dom_binding *pdomb;
289 
290 	nconf = getnetconfigent(netid);
291 	if (nconf == 0)
292 		goto err;
293 
294 	svcaddr = malloc(sizeof (struct netbuf));
295 	if (svcaddr == 0)
296 		goto err;
297 
298 	svcaddr->maxlen = SOCKADDR_SIZE;
299 	svcaddr->buf = malloc(SOCKADDR_SIZE);
300 	if (svcaddr->buf == 0)
301 		goto err;
302 
303 	if (!rpcb_getaddr(YPPROG, YPVERS, nconf, svcaddr, addr))
304 		goto err;
305 
306 	binding = malloc(sizeof (struct ypbind_binding));
307 	if (binding == 0)
308 		goto err;
309 
310 	binding->ypbind_hi_vers = YPVERS;
311 	binding->ypbind_lo_vers = YPVERS;
312 	binding->ypbind_nconf = nconf;
313 	binding->ypbind_svcaddr = svcaddr;
314 	binding->ypbind_servername = (char *)strdup(addr);
315 	if (binding->ypbind_servername == 0)
316 		goto err;
317 
318 	resp.ypbind_status = YPBIND_SUCC_VAL;
319 	resp.ypbind_resp_u.ypbind_bindinfo = binding;
320 
321 	(void) mutex_lock(&bound_domains_lock);
322 	newborn();
323 	pdomb = load_dom_binding(&resp, domain, &status);
324 	(void) mutex_unlock(&bound_domains_lock);
325 
326 	return (pdomb != 0);
327 
328 err:
329 	if (nconf)
330 		freenetconfigent(nconf);
331 	if (svcaddr) {
332 		if (svcaddr->buf)
333 			free(svcaddr->buf);
334 		free(svcaddr);
335 	}
336 	if (binding) {
337 		if (binding->ypbind_servername)
338 			free(binding->ypbind_servername);
339 		free(binding);
340 	}
341 	return (0);
342 }
343 
344 
345 int
__yp_add_binding(char * domain,char * addr)346 __yp_add_binding(char *domain, char *addr) {
347 
348 	int ret = __yp_add_binding_netid(domain, addr, "udp6");
349 
350 	if (ret == 0)
351 		ret = __yp_add_binding_netid(domain, addr, "udp");
352 
353 	return (ret);
354 }
355 
356 
357 /*
358  * This allocates some memory for a domain binding, initialize it, and
359  * returns a pointer to it.  Based on the program version we ended up
360  * talking to ypbind with, fill out an opvector of appropriate protocol
361  * modules.
362  *
363  * MT-safe because it is only invoked from __yp_dobind(), which serializes
364  * all requests.
365  */
366 static struct dom_binding *
load_dom_binding(struct ypbind_resp * ypbind_res,char * domain,int * err)367 load_dom_binding(struct ypbind_resp *ypbind_res, char *domain, int *err)
368 {
369 	int fd;
370 	struct dom_binding *pdomb;
371 
372 	pdomb = NULL;
373 
374 	if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
375 		syslog(LOG_ERR, "load_dom_binding:  malloc failure.");
376 		*err = YPERR_RESRC;
377 		return (NULL);
378 	}
379 
380 	pdomb->dom_binding = ypbind_res->ypbind_resp_u.ypbind_bindinfo;
381 	/*
382 	 * Open up a path to the server, which will remain active globally.
383 	 */
384 	pdomb->dom_client = clnt_tli_create(RPC_ANYFD,
385 					    pdomb->dom_binding->ypbind_nconf,
386 					    pdomb->dom_binding->ypbind_svcaddr,
387 					    YPPROG, YPVERS, __ypipbufsize,
388 					    __ypipbufsize);
389 	if (pdomb->dom_client == NULL) {
390 		clnt_pcreateerror("yp_bind: clnt_tli_create");
391 		free(pdomb);
392 		*err = YPERR_RPC;
393 		return (NULL);
394 	}
395 #ifdef DEBUG
396 (void) printf("yp_bind: clnt_tli_create suceeded\n");
397 #endif
398 
399 	pdomb->dom_pnext = bound_domains;	/* Link this to the list as */
400 	pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
401 	if (pdomb->dom_domain == NULL) {
402 		clnt_destroy(pdomb->dom_client);
403 		free(pdomb);
404 		*err = YPERR_RESRC;
405 		return (NULL);
406 	}
407 	/*
408 	 *  We may not have loaded from a cache file, but we assume the
409 	 *  cache is good until we find out otherwise.
410 	 */
411 	pdomb->cache_bad = 0;
412 	set_rdev(pdomb);
413 	if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd))
414 		(void) fcntl(fd, F_SETFD, 1);  /* make it "close on exec" */
415 
416 	(void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */
417 	pdomb->ref_count = 0;
418 	pdomb->need_free = 0;
419 	(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
420 	bound_domains = pdomb;			/* ... the head entry */
421 	return (pdomb);
422 }
423 
424 /*
425  * XXX special code for handling C2 (passwd.adjunct) lookups when we need
426  * a reserved port.
427  */
428 static int
tli_open_rsvdport(struct netconfig * nconf)429 tli_open_rsvdport(struct netconfig *nconf)
430 {
431 	int fd;
432 
433 	if (nconf == NULL)
434 		return (-1);
435 
436 	fd = t_open(nconf->nc_device, O_RDWR, NULL);
437 	if (fd == -1)
438 		return (-1);
439 
440 	if (netdir_options(nconf, ND_SET_RESERVEDPORT, fd, NULL) == -1) {
441 		if (t_bind(fd, NULL, NULL) == -1) {
442 			(void) t_close(fd);
443 			return (-1);
444 		}
445 	}
446 	return (fd);
447 }
448 
449 /*
450  * This allocates some memory for a domain binding, initialize it, and
451  * returns a pointer to it.  Based on the program version we ended up
452  * talking to ypbind with, fill out an opvector of appropriate protocol
453  * modules.
454  *
455  * MT-safe because it is only invoked from __yp_dobind(), which serializes
456  * all requests.
457  *
458  * XXX special version for handling C2 (passwd.adjunct) lookups when we need
459  * a reserved port.
460  *
461  * Note that the binding is not cached. The caller has to free the binding
462  * using free_dom_binding().
463  */
464 static struct dom_binding *
load_dom_binding_rsvdport(struct ypbind_binding * dom_binding,char * domain,int * err)465 load_dom_binding_rsvdport(struct ypbind_binding *dom_binding, char *domain,
466 								int *err)
467 {
468 	struct dom_binding *pdomb;
469 	int fd;
470 
471 	pdomb = NULL;
472 
473 	if ((pdomb = malloc(sizeof (struct dom_binding))) == NULL) {
474 		syslog(LOG_ERR, "load_dom_binding_rsvdport:  malloc failure.");
475 		*err = YPERR_RESRC;
476 		return (NULL);
477 	}
478 
479 	pdomb->dom_binding = dom_binding;
480 	/*
481 	 * Open up a path to the server, which will remain active globally.
482 	 */
483 	fd = tli_open_rsvdport(pdomb->dom_binding->ypbind_nconf);
484 	if (fd < 0) {
485 		clnt_pcreateerror("yp_bind: tli_open_rsvdport");
486 		free(pdomb);
487 		*err = YPERR_RPC;
488 		return (NULL);
489 	}
490 	pdomb->dom_client = clnt_tli_create(fd,
491 					    pdomb->dom_binding->ypbind_nconf,
492 					    pdomb->dom_binding->ypbind_svcaddr,
493 					    YPPROG, YPVERS, __ypipbufsize,
494 					    __ypipbufsize);
495 	if (pdomb->dom_client == NULL) {
496 		clnt_pcreateerror("yp_bind: clnt_tli_create");
497 		free(pdomb);
498 		*err = YPERR_RPC;
499 		return (NULL);
500 	}
501 #ifdef DEBUG
502 (void) printf("yp_bind: clnt_tli_create suceeded\n");
503 #endif
504 	(void) CLNT_CONTROL(pdomb->dom_client, CLSET_FD_CLOSE, NULL);
505 
506 	pdomb->dom_domain = malloc(strlen(domain) + (unsigned)1);
507 	if (pdomb->dom_domain == NULL) {
508 		clnt_destroy(pdomb->dom_client);
509 		free(pdomb);
510 		*err = YPERR_RESRC;
511 		return (NULL);
512 	}
513 
514 	(void) strcpy(pdomb->dom_domain, domain); /* Remember the domain name */
515 	pdomb->ref_count = 0;
516 	pdomb->need_free = 0;
517 	set_rdev(pdomb);
518 	(void) mutex_init(&pdomb->server_name_lock, USYNC_THREAD, 0);
519 	return (pdomb);
520 }
521 
522 /*
523  * Attempts to locate a yellow pages server that serves a passed domain.  If
524  * one is found, an entry is created on the static list of domain-server pairs
525  * pointed to by cell bound_domains, a udp path to the server is created and
526  * the function returns 0.  Otherwise, the function returns a defined errorcode
527  * YPERR_xxxx.
528  *
529  * MT-safe because it serializes on bound_domains_lock.
530  *
531  * If hardlookup is set then loop forever until success, else try 4
532  * times (each try is relatively short) max.
533  */
534 int
__yp_dobind_cflookup(char * domain,struct dom_binding ** binding,int hardlookup)535 __yp_dobind_cflookup(
536 	char *domain,
537 	struct dom_binding **binding,	/* if result==0, ptr to dom_binding */
538 	int hardlookup)
539 
540 {
541 	struct dom_binding *pdomb;	/* Ptr to new domain binding */
542 	struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */
543 	struct ypbind_domain ypbd;
544 	int status, err = YPERR_DOMAIN;
545 	int first_try = 1;
546 	CLIENT *tb = NULL;
547 
548 	if ((domain == NULL) ||(strlen(domain) == 0))
549 		return (YPERR_BADARGS);
550 
551 	(void) mutex_lock(&bound_domains_lock);
552 	/*
553 	 * ===>
554 	 * If someone managed to fork() while we were holding this lock,
555 	 *   we'll probably end up hanging on the lock.  Tant pis.
556 	 */
557 	newborn();
558 
559 	if (check_binding(domain, binding)) {
560 		/*
561 		 *  If the cache is okay and if the underlying file
562 		 *  descriptor is okay (application did not close it).
563 		 *  then use the binding.
564 		 */
565 		if (!(*binding)->cache_bad && check_rdev(*binding)) {
566 			(*binding)->ref_count += 1;
567 			(void) mutex_unlock(&bound_domains_lock);
568 			return (0);		/* We are bound */
569 		}
570 
571 		/*
572 		 *  If we get here, one of two things happened:  the
573 		 *  cache is bad, or the underlying file descriptor
574 		 *  had changed.
575 		 *
576 		 *  If the cache is bad, then we call yp_unbind to remove
577 		 *  the binding.
578 		 *
579 		 *  If the file descriptor has changed, then we call
580 		 *  yp_unbind to remove the binding (we set cache_bad
581 		 *  to force yp_unbind to do the remove), and then
582 		 *  call check_binding to reload the binding from the
583 		 *  cache again.
584 		 */
585 		if ((*binding)->cache_bad) {
586 			__yp_unbind_nolock(domain);
587 		} else {
588 			(*binding)->cache_bad = 1;
589 			(void) mutex_unlock(&bound_domains_lock);
590 			yp_unbind(domain);
591 			(void) mutex_lock(&bound_domains_lock);
592 			if (check_binding(domain, binding)) {
593 				(*binding)->ref_count += 1;
594 				(void) mutex_unlock(&bound_domains_lock);
595 				return (0);
596 			}
597 		}
598 	}
599 
600 	do {
601 		if (first_try)
602 			first_try = 0;
603 		else {
604 			/*
605 			 * ===> sleep() -- Ugh.  And with the lock held, too.
606 			 */
607 			(void) sleep(_ypsleeptime);
608 		}
609 		tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
610 		if (tb == NULL) {
611 			if (ypbind_running(err, rpc_createerr.cf_stat))
612 				continue;
613 			break;
614 		}
615 		ypbd.ypbind_domainname = domain;
616 		ypbd.ypbind_vers = YPVERS;
617 		/*
618 		 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're
619 		 *   OK as long as we're the only ones who call it and we
620 		 *   serialize all requests (for all domains).  Otherwise,
621 		 *   change the interface (pass in the ypbind_resp struct).
622 		 */
623 		ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
624 		/*
625 		 * Although we talk to ypbind on loopback,
626 		 * it gives us a udp address for the ypserv.
627 		 */
628 		if (ypbind_resp == NULL) {
629 			/* lost ypbind? */
630 			clnt_perror(tb,
631 				"ypbindproc_domain_3: can't contact ypbind");
632 			clnt_destroy(tb);
633 			tb = NULL;
634 			continue;
635 		}
636 		if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
637 			/*
638 			 * Local ypbind has let us in on the ypserv's address,
639 			 * go get in touch with it !
640 			 */
641 			pdomb = load_dom_binding(ypbind_resp, domain, &status);
642 			if (pdomb == 0) {
643 				err = status;
644 				clnt_destroy(tb);
645 				tb = NULL;
646 				continue;
647 			}
648 			clnt_destroy(tb);
649 			pdomb->ref_count += 1;
650 			(void) mutex_unlock(&bound_domains_lock);
651 			*binding = pdomb; /* Return ptr to the binding entry */
652 			return (0);		/* This is the go path */
653 		}
654 		if (ypbind_resp->ypbind_resp_u.ypbind_error ==
655 		    YPBIND_ERR_NOSERV)
656 			err = YPERR_DOMAIN;
657 		else
658 			err = YPERR_YPBIND;
659 		clnt_destroy(tb);
660 		tb = NULL;
661 	} while (hardlookup);
662 
663 	if (tb != NULL)
664 		clnt_destroy(tb);
665 	(void) mutex_unlock(&bound_domains_lock);
666 	if (err)
667 		return (err);
668 	return (YPERR_DOMAIN);
669 }
670 
671 int
__yp_dobind(char * domain,struct dom_binding ** binding)672 __yp_dobind(
673 	char *domain,
674 	struct dom_binding **binding)	/* if result == 0, ptr to dom_binding */
675 {
676 	/* traditional __yp_dobind loops forever so set hardlookup */
677 	return (__yp_dobind_cflookup(domain, binding, 1));
678 }
679 
680 void
__yp_rel_binding(struct dom_binding * binding)681 __yp_rel_binding(struct dom_binding *binding)
682 {
683 	(void) mutex_lock(&bound_domains_lock);
684 	binding->ref_count -= 1;
685 	if (binding->need_free && binding->ref_count == 0)
686 		free_dom_binding(binding);
687 	(void) mutex_unlock(&bound_domains_lock);
688 }
689 
690 /*
691  * Attempts to locate a yellow pages server that serves a passed domain.  If
692  * one is found, an entry is created on the static list of domain-server pairs
693  * pointed to by cell bound_domains, a udp path to the server is created and
694  * the function returns 0.  Otherwise, the function returns a defined errorcode
695  * YPERR_xxxx.
696  *
697  * MT-safe because it serializes on bound_domains_lock.
698  *
699  * XXX special version for handling C2 (passwd.adjunct) lookups when we need
700  * a reserved port.
701  * This returns an uncached binding which the caller has to free using
702  * free_dom_binding().
703  */
704 int
__yp_dobind_rsvdport_cflookup(char * domain,struct dom_binding ** binding,int hardlookup)705 __yp_dobind_rsvdport_cflookup(
706 	char *domain,
707 	struct dom_binding **binding,	/* if result==0, ptr to dom_binding */
708 	int hardlookup)
709 {
710 	struct dom_binding *pdomb;	/* Ptr to new domain binding */
711 	struct ypbind_resp *ypbind_resp; /* Response from local ypbinder */
712 	struct ypbind_domain ypbd;
713 	int status,  err = YPERR_DOMAIN;
714 	int first_try = 1;
715 	CLIENT *tb = NULL;
716 
717 	if ((domain == NULL) ||(strlen(domain) == 0))
718 		return (YPERR_BADARGS);
719 
720 	(void) mutex_lock(&bound_domains_lock);
721 	/*
722 	 * ===>
723 	 * If someone managed to fork() while we were holding this lock,
724 	 *   we'll probably end up hanging on the lock.  Tant pis.
725 	 */
726 	newborn();
727 
728 	/*
729 	 * Check for existing bindings and use the information in the binding
730 	 * to create a transport endpoint with a reserved port.
731 	 */
732 	if (check_binding(domain, binding)) {
733 		/*
734 		 * If the cache is bad, yp_unbind() the entry again and then
735 		 * talk to ypbind.
736 		 */
737 		if ((*binding)->cache_bad) {
738 			__yp_unbind_nolock(domain);
739 		} else {
740 			pdomb = load_dom_binding_rsvdport(
741 						(*binding)->dom_binding,
742 							domain, &status);
743 			if (pdomb == 0) {
744 				(void) mutex_unlock(&bound_domains_lock);
745 				return (status);
746 			}
747 			pdomb->ref_count += 1;
748 			(void) mutex_unlock(&bound_domains_lock);
749 			*binding = pdomb; /* Return ptr to the binding entry */
750 			return (0);
751 		}
752 	}
753 
754 	do {
755 		if (first_try)
756 			first_try = 0;
757 		else {
758 			/*
759 			 * ===> sleep() -- Ugh.  And with the lock held, too.
760 			 */
761 			(void) sleep(_ypsleeptime);
762 		}
763 		tb = __clnt_create_loopback(YPBINDPROG, YPBINDVERS, &err);
764 		if (tb == NULL) {
765 			if (ypbind_running(err, rpc_createerr.cf_stat))
766 				continue;
767 			break;
768 		}
769 		ypbd.ypbind_domainname = domain;
770 		ypbd.ypbind_vers = YPVERS;
771 		/*
772 		 * The interface to ypbindproc_domain_3 is MT-unsafe, but we're
773 		 *   OK as long as we're the only ones who call it and we
774 		 *   serialize all requests (for all domains).  Otherwise,
775 		 *   change the interface (pass in the ypbind_resp struct).
776 		 */
777 		ypbind_resp = ypbindproc_domain_3(&ypbd, tb);
778 		/*
779 		 * Although we talk to ypbind on loopback,
780 		 * it gives us a udp address for the ypserv.
781 		 */
782 		if (ypbind_resp == NULL) {
783 			/* lost ypbind? */
784 			clnt_perror(tb,
785 				"ypbindproc_domain_3: can't contact ypbind");
786 			clnt_destroy(tb);
787 			tb = NULL;
788 			continue;
789 		}
790 		if (ypbind_resp->ypbind_status == YPBIND_SUCC_VAL) {
791 			/*
792 			 * Local ypbind has let us in on the ypserv's address,
793 			 * go get in touch with it !
794 			 */
795 			pdomb = load_dom_binding_rsvdport(
796 				    ypbind_resp->ypbind_resp_u.ypbind_bindinfo,
797 				    domain, &status);
798 			if (pdomb == 0) {
799 				err = status;
800 				clnt_destroy(tb);
801 				tb = NULL;
802 				continue;
803 			}
804 			clnt_destroy(tb);
805 			pdomb->ref_count += 1;
806 			(void) mutex_unlock(&bound_domains_lock);
807 			*binding = pdomb; /* Return ptr to the binding entry */
808 			return (0);		/* This is the go path */
809 		}
810 		if (ypbind_resp->ypbind_resp_u.ypbind_error ==
811 		    YPBIND_ERR_NOSERV)
812 			err = YPERR_DOMAIN;
813 		else
814 			err = YPERR_YPBIND;
815 		clnt_destroy(tb);
816 		tb = NULL;
817 	} while (hardlookup);
818 
819 	if (tb != NULL)
820 		clnt_destroy(tb);
821 	(void) mutex_unlock(&bound_domains_lock);
822 	if (err)
823 		return (err);
824 	return (YPERR_DOMAIN);
825 }
826 
827 int
__yp_dobind_rsvdport(char * domain,struct dom_binding ** binding)828 __yp_dobind_rsvdport(
829 	char *domain,
830 	struct dom_binding **binding)	/* if result==0, ptr to dom_binding */
831 {
832 	/* traditional __yp_dobind_rsvdport loops forever so set hardlookup */
833 	return (__yp_dobind_rsvdport_cflookup(domain, binding, 1));
834 }
835 
836 /*
837  * This is a "wrapper" function for __yp_dobind for vanilla user-level
838  * functions which neither know nor care about struct dom_bindings.
839  */
840 int
yp_bind(char * domain)841 yp_bind(char *domain)
842 {
843 
844 	struct dom_binding *binding;
845 	int    res;
846 
847 	res = __yp_dobind(domain, &binding);
848 	if (res == 0)
849 		__yp_rel_binding(binding);
850 	return (res);
851 }
852 
853 static char *
__default_domain(void)854 __default_domain(void)
855 {
856 	char temp[256];
857 
858 	(void) mutex_lock(&default_domain_lock);
859 
860 	if (default_domain) {
861 		(void) mutex_unlock(&default_domain_lock);
862 		return (default_domain);
863 	}
864 	if (getdomainname(temp, sizeof (temp)) < 0) {
865 		(void) mutex_unlock(&default_domain_lock);
866 		return (0);
867 	}
868 	if (strlen(temp) > 0) {
869 		default_domain = malloc((strlen(temp) + 1));
870 		if (default_domain == 0) {
871 			(void) mutex_unlock(&default_domain_lock);
872 			return (0);
873 		}
874 		(void) strcpy(default_domain, temp);
875 		(void) mutex_unlock(&default_domain_lock);
876 		return (default_domain);
877 	}
878 	(void) mutex_unlock(&default_domain_lock);
879 	return (0);
880 }
881 
882 /*
883  * This is a wrapper for the system call getdomainname which returns a
884  * ypclnt.h error code in the failure case.  It also checks to see that
885  * the domain name is non-null, knowing that the null string is going to
886  * get rejected elsewhere in the yp client package.
887  */
888 int
yp_get_default_domain(char ** domain)889 yp_get_default_domain(char **domain)
890 {
891 	if ((*domain = __default_domain()) != 0)
892 		return (0);
893 	return (YPERR_YPERR);
894 }
895 
896 /*
897  * ===> Nobody uses this, do they?  Can we nuke it?
898  */
899 int
usingypmap(char ** ddn,char * map)900 usingypmap(char **ddn, char *map)
901 {
902 	char in, *outval = NULL;
903 	int outvallen, stat;
904 	char *domain;
905 
906 	if ((domain = __default_domain()) == 0)
907 		return (FALSE);
908 	*ddn = domain;
909 	/* does the map exist ? */
910 	in = (char)0xff;
911 	stat = yp_match(domain, map, &in, 1, &outval, &outvallen);
912 	if (outval != NULL)
913 		free(outval);
914 	switch (stat) {
915 
916 	case 0:  /* it actually succeeded! */
917 	case YPERR_KEY:  /* no such key in map */
918 	case YPERR_NOMORE:
919 	case YPERR_BUSY:
920 		return (TRUE);
921 	}
922 	return (FALSE);
923 }
924 
925 /*
926  * Creates a quick connection on a connection oriented loopback
927  * transport. Fails quickly without timeout. Only naming service
928  * it goes to is straddr.so.
929  */
930 CLIENT *
__clnt_create_loopback(rpcprog_t prog,rpcvers_t vers,int * err)931 __clnt_create_loopback(rpcprog_t prog, rpcvers_t vers, int *err)
932 {
933 	struct netconfig *nconf;
934 	CLIENT *clnt = NULL;
935 	void *nc_handle;	/* Net config handle */
936 
937 	*err = 0;
938 	nc_handle = setnetconfig();
939 	if (nc_handle == NULL) {
940 		/* fails to open netconfig file */
941 		rpc_createerr.cf_stat = RPC_FAILED;
942 		*err = YPERR_RPC;
943 		return (NULL);
944 	}
945 	while (nconf = getnetconfig(nc_handle))
946 		/* Try only one connection oriented loopback transport */
947 		if ((strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) &&
948 			((nconf->nc_semantics == NC_TPI_COTS) ||
949 			(nconf->nc_semantics == NC_TPI_COTS_ORD))) {
950 			clnt = getclnt(prog, vers, nconf, err);
951 			break;
952 		}
953 	(void) endnetconfig(nc_handle);
954 
955 	if (clnt == NULL) {	/* no loopback transport available */
956 		if (rpc_createerr.cf_stat == 0)
957 			rpc_createerr.cf_stat = RPC_UNKNOWNPROTO;
958 		if (*err == 0) *err = YPERR_RPC;
959 	}
960 	return (clnt);
961 }
962 
963 static CLIENT *
getclnt(rpcprog_t prog,rpcvers_t vers,struct netconfig * nconf,int * err)964 getclnt(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf, int *err)
965 {
966 	int fd;
967 	struct netbuf *svcaddr;			/* servers address */
968 	CLIENT *cl;
969 	struct nd_addrlist *nas;
970 	struct nd_hostserv rpcbind_hs;
971 	struct t_call sndcall;
972 	char uaddress[1024]; /* XXX maxlen ?? */
973 	RPCB parms;
974 	enum clnt_stat clnt_st;
975 	char *ua;
976 	struct timeval tv = { 30, 0 };
977 
978 	if (nconf == NULL) {
979 		rpc_createerr.cf_stat = RPC_TLIERROR;
980 		*err = YPERR_RPC;
981 		return (NULL);
982 	}
983 
984 	/*
985 	 *  The ypbind process might cache its transport address.
986 	 *  If we can get at it, then we will use it and avoid
987 	 *  wasting time talking to rpcbind.
988 	 */
989 
990 	if (get_cached_transport(nconf, vers, uaddress, sizeof (uaddress))) {
991 		goto create_client;
992 	}
993 
994 	/*
995 	 * Check to see if local rpcbind is up or not. If it
996 	 * isn't, it is best that the application should realize
997 	 * yp is not up and take a remedial action. This is to
998 	 * avoid the minute long timeout incurred by rpcbind_getaddr.
999 	 * Looks like the only way to accomplish this it is to unfold
1000 	 * rpcb_getaddr and make a few changes. Alas !
1001 	 */
1002 	rpcbind_hs.h_host = HOST_SELF_CONNECT;
1003 	rpcbind_hs.h_serv = "rpcbind";
1004 	if (netdir_getbyname(nconf, &rpcbind_hs, &nas) != ND_OK) {
1005 		rpc_createerr.cf_stat = RPC_N2AXLATEFAILURE;
1006 		*err = YPERR_RPC;
1007 		return (NULL);
1008 	}
1009 	if ((fd = t_open(nconf->nc_device, O_RDWR, NULL)) == -1) {
1010 		rpc_createerr.cf_stat = RPC_TLIERROR;
1011 		*err = YPERR_RPC;
1012 		return (NULL);
1013 	}
1014 	if (t_bind(fd, NULL, NULL) == -1) {
1015 		rpc_createerr.cf_stat = RPC_TLIERROR;
1016 		*err = YPERR_RPC;
1017 		(void) t_close(fd);
1018 		return (NULL);
1019 	}
1020 	sndcall.addr = *(nas->n_addrs);
1021 	sndcall.opt.len = 0;
1022 	sndcall.udata.len = 0;
1023 	if (t_connect(fd, &sndcall, NULL) == -1) {
1024 		netdir_free((char *)nas, ND_ADDRLIST);
1025 		rpc_createerr.cf_stat = RPC_TLIERROR;
1026 		(void) t_close(fd);
1027 		*err = YPERR_PMAP;
1028 		return (NULL);
1029 	}
1030 
1031 	/*
1032 	 * Get the address of the server
1033 	 */
1034 	cl = clnt_tli_create(fd, nconf, nas->n_addrs,
1035 		RPCBPROG, RPCBVERS, __ypipbufsize, __ypipbufsize);
1036 	netdir_free((char *)nas, ND_ADDRLIST);
1037 	if (cl == NULL) {
1038 		(void) t_close(fd);
1039 		*err = YPERR_PMAP;
1040 		return (NULL);
1041 	}
1042 	parms.r_prog = prog;
1043 	parms.r_vers = vers;
1044 	parms.r_netid = nconf->nc_netid;
1045 	parms.r_addr = nullstring;
1046 	parms.r_owner = nullstring;
1047 	ua = uaddress;
1048 	clnt_st = CLNT_CALL(cl, RPCBPROC_GETADDR, xdr_rpcb, (char *)&parms,
1049 		xdr_wrapstring, (char *)&ua, tv);
1050 	(void) t_close(fd);
1051 	clnt_destroy(cl);
1052 	if (clnt_st != RPC_SUCCESS) {
1053 		*err = YPERR_YPBIND;
1054 		return (NULL);
1055 	}
1056 	if (strlen(uaddress) == 0) {
1057 		*err = YPERR_YPBIND;
1058 		rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
1059 		return (NULL);
1060 	}
1061 
1062 create_client:
1063 	svcaddr = uaddr2taddr(nconf, uaddress);
1064 	cl = clnt_tli_create(RPC_ANYFD, nconf, svcaddr, prog, vers,
1065 					__ypipbufsize, __ypipbufsize);
1066 	netdir_free((char *)svcaddr, ND_ADDR);
1067 	if (cl == NULL) {
1068 		*err = YPERR_YPBIND;
1069 		return (NULL);
1070 	}
1071 	/*
1072 	 * The fd should be closed while destroying the handle.
1073 	 */
1074 	return (cl);
1075 }
1076 
1077 static int
get_cached_transport(struct netconfig * nconf,int vers,char * uaddress,int ulen)1078 get_cached_transport(struct netconfig *nconf, int vers, char *uaddress,
1079 								int ulen)
1080 {
1081 	ssize_t st;
1082 	int fd;
1083 
1084 	(void) snprintf(uaddress, ulen,
1085 		"%s/xprt.%s.%d", BINDING, nconf->nc_netid, vers);
1086 	fd = open(uaddress, O_RDONLY);
1087 	if (fd == -1)
1088 		return (0);
1089 
1090 	/* if first byte is not locked, then ypbind must not be running */
1091 	st = lockf(fd, F_TEST, 1);
1092 	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1093 		(void) close(fd);
1094 		return (0);
1095 	}
1096 
1097 	st = read(fd, uaddress, ulen);
1098 	if (st == -1) {
1099 		(void) close(fd);
1100 		return (0);
1101 	}
1102 
1103 	(void) close(fd);
1104 	return (1);
1105 }
1106 
1107 static ypbind_resp *
get_cached_domain(char * domain)1108 get_cached_domain(char *domain)
1109 {
1110 	FILE *fp;
1111 	int st;
1112 	char filename[300];
1113 	static ypbind_resp res;
1114 	XDR xdrs;
1115 
1116 	(void) snprintf(filename, sizeof (filename),
1117 					"%s/%s/cache_binding", BINDING, domain);
1118 	fp = fopen(filename, "rF");
1119 	if (fp == 0)
1120 		return (0);
1121 
1122 	/* if first byte is not locked, then ypbind must not be running */
1123 	st = lockf(fileno(fp), F_TEST, 1);
1124 	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1125 		(void) fclose(fp);
1126 		return (0);
1127 	}
1128 
1129 	xdrstdio_create(&xdrs, fp, XDR_DECODE);
1130 
1131 	(void) memset((char *)&res, 0, sizeof (res));
1132 	st = xdr_ypbind_resp(&xdrs, &res);
1133 
1134 	xdr_destroy(&xdrs);
1135 	(void) fclose(fp);
1136 
1137 	if (st)
1138 		return (&res);
1139 
1140 	return (0);
1141 }
1142 
1143 static int
ypbind_running(int err,int status)1144 ypbind_running(int err, int status)
1145 {
1146 	char filename[300];
1147 	int st;
1148 	int fd;
1149 
1150 	(void) snprintf(filename, sizeof (filename), "%s/ypbind.pid", BINDING);
1151 	fd = open(filename, O_RDONLY);
1152 	if (fd == -1) {
1153 		if ((err == YPERR_YPBIND) && (status != RPC_PROGNOTREGISTERED))
1154 			return (1);
1155 		return (0);
1156 	}
1157 
1158 	/* if first byte is not locked, then ypbind must not be running */
1159 	st = lockf(fd, F_TEST, 1);
1160 	if (st != -1 || (errno != EAGAIN && errno != EACCES)) {
1161 		(void) close(fd);
1162 		return (0);
1163 	}
1164 
1165 	(void) close(fd);
1166 	return (1);
1167 }
1168 
1169 static void
set_rdev(struct dom_binding * pdomb)1170 set_rdev(struct dom_binding *pdomb)
1171 {
1172 	int fd;
1173 	struct stat stbuf;
1174 
1175 	if (clnt_control(pdomb->dom_client, CLGET_FD, (char *)&fd) != TRUE ||
1176 	    fstat(fd, &stbuf) == -1) {
1177 		syslog(LOG_DEBUG, "ypbind client:  can't get rdev");
1178 		pdomb->fd = -1;
1179 		return;
1180 	}
1181 	pdomb->fd = fd;
1182 	pdomb->rdev = stbuf.st_rdev;
1183 }
1184 
1185 static int
check_rdev(struct dom_binding * pdomb)1186 check_rdev(struct dom_binding *pdomb)
1187 {
1188 	struct stat stbuf;
1189 
1190 	if (pdomb->fd == -1)
1191 		return (1);    /* can't check it, assume it is okay */
1192 
1193 	if (fstat(pdomb->fd, &stbuf) == -1) {
1194 		syslog(LOG_DEBUG, "yp_bind client:  can't stat %d", pdomb->fd);
1195 		/* could be because file descriptor was closed */
1196 		/* it's not our file descriptor, so don't try to close it */
1197 		clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
1198 		return (0);
1199 	}
1200 	if (pdomb->rdev != stbuf.st_rdev) {
1201 		syslog(LOG_DEBUG,
1202 		    "yp_bind client:  fd %d changed, old=0x%x, new=0x%x",
1203 		    pdomb->fd, pdomb->rdev, stbuf.st_rdev);
1204 		/* it's not our file descriptor, so don't try to close it */
1205 		clnt_control(pdomb->dom_client, CLSET_FD_NCLOSE, NULL);
1206 		return (0);
1207 	}
1208 	return (1);    /* fd is okay */
1209 }
1210