1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 31 * under license from the Regents of the University of 32 * California. 33 */ 34 35 #include <stdio.h> 36 #include <stdio_ext.h> 37 #include <stdlib.h> 38 #include <signal.h> 39 #include <rpc/rpc.h> 40 #include <memory.h> 41 #include <netconfig.h> 42 #include <syslog.h> 43 #include <rpcsvc/yp_prot.h> 44 #include "yp_b.h" 45 #include <sys/resource.h> 46 #include <sys/stropts.h> 47 #include <unistd.h> 48 #include <rpc/nettype.h> 49 #include <string.h> 50 #include <tiuser.h> 51 52 53 #ifdef DEBUG 54 #define RPC_SVC_FG 55 #endif 56 57 #define _RPCSVC_CLOSEDOWN 120 58 #define YPBIND_ERR_ERR 1 /* Internal error */ 59 #define YPBIND_ERR_NOSERV 2 /* No bound server for passed domain */ 60 #define YPBIND_ERR_RESC 3 /* System resource allocation failure */ 61 #define YPBIND_ERR_NODOMAIN 4 /* Domain doesn't exist */ 62 63 static int _rpcpmstart; /* Started by a port monitor ? */ 64 static int _rpcsvcdirty; /* Still serving ? */ 65 int setok = YPSETNONE; /* who is allowed to ypset */ 66 int broadcast = 0; 67 int cache_okay = 0; /* if set, then bindings are cached in files */ 68 69 extern int sigcld_event; 70 extern void broadcast_proc_exit(); 71 extern int __rpc_negotiate_uid(); 72 extern bool_t __rpcbind_is_up(); 73 extern void ypbind_init_default(); 74 static void set_signal_handlers(); 75 static void clear_bindings(); 76 static void unregister(int); 77 static int void_close(void *, int); 78 void closedown(); 79 void ypbindprog_3(); 80 void ypbindprog_2(); 81 void msgout(); 82 extern void cache_transport(); 83 extern void clean_cache(); 84 85 int 86 main(argc, argv) 87 int argc; 88 char **argv; 89 { 90 pid_t pid; 91 int pfd[2]; 92 char domain[256], servers[300]; 93 char **Argv = argv; 94 struct netconfig *nconf; 95 void *nc_handle; 96 int loopback_found = 0, udp_found = 0; 97 int pipe_closed = 0; 98 struct rlimit rl; 99 int connmaxrec = RPC_MAXDATASIZE; 100 uint32_t inet_tpts = 0, inet6_tpts = 0; 101 uint32_t inet_desired_tpts = 0, inet6_desired_tpts = 0; 102 bool_t exclbind = TRUE; 103 104 if (geteuid() != 0) { 105 (void) fprintf(stderr, "must be root to run %s\n", argv[0]); 106 exit(1); 107 } 108 109 argc--; 110 argv++; 111 112 while (argc > 0) { 113 if (strcmp(*argv, "-ypset") == 0) { 114 setok = YPSETALL; 115 } else if (strcmp(*argv, "-ypsetme") == 0) { 116 setok = YPSETLOCAL; 117 } else if (strcmp(*argv, "-broadcast") == 0) { 118 broadcast = TRUE; 119 } else { 120 fprintf(stderr, 121 "usage: ypbind [-broadcast] [-ypset] [-ypsetme]\n"); 122 exit(1); 123 } 124 argc--, 125 argv++; 126 } 127 128 if (setok == YPSETALL) { 129 fprintf(stderr, 130 "ypbind -ypset: allowing ypset! (this is REALLY insecure)\n"); 131 } 132 if (setok == YPSETLOCAL) { 133 fprintf(stderr, 134 "ypbind -ypsetme: allowing local ypset! (this is insecure)\n"); 135 } 136 if (broadcast == TRUE) { 137 fprintf(stderr, 138 "ypbind -broadcast: allowing broadcast! \ 139 (insecure and transport dependent)\n"); 140 } 141 142 if (getdomainname(domain, sizeof (domain)) == 0) { 143 sprintf(servers, "%s/%s/ypservers", BINDING, domain); 144 if (!broadcast && access(servers, R_OK) != 0) { 145 (void) fprintf(stderr, 146 "%s: no info on servers - run ypinit -c\n", Argv[0]); 147 exit(1); 148 } 149 } else { 150 (void) fprintf(stderr, "%s: domainname not set - exiting\n", 151 Argv[0]); 152 exit(1); 153 } 154 155 getrlimit(RLIMIT_NOFILE, &rl); 156 rl.rlim_cur = rl.rlim_max; 157 setrlimit(RLIMIT_NOFILE, &rl); 158 159 (void) enable_extended_FILE_stdio(-1, -1); 160 161 openlog("ypbind", LOG_PID, LOG_DAEMON); 162 163 /* 164 * If stdin looks like a TLI endpoint, we assume 165 * that we were started by a port monitor. If 166 * t_getstate fails with TBADF, this is not a 167 * TLI endpoint. 168 */ 169 _rpcpmstart = (t_getstate(0) != -1 || t_errno != TBADF); 170 171 if (!__rpcbind_is_up()) { 172 msgout("terminating: rpcbind is not running"); 173 exit(1); 174 } 175 176 if (_rpcpmstart) { 177 /* 178 * We were invoked by ypbind with the request on stdin. 179 * 180 * XXX - This is not the normal way ypbind is used 181 * and has never been tested. 182 */ 183 char *netid; 184 struct netconfig *nconf = NULL; 185 SVCXPRT *transp; 186 int pmclose; 187 extern char *getenv(); 188 189 /* 190 * Set non-blocking mode and maximum record size for 191 * connection oriented RPC transports. 192 */ 193 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { 194 msgout("unable to set maximum RPC record size"); 195 } 196 197 clear_bindings(); 198 if ((netid = getenv("NLSPROVIDER")) == NULL) { 199 #ifdef DEBUG 200 msgout("cannot get transport name"); 201 #endif 202 } else if ((nconf = getnetconfigent(netid)) == NULL) { 203 #ifdef DEBUG 204 msgout("cannot get transport info"); 205 #endif 206 } 207 208 pmclose = (t_getstate(0) != T_DATAXFER); 209 if ((transp = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) { 210 msgout("cannot create server handle"); 211 exit(1); 212 } 213 214 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { 215 if ((setok != YPSETNONE) && 216 __rpc_negotiate_uid(transp->xp_fd)) { 217 syslog(LOG_ERR, 218 "could not negotiate with loopback tranport %s", 219 nconf->nc_netid); 220 } 221 } 222 if (nconf) 223 freenetconfigent(nconf); 224 if (!svc_reg(transp, YPBINDPROG, YPBINDVERS, ypbindprog_3, 0)) { 225 msgout("unable to register (YPBINDPROG, YPBINDVERS)."); 226 exit(1); 227 } 228 if (!svc_reg(transp, YPBINDPROG, YPBINDVERS_2, 229 ypbindprog_2, 0)) { 230 msgout( 231 "unable to register (YPBINDPROG, YPBINDVERS_2)."); 232 exit(1); 233 } 234 /* version 2 and version 1 are the same as far as we care */ 235 if (!svc_reg(transp, YPBINDPROG, YPBINDVERS_1, 236 ypbindprog_2, 0)) { 237 msgout( 238 "unable to register (YPBINDPROG, YPBINDVERS_1)."); 239 exit(1); 240 } 241 set_signal_handlers(); 242 if (pmclose) { 243 (void) signal(SIGALRM, closedown); 244 (void) alarm(_RPCSVC_CLOSEDOWN); 245 } 246 #ifdef INIT_DEFAULT 247 ypbind_init_default(); 248 #endif 249 svc_run(); 250 msgout("svc_run returned"); 251 exit(1); 252 /* NOTREACHED */ 253 } 254 #ifndef RPC_SVC_FG 255 /* 256 * In normal operation, ypbind forks a child to do all the work 257 * so that it can run in background. But, if the parent exits 258 * too soon during system startup, clients will start trying to 259 * talk to the child ypbind before it is ready. This can cause 260 * spurious client errors. 261 * 262 * To prevent these problems, the parent process creates a pipe, 263 * which is inherited by the child, and waits for the child to 264 * close its end. This happens explicitly before the child goes 265 * into svc_run(), or as a side-effect of exiting. 266 */ 267 if (pipe(pfd) == -1) { 268 perror("pipe"); 269 exit(1); 270 } 271 pid = fork(); 272 if (pid < 0) { 273 perror("cannot fork"); 274 exit(1); 275 } 276 if (pid) { 277 /* 278 * The parent waits for the child to close its end of 279 * the pipe (to indicate that it is ready to process 280 * requests). The read blocks until the child does 281 * a close (the "domain" array is just a handy buffer). 282 */ 283 close(pfd[1]); 284 read(pfd[0], domain, sizeof (domain)); 285 exit(0); 286 } 287 /* close all files except pfd[1] */ 288 (void) fdwalk(void_close, &pfd[1]); 289 (void) open("/dev/null", O_RDONLY); 290 (void) open("/dev/null", O_WRONLY); 291 (void) dup(1); 292 setsid(); 293 #endif 294 clean_cache(); /* make sure there are no left-over files */ 295 cache_okay = cache_check(); 296 cache_pid(); 297 298 /* 299 * Set non-blocking mode and maximum record size for 300 * connection oriented RPC transports. 301 */ 302 if (!rpc_control(RPC_SVC_CONNMAXREC_SET, &connmaxrec)) { 303 msgout("unable to set maximum RPC record size"); 304 } 305 306 /* 307 * Prevent our non-priv udp and tcp ports bound w/wildcard addr 308 * from being hijacked by a bind to a more specific addr. 309 */ 310 if (!rpc_control(__RPC_SVC_EXCLBIND_SET, &exclbind)) { 311 msgout("warning: unable to set udp/tcp EXCLBIND"); 312 } 313 314 #ifdef INIT_DEFAULT 315 ypbind_init_default(); 316 #endif 317 318 nc_handle = __rpc_setconf("netpath"); /* open netconfig file */ 319 if (nc_handle == NULL) { 320 syslog(LOG_ERR, "could not read /etc/netconfig, exiting.."); 321 exit(1); 322 } 323 324 /* 325 * The parent waits for the child to close its end of 326 * the pipe (to indicate that it is ready to process 327 * requests). Now the non-diskless client will wait because the 328 * cache file is valid. 329 */ 330 if (cache_okay) { 331 close(pfd[1]); 332 pipe_closed = 1; 333 } 334 335 clear_bindings(); 336 337 while (nconf = __rpc_getconf(nc_handle)) { 338 SVCXPRT *xprt; 339 340 if (!__rpcbind_is_up()) { 341 msgout("terminating: rpcbind is not running"); 342 exit(1); 343 } 344 if ((xprt = svc_tp_create(ypbindprog_3, 345 YPBINDPROG, YPBINDVERS, nconf)) == NULL) { 346 msgout("terminating: cannot create rpcbind handle"); 347 exit(1); 348 } 349 350 cache_transport(nconf, xprt, YPBINDVERS); 351 352 /* support ypbind V2 and V1, but only on udp/tcp transports */ 353 if (((strcmp(nconf->nc_protofmly, NC_INET) == 0) || 354 (strcmp(nconf->nc_protofmly, NC_INET6))) && 355 ((nconf->nc_semantics == NC_TPI_CLTS) || 356 (nconf->nc_semantics == NC_TPI_COTS_ORD))) { 357 358 if (strcmp(nconf->nc_protofmly, NC_INET)) { 359 inet_desired_tpts |= 1 >> nconf->nc_semantics; 360 } else { 361 inet6_desired_tpts |= 1 >> nconf->nc_semantics; 362 } 363 364 (void) rpcb_unset(YPBINDPROG, YPBINDVERS_2, nconf); 365 if (!svc_reg(xprt, YPBINDPROG, YPBINDVERS_2, 366 ypbindprog_2, nconf)) { 367 syslog(LOG_INFO, 368 "unable to register (YPBINDPROG, YPBINDVERS_2) [%s]", 369 nconf->nc_netid); 370 continue; 371 } 372 373 cache_transport(nconf, xprt, YPBINDVERS_2); 374 375 /* For NC_INET, register v1 as well; error is fatal */ 376 if (strcmp(nconf->nc_protofmly, NC_INET) == 0) { 377 (void) rpcb_unset(YPBINDPROG, YPBINDVERS_1, 378 nconf); 379 if (!svc_reg(xprt, YPBINDPROG, YPBINDVERS_1, 380 ypbindprog_2, nconf)) { 381 syslog(LOG_ERR, 382 "unable to register (YPBINDPROG, YPBINDVERS_1)."); 383 exit(1); 384 } 385 } 386 387 cache_transport(nconf, xprt, YPBINDVERS_1); 388 389 if (nconf->nc_semantics == NC_TPI_CLTS) 390 udp_found++; 391 392 if (strcmp(nconf->nc_protofmly, NC_INET)) { 393 inet_tpts |= 1 >> nconf->nc_semantics; 394 } else { 395 inet6_tpts |= 1 >> nconf->nc_semantics; 396 } 397 } 398 if (strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0) { 399 loopback_found++; 400 if ((setok != YPSETNONE) && 401 __rpc_negotiate_uid(xprt->xp_fd)) { 402 syslog(LOG_ERR, 403 "could not negotiate with loopback tranport %s", 404 nconf->nc_netid); 405 } 406 /* 407 * On a diskless client: 408 * The parent waits for the child to close its end of 409 * the pipe (to indicate that it is ready to process 410 * requests). Now the diskless client will wait 411 * only if ypbind is registered on the loopback. 412 */ 413 if ((!pipe_closed) && 414 ((nconf->nc_semantics == NC_TPI_COTS) || 415 (nconf->nc_semantics == NC_TPI_COTS_ORD))) { 416 close(pfd[1]); 417 pipe_closed = 1; 418 } 419 } 420 } 421 422 /* Did we manage to register all IPv4 or all IPv6 transports ? */ 423 if (inet_tpts != 0 && inet_tpts != inet_desired_tpts) { 424 syslog(LOG_ERR, 425 "unable to register all %s transports, exiting..", 426 NC_INET); 427 exit(1); 428 } else if (inet6_tpts != 0 && inet6_tpts != inet6_desired_tpts) { 429 syslog(LOG_ERR, 430 "unable to register all %s transports, exiting..", 431 NC_INET6); 432 exit(1); 433 } 434 435 if (!pipe_closed) { 436 close(pfd[1]); 437 pipe_closed = 1; 438 } 439 __rpc_endconf(nc_handle); 440 if (!loopback_found) { 441 syslog(LOG_ERR, 442 "could not find loopback transports, exiting.."); 443 exit(1); 444 } 445 if (!udp_found) { 446 syslog(LOG_ERR, 447 "could not find inet-clts (udp) transport, exiting.."); 448 exit(1); 449 } 450 set_signal_handlers(); 451 svc_run(); 452 syslog(LOG_ERR, "svc_run returned, exiting.."); 453 exit(1); 454 /* NOTREACHED */ 455 } 456 457 /* 458 * Callback function for fdwalk() to close all files. 459 */ 460 static int 461 void_close(void *pfdp, int fd) 462 { 463 if (fd != *(int *)pfdp) 464 (void) close(fd); 465 return (0); 466 } 467 468 void 469 ypbindprog_3(rqstp, transp) 470 struct svc_req *rqstp; 471 register SVCXPRT *transp; 472 { 473 union { 474 ypbind_domain ypbindproc_domain_3_arg; 475 ypbind_setdom ypbindproc_setdom_3_arg; 476 } argument; 477 char *result; 478 bool_t (*xdr_argument)(), (*xdr_result)(); 479 char *(*local)(); 480 481 if (sigcld_event) 482 broadcast_proc_exit(); 483 484 _rpcsvcdirty = 1; 485 switch (rqstp->rq_proc) { 486 case YPBINDPROC_NULL: 487 xdr_argument = xdr_void; 488 xdr_result = xdr_void; 489 local = (char *(*)()) ypbindproc_null_3; 490 break; 491 492 case YPBINDPROC_DOMAIN: 493 xdr_argument = xdr_ypbind_domain; 494 xdr_result = xdr_ypbind_resp; 495 local = (char *(*)()) ypbindproc_domain_3; 496 break; 497 498 case YPBINDPROC_SETDOM: 499 xdr_argument = xdr_ypbind_setdom; 500 xdr_result = xdr_void; 501 local = (char *(*)()) ypbindproc_setdom_3; 502 break; 503 504 default: 505 svcerr_noproc(transp); 506 _rpcsvcdirty = 0; 507 return; 508 } 509 (void) memset((char *)&argument, 0, sizeof (argument)); 510 if (!svc_getargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { 511 svcerr_decode(transp); 512 _rpcsvcdirty = 0; 513 return; 514 } 515 if (rqstp->rq_proc == YPBINDPROC_SETDOM) 516 result = (*local)(&argument, rqstp, transp); 517 else 518 result = (*local)(&argument, rqstp); 519 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 520 svcerr_systemerr(transp); 521 } 522 if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { 523 syslog(LOG_ERR, "unable to free arguments"); 524 exit(1); 525 } 526 _rpcsvcdirty = 0; 527 } 528 529 void 530 ypbindprog_2(rqstp, transp) 531 struct svc_req *rqstp; 532 register SVCXPRT *transp; 533 { 534 union { 535 domainname_2 ypbindproc_domain_2_arg; 536 ypbind_setdom_2 ypbindproc_setdom_2_arg; 537 } argument; 538 char *result; 539 bool_t (*xdr_argument)(), (*xdr_result)(); 540 char *(*local)(); 541 542 if (sigcld_event) 543 broadcast_proc_exit(); 544 545 _rpcsvcdirty = 1; 546 switch (rqstp->rq_proc) { 547 case YPBINDPROC_NULL: 548 xdr_argument = xdr_void; 549 xdr_result = xdr_void; 550 /* XXX - don't need two null procedures */ 551 local = (char *(*)()) ypbindproc_null_3; 552 break; 553 554 case YPBINDPROC_DOMAIN: 555 xdr_argument = (bool_t (*)())xdr_ypdomain_wrap_string; 556 xdr_result = xdr_ypbind_resp_2; 557 local = (char *(*)()) ypbindproc_domain_2; 558 break; 559 560 case YPBINDPROC_SETDOM: /* not supported, fall through to error */ 561 default: 562 svcerr_noproc(transp); 563 _rpcsvcdirty = 0; 564 return; 565 } 566 (void) memset((char *)&argument, 0, sizeof (argument)); 567 if (!svc_getargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { 568 svcerr_decode(transp); 569 _rpcsvcdirty = 0; 570 return; 571 } 572 result = (*local)(&argument, rqstp); 573 if (result != NULL && !svc_sendreply(transp, xdr_result, result)) { 574 svcerr_systemerr(transp); 575 } 576 if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)&argument)) { 577 syslog(LOG_ERR, "unable to free arguments"); 578 exit(1); 579 } 580 _rpcsvcdirty = 0; 581 } 582 583 /* 584 * We clear out any old bindings that might have been 585 * left behind. If there is already a ypbind running, 586 * it will no longer get requests. We are in control 587 * now. We ignore the error from rpcb_unset() because 588 * this is just a "best effort". If the rpcb_unset() 589 * does fail, we will get an error in svc_reg(). By 590 * using 0 for the last argument we are telling the 591 * portmapper to remove the bindings for all transports. 592 */ 593 static 594 void 595 clear_bindings() 596 { 597 rpcb_unset(YPBINDPROG, YPBINDVERS, 0); 598 rpcb_unset(YPBINDPROG, YPBINDVERS_2, 0); 599 rpcb_unset(YPBINDPROG, YPBINDVERS_1, 0); 600 } 601 602 /* 603 * This routine is called when we are killed (by most signals). 604 * It first tries to unregister with the portmapper. Then it 605 * resets the signal handler to the default so that if we get 606 * the same signal, we will just go away. We clean up our 607 * children by doing a hold in SIGTERM and then killing the 608 * process group (-getpid()) with SIGTERM. Finally, we redeliver 609 * the signal to ourselves (the handler was reset to the default) 610 * so that we will do the normal handling (e.g., coredump). 611 * If we can't kill ourselves, we get drastic and just exit 612 * after sleeping for a couple of seconds. 613 * 614 * This code was taken from the SunOS version of ypbind. 615 */ 616 static 617 void 618 unregister(int code) 619 { 620 clear_bindings(); 621 clean_cache(); 622 signal(code, SIG_DFL); /* to prevent recursive calls to unregister */ 623 fprintf(stderr, "ypbind: goind down on signal %d\n", code); 624 sighold(SIGCHLD); 625 sighold(SIGTERM); 626 kill(-getpid(), SIGTERM); /* kill process group (i.e., children) */ 627 sigrelse(SIGTERM); 628 kill(getpid(), code); /* throw signal again */ 629 sleep(2); 630 exit(-1); 631 } 632 633 static 634 void 635 set_signal_handlers() 636 { 637 int i; 638 639 for (i = 1; i <= SIGTERM; i++) { 640 if (i == SIGCHLD) 641 continue; 642 else if (i == SIGHUP) 643 signal(i, SIG_IGN); 644 else 645 signal(i, unregister); 646 } 647 } 648 649 void 650 msgout(msg) 651 char *msg; 652 { 653 #ifdef RPC_SVC_FG 654 if (_rpcpmstart) 655 syslog(LOG_ERR, msg); 656 else 657 (void) fprintf(stderr, "%s\n", msg); 658 #else 659 syslog(LOG_ERR, msg); 660 #endif 661 } 662 663 void 664 closedown() 665 { 666 if (_rpcsvcdirty == 0) { 667 int i, openfd; 668 struct t_info tinfo; 669 670 if (t_getinfo(0, &tinfo) || (tinfo.servtype == T_CLTS)) 671 exit(0); 672 673 for (i = 0, openfd = 0; i < svc_max_pollfd && openfd < 2; i++) 674 if (svc_pollfd[i].fd >= 0) 675 openfd++; 676 677 if (openfd <= 1) 678 exit(0); 679 } 680 (void) alarm(_RPCSVC_CLOSEDOWN); 681 } 682