Magellan Linux

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1297 - (show 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 /*
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 #include "dummypmap.h"
19 #include "sunrpc.h"
20
21 extern const char *progname;
22
23 struct portmap_args {
24 uint32_t program;
25 uint32_t version;
26 uint32_t proto;
27 uint32_t port;
28 };
29
30 struct portmap_call {
31 struct rpc_call rpc;
32 struct portmap_args args;
33 };
34
35 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 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 static int dummy_portmap(int sock, FILE *portmap_file)
148 {
149 struct sockaddr_in sin;
150 int pktlen, addrlen;
151 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 struct portmap_reply rply;
158
159 for (;;) {
160 addrlen = sizeof sin;
161 pktlen = recvfrom(sock, &pkt, sizeof pkt, 0,
162 (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 if (rpc->hdr.udp.msg_type != htonl(RPC_CALL))
176 continue; /* Bad packet */
177
178 memset(&rply, 0, sizeof rply);
179
180 rply.rpc.hdr.udp.xid = rpc->hdr.udp.xid;
181 rply.rpc.hdr.udp.msg_type = htonl(RPC_REPLY);
182
183 cred = (struct rpc_auth *) &rpc->cred_flavor;
184 if (rpc->rpc_vers != htonl(2)) {
185 rply.rpc.reply_state = htonl(REPLY_DENIED);
186 /* state <- RPC_MISMATCH == 0 */
187 } else if (rpc->program != htonl(PORTMAP_PROGRAM)) {
188 rply.rpc.reply_state = htonl(PROG_UNAVAIL);
189 } else if (rpc->prog_vers != htonl(2)) {
190 rply.rpc.reply_state = htonl(PROG_MISMATCH);
191 } 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 /* Can't deal with credentials data; the kernel
197 won't send them */
198 rply.rpc.reply_state = htonl(SYSTEM_ERR);
199 } else {
200 switch (ntohl(rpc->proc)) {
201 case PMAP_PROC_NULL:
202 break;
203 case PMAP_PROC_SET:
204 if (args->proto == htonl(IPPROTO_TCP) ||
205 args->proto == htonl(IPPROTO_UDP)) {
206 if (portmap_file)
207 fprintf(portmap_file,
208 "%u %u %s %u\n",
209 ntohl(args->program),
210 ntohl(args->version),
211 protoname(args->proto),
212 ntohl(args->port));
213 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 fclose(portmap_filep);
253 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 fclose(portmap_filep);
262 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 }