/*
* 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 2009 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.
*/
/*
* The commom server procedure for the rpcbind.
*/
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <rpc/rpcb_prot.h>
#include <rpcsvc/svc_dg_priv.h>
#include <netconfig.h>
#include <errno.h>
#include <zone.h>
#ifdef PORTMAP
#include <rpc/pmap_prot.h>
#else
#endif /* PORTMAP */
#include <syslog.h>
#include <netdir.h>
#include <ucred.h>
#include <alloca.h>
#include <rpc/key_prot.h>
#include <rpcsvc/yppasswd.h>
#include <assert.h>
#include <synch.h>
#include "rpcbind.h"
static void forward_destroy(struct finfo *);
static void handle_reply(svc_input_id_t, int, unsigned int, void *);
static void netbuffree(struct netbuf *);
#ifdef PORTMAP
static int add_pmaplist(RPCB *);
#endif
/*
* Set a mapping of program, version, netid
*/
int rpcbversnum)
{
return (TRUE);
}
{
RPCB *a;
/*
* check to see if already used
* find_service returns a hit even if
* the versions don't match, so check for it
*/
(void) rw_wrlock(&list_rbl_lock);
#ifdef PORTMAP
(void) rw_wrlock(&list_pml_lock);
#endif /* PORTMAP */
/*
* if these match then it is already
* registered so just say "OK".
*/
#ifdef PORTMAP
(void) rw_unlock(&list_pml_lock);
#endif /* PORTMAP */
(void) rw_unlock(&list_rbl_lock);
return (TRUE);
} else {
/*
* Check if server is up. If so, return FALSE.
* If not, cleanup old registrations for the
* program and register the new server.
*/
#ifdef PORTMAP
(void) rw_unlock(&list_pml_lock);
#endif /* PORTMAP */
(void) rw_unlock(&list_rbl_lock);
return (FALSE);
}
}
}
#ifdef PORTMAP
(void) rw_unlock(&list_pml_lock);
#endif /* PORTMAP */
/*
* add to the end of the list
*/
(void) rw_unlock(&list_rbl_lock);
return (FALSE);
}
(void) rw_unlock(&list_rbl_lock);
return (FALSE);
}
} else {
;
}
#ifdef PORTMAP
(void) add_pmaplist(regp);
#endif
(void) rw_unlock(&list_rbl_lock);
return (TRUE);
}
/*
* Unset a mapping of program, version, netid
*/
int rpcbversnum)
{
return (TRUE);
}
{
#ifdef PORTMAP
int ans = 0;
#endif
return (0);
(void) rw_wrlock(&list_rbl_lock);
/* prev moves forwards */
continue;
}
/*
* Check whether appropriate uid. Unset only
* if superuser or the owner itself.
*/
(void) rw_unlock(&list_rbl_lock);
return (0);
}
/* prev stays */
#ifdef PORTMAP
ans = 1;
#endif
else
}
#ifdef PORTMAP
if (ans != 0) {
(void) rw_wrlock(&list_pml_lock);
(void) del_pmaplist(regp);
(void) rw_unlock(&list_pml_lock);
}
#endif
(void) rw_unlock(&list_rbl_lock);
/*
* We return 1 either when the entry was not there or it
* was able to unset it. It can come to this point only if
* at least one of the conditions is true.
*/
return (1);
}
void
{
}
void
{
continue;
}
#ifdef PORTMAP
#endif
else
}
}
/*
* Lookup the mapping for a program, version and return its
* address. Assuming that the caller wants the address of the
* server running on the transport on which the request came.
*
* For RPCBPROC_GETVERSADDR it will return a service with the exact version
* number only.
*
* Otherwise, even if a service with a different version number is available,
* it will return that address. The client should check with an
* clnt_call to verify whether the service is the one that is desired.
*
* We also try to resolve the universal address in terms of
* address of the caller.
*/
{
/*
* There is a potential window at startup during which rpcbind
* service has been established over IPv6 but not over IPv4. If an
* IPv4 request comes in during that window, the IP code will map
* it into IPv6. We could patch up the request so that it looks
* like IPv4 (so that rpcbind returns an IPv4 uaddr to the caller),
* but that requires some non-trivial code and it's hard to test.
* Instead, drop the request on the floor and force the caller to
* retransmit. By the time rpcbind sees the retransmission, IPv4
* service should be in place and it should see the request as
* IPv4, as desired.
*/
"IPv4 GETADDR request mapped to IPv6: ignoring");
return (FALSE);
}
}
(void) rw_rdlock(&list_rbl_lock);
} /* should use */
/* Try whatever we have */
} else if (!(*result)[0]) {
if (!pml_locked) {
(void) rw_unlock(&list_rbl_lock);
(void) rw_wrlock(&list_rbl_lock);
#ifdef PORTMAP
(void) rw_wrlock(&list_pml_lock);
#endif /* PORTMAP */
pml_locked = TRUE;
goto retry;
}
/*
* The server died. Unset all versions of this prog.
*/
}
} else {
}
#ifdef PORTMAP
if (pml_locked)
(void) rw_unlock(&list_pml_lock);
#endif /* PORTMAP */
(void) rw_unlock(&list_rbl_lock);
return (TRUE);
}
/* ARGSUSED */
{
/*
* list_rbl_lock is unlocked in xdr_rpcblist_ptr_ptr()
*/
(void) rw_rdlock(&list_rbl_lock);
return (TRUE);
}
{
/*
* list_rbl_lock is locked in rpcbproc_dump_com()
*/
return (TRUE);
}
}
/* ARGSUSED */
{
return (TRUE);
}
/*
* Convert uaddr to taddr. Should be used only by
*/
{
return (TRUE);
}
return (TRUE);
}
/*
* Convert taddr to uaddr. Should be used only by
*/
{
else
return (TRUE);
}
/*
* Stuff for the rmtcall service
*/
{
return (FALSE);
return (FALSE);
return (FALSE);
return (FALSE);
return (TRUE);
}
#ifdef PORTMAP
{
return (FALSE);
return (FALSE);
return (TRUE);
}
#endif
{
return (FALSE);
return (FALSE);
return (TRUE);
}
struct rmtcallfd_list {
int fd;
char *netid;
};
int
{
int fd;
if (debugging)
"\"%s\" (errno %d, t_errno %d)\n",
return (-1);
}
if (debugging)
"fd for \"%s\" (errno %d, t_errno %d)\n",
return (-1);
}
return (-1);
}
return (-1);
}
return (-1);
}
} else {
}
return (fd);
}
static int
{
}
}
return (-1);
}
struct finfo {
int flag;
int forward_fd;
char *uaddr;
void *res_val;
};
/*
* finfo_lock protects rpcb_rmtcalls, rpcb_rmtcalls_max, lastxid,
* fihead, and fitail.
*/
static int rpcb_rmtcalls;
static int rpcb_rmtcalls_max;
void
{
(void) mutex_lock(&finfo_lock);
if (rpcb_rmtcalls > rpcb_rmtcalls_max) {
}
(void) mutex_unlock(&finfo_lock);
}
/*
* Call a remote procedure service. This procedure is very quiet when things
* go wrong. The proc is written to support broadcast rpc. In the broadcast
* case, a machine should shut-up instead of complain, lest the requestor be
* overrun with complaints at the expense of not hearing a valid reply.
* When receiving a request and verifying that the service exists, we
*
* receive the request
*
* open a new TLI endpoint on the same transport on which we received
* the original request
*
* remember the original request's XID (which requires knowing the format
* of the svc_dg_data structure)
*
* forward the request, with a new XID, to the requested service,
* remembering the XID used to send this request (for later use in
* reassociating the answer with the original request), the requestor's
* address, the file descriptor on which the forwarded request is
* made and the service's address
*
* wait for either the timeout or the condition variable is signalled from
* handle_reply().
*
* At some time in the future, a reply will be received from the service to
* which we forwarded the request. At that time, svc_run() detect that the
* socket used was for forwarding and call handle_reply() to
*
* receive the reply
*
* bundle the reply, along with the service's universal address
*
* put the reply into the particular finfo
*
* signal the condition variable.
*/
/*
*/
/*
* reply_type - which proc number
* versnum - which vers was called
*/
void
int versnum)
{
int stat;
int fd;
(void) mutex_lock(&finfo_lock);
if (!allow_indirect || rpcb_rmtcalls_max == 0) {
(void) mutex_unlock(&finfo_lock);
return;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
return;
}
return; /* Only datagram type accepted */
if (sendsz == 0) { /* data transfer not supported */
if (reply_type == RPCBPROC_INDIRECT)
return;
}
/*
* Should be multiple of 4 for XDR.
*/
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: svc_getargs failed\n");
goto error;
}
/*
* Disallow calling rpcbind for certain procedures.
* Allow calling NULLPROC - per man page on rpcb_rmtcall().
* switch is in alphabetical order.
*/
case KEY_PROG:
if (debugging)
"rpcbind: rejecting KEY_PROG(%d)\n",
goto error;
case MOUNTPROG:
break;
/*
* In Solaris 2.6, the host-based accesss control
* is done by the NFS server on each request.
* Prior to 2.6 we rely on mountd.
*/
if (debugging)
"rpcbind: rejecting MOUNTPROG(%d)\n",
goto error;
case NFS_ACL_PROGRAM:
if (debugging)
"rpcbind: rejecting NFS_ACL_PROGRAM(%d)\n",
goto error;
case NFS_PROGRAM:
/* also NFS3_PROGRAM */
if (debugging)
"rpcbind: rejecting NFS_PROGRAM(%d)\n",
goto error;
case RPCBPROG:
/*
* Disallow calling rpcbind for certain procedures.
* procedure numbers. So, will not check for those.
*/
case RPCBPROC_SET:
case RPCBPROC_UNSET:
case RPCBPROC_CALLIT:
case RPCBPROC_INDIRECT:
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"calling RPCBPROG procs SET, "
"UNSET, CALLIT, or INDIRECT not "
"allowed\n");
goto error;
default:
/*
* Ideally, we should have called rpcb_service()
* or pmap_service() with appropriate parameters
* instead of going about in a roundabout
* manner. Hopefully, this case should happen
* rarely.
*/
break;
}
break;
case RQUOTAPROG:
if (debugging)
"rpcbind: rejecting RQUOTAPROG(%d)\n",
goto error;
case YPPASSWDPROG:
if (debugging)
"rpcbind: rejecting YPPASSWDPROG(%d)\n",
goto error;
case YPU_PROG:
if (debugging)
"rpcbind: rejecting YPU_PROG(%d)\n",
goto error;
case YPBINDPROG:
break;
if (debugging)
"rpcbind: rejecting YPBINDPROG(%d)\n",
goto error;
case YPPROG:
case YPPROC_FIRST:
case YPPROC_NEXT:
case YPPROC_MATCH:
case YPPROC_ALL:
if (debugging)
"rpcbind: rejecting YPPROG(%d)\n",
goto error;
default:
break;
}
break;
default:
break;
}
}
(void) rw_rdlock(&list_rbl_lock);
(void) rw_unlock(&list_rbl_lock);
if (reply_type == RPCBPROC_INDIRECT)
goto error;
}
if (reply_type == RPCBPROC_INDIRECT) {
(void) rw_unlock(&list_rbl_lock);
} else {
(void) rw_unlock(&list_rbl_lock);
}
goto error;
}
/*
* Check whether this entry is valid and a server is present
* Mergeaddr() returns NULL if no such entry is present, and
* returns "" if the entry was present but the server is not
* present (i.e., it crashed).
*/
if (reply_type == RPCBPROC_INDIRECT) {
(void) rw_unlock(&list_rbl_lock);
goto error;
} else {
}
}
(void) rw_unlock(&list_rbl_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: rpcbind_get_conf failed\n");
goto error;
}
/*
* A mergeaddr operation allocates a string, which it stores in
* ma.m_uaddr. It's passed to forward_register() and is
* eventually freed by forward_destroy().
*/
(void) rw_unlock(&list_rbl_lock);
if (stat)
if (reply_type == RPCBPROC_INDIRECT)
goto error;
}
/* forward_register failed. Perhaps no memory. */
if (debugging)
"rpcbproc_callit_com: forward_register failed\n");
goto error;
}
/* forward_register() returns with finfo_lock held when successful */
/*
* A duplicate request for the slow server. Let's not
* beat on it any more.
*/
(void) mutex_unlock(&finfo_lock);
if (debugging)
"rpcbproc_callit_com: duplicate request\n");
goto error;
}
if (sendsz > RPC_BUF_MAX) {
if (outbuf_alloc == NULL) {
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: No memory!\n");
goto error;
}
} else {
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: xdr_callhdr failed\n");
goto error;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: xdr_u_long failed\n");
goto error;
}
auth = authnone_create();
auth = authnone_create();
} else {
/* we do not support any other authentication scheme */
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"AUTH_NONE and oa_flavor != AUTH_SYS\n");
goto error;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"authwhatever_create returned NULL\n");
goto error;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: AUTH_MARSHALL failed\n");
goto error;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: xdr_opaque failed\n");
goto error;
}
if (outbuf_alloc)
else
if (!na) {
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
goto error;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: t_sndudata failed: "
goto error;
}
if (outbuf_alloc != NULL) {
outbuf_alloc = NULL;
}
;
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: timeout\n");
return;
}
(void) mutex_unlock(&finfo_lock);
if (reply_type == RPCBPROC_INDIRECT)
if (debugging)
"rpcbproc_callit_com: error in reply: %s\n",
return;
}
switch (versnum) {
#ifdef PORTMAP
case PMAPVERS:
{
break;
}
break;
#endif
case RPCBVERS:
case RPCBVERS4:
{
(char *)&result);
}
break;
}
(void) mutex_unlock(&finfo_lock);
return;
if (outxdr_created)
}
/*
* Adds an entry into the finfo list for the given request. Returns the finfo
* and finfo_lock is left held. If duplicate request, returns finfo with
* FINFO_ACTIVE, else returns finfo without FINFO_ACTIVE.
* If failed, returns NULL and finfo_lock is left unheld.
*/
static struct finfo *
char *uaddr)
{
(void) mutex_lock(&finfo_lock);
if (rpcb_rmtcalls_max == 0) {
(void) mutex_unlock(&finfo_lock);
return (NULL);
}
/*
* initialization: once this has happened, lastxid will
* never be 0 again, when entering or returning from this function.
*/
if (lastxid == 0)
/*
* Check if it is an duplicate entry
*/
return (fi);
}
}
(void) mutex_unlock(&finfo_lock);
return (NULL);
}
(void) mutex_unlock(&finfo_lock);
return (NULL);
}
/*
* Generate new xid and make sure it is unique.
*/
do {
lastxid++;
/* avoid lastxid wraparound to 0 */
if (lastxid == 0)
lastxid = 1;
/*
* Though uaddr is not allocated here, it will still be freed
* from forward_destroy().
*/
if (rpcb_rmtcalls > rpcb_rmtcalls_max) {
}
return (fi);
}
static void
{
} else {
}
} else {
}
if (rpcb_rmtcalls > rpcb_rmtcalls_max) {
}
}
static struct finfo *
{
return (fi);
}
return (NULL);
}
static int
{
}
static struct netbuf *
{
if (np) {
}
return (np);
}
static void
{
}
static void
{
int res;
unsigned int inlen;
char *buffer;
unsigned int pos;
unsigned int len;
return;
}
do {
int moreflag = 0;
errno = 0;
/* Drop this packet - we have no more space. */
if (debugging)
"with T_MORE flag set\n");
goto done;
}
if (res < 0) {
if (debugging)
goto done;
}
if (debugging)
"handle_reply: xdr_replymsg failed\n");
goto done;
}
goto done;
}
goto done;
}
(void) mutex_lock(&finfo_lock);
(void) mutex_unlock(&finfo_lock);
goto done;
}
(void) mutex_unlock(&finfo_lock);
done:
if (tr_data)
}
/*
* prog: Program Number
* netid: Transport Provider token
* lowvp: Low version number
* highvp: High version number
*/
static void
{
continue;
if (lowv == 0) {
}
}
}
/*
* returns the item with the given program, version number and netid.
* If that version number is not found, it returns the item with that
* program number, so that address is now returned to the caller. The
* caller when makes a call to this program, version number, the call
* will fail and it will return with PROGVERS_MISMATCH. The user can
* then determine the highest and the lowest version number for this
* program using clnt_geterr() and use those program version numbers.
*
* Returns the RPCBLIST for the given prog, vers and netid
*
* prog: Program Number
* vers: Version Number
* netid: Transport Provider token
*/
static rpcblist_ptr
{
continue;
break;
}
return (hit);
}
/*
* If the caller is from our zone and we know
* who it is, we return the uid.
*/
{
return (-1);
} else {
return (ucred_geteuid(uc));
}
}
/*
* Copies the name associated with the uid of the caller and returns
* a pointer to it. Similar to getwd().
*/
char *
{
switch (uid) {
case -1:
case 0:
default:
return (owner);
}
}
#ifdef PORTMAP
/*
* Add this to the pmap list only if it is UDP or TCP.
*/
static int
{
/* It is UDP! */
/* It is TCP */
} else
/* Not a IP protocol */
return (0);
return (0);
/*
* add to END of list
*/
return (1);
}
(void) rw_wrlock(&list_pml_lock);
} else {
/* Attach to the end of the list */
;
}
(void) rw_unlock(&list_pml_lock);
return (0);
}
/*
* Delete this from the pmap list only if it is UDP or TCP.
*/
int
{
/* It is UDP! */
prot = IPPROTO_UDP;
/* It is TCP */
prot = IPPROTO_TCP;
prot = 0; /* Remove all occurrences */
} else {
/* Not a IP protocol */
return (0);
}
/* both pml & prevpml move forwards */
continue;
}
/* found it; pml moves forward, prevpml stays */
else
}
return (0);
}
#endif /* PORTMAP */