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