[comp.protocols.tcp-ip.ibmpc] Bug in SOS, the NFS file server for the PC

stan@lbl-csam.arpa (See-Mong Tan) (03/24/89)

Due to Stephen Nahm of Sun Microsystems, a pretty serious bug in SOS has
been discovered.  The bug is serious but the fix is pretty simple.  I
apologise to Stephen, I've been reticient in getting it fixed and posted
to the net, and I really should've dealt with it right away.  

Not until today when news of workstations that refused to boot,
here at LBL did I remember, and fixed the code while surrounded by 
slightly aggravated scientists :-).

Steve's message is next, and the fix follows:

> From: sxn@Sun.COM (Stephen X. Nahm)
> To: stan@csam.lbl.gov
> Subject: Serious bug in SOS
> 
> Greetings,
> 
> I just tried to bring up SOS in our lab and found several problems and one
> serious bug.
> 
> First, the bug:  your manual page says you partially implement the portmapper.
> That's sort-of okay, but you mishandle a PMAP procedure that you don't implement.
> Procedure 5, CALLIT, causes the portmapper to submit an RPC call to an RPC
> service.  The specification (see "RPC: Protocol SPecification" in Network
> Programming) explicitly states that if the service requested through CALLIT
> cannot be performed, then the protmapper must remain silent.

I send a negative reply back, saying "service not available."

> 
> The effect of this bug was to uncover a SunOS bug whereby a diskless workstation
> looking for the bootparam daemon will see the incorrect negative reply from SOS
> and the stop trying to boot.  SunOS will be modified to not give up, but behavior
> such as that which SOS is doing could easily congest a network (ie, a 
> broadcast RPC CALLIT followed by every machine on the net saying "sorry, no one
> by that name here").  This really should be fixed right away.
> 
>  [some other stuff deleted]
> 
> Steve
> 

My appreciation goes to Steve Nahm for finding this out, and taking time off
to inform me.  Thanks Steve!

The change is in the portmapper code, file named "pmap.c".  I've included
it all here.  Replace the old "pmap.c" with the new one please!

---------------------------    Cut here    ----------------------------
/*
 *  pmap.c --
 *      Portmapper program for PC NFS file server.
 *      Service runs on top of the net daemon (netd.c).
 *      This is a partial implementation of the UNIX port mapper
 *      portmap(8c).
 *
 *  Author:
 *      See-Mong Tan, 6/12/88
 *      portmapper bug fixed 3/23/89
 */

#include "common.h"
 
/*
 *  bool_t pmap_init() --
 *      Portmapper initializer.  Called by the net daemon.  Creates
 *      and registers it's own transport handler and dispatch routine.
 *      Returns TRUE if successful, or FALSE if an error occurred.
 */
bool_t pmap_init()
{
	SVCXPRT *transp;
	struct sockaddr_in addr;
	int sock;

	addr.sin_family = AF_INET;
	addr.sin_port = htons(PMAPPORT);
	addr.sin_addr.s_addr = INADDR_ANY;
	if ((sock = sock_create(SOCK_DGRAM, IPPROTO_UDP, &addr)) < 0) {
		(void) fprintf(stderr, "pmap: cannot create UDP socket\n");
		return FALSE;
	}
	if ((transp = svcudp_create(sock, 0)) == (SVCXPRT *) NULL)
		return FALSE;

	if (! svc_register(transp, PMAPPROG, PMAPVERS, 
			    pmap_dispatch, IPPROTO_UDP)) {
		(void) fprintf(stderr, "pmap_init: cannot register handle\n");
		sock_close(sock);
		return FALSE;
	}
	return TRUE;
}
		
/*
 *  bool_t pmap_dispatch(struct svc_req *rqstp, SVCXPRT *transp) --
 *      Dispatch routine for portmapper.  This is only a partial
 *      implementation, in particular, PMAPPROC_SET and PMAPPROC_UNSET
 *      are not supported.
 */
bool_t pmap_dispatch(rqstp, transp)
	struct svc_req *rqstp;
	SVCXPRT *transp;
{
	int NOTSUPP = 0;     /* error not supported */
	struct pmap reg;
	struct sockaddr_in addr;
	int port;

	switch((int) rqstp->rq_proc) {

	case PMAPPROC_NULL:   /* NULL procedure call */
		if (NFS_VERBOSE)
			(void) printf(">>> PMMAPPROC_NULL\n");
		if (! svc_sendreply(transp, xdr_void, NULL))
			(void) fprintf(stderr, "pmap: cannot send reply\n");
		break;

	case PMAPPROC_SET:   /* these reqeusts are not supported */
	case PMAPPROC_UNSET:
		if (NFS_VERBOSE)
			(void) printf("pmap: PMAPPROC_SET/UNSET\n");
		if (! svc_sendreply(transp, xdr_long, &NOTSUPP))
			(void) fprintf(stderr, "pmap: cannot send reply\n");
		break;

	case PMAPPROC_GETPORT:  /* get a port */
		if (NFS_VERBOSE)
			(void) printf("\n>>> PMAPPROC_GETPORT\t"); 
		if (! svc_getargs(transp, xdr_pmap, &reg)) {
			svcerr_decode(transp);
			break;
		}
		if (NFS_VERBOSE)
			(void) printf("--> prog = %lu, vers = %lu:\t", 
				      reg.pm_prog, reg.pm_vers);
		if (reg.pm_prog == NFS_PROGRAM && reg.pm_vers == NFS_VERSION) {
			port = NFS_PORT;
			if (NFS_VERBOSE)
				(void) printf(" --> NFS_PORT\n");
			if (! svc_sendreply(transp, xdr_long, (caddr_t)&port)) {
				(void) fprintf(stderr, 
						"pmap: cannot send reply\n");
				break;
			}
			break;
		}
		if (reg.pm_prog== MOUNTPROG && reg.pm_vers== MOUNTVERS) {
			port = MOUNTPORT;
			if (NFS_VERBOSE)
				(void) printf("--> MOUNTPORT\n");
			if (! svc_sendreply(transp, xdr_long, (caddr_t)&port)) {
				(void) fprintf(stderr, "pmap: cannot reply\n");
				break;
			}
			break;
		}
		/* everything else is not supported */
		port = 0;
		if (NFS_VERBOSE)
			(void) printf("--> NO PORT REGISTERED\n");
		if (! svc_sendreply(transp, xdr_long, (caddr_t)&port))
			(void) fprintf(stderr, "cannot reply\n");
		break;

		/* The fix  for the portmapper bug discovered by
		 * Stephen Nahm follows
		 */
	case PMAPPROC_CALLIT:
		if (NFS_VERBOSE)
			(void) printf(">>> PMAPPROC_DUMP/PMAPPROC_CALLIT\n");
		break;    /* should break out and not reply */
		
	case PMAPPROC_DUMP:
		/* FALLTHROUGH */
	default:
		svcerr_noproc(transp);
	}
}

/*
 *  bool_t xdr_pmap(XDR *xdrs, struct pmap *regs) --
 *      XDR a pmap request.
 */
bool_t xdr_pmap(xdrs, regs)
	XDR *xdrs;
	struct pmap *regs;
{

	if (xdr_u_long(xdrs, &regs->pm_prog) && 
		xdr_u_long(xdrs, &regs->pm_vers) && 
		xdr_u_long(xdrs, &regs->pm_prot))
		return (xdr_u_long(xdrs, &regs->pm_port));
	return (FALSE);
}