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