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