rpcbind.c revision 8f6d9dae92449b59bdafcb7777bc32f1b2726e48
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
*/
/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
/* All Rights Reserved */
/*
* University Copyright- Copyright (c) 1982, 1986, 1988
* The Regents of the University of California
* All Rights Reserved
*
* University Acknowledgment- Portions of this document are derived from
* software developed by the University of California, Berkeley, and its
* contributors.
*/
/*
* This is an implementation of RCPBIND according the RFC 1833: Binding
* Protocols for ONC RPC Version 2. The RFC specifies three versions of the
* binding protocol:
*
* 1) RPCBIND Version 3 (Section 2.2.1 of the RFC)
* 2) RPCBIND, Version 4 (Section 2.2.2 of the RFC)
* 3) Port Mapper Program Protocol (Section 3 of the RFC)
*
* Where the "Port Mapper Program Protocol" is refered as Version 2 of the
* binding protocol. The implementation of the Version 2 of the binding
* protocol is compiled in only in a case the PORTMAP macro is defined (by
* default it is defined).
*
* The implementation is based on top of the networking services library -
* libnsl(3lib) and uses Automatic MT mode (see rcp_control(3nsl) and
* svc_run(3nsl) for more details).
*
* Usually, when a thread handles an RPCBIND procedure (one that arrived from a
* client), it obtains the data for the response internally, and immediately
* sends the response back to the client. The only exception to this rule are
* remote (aka indirect) RPC calls, for example RPCBPROC_INDIRECT. Such
* procedures are designed to forward the RPC request from the client to some
* other RPC service specified by the client, wait for the result, and forward
* the result back to the client. This is implemented in rpcbproc_callit_com().
*
* The response from the other (remote) RPC service is handled in
* handle_reply(), where the thread waiting in rpcbproc_callit_com() is woken
* up to finish the handling and to send (forward) the response back to the
* client.
*
* The thread implementing the indirect RPC call might be blocked in the
* rpcbproc_callit_com() waiting for the response from the other RPC service
* for very long time. During this time the thread is unable to handle other
* RPCBIND requests. To avoid a case when all threads are waiting in
* rpcbproc_callit_com() and there is no free thread able to handle other
* RPCBIND requests, the implementation has reserved eight threads to never be
* used for the remote RPC calls. The number of active remote RPC calls is in
* rpcb_rmtcalls, the upper limit of such calls is in rpcb_rmtcalls_max.
*
* In addition to the worker threads described above, there are two other
* threads. The logthread() thread is responsible for asynchronous logging to
* syslog. The terminate() thread is signal handler responsible for reload of
* the rpcbind configuration (on SIGHUP), or for gracefully shutting down
* rpcbind (otherwise).
*
* There are two global lists used for holding the information about the
* registered services: list_rbl is for Version 3 and 4 of the binding
* protocol, and list_pml is for Version 2. To protect these lists, two global
* implementation: list_rbl_lock protecting list_rbl, and list_pml_lock,
* protecting list_pml.
*
* The defined locking order is: list_rbl_lock first, list_pml_lock second.
*/
#include <dlfcn.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <netconfig.h>
#include <netdir.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <thread.h>
#include <synch.h>
#include <stdarg.h>
#ifdef PORTMAP
#endif
#include "rpcbind.h"
#include <syslog.h>
#include <string.h>
#include <sys/resource.h>
#include <rpcsvc/daemon_utils.h>
#include <priv_utils.h>
#include <libscf.h>
#include <sys/ccompile.h>
#include <zone.h>
#include <ctype.h>
#include <limits.h>
#include <assert.h>
static sigset_t sigwaitset;
static void terminate(void);
static void detachfromtty(void);
static void parseargs(int, char *[]);
static int init_transport(struct netconfig *);
static int check_netconfig(void);
static int setopt_reuseaddr(int);
static int setopt_anon_mlp(int);
static int setup_callit(int);
static void rpcb_check_init(void);
/* Global variables */
int debugging = 0; /* Tell me what's going on */
static int ipv6flag = 0;
int doabort = 0; /* When debugging, do an abort on errors */
static int listen_backlog;
static const int reserved_threads = 8;
/*
* list_rbl_lock protects list_rbl
* lock order: list_rbl_lock, list_pml_lock
*/
char *loopback_dg; /* Datagram loopback transport, for set and unset */
char *loopback_vc; /* COTS loopback transport, for set and unset */
char *loopback_vc_ord; /* COTS_ORD loopback transport, for set and unset */
/* Local Variable */
static int warmstart = 0; /* Grab a old copy of registrations */
#ifdef PORTMAP
/*
* list_pml_lock protects list_pml
* lock order: list_rbl_lock, list_pml_lock
*/
char *udptrans; /* Name of UDP transport */
char *tcptrans; /* Name of TCP transport */
char *udp_uaddr; /* Universal UDP address */
char *tcp_uaddr; /* Universal TCP address */
#endif
static char servname[] = "rpcbind";
static char superuser[] = "superuser";
static const char daemon_dir[] = DAEMON_DIR;
static void
block_signals(void)
{
(void) sigemptyset(&sigwaitset);
/* ignore other signals that could get sent */
}
int
{
void *nc_handle; /* Net config handle */
int rpc_svc_fdunlim = 1;
int rpc_svc_mode = RPC_SVC_MT_AUTO;
int maxrecsz = RPC_MAXDATASIZE;
} else {
}
/*
*/
} else {
}
/*
* These privileges are required for the t_bind check rpcbind uses
* to determine whether a service is still live or not.
*/
exit(1);
}
/*
* Set number of file descriptors to unlimited
*/
}
/*
* Tell RPC that we want automatic thread mode.
* A new thread will be spawned for each request.
*/
exit(1);
}
/*
* Enable non-blocking mode and maximum record size checks for
* connection oriented transports.
*/
}
{
/*
* rpcbind is the first application to encounter the
* various netconfig files. check_netconfig() verifies
* that they are set up correctly and complains loudly
* if not.
*/
int trouble;
trouble = check_netconfig();
if (trouble) {
"configuration files. Exiting.\n",
exit(1);
}
}
loopback_dg = "";
loopback_vc = "";
loopback_vc_ord = "";
#ifdef PORTMAP
udptrans = "";
tcptrans = "";
#endif
ipv6flag = Is_ipv6present();
exit(1);
}
}
(loopback_vc_ord[0] == NULL)) {
exit(1);
}
if (warmstart) {
}
/* Create terminate signal handler for graceful exit */
exit(1);
}
if (debugging) {
printf("rpcbind debugging enabled.");
if (doabort) {
printf(" Will abort on errors!\n");
} else {
printf("\n");
}
} else {
}
/* These are basic privileges we do not need */
svc_run();
/* NOTREACHED */
}
/*
* Increments a counter each time a problem is found with the network
* configuration information.
*/
static int
check_netconfig(void)
{
void *nc;
void *dlcookie;
int busted = 0;
int i;
nc = setnetconfig();
if (debugging)
"setnetconfig() failed: %s\n", nc_sperror());
return (1);
}
continue;
if (debugging)
switch (np->nc_semantics) {
case NC_TPI_CLTS:
lo_clts_found = 1;
break;
case NC_TPI_COTS:
lo_cots_found = 1;
break;
case NC_TPI_COTS_ORD:
lo_cotsord_found = 1;
break;
}
if (debugging)
busted++;
} else
if (debugging)
for (i = 0; i < np->nc_nlookups; i++) {
char *dlerrstr;
if (debugging) {
"name-to-address library %s "
"failed\ndlerror: %s",
}
"name-to-address library %s failed",
if (dlerrstr)
busted++;
} else {
if (debugging)
"name-to-address library %s "
"succeeded\n", libname);
}
}
busted++;
busted++;
busted++;
busted++;
}
if (lo_clts_found) {
if (debugging)
} else {
if (debugging)
}
if (lo_cots_found) {
if (debugging)
} else {
if (debugging)
}
if (lo_cotsord_found) {
if (debugging)
} else {
if (debugging)
"no COTS ORD loopback transport found\n");
}
return (busted);
}
/*
* Adds the entry into the rpcbind database.
* If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also
* Returns 0 if succeeds, else fails
*/
static int
{
int fd;
struct nd_addrlist *nas;
struct nd_hostserv hs;
static int msgprt = 0;
return (1); /* not my type */
if (!msgprt)
"IPv6 is not configured");
msgprt++;
return (1);
}
return (1);
}
if (is_system_labeled() &&
}
/*
* Negotiate for returning the ucred of the caller. This should
* done before enabling the endpoint for service via
* t_bind() so that requests to rpcbind contain the uid.
*/
exit(1);
}
/* Get rpcbind's address on this transport */
goto error;
/* Copy the address */
else
/*
* Sm: If we are running then set SO_REUSEADDR option
* so that we can bind to our preferred address even if
* previous connections are in FIN_WAIT state
*/
}
}
goto error;
}
goto error;
}
goto error;
}
/* set up multicast address for RPC CALL_IT, IPv6 */
if (setup_callit(fd) < 0) {
"for rpc broadcast %s", RPCB_MULTICAST_ADDR);
}
}
}
#ifdef PORTMAP
/*
*/
pmap_service, NULL)) {
goto error;
}
exit(1);
}
if (tcptrans[0]) {
"cannot have more than one TCP transport");
goto error;
}
/* Let's snarf the universal address */
/* "h1.h2.h3.h4.p1.p2" */
} else {
if (udptrans[0]) {
"cannot have more than one UDP transport");
goto error;
}
/* Let's snarf the universal address */
/* "h1.h2.h3.h4.p1.p2" */
}
/* Add version 3 information */
exit(1);
}
/* Add version 4 information */
exit(1);
}
/* Also add version 2 stuff to rpcbind list */
}
#endif
/* version 3 registration */
goto error;
}
/* version 4 registration */
goto error;
}
}
/* decide if bound checking works for this transport */
/*
* rmtcall only supported on CLTS transports for now.
*/
(void) create_rmtcall_fd(nconf);
return (0);
return (1);
}
static void
{
exit(1);
}
exit(1);
}
}
/*
* Catch the signal and die, if not SIGHUP
*/
static void
terminate(void)
{
int sig;
for (;;) {
continue;
}
break;
}
#ifdef PORTMAP
#endif
write_warmstart(); /* Dump yourself */
exit(2);
}
void
rpcbind_abort(void)
{
/*
* We need to hold write locks to make sure
* write_warmstart() is executed exactly once
*/
#ifdef PORTMAP
#endif
write_warmstart(); /* Dump yourself */
abort();
}
/*
* detach from tty
*/
static void
detachfromtty(void)
{
close(0);
close(1);
close(2);
switch (forkall()) {
case (pid_t)-1:
perror("fork");
break;
case 0:
break;
default:
exit(0);
}
setsid();
dup(0);
dup(0);
}
static int
{
long lval;
return (-1);
return (-2);
return (0);
}
static int get_smf_iprop(const char *, int, int, int);
/* get command line options */
static void
{
int c;
int tmp;
switch (c) {
case 'd':
debugging = 1;
break;
case 'a':
break; /* errors; for rpcbind developers */
/* only! */
case 'w':
warmstart = 1;
break;
case 'l':
"listen_backlog option, using defaults\n",
argv[0]);
break;
}
break;
default: /* error */
"usage: rpcbind [-d] [-w] [-l listen_backlog]\n");
exit(1);
}
}
"-a (abort) specified without -d "
"(debugging) -- ignored.\n");
doabort = 0;
}
}
static int
{
struct {
int value;
} optdata;
t_error("t_optmgmt");
return (-1);
}
return (0);
}
static int
setopt_reuseaddr(int fd)
{
}
static int
setopt_anon_mlp(int fd)
{
}
static int
setup_callit(int fd)
{
/* multicast address */
mreq.ipv6mr_interface = 0;
/* insert it into opt */
t_error("t_optmgmt");
return (-1);
}
return (0);
}
static boolean_t
{
struct nd_hostserv nh;
struct nd_addrlist *na;
int retval;
hostname = "HOST_SELF";
hostname = "HOST_SELF_CONNECT";
if (serv[0] == '\0')
servname = "<any>";
if (debugging) {
}
return (B_FALSE);
}
if (debugging) {
}
return (B_TRUE);
}
/* Maximum outstanding syslog requests */
#define MAXLOG 100
/* Maximum length: the messages generated are fairly short; no hostnames. */
#define MAXMSG 128
typedef struct logmsg {
int log_pri;
} logmsg;
static int logcount = 0;
/* ARGSUSED */
static void * __NORETURN
{
for (;;) {
(void) mutex_lock(&logmutex);
logcount--;
logcount = 0;
}
(void) mutex_unlock(&logmutex);
}
/* NOTREACHED */
}
static boolean_t
{
}
}
return (res);
}
static int
{
else
}
}
def_val);
}
return (res);
}
/*
* Initialize: read the configuration parameters from SMF.
* This function must be idempotent because it can be called from the
* signal handler.
*/
static void
rpcb_check_init(void)
{
int max_threads;
static int thr_running;
if (wrap_enabled && !thr_running) {
thr_running = 1;
}
/*
* Set the maximum number of threads.
*/
int tmp;
/*
* The following rpc_control() call cannot fail
*/
assert(0);
if (tmp != max_threads) {
max_threads = tmp;
}
}
/*
* Set rpcb_rmtcalls_max.
*/
if (max_threads < reserved_threads)
else
}
/*
* qsyslog() - queue a request for syslog(); if syslog blocks, the other
* thread blocks; we make sure we don't run out of memory by allowing
* only a limited number of outstandig syslog() requests.
*/
void
{
return;
(void) mutex_lock(&logmutex);
if (logcount == 0)
(void) cond_signal(&logcond);
logcount++;
(void) mutex_unlock(&logmutex);
} else {
(void) mutex_unlock(&logmutex);
}
}