Magellan Linux

Annotation of /trunk/mkinitrd-magellan/klibc/usr/kinit/nfsmount/dummypmap.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1297 - (hide annotations) (download)
Fri May 27 15:12:11 2011 UTC (12 years, 11 months ago) by niro
File MIME type: text/plain
File size: 5797 byte(s)
-updated to klibc-1.5.22 with mntproc definitions patch included
1 niro 532 /*
2     * Enough portmapper functionality that mount doesn't hang trying
3     * to start lockd. Enables nfsroot with locking functionality.
4     *
5     * Note: the kernel will only speak to the local portmapper
6     * using RPC over UDP.
7     */
8    
9     #include <sys/types.h>
10     #include <netinet/in.h>
11     #include <stdio.h>
12     #include <stdlib.h>
13     #include <errno.h>
14     #include <unistd.h>
15     #include <string.h>
16     #include <sys/socket.h>
17    
18 niro 815 #include "dummypmap.h"
19 niro 532 #include "sunrpc.h"
20    
21     extern const char *progname;
22    
23 niro 815 struct portmap_args {
24 niro 532 uint32_t program;
25     uint32_t version;
26     uint32_t proto;
27     uint32_t port;
28     };
29    
30 niro 815 struct portmap_call {
31     struct rpc_call rpc;
32     struct portmap_args args;
33     };
34    
35 niro 532 struct portmap_reply {
36     struct rpc_reply rpc;
37     uint32_t port;
38     };
39    
40     static int bind_portmap(void)
41     {
42     int sock = socket(PF_INET, SOCK_DGRAM, 0);
43     struct sockaddr_in sin;
44    
45     if (sock < 0)
46     return -1;
47    
48     memset(&sin, 0, sizeof sin);
49     sin.sin_family = AF_INET;
50     sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
51     sin.sin_port = htons(RPC_PMAP_PORT);
52     if (bind(sock, (struct sockaddr *)&sin, sizeof sin) < 0) {
53     int err = errno;
54     close(sock);
55     errno = err;
56     return -1;
57     }
58    
59     return sock;
60     }
61    
62     static const char *protoname(uint32_t proto)
63     {
64     switch (ntohl(proto)) {
65     case IPPROTO_TCP:
66     return "tcp";
67     case IPPROTO_UDP:
68     return "udp";
69     default:
70     return NULL;
71     }
72     }
73    
74 niro 815 static void * get_auth(struct rpc_auth *auth)
75     {
76     switch (ntohl(auth->flavor)) {
77     case AUTH_NULL:
78     /* Fallthrough */
79     case AUTH_UNIX:
80     return (char *)&auth->body + ntohl(auth->len);
81     default:
82     return NULL;
83     }
84     }
85    
86     static int check_unix_cred(struct rpc_auth *cred)
87     {
88     uint32_t len;
89     int quad_len;
90     uint32_t node_name_len;
91     int quad_name_len;
92     uint32_t *base;
93     uint32_t *pos;
94     int ret = -1;
95    
96     len = ntohl(cred->len);
97     quad_len = (len + 3) >> 2;
98     if (quad_len < 6)
99     /* Malformed creds */
100     goto out;
101    
102     base = pos = cred->body;
103    
104     /* Skip timestamp */
105     pos++;
106    
107     /* Skip node name: only localhost can succeed. */
108     node_name_len = ntohl(*pos++);
109     quad_name_len = (node_name_len + 3) >> 2;
110     if (pos + quad_name_len + 3 > base + quad_len)
111     /* Malformed creds */
112     goto out;
113     pos += quad_name_len;
114    
115     /* uid must be 0 */
116     if (*pos++ != 0)
117     goto out;
118    
119     /* gid must be 0 */
120     if (*pos++ != 0)
121     goto out;
122    
123     /* Skip remaining gids */
124     ret = 0;
125    
126     out:
127     return ret;
128     }
129    
130     static int check_cred(struct rpc_auth *cred)
131     {
132     switch (ntohl(cred->flavor)) {
133     case AUTH_NULL:
134     return 0;
135     case AUTH_UNIX:
136     return check_unix_cred(cred);
137     default:
138     return -1;
139     }
140     }
141    
142     static int check_vrf(struct rpc_auth *vrf)
143     {
144     return (vrf->flavor == htonl(AUTH_NULL)) ? 0 : -1;
145     }
146    
147 niro 532 static int dummy_portmap(int sock, FILE *portmap_file)
148     {
149     struct sockaddr_in sin;
150     int pktlen, addrlen;
151 niro 815 unsigned char pkt[65536]; /* Max UDP packet size */
152     /* RPC UDP packets do not include TCP fragment size */
153     struct rpc_call *rpc = (struct rpc_call *) &pkt[-4];
154     struct rpc_auth *cred;
155     struct rpc_auth *vrf;
156     struct portmap_args *args;
157 niro 532 struct portmap_reply rply;
158    
159     for (;;) {
160     addrlen = sizeof sin;
161 niro 815 pktlen = recvfrom(sock, &pkt, sizeof pkt, 0,
162 niro 532 (struct sockaddr *)&sin, &addrlen);
163    
164     if (pktlen < 0) {
165     if (errno == EINTR)
166     continue;
167    
168     return -1;
169     }
170    
171     /* +4 to skip the TCP fragment header */
172     if (pktlen + 4 < sizeof(struct portmap_call))
173     continue; /* Bad packet */
174    
175 niro 815 if (rpc->hdr.udp.msg_type != htonl(RPC_CALL))
176 niro 532 continue; /* Bad packet */
177    
178     memset(&rply, 0, sizeof rply);
179    
180 niro 815 rply.rpc.hdr.udp.xid = rpc->hdr.udp.xid;
181 niro 532 rply.rpc.hdr.udp.msg_type = htonl(RPC_REPLY);
182    
183 niro 815 cred = (struct rpc_auth *) &rpc->cred_flavor;
184     if (rpc->rpc_vers != htonl(2)) {
185 niro 532 rply.rpc.reply_state = htonl(REPLY_DENIED);
186     /* state <- RPC_MISMATCH == 0 */
187 niro 815 } else if (rpc->program != htonl(PORTMAP_PROGRAM)) {
188 niro 532 rply.rpc.reply_state = htonl(PROG_UNAVAIL);
189 niro 815 } else if (rpc->prog_vers != htonl(2)) {
190 niro 532 rply.rpc.reply_state = htonl(PROG_MISMATCH);
191 niro 815 } else if (!(vrf = get_auth(cred)) ||
192     (char *)vrf > (char *)pkt + pktlen - 8 - sizeof(*args) ||
193     !(args = get_auth(vrf)) ||
194     (char *)args > (char *)pkt + pktlen - sizeof(*args) ||
195     check_cred(cred) || check_vrf(vrf)) {
196 niro 532 /* Can't deal with credentials data; the kernel
197     won't send them */
198     rply.rpc.reply_state = htonl(SYSTEM_ERR);
199     } else {
200 niro 815 switch (ntohl(rpc->proc)) {
201 niro 532 case PMAP_PROC_NULL:
202     break;
203     case PMAP_PROC_SET:
204 niro 815 if (args->proto == htonl(IPPROTO_TCP) ||
205     args->proto == htonl(IPPROTO_UDP)) {
206 niro 532 if (portmap_file)
207     fprintf(portmap_file,
208     "%u %u %s %u\n",
209 niro 815 ntohl(args->program),
210     ntohl(args->version),
211     protoname(args->proto),
212     ntohl(args->port));
213 niro 532 rply.port = htonl(1); /* TRUE = success */
214     }
215     break;
216     case PMAP_PROC_UNSET:
217     rply.port = htonl(1); /* TRUE = success */
218     break;
219     case PMAP_PROC_GETPORT:
220     break;
221     case PMAP_PROC_DUMP:
222     break;
223     default:
224     rply.rpc.reply_state = htonl(PROC_UNAVAIL);
225     break;
226     }
227     }
228    
229     sendto(sock, &rply.rpc.hdr.udp, sizeof rply - 4, 0,
230     (struct sockaddr *)&sin, addrlen);
231     }
232     }
233    
234     pid_t start_dummy_portmap(const char *file)
235     {
236     FILE *portmap_filep;
237     int sock;
238     pid_t spoof_portmap;
239    
240     portmap_filep = fopen(file, "w");
241     if (!portmap_filep) {
242     fprintf(stderr, "%s: cannot write portmap file: %s\n",
243     progname, file);
244     return -1;
245     }
246    
247     sock = bind_portmap();
248     if (sock == -1) {
249     if (errno == EINVAL || errno == EADDRINUSE)
250     return 0; /* Assume not needed */
251     else {
252 niro 1297 fclose(portmap_filep);
253 niro 532 fprintf(stderr, "%s: portmap spoofing failed\n",
254     progname);
255     return -1;
256     }
257     }
258    
259     spoof_portmap = fork();
260     if (spoof_portmap == -1) {
261 niro 1297 fclose(portmap_filep);
262 niro 532 fprintf(stderr, "%s: cannot fork\n", progname);
263     return -1;
264     } else if (spoof_portmap == 0) {
265     /* Child process */
266     dummy_portmap(sock, portmap_filep);
267     _exit(255); /* Error */
268     } else {
269     /* Parent process */
270     close(sock);
271     return spoof_portmap;
272     }
273     }