aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2004-06-17 04:46:38 +0000
committerH. Peter Anvin <hpa@zytor.com>2004-06-17 04:46:38 +0000
commit4274cf268f055e0e0a9b3f6b036426e8982837b8 (patch)
treec531eabd8b1a0c06e334afca5b414ecf9ec532c0
parent30a7b06b2560f415b958ce446412e13ee6d1ddf7 (diff)
downloadklibc-4274cf268f055e0e0a9b3f6b036426e8982837b8.tar.gz
Add the ability to spoof portmap, and put out a file that thenklibc-0.144
should get fed to pmap_set when starting the real portmapper. This allows for nfsroot with lockd enabled.
-rw-r--r--nfsmount/Makefile7
-rw-r--r--nfsmount/dummypmap.c141
-rw-r--r--nfsmount/dummypmap.h11
-rw-r--r--nfsmount/dummypmap_test.c2
-rw-r--r--nfsmount/main.c25
-rw-r--r--nfsmount/sunrpc.h13
6 files changed, 197 insertions, 2 deletions
diff --git a/nfsmount/Makefile b/nfsmount/Makefile
index 1fa6091af4d0b..bca293b9ea0a8 100644
--- a/nfsmount/Makefile
+++ b/nfsmount/Makefile
@@ -1,6 +1,6 @@
include MCONFIG
-COMMON_OBJS = main.o mount.o portmap.o sunrpc.o
+COMMON_OBJS = main.o mount.o portmap.o dummypmap.o sunrpc.o
PROG = nfsmount
LIB = libnfsmount.a
LIBS = $(KLIBC) $(LIBGCC)
@@ -11,6 +11,11 @@ all: $(PROG) $(LIB)
nfsmount: $(OBJS) $(CRT0) $(LIBS)
$(LD) $(LDFLAGS) -o $@ $(CRT0) $(OBJS) $(LIBS)
+ cp -f $@ $@.g
+ $(STRIP) $@
+
+dummypmap: dummypmap_test.o
+ $(LD) $(LDFLAGS) -o $@ $(CRT0) $^ $(LIBS)
$(LIB): $(OBJS)
$(AR) cru $(LIB) $(OBJS)
diff --git a/nfsmount/dummypmap.c b/nfsmount/dummypmap.c
new file mode 100644
index 0000000000000..adf64c9e6fe89
--- /dev/null
+++ b/nfsmount/dummypmap.c
@@ -0,0 +1,141 @@
+/*
+ * dummypmap.c
+ *
+ * Enough portmapper functionality that mount doesn't hang trying
+ * to start lockd. Enables nfsroot with locking functionality.
+ *
+ * Note: the kernel will only speak to the local portmapper
+ * using RPC over UDP.
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include "sunrpc.h"
+
+struct portmap_call
+{
+ struct rpc_call rpc;
+ __u32 program;
+ __u32 version;
+ __u32 proto;
+ __u32 port;
+};
+
+struct portmap_reply
+{
+ struct rpc_reply rpc;
+ __u32 port;
+};
+
+FILE *portmap_file = 0;
+
+int dummy_portmap(void)
+{
+ int sock = socket(PF_INET, SOCK_DGRAM, 0);
+ struct sockaddr_in sin;
+ int pktlen, addrlen;
+ union {
+ struct portmap_call c;
+ unsigned char b[65536]; /* Max UDP packet size */
+ } pkt;
+ struct portmap_reply rply;
+
+ if ( sock < 0 )
+ return -1;
+
+ memset(&sin, 0, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
+ sin.sin_port = htons(RPC_PMAP_PORT);
+ if ( bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0 ) {
+ int err = errno;
+ close(sock);
+ errno = err;
+ return -1;
+ }
+
+ for(;;) {
+ addrlen = sizeof sin;
+ pktlen = recvfrom(sock, &pkt.c.rpc.hdr.udp, sizeof pkt, 0,
+ (struct sockaddr *)&sin, &addrlen);
+
+ if ( pktlen < 0 ) {
+ if ( errno == EINTR )
+ continue;
+
+ return -1;
+ }
+
+ /* +4 to skip the TCP fragment header */
+ if ( pktlen+4 < sizeof(struct portmap_call) )
+ continue; /* Bad packet */
+
+ if ( pkt.c.rpc.hdr.udp.msg_type != htonl(RPC_CALL) )
+ continue; /* Bad packet */
+
+ memset(&rply, 0, sizeof rply);
+
+ rply.rpc.hdr.udp.xid = pkt.c.rpc.hdr.udp.xid;
+ rply.rpc.hdr.udp.msg_type = htonl(RPC_REPLY);
+
+ if ( pkt.c.rpc.rpc_vers != htonl(2) ) {
+ rply.rpc.reply_state = htonl(REPLY_DENIED);
+ /* state <- RPC_MISMATCH == 0 */
+ } else if ( pkt.c.rpc.program != htonl(PORTMAP_PROGRAM) ) {
+ rply.rpc.reply_state = htonl(PROG_UNAVAIL);
+ } else if ( pkt.c.rpc.prog_vers != htonl(2) ) {
+ rply.rpc.reply_state = htonl(PROG_MISMATCH);
+ } else if ( pkt.c.rpc.cred_len != 0 ||
+ pkt.c.rpc.vrf_len != 0 ) {
+ /* Can't deal with credentials data; the kernel won't send them */
+ rply.rpc.reply_state = htonl(SYSTEM_ERR);
+ } else {
+ switch ( ntohl(pkt.c.rpc.proc) ) {
+ case PMAP_PROC_NULL:
+ break;
+ case PMAP_PROC_SET:
+ if ( pkt.c.proto == htonl(IPPROTO_TCP) ||
+ pkt.c.proto == htonl(IPPROTO_UDP) ) {
+ if ( portmap_file )
+ fprintf(portmap_file, "%u %u %s %u\n",
+ ntohl(pkt.c.program), ntohl(pkt.c.version),
+ pkt.c.proto == htonl(IPPROTO_TCP) ? "tcp" : "udp",
+ ntohl(pkt.c.port));
+ rply.port = htonl(1); /* TRUE = success */
+ }
+ break;
+ case PMAP_PROC_UNSET:
+ rply.port = htonl(1); /* TRUE = success */
+ break;
+ case PMAP_PROC_GETPORT:
+ break;
+ case PMAP_PROC_DUMP:
+ break;
+ default:
+ rply.rpc.reply_state = htonl(PROC_UNAVAIL);
+ break;
+ }
+ }
+
+ sendto(sock, &rply.rpc.hdr.udp, sizeof rply - 4, 0,
+ (struct sockaddr *)&sin, addrlen);
+ }
+}
+
+#ifdef TEST
+int main(int argc, char *argv[])
+{
+ if ( argc > 1 )
+ portmap_file = fopen(argv[1], "a");
+
+ return dummy_portmap();
+}
+#endif
+
diff --git a/nfsmount/dummypmap.h b/nfsmount/dummypmap.h
new file mode 100644
index 0000000000000..2625133d9114f
--- /dev/null
+++ b/nfsmount/dummypmap.h
@@ -0,0 +1,11 @@
+/*
+ * dummyportmap.h
+ *
+ * Functions for the portmap spoofer
+ */
+
+#include <stdio.h>
+
+extern FILE *portmap_file;
+int dummy_portmap(void);
+
diff --git a/nfsmount/dummypmap_test.c b/nfsmount/dummypmap_test.c
new file mode 100644
index 0000000000000..d81a14156cad4
--- /dev/null
+++ b/nfsmount/dummypmap_test.c
@@ -0,0 +1,2 @@
+#define TEST
+#include "dummypmap.c"
diff --git a/nfsmount/main.c b/nfsmount/main.c
index 361a5451e2f17..c5eae47a4e886 100644
--- a/nfsmount/main.c
+++ b/nfsmount/main.c
@@ -10,11 +10,13 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <signal.h>
#include <linux/nfs_mount.h>
#include "nfsmount.h"
#include "sunrpc.h"
+#include "dummypmap.h"
static char *progname;
@@ -161,17 +163,22 @@ int nfsmount_main(int argc, char *argv[])
char *hostname;
char *path;
int c;
+ int spoof_portmap = 0;
progname = argv[0];
gettimeofday(&now, NULL);
srand48(now.tv_usec ^ (now.tv_sec << 24));
- while ((c = getopt(argc, argv, "o:")) != EOF) {
+ while ((c = getopt(argc, argv, "o:p:")) != EOF) {
switch (c) {
case 'o':
parse_opts(optarg);
break;
+ case 'p':
+ spoof_portmap = 1;
+ portmap_file = fopen(optarg, "w");
+ break;
case '?':
fprintf(stderr, "%s: invalid option -%c\n",
progname, optopt);
@@ -213,10 +220,26 @@ int nfsmount_main(int argc, char *argv[])
check_path(path);
+ if ( spoof_portmap ) {
+ spoof_portmap = fork();
+ if ( spoof_portmap == -1 ) {
+ fprintf(stderr, "%s: cannot fork\n", progname);
+ exit(1);
+ } else if ( spoof_portmap == 0 ) {
+ /* Child process */
+ dummy_portmap();
+ _exit(255); /* Error */
+ }
+ }
+
if (nfs_mount(rem_name, hostname, server, rem_path, path,
&mount_data) != 0)
return 1;
+ /* If we set up the spoofer, tear it down now */
+ if ( spoof_portmap )
+ kill(SIGTERM, spoof_portmap);
+
free(rem_name);
return 0;
diff --git a/nfsmount/sunrpc.h b/nfsmount/sunrpc.h
index 7706eea36d4d0..563e039026121 100644
--- a/nfsmount/sunrpc.h
+++ b/nfsmount/sunrpc.h
@@ -9,16 +9,29 @@
#define RPC_REPLY 1
#define PORTMAP_PROGRAM 100000
+#define NLM_PROGRAM 100021
#define RPC_PMAP_PROGRAM 100000
#define RPC_PMAP_VERSION 2
#define RPC_PMAP_PORT 111
+#define PMAP_PROC_NULL 0
+#define PMAP_PROC_SET 1
+#define PMAP_PROC_UNSET 2
#define PMAP_PROC_GETPORT 3
+#define PMAP_PROC_DUMP 4
#define LAST_FRAG 0x80000000
#define REPLY_OK 0
+#define REPLY_DENIED 1
+
+#define SUCCESS 0
+#define PROG_UNAVAIL 1
+#define PROG_MISMATCH 2
+#define PROC_UNAVAIL 3
+#define GARBAGE_ARGS 4
+#define SYSTEM_ERR 5
struct rpc_udp_header
{