| /* |
| * 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" |
| |
| extern const char *progname; |
| |
| struct portmap_call { |
| struct rpc_call rpc; |
| uint32_t program; |
| uint32_t version; |
| uint32_t proto; |
| uint32_t port; |
| }; |
| |
| struct portmap_reply { |
| struct rpc_reply rpc; |
| uint32_t port; |
| }; |
| |
| static int bind_portmap(void) |
| { |
| int sock = socket(PF_INET, SOCK_DGRAM, 0); |
| struct sockaddr_in sin; |
| |
| 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; |
| } |
| |
| return sock; |
| } |
| |
| static const char *protoname(uint32_t proto) |
| { |
| switch (ntohl(proto)) { |
| case IPPROTO_TCP: |
| return "tcp"; |
| case IPPROTO_UDP: |
| return "udp"; |
| default: |
| return NULL; |
| } |
| } |
| |
| static int dummy_portmap(int sock, FILE *portmap_file) |
| { |
| 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; |
| |
| 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), |
| protoname(pkt.c.proto), |
| 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); |
| } |
| } |
| |
| pid_t start_dummy_portmap(const char *file) |
| { |
| FILE *portmap_filep; |
| int sock; |
| pid_t spoof_portmap; |
| |
| portmap_filep = fopen(file, "w"); |
| if (!portmap_filep) { |
| fprintf(stderr, "%s: cannot write portmap file: %s\n", |
| progname, file); |
| return -1; |
| } |
| |
| sock = bind_portmap(); |
| if (sock == -1) { |
| if (errno == EINVAL || errno == EADDRINUSE) |
| return 0; /* Assume not needed */ |
| else { |
| fprintf(stderr, "%s: portmap spoofing failed\n", |
| progname); |
| return -1; |
| } |
| } |
| |
| spoof_portmap = fork(); |
| if (spoof_portmap == -1) { |
| fprintf(stderr, "%s: cannot fork\n", progname); |
| return -1; |
| } else if (spoof_portmap == 0) { |
| /* Child process */ |
| dummy_portmap(sock, portmap_filep); |
| _exit(255); /* Error */ |
| } else { |
| /* Parent process */ |
| close(sock); |
| return spoof_portmap; |
| } |
| } |