1 /* $OpenBSD: ypbind.c,v 1.68 2016/07/08 19:32:26 millert 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 #include <poll.h> 57 58 #define SERVERSDIR "/etc/yp" 59 #define BINDINGDIR "/var/yp/binding" 60 #define YPBINDLOCK "/var/run/ypbind.lock" 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, lockfd, 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 snprintf(path, sizeof(path), "%s/%s", BINDINGDIR, 373 dent->d_name); 374 (void) unlink(path); 375 } 376 closedir(dirp); 377 } else { 378 printf("Enabling yp client subsystem.\n"); 379 printf("To disable: kill ypbind and remove %s\n", 380 BINDINGDIR); 381 (void)mkdir(BINDINGDIR, 0755); 382 } 383 384 #ifdef O_SHLOCK 385 if ((lockfd = open(YPBINDLOCK, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 386 0644)) == -1) { 387 fprintf(stderr, "ypbind: cannot create %s\n", YPBINDLOCK); 388 exit(1); 389 } 390 #else 391 if ((lockfd = open(YPBINDLOCK, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) { 392 fprintf(stderr, "ypbind: cannot create %s.\n", YPBINDLOCK); 393 exit(1); 394 } 395 flock(lockfd, LOCK_SH); 396 #endif 397 398 if (fchmod(lockfd, 0644) == -1) 399 err(1, "fchmod"); 400 401 (void)pmap_unset(YPBINDPROG, YPBINDVERS); 402 403 udptransp = svcudp_create(RPC_ANYSOCK); 404 if (udptransp == NULL) { 405 fprintf(stderr, "cannot create udp service.\n"); 406 exit(1); 407 } 408 if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, 409 IPPROTO_UDP)) { 410 fprintf(stderr, 411 "unable to register (YPBINDPROG, YPBINDVERS, udp).\n"); 412 exit(1); 413 } 414 415 tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0); 416 if (tcptransp == NULL) { 417 fprintf(stderr, "cannot create tcp service.\n"); 418 exit(1); 419 } 420 if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2, 421 IPPROTO_TCP)) { 422 fprintf(stderr, 423 "unable to register (YPBINDPROG, YPBINDVERS, tcp).\n"); 424 exit(1); 425 } 426 427 if (ypsetmode == YPSET_LOCAL) { 428 /* build UDP local port */ 429 if ((lsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) { 430 syslog(LOG_ERR, "cannot create local udp socket: %m"); 431 exit(1); 432 } 433 (void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one, 434 (socklen_t)sizeof one); 435 len = sizeof(sin); 436 if (getsockname(udptransp->xp_sock, (struct sockaddr *)&sin, 437 &len) == -1) { 438 syslog(LOG_ERR, "cannot getsockname local udp: %m"); 439 exit(1); 440 } 441 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 442 sin.sin_port = htons(udptransp->xp_port); 443 if (bind(lsock, (struct sockaddr *)&sin, len) != 0) { 444 syslog(LOG_ERR, "cannot bind local udp: %m"); 445 exit(1); 446 } 447 if ((ludptransp = svcudp_create(lsock)) == NULL) { 448 fprintf(stderr, "cannot create udp service.\n"); 449 exit(1); 450 } 451 452 /* build TCP local port */ 453 if ((lsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { 454 syslog(LOG_ERR, "cannot create udp socket: %m"); 455 exit(1); 456 } 457 (void)setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, &one, 458 (socklen_t)sizeof one); 459 len = sizeof(sin); 460 if (getsockname(tcptransp->xp_sock, (struct sockaddr *)&sin, 461 &len) == -1) { 462 syslog(LOG_ERR, "cannot getsockname udp: %m"); 463 exit(1); 464 } 465 sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 466 sin.sin_port = htons(tcptransp->xp_port); 467 if (bind(lsock, (struct sockaddr *)&sin, len) == -1) { 468 syslog(LOG_ERR, "cannot bind local tcp: %m"); 469 exit(1); 470 } 471 if ((ltcptransp = svctcp_create(lsock, 0, 0)) == NULL) { 472 fprintf(stderr, "cannot create tcp service.\n"); 473 exit(1); 474 } 475 } 476 477 if ((rpcsock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) < 0) { 478 perror("socket"); 479 return -1; 480 } 481 memset(&sin, 0, sizeof sin); 482 sin.sin_family = AF_INET; 483 sin.sin_addr.s_addr = htonl(INADDR_ANY); 484 sin.sin_port = 0; 485 bindresvport(rpcsock, &sin); 486 487 if ((pingsock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) < 0) { 488 perror("socket"); 489 return -1; 490 } 491 memset(&sin, 0, sizeof sin); 492 sin.sin_family = AF_INET; 493 sin.sin_addr.s_addr = htonl(INADDR_ANY); 494 sin.sin_port = 0; 495 bindresvport(pingsock, &sin); 496 497 setsockopt(rpcsock, SOL_SOCKET, SO_BROADCAST, &one, 498 (socklen_t)sizeof(one)); 499 rmtca.prog = YPPROG; 500 rmtca.vers = YPVERS; 501 rmtca.proc = YPPROC_DOMAIN_NONACK; 502 rmtca.xdr_args = NULL; /* set at call time */ 503 rmtca.args_ptr = NULL; /* set at call time */ 504 rmtcr.port_ptr = &rmtcr_port; 505 rmtcr.xdr_results = xdr_bool; 506 rmtcr.results_ptr = (caddr_t)&rmtcr_outval; 507 508 if (strchr(domain, '/')) 509 errx(1, "bad domainname %s", domain); 510 511 /* build initial domain binding, make it "unsuccessful" */ 512 ypbindlist = malloc(sizeof *ypbindlist); 513 if (ypbindlist == NULL) 514 errx(1, "no memory"); 515 memset(ypbindlist, 0, sizeof *ypbindlist); 516 strncpy(ypbindlist->dom_domain, domain, sizeof ypbindlist->dom_domain-1); 517 ypbindlist->dom_domain[sizeof (ypbindlist->dom_domain)-1] = '\0'; 518 ypbindlist->dom_vers = YPVERS; 519 snprintf(ypbindlist->dom_servlist, sizeof ypbindlist->dom_servlist, 520 "%s/%s", SERVERSDIR, ypbindlist->dom_domain); 521 ypbindlist->dom_servlistfp = fopen(ypbindlist->dom_servlist, "r"); 522 ypbindlist->dom_alive = 0; 523 ypbindlist->dom_lockfd = -1; 524 ypbindlist->dom_xid = unique_xid(ypbindlist); 525 snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR, 526 ypbindlist->dom_domain, (int)ypbindlist->dom_vers); 527 (void)unlink(path); 528 529 checkwork(); 530 531 while (1) { 532 if (pfd == NULL || width != svc_max_pollfd + 2) { 533 width = svc_max_pollfd + 2; 534 pfd = reallocarray(pfd, width, sizeof *pfd); 535 if (pfd == NULL) 536 err(1, NULL); 537 } 538 539 pfd[0].fd = rpcsock; 540 pfd[0].events = POLLIN; 541 pfd[1].fd = pingsock; 542 pfd[1].events = POLLIN; 543 memcpy(pfd + 2, svc_pollfd, sizeof(*pfd) * svc_max_pollfd); 544 545 nready = poll(pfd, width, 1000); 546 switch (nready) { 547 case 0: 548 checkwork(); 549 break; 550 case -1: 551 if (errno != EINTR) 552 perror("poll"); 553 break; 554 default: 555 /* No need to check for POLLHUP on UDP sockets. */ 556 if (pfd[0].revents & POLLIN) { 557 handle_replies(); 558 nready--; 559 } 560 if (pfd[1].revents & POLLIN) { 561 handle_ping(); 562 nready--; 563 } 564 svc_getreq_poll(pfd + 2, nready); 565 if (check) 566 checkwork(); 567 break; 568 } 569 570 #ifdef DAEMON 571 if (!evil && ypbindlist->dom_alive) { 572 evil = 1; 573 daemon(0, 0); 574 } 575 #endif 576 } 577 } 578 579 /* 580 * State transition is done like this: 581 * 582 * STATE EVENT ACTION NEWSTATE TIMEOUT 583 * no binding timeout broadcast no binding 5 sec 584 * no binding answer -- binding 60 sec 585 * binding timeout ping server checking 5 sec 586 * checking timeout ping server + broadcast checking 5 sec 587 * checking answer -- binding 60 sec 588 */ 589 void 590 checkwork(void) 591 { 592 struct _dom_binding *ypdb; 593 time_t t; 594 595 time(&t); 596 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) { 597 if (ypdb->dom_check_t < t) { 598 if (ypdb->dom_alive == 1) 599 ping(ypdb); 600 else 601 pings(ypdb); 602 time(&t); 603 ypdb->dom_check_t = t + 5; 604 } 605 } 606 check = 0; 607 } 608 609 int 610 ping(struct _dom_binding *ypdb) 611 { 612 domainname dom = ypdb->dom_domain; 613 struct rpc_msg msg; 614 char buf[1400]; 615 enum clnt_stat st; 616 int outlen; 617 AUTH *rpcua; 618 XDR xdr; 619 620 memset(&xdr, 0, sizeof xdr); 621 memset(&msg, 0, sizeof msg); 622 623 rpcua = authunix_create_default(); 624 if (rpcua == (AUTH *)NULL) { 625 /*printf("cannot get unix auth\n");*/ 626 return RPC_SYSTEMERROR; 627 } 628 msg.rm_direction = CALL; 629 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 630 msg.rm_call.cb_prog = YPPROG; 631 msg.rm_call.cb_vers = YPVERS; 632 msg.rm_call.cb_proc = YPPROC_DOMAIN_NONACK; 633 msg.rm_call.cb_cred = rpcua->ah_cred; 634 msg.rm_call.cb_verf = rpcua->ah_verf; 635 636 msg.rm_xid = ypdb->dom_xid; 637 xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE); 638 if (!xdr_callmsg(&xdr, &msg)) { 639 st = RPC_CANTENCODEARGS; 640 AUTH_DESTROY(rpcua); 641 return st; 642 } 643 if (!xdr_domainname(&xdr, &dom)) { 644 st = RPC_CANTENCODEARGS; 645 AUTH_DESTROY(rpcua); 646 return st; 647 } 648 outlen = (int)xdr_getpos(&xdr); 649 xdr_destroy(&xdr); 650 if (outlen < 1) { 651 st = RPC_CANTENCODEARGS; 652 AUTH_DESTROY(rpcua); 653 return st; 654 } 655 AUTH_DESTROY(rpcua); 656 657 ypdb->dom_alive = 2; 658 if (sendto(pingsock, buf, outlen, 0, 659 (struct sockaddr *)&ypdb->dom_server_addr, 660 (socklen_t)sizeof ypdb->dom_server_addr) < 0) 661 perror("sendto"); 662 return 0; 663 664 } 665 666 int 667 pings(struct _dom_binding *ypdb) 668 { 669 domainname dom = ypdb->dom_domain; 670 struct rpc_msg msg; 671 struct sockaddr_in bindsin; 672 char buf[1400]; 673 char path[PATH_MAX]; 674 enum clnt_stat st; 675 int outlen; 676 AUTH *rpcua; 677 XDR xdr; 678 679 rmtca.xdr_args = xdr_domainname; 680 rmtca.args_ptr = (char *)&dom; 681 682 memset(&xdr, 0, sizeof xdr); 683 memset(&msg, 0, sizeof msg); 684 685 rpcua = authunix_create_default(); 686 if (rpcua == (AUTH *)NULL) { 687 /*printf("cannot get unix auth\n");*/ 688 return RPC_SYSTEMERROR; 689 } 690 msg.rm_direction = CALL; 691 msg.rm_call.cb_rpcvers = RPC_MSG_VERSION; 692 msg.rm_call.cb_prog = PMAPPROG; 693 msg.rm_call.cb_vers = PMAPVERS; 694 msg.rm_call.cb_proc = PMAPPROC_CALLIT; 695 msg.rm_call.cb_cred = rpcua->ah_cred; 696 msg.rm_call.cb_verf = rpcua->ah_verf; 697 698 msg.rm_xid = ypdb->dom_xid; 699 xdrmem_create(&xdr, buf, sizeof buf, XDR_ENCODE); 700 if (!xdr_callmsg(&xdr, &msg)) { 701 st = RPC_CANTENCODEARGS; 702 AUTH_DESTROY(rpcua); 703 return st; 704 } 705 if (!xdr_rmtcall_args(&xdr, &rmtca)) { 706 st = RPC_CANTENCODEARGS; 707 AUTH_DESTROY(rpcua); 708 return st; 709 } 710 outlen = (int)xdr_getpos(&xdr); 711 xdr_destroy(&xdr); 712 if (outlen < 1) { 713 st = RPC_CANTENCODEARGS; 714 AUTH_DESTROY(rpcua); 715 return st; 716 } 717 AUTH_DESTROY(rpcua); 718 719 if (ypdb->dom_lockfd != -1) { 720 close(ypdb->dom_lockfd); 721 ypdb->dom_lockfd = -1; 722 snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR, 723 ypdb->dom_domain, (int)ypdb->dom_vers); 724 unlink(path); 725 } 726 727 if (ypdb->dom_alive == 2) { 728 /* 729 * This resolves the following situation: 730 * ypserver on other subnet was once bound, 731 * but rebooted and is now using a different port 732 */ 733 memset(&bindsin, 0, sizeof bindsin); 734 bindsin.sin_family = AF_INET; 735 bindsin.sin_len = sizeof(bindsin); 736 bindsin.sin_port = htons(PMAPPORT); 737 bindsin.sin_addr = ypdb->dom_server_addr.sin_addr; 738 if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin, 739 (socklen_t)sizeof bindsin) < 0) 740 perror("sendto"); 741 } 742 if (ypdb->dom_servlistfp) 743 return direct(ypdb, buf, outlen); 744 return broadcast(ypdb, buf, outlen); 745 } 746 747 int 748 broadcast(struct _dom_binding *ypdb, char *buf, int outlen) 749 { 750 struct ifaddrs *ifap, *ifa; 751 struct sockaddr_in bindsin; 752 struct in_addr in; 753 754 memset(&bindsin, 0, sizeof bindsin); 755 bindsin.sin_family = AF_INET; 756 bindsin.sin_len = sizeof(bindsin); 757 bindsin.sin_port = htons(PMAPPORT); 758 759 if (getifaddrs(&ifap) != 0) { 760 perror("getifaddrs"); 761 return -1; 762 } 763 for (ifa = ifap; ifa; ifa = ifa->ifa_next) { 764 if (ifa->ifa_addr->sa_family != AF_INET) 765 continue; 766 if ((ifa->ifa_flags & IFF_UP) == 0) 767 continue; 768 769 switch (ifa->ifa_flags & (IFF_LOOPBACK | IFF_BROADCAST)) { 770 case IFF_BROADCAST: 771 if (!ifa->ifa_broadaddr) 772 continue; 773 if (ifa->ifa_broadaddr->sa_family != AF_INET) 774 continue; 775 in = ((struct sockaddr_in *)ifa->ifa_broadaddr)->sin_addr; 776 break; 777 case IFF_LOOPBACK: 778 in = ((struct sockaddr_in *)ifa->ifa_addr)->sin_addr; 779 break; 780 default: 781 continue; 782 } 783 784 bindsin.sin_addr = in; 785 if (sendto(rpcsock, buf, outlen, 0, (struct sockaddr *)&bindsin, 786 (socklen_t)bindsin.sin_len) < 0) 787 perror("sendto"); 788 } 789 freeifaddrs(ifap); 790 return 0; 791 } 792 793 int 794 direct(struct _dom_binding *ypdb, char *buf, int outlen) 795 { 796 char line[1024], *p; 797 struct hostent *hp; 798 struct sockaddr_in bindsin; 799 int i, c; 800 struct stat fst, st; 801 802 if (fstat(fileno(ypdb->dom_servlistfp), &fst) != -1 && 803 stat(ypdb->dom_servlist, &st) != -1 && 804 (st.st_dev != fst.st_dev || st.st_ino != fst.st_ino)) { 805 FILE *fp; 806 807 fp = fopen(ypdb->dom_servlist, "r"); 808 if (fp) { 809 fclose(ypdb->dom_servlistfp); 810 ypdb->dom_servlistfp = fp; 811 } 812 } 813 (void) rewind(ypdb->dom_servlistfp); 814 815 memset(&bindsin, 0, sizeof bindsin); 816 bindsin.sin_family = AF_INET; 817 bindsin.sin_len = sizeof(bindsin); 818 bindsin.sin_port = htons(PMAPPORT); 819 820 while (fgets(line, sizeof(line), ypdb->dom_servlistfp) != NULL) { 821 /* skip lines that are too big */ 822 p = strchr(line, '\n'); 823 if (p == NULL) { 824 while ((c = getc(ypdb->dom_servlistfp)) != '\n' && c != EOF) 825 ; 826 continue; 827 } 828 *p = '\0'; 829 p = line; 830 while (isspace((unsigned char)*p)) 831 p++; 832 if (*p == '#') 833 continue; 834 hp = gethostbyname(p); 835 if (!hp) 836 continue; 837 /* step through all addresses in case first is unavailable */ 838 for (i = 0; hp->h_addr_list[i]; i++) { 839 memmove(&bindsin.sin_addr, hp->h_addr_list[0], 840 hp->h_length); 841 if (sendto(rpcsock, buf, outlen, 0, 842 (struct sockaddr *)&bindsin, 843 (socklen_t)sizeof bindsin) < 0) { 844 perror("sendto"); 845 continue; 846 } 847 } 848 } 849 return 0; 850 } 851 852 enum clnt_stat 853 handle_replies(void) 854 { 855 char buf[1400]; 856 int inlen; 857 socklen_t fromlen; 858 struct _dom_binding *ypdb; 859 struct sockaddr_in raddr; 860 struct rpc_msg msg; 861 XDR xdr; 862 863 recv_again: 864 memset(&xdr, 0, sizeof(xdr)); 865 memset(&msg, 0, sizeof(msg)); 866 msg.acpted_rply.ar_verf = _null_auth; 867 msg.acpted_rply.ar_results.where = (caddr_t)&rmtcr; 868 msg.acpted_rply.ar_results.proc = xdr_rmtcallres; 869 870 try_again: 871 fromlen = sizeof (struct sockaddr); 872 inlen = recvfrom(rpcsock, buf, sizeof buf, 0, 873 (struct sockaddr *)&raddr, &fromlen); 874 if (inlen < 0) { 875 if (errno == EINTR) 876 goto try_again; 877 return RPC_CANTRECV; 878 } 879 if (inlen < sizeof(u_int32_t)) 880 goto recv_again; 881 882 /* 883 * see if reply transaction id matches sent id. 884 * If so, decode the results. 885 */ 886 xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE); 887 if (xdr_replymsg(&xdr, &msg)) { 888 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 889 (msg.acpted_rply.ar_stat == SUCCESS)) { 890 raddr.sin_port = htons((u_short)rmtcr_port); 891 ypdb = xid2ypdb(msg.rm_xid); 892 if (ypdb) 893 rpc_received(ypdb->dom_domain, &raddr, 0); 894 } 895 } 896 xdr.x_op = XDR_FREE; 897 msg.acpted_rply.ar_results.proc = xdr_void; 898 xdr_destroy(&xdr); 899 900 return RPC_SUCCESS; 901 } 902 903 enum clnt_stat 904 handle_ping(void) 905 { 906 char buf[1400]; 907 int inlen; 908 socklen_t fromlen; 909 struct _dom_binding *ypdb; 910 struct sockaddr_in raddr; 911 struct rpc_msg msg; 912 XDR xdr; 913 bool_t res; 914 915 recv_again: 916 memset(&xdr, 0, sizeof(xdr)); 917 memset(&msg, 0, sizeof(msg)); 918 msg.acpted_rply.ar_verf = _null_auth; 919 msg.acpted_rply.ar_results.where = (caddr_t)&res; 920 msg.acpted_rply.ar_results.proc = xdr_bool; 921 922 try_again: 923 fromlen = sizeof (struct sockaddr); 924 inlen = recvfrom(pingsock, buf, sizeof buf, 0, 925 (struct sockaddr *)&raddr, &fromlen); 926 if (inlen < 0) { 927 if (errno == EINTR) 928 goto try_again; 929 return RPC_CANTRECV; 930 } 931 if (inlen < sizeof(u_int32_t)) 932 goto recv_again; 933 934 /* 935 * see if reply transaction id matches sent id. 936 * If so, decode the results. 937 */ 938 xdrmem_create(&xdr, buf, (u_int)inlen, XDR_DECODE); 939 if (xdr_replymsg(&xdr, &msg)) { 940 if ((msg.rm_reply.rp_stat == MSG_ACCEPTED) && 941 (msg.acpted_rply.ar_stat == SUCCESS)) { 942 ypdb = xid2ypdb(msg.rm_xid); 943 if (ypdb) 944 rpc_received(ypdb->dom_domain, &raddr, 0); 945 } 946 } 947 xdr.x_op = XDR_FREE; 948 msg.acpted_rply.ar_results.proc = xdr_void; 949 xdr_destroy(&xdr); 950 951 return RPC_SUCCESS; 952 } 953 954 /* 955 * We prefer loopback connections. 956 */ 957 void 958 rpc_received(char *dom, struct sockaddr_in *raddrp, int force) 959 { 960 struct _dom_binding *ypdb; 961 struct iovec iov[2]; 962 struct ypbind_resp ybr; 963 char path[PATH_MAX]; 964 int fd; 965 966 if (strchr(dom, '/')) 967 return; 968 969 #ifdef DEBUG 970 printf("returned from %s about %s\n", inet_ntoa(raddrp->sin_addr), dom); 971 #endif 972 973 if (dom == NULL) 974 return; 975 976 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) 977 if (!strcmp(ypdb->dom_domain, dom)) 978 break; 979 980 if (ypdb == NULL) { 981 if (force == 0) 982 return; 983 ypdb = malloc(sizeof *ypdb); 984 if (ypdb == NULL) 985 return; 986 memset(ypdb, 0, sizeof *ypdb); 987 strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain-1); 988 ypdb->dom_domain[sizeof (ypdb->dom_domain)-1] = '\0'; 989 ypdb->dom_lockfd = -1; 990 ypdb->dom_xid = unique_xid(ypdb); 991 ypdb->dom_pnext = ypbindlist; 992 ypbindlist = ypdb; 993 } 994 995 /* we do not support sunos 3.0 insecure servers */ 996 if (insecure == 0 && ntohs(raddrp->sin_port) >= IPPORT_RESERVED) 997 return; 998 999 /* soft update, alive */ 1000 if (ypdb->dom_alive == 1 && force == 0) { 1001 if (!memcmp(&ypdb->dom_server_addr, raddrp, 1002 sizeof ypdb->dom_server_addr)) { 1003 ypdb->dom_alive = 1; 1004 /* recheck binding in 60 sec */ 1005 ypdb->dom_check_t = time(NULL) + 60; 1006 } 1007 if (raddrp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) { 1008 /* 1009 * we are alive and already have a binding, but 1010 * after a broadcast we prefer the localhost 1011 */ 1012 memcpy(&ypdb->dom_server_addr, raddrp, 1013 sizeof ypdb->dom_server_addr); 1014 } 1015 return; 1016 } 1017 1018 memcpy(&ypdb->dom_server_addr, raddrp, sizeof ypdb->dom_server_addr); 1019 /* recheck binding in 60 seconds */ 1020 ypdb->dom_check_t = time(NULL) + 60; 1021 ypdb->dom_vers = YPVERS; 1022 ypdb->dom_alive = 1; 1023 1024 if (ypdb->dom_lockfd != -1) 1025 close(ypdb->dom_lockfd); 1026 1027 snprintf(path, sizeof path, "%s/%s.%d", BINDINGDIR, 1028 ypdb->dom_domain, (int)ypdb->dom_vers); 1029 #ifdef O_SHLOCK 1030 if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) { 1031 (void)mkdir(BINDINGDIR, 0755); 1032 if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 1033 0644)) == -1) 1034 return; 1035 } 1036 #else 1037 if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) { 1038 (void)mkdir(BINDINGDIR, 0755); 1039 if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) 1040 return; 1041 } 1042 flock(fd, LOCK_SH); 1043 #endif 1044 1045 if (fchmod(fd, 0644) == -1) 1046 err(1, "fchmod"); 1047 1048 /* 1049 * ok, if BINDINGDIR exists, and we can create the binding file, 1050 * then write to it.. 1051 */ 1052 ypdb->dom_lockfd = fd; 1053 1054 iov[0].iov_base = (caddr_t)&(udptransp->xp_port); 1055 iov[0].iov_len = sizeof udptransp->xp_port; 1056 iov[1].iov_base = (caddr_t)&ybr; 1057 iov[1].iov_len = sizeof ybr; 1058 1059 memset(&ybr, 0, sizeof ybr); 1060 ybr.ypbind_status = YPBIND_SUCC_VAL; 1061 memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr, 1062 &raddrp->sin_addr, 1063 sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr)); 1064 memmove(&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port, 1065 &raddrp->sin_port, 1066 sizeof(ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port)); 1067 1068 if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) { 1069 perror("write"); 1070 close(ypdb->dom_lockfd); 1071 unlink(path); 1072 ypdb->dom_lockfd = -1; 1073 return; 1074 } 1075 } 1076 1077 struct _dom_binding * 1078 xid2ypdb(u_int32_t xid) 1079 { 1080 struct _dom_binding *ypdb; 1081 1082 for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) 1083 if (ypdb->dom_xid == xid) 1084 break; 1085 return (ypdb); 1086 } 1087 1088 u_int32_t 1089 unique_xid(struct _dom_binding *ypdb) 1090 { 1091 u_int32_t xid; 1092 1093 xid = arc4random(); 1094 while (xid2ypdb(xid) != NULL) 1095 xid++; 1096 1097 return (xid); 1098 } 1099