diff options
author | H. Peter Anvin <hpa@zytor.com> | 2004-06-17 04:46:38 +0000 |
---|---|---|
committer | H. Peter Anvin <hpa@zytor.com> | 2004-06-17 04:46:38 +0000 |
commit | 4274cf268f055e0e0a9b3f6b036426e8982837b8 (patch) | |
tree | c531eabd8b1a0c06e334afca5b414ecf9ec532c0 | |
parent | 30a7b06b2560f415b958ce446412e13ee6d1ddf7 (diff) | |
download | klibc-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/Makefile | 7 | ||||
-rw-r--r-- | nfsmount/dummypmap.c | 141 | ||||
-rw-r--r-- | nfsmount/dummypmap.h | 11 | ||||
-rw-r--r-- | nfsmount/dummypmap_test.c | 2 | ||||
-rw-r--r-- | nfsmount/main.c | 25 | ||||
-rw-r--r-- | nfsmount/sunrpc.h | 13 |
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 { |