xref: /openbsd/usr.sbin/ypbind/ypbind.c (revision 8529ddd3)
1 /*	$OpenBSD: ypbind.c,v 1.63 2015/01/16 06:40:22 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 1992, 1993, 1996, 1997, 1998 Theo de Raadt <deraadt@openbsd.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
17  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/stat.h>
32 #include <sys/file.h>
33 #include <sys/fcntl.h>
34 #include <sys/uio.h>
35 #include <sys/syslog.h>
36 #include <net/if.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <limits.h>
40 #include <errno.h>
41 #include <ctype.h>
42 #include <netdb.h>
43 #include <string.h>
44 #include <dirent.h>
45 #include <rpc/rpc.h>
46 #include <rpc/xdr.h>
47 #include <arpa/inet.h>
48 #include <rpc/pmap_clnt.h>
49 #include <rpc/pmap_prot.h>
50 #include <rpc/pmap_rmt.h>
51 #include <unistd.h>
52 #include <err.h>
53 #include <rpcsvc/yp.h>
54 #include <rpcsvc/ypclnt.h>
55 #include <ifaddrs.h>
56 
57 #define SERVERSDIR	"/etc/yp"
58 #define BINDINGDIR	"/var/yp/binding"
59 #define YPBINDLOCK	"/var/run/ypbind.lock"
60 
61 struct _dom_binding {
62 	struct _dom_binding *dom_pnext;
63 	char dom_domain[YPMAXDOMAIN + 1];
64 	struct sockaddr_in dom_server_addr;
65 	unsigned short int dom_server_port;
66 	int dom_socket;
67 	CLIENT *dom_client;
68 	long dom_vers;
69 	time_t dom_check_t;
70 	time_t dom_ask_t;
71 	int dom_lockfd;
72 	int dom_alive;
73 	u_int32_t dom_xid;
74 	char dom_servlist[PATH_MAX];
75 	FILE *dom_servlistfp;
76 };
77 
78 void rpc_received(char *dom, struct sockaddr_in *raddrp, int force);
79 void checkwork(void);
80 enum clnt_stat handle_replies(void);
81 enum clnt_stat handle_ping(void);
82 int broadcast(struct _dom_binding *ypdb, char *, int);
83 int direct(struct _dom_binding *ypdb, char *, int);
84 int ping(struct _dom_binding *ypdb);
85 int pings(struct _dom_binding *ypdb);
86 
87 char *domain;
88 
89 struct _dom_binding *ypbindlist;
90 int check;
91 
92 #define YPSET_NO	0
93 #define YPSET_LOCAL	1
94 #define YPSET_ALL	2
95 int ypsetmode = YPSET_NO;
96 int insecure = 0;
97 
98 int rpcsock, pingsock;
99 struct rmtcallargs rmtca;
100 struct rmtcallres rmtcr;
101 bool_t rmtcr_outval;
102 u_long rmtcr_port;
103 SVCXPRT *udptransp, *tcptransp;
104 SVCXPRT *ludptransp, *ltcptransp;
105 
106 struct _dom_binding *xid2ypdb(u_int32_t xid);
107 u_int32_t unique_xid(struct _dom_binding *ypdb);
108 
109 /*
110  * We name the local RPC functions ypbindproc_XXX_2x() instead
111  * of ypbindproc_XXX_2() because we need to pass an additional
112  * parameter. ypbindproc_setdom_2x() does a security check, and
113  * hence needs the CLIENT *
114  *
115  * We are faced with either making ypbindprog_2() do the security
116  * check before calling ypbindproc_setdom_2().. or we can simply
117  * declare sun's interface insufficient and roll our own.
118  */
119 
120 /*ARGSUSED*/
121 static void *
122 ypbindproc_null_2x(SVCXPRT *transp, void *argp, CLIENT *clnt)
123 {
124 	static char res;
125 
126 	memset(&res, 0, sizeof(res));
127 	return (void *)&res;
128 }
129 
130 /*ARGSUSED*/
131 static struct ypbind_resp *
132 ypbindproc_domain_2x(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
133 {
134 	static struct ypbind_resp res;
135 	struct _dom_binding *ypdb;
136 	char path[PATH_MAX];
137 	time_t now;
138 	int count = 0;
139 
140 	if (strchr((char *)argp, '/'))
141 		return NULL;
142 
143 	memset(&res, 0, sizeof(res));
144 	res.ypbind_status = YPBIND_FAIL_VAL;
145 
146 	for (ypdb = ypbindlist; ypdb && count < 100; ypdb = ypdb->dom_pnext)
147 		count++;
148 	if (count >= 100)
149 		return NULL;	/* prevent DOS: sorry, you lose */
150 
151 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
152 		if (!strcmp(ypdb->dom_domain, *argp))
153 			break;
154 
155 	if (ypdb == NULL) {
156 		ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
157 		if (ypdb == NULL)
158 			return NULL;
159 		memset(ypdb, 0, sizeof *ypdb);
160 		strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain-1);
161 		ypdb->dom_domain[sizeof ypdb->dom_domain-1] = '\0';
162 		ypdb->dom_vers = YPVERS;
163 		ypdb->dom_alive = 0;
164 		ypdb->dom_lockfd = -1;
165 		snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
166 		    ypdb->dom_domain, (int)ypdb->dom_vers);
167 		unlink(path);
168 		snprintf(ypdb->dom_servlist, sizeof ypdb->dom_servlist,
169 		    "%s/%s", SERVERSDIR, ypdb->dom_domain);
170 		ypdb->dom_servlistfp = fopen(ypdb->dom_servlist, "r");
171 		ypdb->dom_xid = unique_xid(ypdb);
172 		ypdb->dom_pnext = ypbindlist;
173 		ypbindlist = ypdb;
174 		check++;
175 		return NULL;
176 	}
177 
178 	if (ypdb->dom_alive == 0)
179 		return NULL;
180 
181 #ifdef HEURISTIC
182 	time(&now);
183 	if (now < ypdb->dom_ask_t + 5) {
184 		/*
185 		 * Hmm. More than 2 requests in 5 seconds have indicated
186 		 * that my binding is possibly incorrect.
187 		 * Ok, do an immediate poll of the server.
188 		 */
189 		if (ypdb->dom_check_t >= now) {
190 			/* don't flood it */
191 			ypdb->dom_check_t = 0;
192 			check++;
193 		}
194 	}
195 	ypdb->dom_ask_t = now;
196 #endif
197 
198 	res.ypbind_status = YPBIND_SUCC_VAL;
199 	memmove(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
200 	    &ypdb->dom_server_addr.sin_addr,
201 	    sizeof(res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr));
202 	memmove(&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
203 	    &ypdb->dom_server_port,
204 	    sizeof(res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port));
205 #ifdef DEBUG
206 	printf("domain %s at %s/%d\n", ypdb->dom_domain,
207 	    inet_ntoa(ypdb->dom_server_addr.sin_addr),
208 	    ntohs(ypdb->dom_server_addr.sin_port));
209 #endif
210 	return &res;
211 }
212 
213 /*ARGSUSED*/
214 static bool_t *
215 ypbindproc_setdom_2x(SVCXPRT *transp, struct ypbind_setdom *argp, CLIENT *clnt)
216 {
217 	struct sockaddr_in *fromsin, bindsin;
218 	static bool_t res = 1;
219 
220 	fromsin = svc_getcaller(transp);
221 
222 	switch (ypsetmode) {
223 	case YPSET_LOCAL:
224 		if (transp != ludptransp && transp != ltcptransp) {
225 			syslog(LOG_WARNING, "attempted spoof of ypsetme");
226 			svcerr_weakauth(transp);
227 			return NULL;
228 		}
229 		if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
230 			svcerr_weakauth(transp);
231 			return NULL;
232 		}
233 		break;
234 	case YPSET_ALL:
235 		break;
236 	case YPSET_NO:
237 	default:
238 		svcerr_weakauth(transp);
239 		return NULL;
240 	}
241 
242 	if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
243 		svcerr_weakauth(transp);
244 		return NULL;
245 	}
246 
247 	if (argp->ypsetdom_vers != YPVERS) {
248 		svcerr_noprog(transp);
249 		return NULL;
250 	}
251 
252 	memset(&bindsin, 0, sizeof bindsin);
253 	bindsin.sin_family = AF_INET;
254 	bindsin.sin_len = sizeof(bindsin);
255 	memcpy(&bindsin.sin_addr, &argp->ypsetdom_binding.ypbind_binding_addr,
256 	    sizeof(argp->ypsetdom_binding.ypbind_binding_addr));
257 	memcpy(&bindsin.sin_port, &argp->ypsetdom_binding.ypbind_binding_port,
258 	    sizeof(argp->ypsetdom_binding.ypbind_binding_port));
259 	rpc_received(argp->ypsetdom_domain, &bindsin, 1);
260 
261 	return &res;
262 }
263 
264 static void
265 ypbindprog_2(struct svc_req *rqstp, SVCXPRT *transp)
266 {
267 	union argument {
268 		domainname ypbindproc_domain_2_arg;
269 		struct ypbind_setdom ypbindproc_setdom_2_arg;
270 	} argument;
271 	struct authunix_parms *creds;
272 	char *result;
273 	xdrproc_t xdr_argument, xdr_result;
274 	char *(*local)(SVCXPRT *, union argument *, struct svc_req *);
275 
276 	switch (rqstp->rq_proc) {
277 	case YPBINDPROC_NULL:
278 		xdr_argument = xdr_void;
279 		xdr_result = xdr_void;
280 		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
281 		    ypbindproc_null_2x;
282 		break;
283 
284 	case YPBINDPROC_DOMAIN:
285 		xdr_argument = xdr_domainname;
286 		xdr_result = xdr_ypbind_resp;
287 		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
288 		    ypbindproc_domain_2x;
289 		break;
290 
291 	case YPBINDPROC_SETDOM:
292 		switch (rqstp->rq_cred.oa_flavor) {
293 		case AUTH_UNIX:
294 			creds = (struct authunix_parms *)rqstp->rq_clntcred;
295 			if (creds->aup_uid != 0) {
296 				svcerr_auth(transp, AUTH_BADCRED);
297 				return;
298 			}
299 			break;
300 		default:
301 			svcerr_auth(transp, AUTH_TOOWEAK);
302 			return;
303 		}
304 
305 		xdr_argument = xdr_ypbind_setdom;
306 		xdr_result = xdr_void;
307 		local = (char *(*)(SVCXPRT *, union argument *, struct svc_req *))
308 		    ypbindproc_setdom_2x;
309 		break;
310 
311 	default:
312 		svcerr_noproc(transp);
313 		return;
314 	}
315 	memset(&argument, 0, sizeof(argument));
316 	if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
317 		svcerr_decode(transp);
318 		return;
319 	}
320 	result = (*local)(transp, &argument, rqstp);
321 	if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
322 		svcerr_systemerr(transp);
323 	}
324 	return;
325 }
326 
327 static void
328 usage(void)
329 {
330 	fprintf(stderr, "usage: ypbind [-insecure] [-ypset] [-ypsetme]\n");
331 	exit(1);
332 }
333 
334 int
335 main(int argc, char *argv[])
336 {
337 	char path[PATH_MAX];
338 	struct sockaddr_in sin;
339 	struct timeval tv;
340 	fd_set *fdsrp = NULL;
341 	int fdsrl = 0;
342 	int width, lockfd, lsock;
343 	socklen_t len;
344 	int evil = 0, one = 1;
345 	DIR *dirp;
346 	struct dirent *dent;
347 
348 	yp_get_default_domain(&domain);
349 	if (domain[0] == '\0') {
350 		fprintf(stderr, "domainname not set. Aborting.\n");
351 		exit(1);
352 	}
353 
354 	while (--argc) {
355 		++argv;
356 		if (!strcmp("-insecure", *argv))
357 			insecure = 1;
358 		else if (!strcmp("-ypset", *argv))
359 			ypsetmode = YPSET_ALL;
360 		else if (!strcmp("-ypsetme", *argv))
361 			ypsetmode = YPSET_LOCAL;
362 		else
363 			usage();
364 	}
365 
366 	/* blow away everything in BINDINGDIR */
367 	dirp = opendir(BINDINGDIR);
368 	if (dirp) {
369 		while ((dent = readdir(dirp))) {
370 			if (!strcmp(dent->d_name, ".") ||
371 			    !strcmp(dent->d_name, ".."))
372 				continue;
373 			snprintf(path, sizeof(path), "%s/%s", BINDINGDIR,
374 			    dent->d_name);
375 			(void) unlink(path);
376 		}
377 		closedir(dirp);
378 	} else {
379 		printf("Enabling yp client subsystem.\n");
380 		printf("To disable: kill ypbind and remove %s\n",
381 		    BINDINGDIR);
382 		(void)mkdir(BINDINGDIR, 0755);
383 	}
384 
385 #ifdef O_SHLOCK
386 	if ((lockfd = open(YPBINDLOCK, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC,
387 	    0644)) == -1) {
388 		fprintf(stderr, "ypbind: cannot create %s\n", YPBINDLOCK);
389 		exit(1);
390 	}
391 #else
392 	if ((lockfd = open(YPBINDLOCK, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
393 		fprintf(stderr, "ypbind: cannot create %s.\n", YPBINDLOCK);
394 		exit(1);
395 	}
396 	flock(lockfd, LOCK_SH);
397 #endif
398 
399 	if (fchmod(lockfd, 0644) == -1)
400 		err(1, "fchmod");
401 
402 	(void)pmap_unset(YPBINDPROG, YPBINDVERS);
403 
404 	udptransp = svcudp_create(RPC_ANYSOCK);
405 	if (udptransp == NULL) {
406 		fprintf(stderr, "cannot create udp service.\n");
407 		exit(1);
408 	}
409 	if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
410 	    IPPROTO_UDP)) {
411 		fprintf(stderr,
412 		    "unable to register (YPBINDPROG, YPBINDVERS, udp).\n");
413 		exit(1);
414 	}
415 
416 	tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
417 	if (tcptransp == NULL) {
418 		fprintf(stderr, "cannot create tcp service.\n");
419 		exit(1);
420 	}
421 	if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
422 	    IPPROTO_TCP)) {
423 		fprintf(stderr,
424 		    "unable to register (YPBINDPROG, YPBINDVERS, tcp).\n");
425 		exit(1);
426 	}
427 
428 	if (ypsetmode == YPSET_LOCAL) {
429 		/* build UDP local port */
430 		if ((lsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
431 			syslog(LOG_ERR, "cannot create local udp socket: %m");
432 			exit(1);
433 		}
434 		(void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one,
435 		    (socklen_t)sizeof one);
436 		len = sizeof(sin);
437 		if (getsockname(udptransp->xp_sock, (struct sockaddr *)&sin,
438 		    &len) == -1) {
439 			syslog(LOG_ERR, "cannot getsockname local udp: %m");
440 			exit(1);
441 		}
442 		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
443 		sin.sin_port = htons(udptransp->xp_port);
444 		if (bind(lsock, (struct sockaddr *)&sin, len) != 0) {
445 			syslog(LOG_ERR, "cannot bind local udp: %m");
446 			exit(1);
447 		}
448 		if ((ludptransp = svcudp_create(lsock)) == NULL) {
449 			fprintf(stderr, "cannot create udp service.\n");
450 			exit(1);
451 		}
452 
453 		/* build TCP local port */
454 		if ((lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
455 			syslog(LOG_ERR, "cannot create udp socket: %m");
456 			exit(1);
457 		}
458 		(void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one,
459 		    (socklen_t)sizeof one);
460 		len = sizeof(sin);
461 		if (getsockname(tcptransp->xp_sock, (struct sockaddr *)&sin,
462 		    &len) == -1) {
463 			syslog(LOG_ERR, "cannot getsockname udp: %m");
464 			exit(1);
465 		}
466 		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
467 		sin.sin_port = htons(tcptransp->xp_port);
468 		if (bind(lsock, (struct sockaddr *)&sin, len) == -1) {
469 			syslog(LOG_ERR, "cannot bind local tcp: %m");
470 			exit(1);
471 		}
472 		if ((ltcptransp = svctcp_create(lsock, 0, 0)) == NULL) {
473 			fprintf(stderr, "cannot create tcp service.\n");
474 			exit(1);
475 		}
476 	}
477 
478 	if ((rpcsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
479 		perror("socket");
480 		return -1;
481 	}
482 	memset(&sin, 0, sizeof sin);
483 	sin.sin_family = AF_INET;
484 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
485 	sin.sin_port = 0;
486 	bindresvport(rpcsock, &sin);
487 
488 	if ((pingsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
489 		perror("socket");
490 		return -1;
491 	}
492 	memset(&sin, 0, sizeof sin);
493 	sin.sin_family = AF_INET;
494 	sin.sin_addr.s_addr = htonl(INADDR_ANY);
495 	sin.sin_port = 0;
496 	bindresvport(pingsock, &sin);
497 
498 	fcntl(rpcsock, F_SETFL, fcntl(rpcsock, F_GETFL, 0) | FNDELAY);
499 	fcntl(pingsock, F_SETFL, fcntl(pingsock, F_GETFL, 0) | FNDELAY);
500 	setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one,
501 	    (socklen_t)sizeof(one));
502 	rmtca.prog = YPPROG;
503 	rmtca.vers = YPVERS;
504 	rmtca.proc = YPPROC_DOMAIN_NONACK;
505 	rmtca.xdr_args = NULL;		/* set at call time */
506 	rmtca.args_ptr = NULL;		/* set at call time */
507 	rmtcr.port_ptr = &rmtcr_port;
508 	rmtcr.xdr_results = xdr_bool;
509 	rmtcr.results_ptr = (caddr_t)&rmtcr_outval;
510 
511 	if (strchr(domain, '/'))
512 		errx(1, "bad domainname %s", domain);
513 
514 	/* build initial domain binding, make it "unsuccessful" */
515 	ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
516 	if (ypbindlist == NULL)
517 		errx(1, "no memory");
518 	memset(ypbindlist, 0, sizeof *ypbindlist);
519 	strncpy(ypbindlist->dom_domain, domain, sizeof ypbindlist->dom_domain-1);
520 	ypbindlist->dom_domain[sizeof (ypbindlist->dom_domain)-1] = '\0';
521 	ypbindlist->dom_vers = YPVERS;
522 	snprintf(ypbindlist->dom_servlist, sizeof ypbindlist->dom_servlist,
523 	    "%s/%s", SERVERSDIR, ypbindlist->dom_domain);
524 	ypbindlist->dom_servlistfp = fopen(ypbindlist->dom_servlist, "r");
525 	ypbindlist->dom_alive = 0;
526 	ypbindlist->dom_lockfd = -1;
527 	ypbindlist->dom_xid = unique_xid(ypbindlist);
528 	snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
529 	    ypbindlist->dom_domain, (int)ypbindlist->dom_vers);
530 	(void)unlink(path);
531 
532 	checkwork();
533 
534 	while (1) {
535 		extern int __svc_fdsetsize;
536 		extern void *__svc_fdset;
537 
538 		if (fdsrp == NULL || fdsrl != __svc_fdsetsize) {
539 			if (fdsrp)
540 				free(fdsrp);
541 
542 			fdsrl = __svc_fdsetsize;
543 			width = __svc_fdsetsize;
544 			if (rpcsock > __svc_fdsetsize)
545 				width = rpcsock;
546 			if (pingsock > __svc_fdsetsize)
547 				width = pingsock;
548 			fdsrp = (fd_set *)calloc(howmany(width+1, NFDBITS),
549 			    sizeof(fd_mask));
550 			if (fdsrp == NULL)
551 				errx(1, "no memory");
552 		}
553 
554 		bcopy(__svc_fdset, fdsrp, howmany(fdsrl+1, NFDBITS) *
555 		    sizeof(fd_mask));
556 		FD_SET(rpcsock, fdsrp);
557 		FD_SET(pingsock, fdsrp);
558 
559 		tv.tv_sec = 1;
560 		tv.tv_usec = 0;
561 
562 		switch (select(width+1, fdsrp, NULL, NULL, &tv)) {
563 		case 0:
564 			checkwork();
565 			break;
566 		case -1:
567 			perror("select\n");
568 			break;
569 		default:
570 			if (FD_ISSET(rpcsock, fdsrp))
571 				handle_replies();
572 			if (FD_ISSET(pingsock, fdsrp))
573 				handle_ping();
574 			svc_getreqset2(fdsrp, width);
575 			if (check)
576 				checkwork();
577 			break;
578 		}
579 
580 #ifdef DAEMON
581 		if (!evil && ypbindlist->dom_alive) {
582 			evil = 1;
583 			daemon(0, 0);
584 		}
585 #endif
586 	}
587 }
588 
589 /*
590  * State transition is done like this:
591  *
592  * STATE	EVENT		ACTION			NEWSTATE	TIMEOUT
593  * no binding	timeout		broadcast		no binding	5 sec
594  * no binding	answer		--			binding		60 sec
595  * binding	timeout		ping server		checking	5 sec
596  * checking	timeout		ping server + broadcast	checking	5 sec
597  * checking	answer		--			binding		60 sec
598  */
599 void
600 checkwork(void)
601 {
602 	struct _dom_binding *ypdb;
603 	time_t t;
604 
605 	time(&t);
606 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
607 		if (ypdb->dom_check_t < t) {
608 			if (ypdb->dom_alive == 1)
609 				ping(ypdb);
610 			else
611 				pings(ypdb);
612 			time(&t);
613 			ypdb->dom_check_t = t + 5;
614 		}
615 	}
616 	check = 0;
617 }
618 
619 int
620 ping(struct _dom_binding *ypdb)
621 {
622 	domainname dom = ypdb->dom_domain;
623 	struct rpc_msg msg;
624 	char buf[1400];
625 	enum clnt_stat st;
626 	int outlen;
627 	AUTH *rpcua;
628 	XDR xdr;
629 
630 	memset(&xdr, 0, sizeof xdr);
631 	memset(&msg, 0, sizeof msg);
632 
633 	rpcua = authunix_create_default();
634 	if (rpcua == (AUTH *)NULL) {
635 		/*printf("cannot get unix auth\n");*/
636 		return RPC_SYSTEMERROR;
637 	}
638 	msg.rm_direction = CALL;
639 	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
640 	msg.rm_call.cb_prog = YPPROG;
641 	msg.rm_call.cb_vers = YPVERS;
642 	msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK;
643 	msg.rm_call.cb_cred = rpcua->ah_cred;
644 	msg.rm_call.cb_verf = rpcua->ah_verf;
645 
646 	msg.rm_xid = ypdb->dom_xid;
647 	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
648 	if (!xdr_callmsg(&xdr, &msg)) {
649 		st = RPC_CANTENCODEARGS;
650 		AUTH_DESTROY(rpcua);
651 		return st;
652 	}
653 	if (!xdr_domainname(&xdr, &dom)) {
654 		st = RPC_CANTENCODEARGS;
655 		AUTH_DESTROY(rpcua);
656 		return st;
657 	}
658 	outlen = (int)xdr_getpos(&xdr);
659 	xdr_destroy(&xdr);
660 	if (outlen < 1) {
661 		st = RPC_CANTENCODEARGS;
662 		AUTH_DESTROY(rpcua);
663 		return st;
664 	}
665 	AUTH_DESTROY(rpcua);
666 
667 	ypdb->dom_alive = 2;
668 	if (sendto(pingsock, buf, outlen, 0,
669 	    (struct sockaddr *)&ypdb->dom_server_addr,
670 	    (socklen_t)sizeof ypdb->dom_server_addr) < 0)
671 		perror("sendto");
672 	return 0;
673 
674 }
675 
676 int
677 pings(struct _dom_binding *ypdb)
678 {
679 	domainname dom = ypdb->dom_domain;
680 	struct rpc_msg msg;
681 	struct sockaddr_in bindsin;
682 	char buf[1400];
683 	char path[PATH_MAX];
684 	enum clnt_stat st;
685 	int outlen;
686 	AUTH *rpcua;
687 	XDR xdr;
688 
689 	rmtca.xdr_args = xdr_domainname;
690 	rmtca.args_ptr = (char *)&dom;
691 
692 	memset(&xdr, 0, sizeof xdr);
693 	memset(&msg, 0, sizeof msg);
694 
695 	rpcua = authunix_create_default();
696 	if (rpcua == (AUTH *)NULL) {
697 		/*printf("cannot get unix auth\n");*/
698 		return RPC_SYSTEMERROR;
699 	}
700 	msg.rm_direction = CALL;
701 	msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
702 	msg.rm_call.cb_prog = PMAPPROG;
703 	msg.rm_call.cb_vers = PMAPVERS;
704 	msg.rm_call.cb_proc = PMAPPROC_CALLIT;
705 	msg.rm_call.cb_cred = rpcua->ah_cred;
706 	msg.rm_call.cb_verf = rpcua->ah_verf;
707 
708 	msg.rm_xid = ypdb->dom_xid;
709 	xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE);
710 	if (!xdr_callmsg(&xdr, &msg)) {
711 		st = RPC_CANTENCODEARGS;
712 		AUTH_DESTROY(rpcua);
713 		return st;
714 	}
715 	if (!xdr_rmtcall_args(&xdr, &rmtca)) {
716 		st = RPC_CANTENCODEARGS;
717 		AUTH_DESTROY(rpcua);
718 		return st;
719 	}
720 	outlen = (int)xdr_getpos(&xdr);
721 	xdr_destroy(&xdr);
722 	if (outlen < 1) {
723 		st = RPC_CANTENCODEARGS;
724 		AUTH_DESTROY(rpcua);
725 		return st;
726 	}
727 	AUTH_DESTROY(rpcua);
728 
729 	if (ypdb->dom_lockfd != -1) {
730 		close(ypdb->dom_lockfd);
731 		ypdb->dom_lockfd = -1;
732 		snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
733 		    ypdb->dom_domain, (int)ypdb->dom_vers);
734 		unlink(path);
735 	}
736 
737 	if (ypdb->dom_alive == 2) {
738 		/*
739 		 * This resolves the following situation:
740 		 * ypserver on other subnet was once bound,
741 		 * but rebooted and is now using a different port
742 		 */
743 		memset(&bindsin, 0, sizeof bindsin);
744 		bindsin.sin_family = AF_INET;
745 		bindsin.sin_len = sizeof(bindsin);
746 		bindsin.sin_port = htons(PMAPPORT);
747 		bindsin.sin_addr = ypdb->dom_server_addr.sin_addr;
748 		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
749 		    (socklen_t)sizeof bindsin) < 0)
750 			perror("sendto");
751 	}
752 	if (ypdb->dom_servlistfp)
753 		return direct(ypdb, buf, outlen);
754 	return broadcast(ypdb, buf, outlen);
755 }
756 
757 int
758 broadcast(struct _dom_binding *ypdb, char *buf, int outlen)
759 {
760 	struct ifaddrs *ifap, *ifa;
761 	struct sockaddr_in bindsin;
762 	struct in_addr in;
763 
764 	memset(&bindsin, 0, sizeof bindsin);
765 	bindsin.sin_family = AF_INET;
766 	bindsin.sin_len = sizeof(bindsin);
767 	bindsin.sin_port = htons(PMAPPORT);
768 
769 	if (getifaddrs(&ifap) != 0) {
770 		perror("getifaddrs");
771 		return -1;
772 	}
773 	for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
774 		if (ifa->ifa_addr->sa_family != AF_INET)
775 			continue;
776 		if ((ifa->ifa_flags & IFF_UP) == 0)
777 			continue;
778 
779 		switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) {
780 		case IFF_BROADCAST:
781 			if (!ifa->ifa_broadaddr)
782 				continue;
783 			if (ifa->ifa_broadaddr->sa_family != AF_INET)
784 				continue;
785 			in = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr;
786 			break;
787 		case IFF_LOOPBACK:
788 			in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
789 			break;
790 		default:
791 			continue;
792 		}
793 
794 		bindsin.sin_addr = in;
795 		if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin,
796 		    (socklen_t)bindsin.sin_len) < 0)
797 			perror("sendto");
798 	}
799 	freeifaddrs(ifap);
800 	return 0;
801 }
802 
803 int
804 direct(struct _dom_binding *ypdb, char *buf, int outlen)
805 {
806 	char line[1024], *p;
807 	struct hostent *hp;
808 	struct sockaddr_in bindsin;
809 	int i, c;
810 	struct stat fst, st;
811 
812 	if (fstat(fileno(ypdb->dom_servlistfp), &fst) != -1 &&
813 	    stat(ypdb->dom_servlist, &st) != -1 &&
814 	    (st.st_dev != fst.st_dev || st.st_ino != fst.st_ino)) {
815 		FILE *fp;
816 
817 		fp = fopen(ypdb->dom_servlist, "r");
818 		if (fp) {
819 			fclose(ypdb->dom_servlistfp);
820 			ypdb->dom_servlistfp = fp;
821 		}
822 	}
823 	(void) rewind(ypdb->dom_servlistfp);
824 
825 	memset(&bindsin, 0, sizeof bindsin);
826 	bindsin.sin_family = AF_INET;
827 	bindsin.sin_len = sizeof(bindsin);
828 	bindsin.sin_port = htons(PMAPPORT);
829 
830 	while (fgets(line, sizeof(line), ypdb->dom_servlistfp) != NULL) {
831 		/* skip lines that are too big */
832 		p = strchr(line, '\n');
833 		if (p == NULL) {
834 			while ((c = getc(ypdb->dom_servlistfp)) != '\n' && c != EOF)
835 				;
836 			continue;
837 		}
838 		*p = '\0';
839 		p = line;
840 		while (isspace((unsigned char)*p))
841 			p++;
842 		if (*p == '#')
843 			continue;
844 		hp = gethostbyname(p);
845 		if (!hp)
846 			continue;
847 		/* step through all addresses in case first is unavailable */
848 		for (i = 0; hp->h_addr_list[i]; i++) {
849 			memmove(&bindsin.sin_addr, hp->h_addr_list[0],
850 			    hp->h_length);
851 			if (sendto(rpcsock, buf, outlen, 0,
852 			    (struct sockaddr *)&bindsin,
853 			    (socklen_t)sizeof bindsin) < 0) {
854 				perror("sendto");
855 				continue;
856 			}
857 		}
858 	}
859 	return 0;
860 }
861 
862 enum clnt_stat
863 handle_replies(void)
864 {
865 	char buf[1400];
866 	int inlen;
867 	socklen_t fromlen;
868 	struct _dom_binding *ypdb;
869 	struct sockaddr_in raddr;
870 	struct rpc_msg msg;
871 	XDR xdr;
872 
873 recv_again:
874 	memset(&xdr, 0, sizeof(xdr));
875 	memset(&msg, 0, sizeof(msg));
876 	msg.acpted_rply.ar_verf = _null_auth;
877 	msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr;
878 	msg.acpted_rply.ar_results.proc = xdr_rmtcallres;
879 
880 try_again:
881 	fromlen = sizeof (struct sockaddr);
882 	inlen = recvfrom(rpcsock, buf, sizeof buf, 0,
883 	    (struct sockaddr *)&raddr, &fromlen);
884 	if (inlen < 0) {
885 		if (errno == EINTR)
886 			goto try_again;
887 		return RPC_CANTRECV;
888 	}
889 	if (inlen < sizeof(u_int32_t))
890 		goto recv_again;
891 
892 	/*
893 	 * see if reply transaction id matches sent id.
894 	 * If so, decode the results.
895 	 */
896 	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
897 	if (xdr_replymsg(&xdr, &msg)) {
898 		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
899 		    (msg.acpted_rply.ar_stat == SUCCESS)) {
900 			raddr.sin_port = htons((u_short)rmtcr_port);
901 			ypdb = xid2ypdb(msg.rm_xid);
902 			if (ypdb)
903 				rpc_received(ypdb->dom_domain, &raddr, 0);
904 		}
905 	}
906 	xdr.x_op = XDR_FREE;
907 	msg.acpted_rply.ar_results.proc = xdr_void;
908 	xdr_destroy(&xdr);
909 
910 	return RPC_SUCCESS;
911 }
912 
913 enum clnt_stat
914 handle_ping(void)
915 {
916 	char buf[1400];
917 	int inlen;
918 	socklen_t fromlen;
919 	struct _dom_binding *ypdb;
920 	struct sockaddr_in raddr;
921 	struct rpc_msg msg;
922 	XDR xdr;
923 	bool_t res;
924 
925 recv_again:
926 	memset(&xdr, 0, sizeof(xdr));
927 	memset(&msg, 0, sizeof(msg));
928 	msg.acpted_rply.ar_verf = _null_auth;
929 	msg.acpted_rply.ar_results.where = (caddr_t)&res;
930 	msg.acpted_rply.ar_results.proc = xdr_bool;
931 
932 try_again:
933 	fromlen = sizeof (struct sockaddr);
934 	inlen = recvfrom(pingsock, buf, sizeof buf, 0,
935 	    (struct sockaddr *)&raddr, &fromlen);
936 	if (inlen < 0) {
937 		if (errno == EINTR)
938 			goto try_again;
939 		return RPC_CANTRECV;
940 	}
941 	if (inlen < sizeof(u_int32_t))
942 		goto recv_again;
943 
944 	/*
945 	 * see if reply transaction id matches sent id.
946 	 * If so, decode the results.
947 	 */
948 	xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE);
949 	if (xdr_replymsg(&xdr, &msg)) {
950 		if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) &&
951 		    (msg.acpted_rply.ar_stat == SUCCESS)) {
952 			ypdb = xid2ypdb(msg.rm_xid);
953 			if (ypdb)
954 				rpc_received(ypdb->dom_domain, &raddr, 0);
955 		}
956 	}
957 	xdr.x_op = XDR_FREE;
958 	msg.acpted_rply.ar_results.proc = xdr_void;
959 	xdr_destroy(&xdr);
960 
961 	return RPC_SUCCESS;
962 }
963 
964 /*
965  * We prefer loopback connections.
966  */
967 void
968 rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
969 {
970 	struct _dom_binding *ypdb;
971 	struct iovec iov[2];
972 	struct ypbind_resp ybr;
973 	char path[PATH_MAX];
974 	int fd;
975 
976 	if (strchr(dom, '/'))
977 		return;
978 
979 #ifdef DEBUG
980 	printf("returned from %s about %s\n", inet_ntoa(raddrp->sin_addr), dom);
981 #endif
982 
983 	if (dom == NULL)
984 		return;
985 
986 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
987 		if (!strcmp(ypdb->dom_domain, dom))
988 			break;
989 
990 	if (ypdb == NULL) {
991 		if (force == 0)
992 			return;
993 		ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
994 		if (ypdb == NULL)
995 			return;
996 		memset(ypdb, 0, sizeof *ypdb);
997 		strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain-1);
998 		ypdb->dom_domain[sizeof (ypdb->dom_domain)-1] = '\0';
999 		ypdb->dom_lockfd = -1;
1000 		ypdb->dom_xid = unique_xid(ypdb);
1001 		ypdb->dom_pnext = ypbindlist;
1002 		ypbindlist = ypdb;
1003 	}
1004 
1005 	/* we do not support sunos 3.0 insecure servers */
1006 	if (insecure == 0 && ntohs(raddrp->sin_port) >= IPPORT_RESERVED)
1007 		return;
1008 
1009 	/* soft update, alive */
1010 	if (ypdb->dom_alive == 1 && force == 0) {
1011 		if (!memcmp(&ypdb->dom_server_addr, raddrp,
1012 		    sizeof ypdb->dom_server_addr)) {
1013 			ypdb->dom_alive = 1;
1014 			/* recheck binding in 60 sec */
1015 			ypdb->dom_check_t = time(NULL) + 60;
1016 		}
1017 		if (raddrp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) {
1018 			/*
1019 			 * we are alive and already have a binding, but
1020 			 * after a broadcast we prefer the localhost
1021 			 */
1022 			memcpy(&ypdb->dom_server_addr, raddrp,
1023 			    sizeof ypdb->dom_server_addr);
1024 		}
1025 		return;
1026 	}
1027 
1028 	memcpy(&ypdb->dom_server_addr, raddrp, sizeof ypdb->dom_server_addr);
1029 	/* recheck binding in 60 seconds */
1030 	ypdb->dom_check_t = time(NULL) + 60;
1031 	ypdb->dom_vers = YPVERS;
1032 	ypdb->dom_alive = 1;
1033 
1034 	if (ypdb->dom_lockfd != -1)
1035 		close(ypdb->dom_lockfd);
1036 
1037 	snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR,
1038 	    ypdb->dom_domain, (int)ypdb->dom_vers);
1039 #ifdef O_SHLOCK
1040 	if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
1041 		(void)mkdir(BINDINGDIR, 0755);
1042 		if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC,
1043 		    0644)) == -1)
1044 			return;
1045 	}
1046 #else
1047 	if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
1048 		(void)mkdir(BINDINGDIR, 0755);
1049 		if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
1050 			return;
1051 	}
1052 	flock(fd, LOCK_SH);
1053 #endif
1054 
1055 	if (fchmod(fd, 0644) == -1)
1056 		err(1, "fchmod");
1057 
1058 	/*
1059 	 * ok, if BINDINGDIR exists, and we can create the binding file,
1060 	 * then write to it..
1061 	 */
1062 	ypdb->dom_lockfd = fd;
1063 
1064 	iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
1065 	iov[0].iov_len = sizeof udptransp->xp_port;
1066 	iov[1].iov_base = (caddr_t)&ybr;
1067 	iov[1].iov_len = sizeof ybr;
1068 
1069 	memset(&ybr, 0, sizeof ybr);
1070 	ybr.ypbind_status = YPBIND_SUCC_VAL;
1071 	memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr,
1072 	    &raddrp->sin_addr,
1073 	    sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr));
1074 	memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port,
1075 	    &raddrp->sin_port,
1076 	    sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port));
1077 
1078 	if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
1079 		perror("write");
1080 		close(ypdb->dom_lockfd);
1081 		unlink(path);
1082 		ypdb->dom_lockfd = -1;
1083 		return;
1084 	}
1085 }
1086 
1087 struct _dom_binding *
1088 xid2ypdb(u_int32_t xid)
1089 {
1090 	struct _dom_binding *ypdb;
1091 
1092 	for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
1093 		if (ypdb->dom_xid == xid)
1094 			break;
1095 	return (ypdb);
1096 }
1097 
1098 u_int32_t
1099 unique_xid(struct _dom_binding *ypdb)
1100 {
1101 	u_int32_t xid;
1102 
1103 	xid = arc4random();
1104 	while (xid2ypdb(xid) != NULL)
1105 		xid++;
1106 
1107 	return (xid);
1108 }
1109