1 | /* $NetBSD: krpc_subr.c,v 1.42 2016/06/10 13:27:16 ozaki-r Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 1995 Gordon Ross, Adam Glass |
5 | * Copyright (c) 1992 Regents of the University of California. |
6 | * All rights reserved. |
7 | * |
8 | * This software was developed by the Computer Systems Engineering group |
9 | * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and |
10 | * contributed to Berkeley. |
11 | * |
12 | * Redistribution and use in source and binary forms, with or without |
13 | * modification, are permitted provided that the following conditions |
14 | * are met: |
15 | * 1. Redistributions of source code must retain the above copyright |
16 | * notice, this list of conditions and the following disclaimer. |
17 | * 2. Redistributions in binary form must reproduce the above copyright |
18 | * notice, this list of conditions and the following disclaimer in the |
19 | * documentation and/or other materials provided with the distribution. |
20 | * 3. All advertising materials mentioning features or use of this software |
21 | * must display the following acknowledgement: |
22 | * This product includes software developed by the University of |
23 | * California, Lawrence Berkeley Laboratory and its contributors. |
24 | * 4. Neither the name of the University nor the names of its contributors |
25 | * may be used to endorse or promote products derived from this software |
26 | * without specific prior written permission. |
27 | * |
28 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
29 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
30 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
31 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
32 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
33 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
34 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
35 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
36 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
37 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
38 | * SUCH DAMAGE. |
39 | * |
40 | * partially based on: |
41 | * libnetboot/rpc.c |
42 | * @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp (LBL) |
43 | */ |
44 | |
45 | #include <sys/cdefs.h> |
46 | __KERNEL_RCSID(0, "$NetBSD: krpc_subr.c,v 1.42 2016/06/10 13:27:16 ozaki-r Exp $" ); |
47 | |
48 | #include <sys/param.h> |
49 | #include <sys/systm.h> |
50 | #include <sys/ioctl.h> |
51 | #include <sys/proc.h> |
52 | #include <sys/mount.h> |
53 | #include <sys/mbuf.h> |
54 | #include <sys/reboot.h> |
55 | #include <sys/socket.h> |
56 | #include <sys/socketvar.h> |
57 | |
58 | #include <netinet/in.h> |
59 | |
60 | #include <nfs/rpcv2.h> |
61 | #include <nfs/krpc.h> |
62 | #include <nfs/xdr_subs.h> |
63 | #include <nfs/nfsproto.h> /* XXX NFSX_V3FHMAX for next */ |
64 | #include <nfs/nfs.h> |
65 | #include <nfs/nfsmount.h> |
66 | #include <nfs/nfsdiskless.h> /* XXX decl nfs_boot_sendrecv */ |
67 | |
68 | /* |
69 | * Kernel support for Sun RPC |
70 | * |
71 | * Used currently for bootstrapping in nfs diskless configurations. |
72 | */ |
73 | |
74 | /* |
75 | * Generic RPC headers |
76 | */ |
77 | |
78 | struct auth_info { |
79 | u_int32_t authtype; /* auth type */ |
80 | u_int32_t authlen; /* auth length */ |
81 | }; |
82 | |
83 | struct auth_unix { |
84 | int32_t ua_time; |
85 | int32_t ua_hostname; /* null */ |
86 | int32_t ua_uid; |
87 | int32_t ua_gid; |
88 | int32_t ua_gidlist; /* null */ |
89 | }; |
90 | |
91 | struct rpc_call { |
92 | u_int32_t rp_xid; /* request transaction id */ |
93 | int32_t rp_direction; /* call direction (0) */ |
94 | u_int32_t rp_rpcvers; /* rpc version (2) */ |
95 | u_int32_t rp_prog; /* program */ |
96 | u_int32_t rp_vers; /* version */ |
97 | u_int32_t rp_proc; /* procedure */ |
98 | struct auth_info rpc_auth; |
99 | struct auth_unix rpc_unix; |
100 | struct auth_info rpc_verf; |
101 | }; |
102 | |
103 | struct rpc_reply { |
104 | u_int32_t rp_xid; /* request transaction id */ |
105 | int32_t rp_direction; /* call direction (1) */ |
106 | int32_t rp_astatus; /* accept status (0: accepted) */ |
107 | union { |
108 | /* rejected */ |
109 | struct { |
110 | u_int32_t rej_stat; |
111 | u_int32_t rej_val1; |
112 | u_int32_t rej_val2; |
113 | } rpu_rej; |
114 | /* accepted */ |
115 | struct { |
116 | struct auth_info rok_auth; |
117 | u_int32_t rok_status; |
118 | } rpu_rok; |
119 | } rp_u; |
120 | }; |
121 | #define rp_rstat rp_u.rpu_rej.rej_stat |
122 | #define rp_auth rp_u.rpu_rok.rok_auth |
123 | #define rp_status rp_u.rpu_rok.rok_status |
124 | |
125 | #define MIN_REPLY_HDR 16 /* xid, dir, astat, errno */ |
126 | |
127 | static int krpccheck(struct mbuf**, void*); |
128 | |
129 | /* |
130 | * Call portmap to lookup a port number for a particular rpc program |
131 | * Returns non-zero error on failure. |
132 | */ |
133 | int |
134 | krpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int proto, u_int16_t *portp, struct lwp *l) |
135 | /* sin: server address */ |
136 | /* prog, vers, proto: host order */ |
137 | /* portp: network order */ |
138 | { |
139 | struct sdata { |
140 | u_int32_t prog; /* call program */ |
141 | u_int32_t vers; /* call version */ |
142 | u_int32_t proto; /* call protocol */ |
143 | u_int32_t port; /* call port (unused) */ |
144 | } *sdata; |
145 | struct rdata { |
146 | u_int16_t pad; |
147 | u_int16_t port; |
148 | } *rdata; |
149 | struct mbuf *m; |
150 | int error; |
151 | |
152 | /* The portmapper port is fixed. */ |
153 | if (prog == PMAPPROG) { |
154 | *portp = htons(PMAPPORT); |
155 | return 0; |
156 | } |
157 | |
158 | m = m_get(M_WAIT, MT_DATA); |
159 | sdata = mtod(m, struct sdata *); |
160 | m->m_len = sizeof(*sdata); |
161 | |
162 | /* Do the RPC to get it. */ |
163 | sdata->prog = txdr_unsigned(prog); |
164 | sdata->vers = txdr_unsigned(vers); |
165 | sdata->proto = txdr_unsigned(proto); |
166 | sdata->port = 0; |
167 | |
168 | sin->sin_port = htons(PMAPPORT); |
169 | error = krpc_call(sin, PMAPPROG, PMAPVERS, |
170 | PMAPPROC_GETPORT, &m, NULL, l); |
171 | if (error) |
172 | return error; |
173 | |
174 | if (m->m_len < sizeof(*rdata)) { |
175 | m = m_pullup(m, sizeof(*rdata)); |
176 | if (m == NULL) |
177 | return ENOBUFS; |
178 | } |
179 | rdata = mtod(m, struct rdata *); |
180 | *portp = rdata->port; |
181 | |
182 | m_freem(m); |
183 | return 0; |
184 | } |
185 | |
186 | static int krpccheck(struct mbuf **mp, void *context) |
187 | { |
188 | struct rpc_reply *reply; |
189 | struct mbuf *m = *mp; |
190 | |
191 | /* Does the reply contain at least a header? */ |
192 | if (m->m_pkthdr.len < MIN_REPLY_HDR) |
193 | return(-1); |
194 | if (m->m_len < sizeof(struct rpc_reply)) { |
195 | m = *mp = m_pullup(m, sizeof(struct rpc_reply)); |
196 | if (m == NULL) |
197 | return(-1); |
198 | } |
199 | reply = mtod(m, struct rpc_reply *); |
200 | |
201 | /* Is it the right reply? */ |
202 | if (reply->rp_direction != txdr_unsigned(RPC_REPLY)) |
203 | return(-1); |
204 | |
205 | if (reply->rp_xid != txdr_unsigned(*(u_int32_t*)context)) |
206 | return(-1); |
207 | |
208 | return(0); |
209 | } |
210 | |
211 | /* |
212 | * Do a remote procedure call (RPC) and wait for its reply. |
213 | * If from_p is non-null, then we are doing broadcast, and |
214 | * the address from whence the response came is saved there. |
215 | */ |
216 | int |
217 | krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func, struct mbuf **data, struct mbuf **from_p, struct lwp *l) |
218 | /* data: input/output */ |
219 | /* from_p: output */ |
220 | { |
221 | struct socket *so; |
222 | struct sockaddr_in sin; |
223 | struct mbuf *m, *mhead, *from; |
224 | struct rpc_call *call; |
225 | struct rpc_reply *reply; |
226 | int error, len; |
227 | static u_int32_t xid = ~0xFF; |
228 | u_int16_t tport; |
229 | |
230 | /* |
231 | * Validate address family. |
232 | * Sorry, this is INET specific... |
233 | */ |
234 | if (sa->sin_family != AF_INET) |
235 | return (EAFNOSUPPORT); |
236 | |
237 | /* Free at end if not null. */ |
238 | mhead = NULL; |
239 | from = NULL; |
240 | |
241 | /* |
242 | * Create socket and set its receive timeout. |
243 | */ |
244 | if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0, l, NULL))) |
245 | return error; |
246 | |
247 | if ((error = nfs_boot_setrecvtimo(so))) |
248 | goto out; |
249 | |
250 | /* |
251 | * Enable broadcast if necessary. |
252 | */ |
253 | if (from_p) { |
254 | if ((error = nfs_boot_enbroadcast(so))) |
255 | goto out; |
256 | } |
257 | |
258 | /* |
259 | * Bind the local endpoint to a reserved port, |
260 | * because some NFS servers refuse requests from |
261 | * non-reserved (non-privileged) ports. |
262 | */ |
263 | tport = IPPORT_RESERVED; |
264 | do { |
265 | tport--; |
266 | error = nfs_boot_sobind_ipport(so, tport, l); |
267 | } while (error == EADDRINUSE && |
268 | tport > IPPORT_RESERVED / 2); |
269 | if (error) { |
270 | printf("bind failed\n" ); |
271 | goto out; |
272 | } |
273 | |
274 | /* |
275 | * Setup socket address for the server. |
276 | */ |
277 | sin = *sa; |
278 | |
279 | /* |
280 | * Prepend RPC message header. |
281 | */ |
282 | mhead = m_gethdr(M_WAIT, MT_DATA); |
283 | mhead->m_next = *data; |
284 | call = mtod(mhead, struct rpc_call *); |
285 | mhead->m_len = sizeof(*call); |
286 | memset((void *)call, 0, sizeof(*call)); |
287 | /* rpc_call part */ |
288 | xid++; |
289 | call->rp_xid = txdr_unsigned(xid); |
290 | /* call->rp_direction = 0; */ |
291 | call->rp_rpcvers = txdr_unsigned(2); |
292 | call->rp_prog = txdr_unsigned(prog); |
293 | call->rp_vers = txdr_unsigned(vers); |
294 | call->rp_proc = txdr_unsigned(func); |
295 | /* rpc_auth part (auth_unix as root) */ |
296 | call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX); |
297 | call->rpc_auth.authlen = txdr_unsigned(sizeof(struct auth_unix)); |
298 | /* rpc_verf part (auth_null) */ |
299 | call->rpc_verf.authtype = 0; |
300 | call->rpc_verf.authlen = 0; |
301 | |
302 | /* |
303 | * Setup packet header |
304 | */ |
305 | len = 0; |
306 | m = mhead; |
307 | while (m) { |
308 | len += m->m_len; |
309 | m = m->m_next; |
310 | } |
311 | mhead->m_pkthdr.len = len; |
312 | m_reset_rcvif(mhead); |
313 | |
314 | error = nfs_boot_sendrecv(so, &sin, NULL, mhead, krpccheck, &m, &from, |
315 | &xid, l); |
316 | if (error) |
317 | goto out; |
318 | |
319 | /* m_pullup() was done in krpccheck() */ |
320 | reply = mtod(m, struct rpc_reply *); |
321 | |
322 | /* Was RPC accepted? (authorization OK) */ |
323 | if (reply->rp_astatus != 0) { |
324 | /* Note: This is NOT an error code! */ |
325 | error = fxdr_unsigned(u_int32_t, reply->rp_rstat); |
326 | switch (error) { |
327 | case RPC_MISMATCH: |
328 | /* .re_status = RPC_VERSMISMATCH; */ |
329 | error = ERPCMISMATCH; |
330 | break; |
331 | case RPC_AUTHERR: |
332 | /* .re_status = RPC_AUTHERROR; */ |
333 | error = EAUTH; |
334 | break; |
335 | default: |
336 | /* unexpected */ |
337 | error = EBADRPC; |
338 | break; |
339 | } |
340 | goto out; |
341 | } |
342 | |
343 | /* Did the call succeed? */ |
344 | if (reply->rp_status != 0) { |
345 | /* Note: This is NOT an error code! */ |
346 | error = fxdr_unsigned(u_int32_t, reply->rp_status); |
347 | switch (error) { |
348 | case RPC_PROGUNAVAIL: |
349 | error = EPROGUNAVAIL; |
350 | break; |
351 | case RPC_PROGMISMATCH: |
352 | error = EPROGMISMATCH; |
353 | break; |
354 | case RPC_PROCUNAVAIL: |
355 | error = EPROCUNAVAIL; |
356 | break; |
357 | case RPC_GARBAGE: |
358 | default: |
359 | error = EBADRPC; |
360 | } |
361 | goto out; |
362 | } |
363 | |
364 | /* |
365 | * OK, we have received a good reply! |
366 | * Get its length, then strip it off. |
367 | */ |
368 | len = sizeof(*reply); |
369 | if (reply->rp_auth.authtype != 0) { |
370 | len += fxdr_unsigned(u_int32_t, reply->rp_auth.authlen); |
371 | len = (len + 3) & ~3; /* XXX? */ |
372 | } |
373 | m_adj(m, len); |
374 | |
375 | /* result */ |
376 | *data = m; |
377 | if (from_p && error == 0) { |
378 | *from_p = from; |
379 | from = NULL; |
380 | } |
381 | |
382 | out: |
383 | if (mhead) m_freem(mhead); |
384 | if (from) m_freem(from); |
385 | soclose(so); |
386 | return error; |
387 | } |
388 | |
389 | /* |
390 | * eXternal Data Representation routines. |
391 | * (but with non-standard args...) |
392 | */ |
393 | |
394 | /* |
395 | * String representation for RPC. |
396 | */ |
397 | struct xdr_string { |
398 | u_int32_t len; /* length without null or padding */ |
399 | char data[4]; /* data (longer, of course) */ |
400 | /* data is padded to a long-word boundary */ |
401 | }; |
402 | |
403 | struct mbuf * |
404 | xdr_string_encode(char *str, int len) |
405 | { |
406 | struct mbuf *m; |
407 | struct xdr_string *xs; |
408 | int dlen; /* padded string length */ |
409 | int mlen; /* message length */ |
410 | |
411 | dlen = (len + 3) & ~3; |
412 | mlen = dlen + 4; |
413 | |
414 | if (mlen > MCLBYTES) /* If too big, we just can't do it. */ |
415 | return (NULL); |
416 | |
417 | m = m_get(M_WAIT, MT_DATA); |
418 | if (mlen > MLEN) { |
419 | m_clget(m, M_WAIT); |
420 | if ((m->m_flags & M_EXT) == 0) { |
421 | (void) m_free(m); /* There can be only one. */ |
422 | return (NULL); |
423 | } |
424 | } |
425 | xs = mtod(m, struct xdr_string *); |
426 | m->m_len = mlen; |
427 | xs->len = txdr_unsigned(len); |
428 | memcpy(xs->data, str, len); |
429 | return (m); |
430 | } |
431 | |
432 | struct mbuf * |
433 | xdr_string_decode(struct mbuf *m, char *str, int *len_p) |
434 | /* len_p: bufsize - 1 */ |
435 | { |
436 | struct xdr_string *xs; |
437 | int mlen; /* message length */ |
438 | int slen; /* string length */ |
439 | |
440 | if (m->m_len < 4) { |
441 | m = m_pullup(m, 4); |
442 | if (m == NULL) |
443 | return (NULL); |
444 | } |
445 | xs = mtod(m, struct xdr_string *); |
446 | slen = fxdr_unsigned(u_int32_t, xs->len); |
447 | mlen = 4 + ((slen + 3) & ~3); |
448 | |
449 | if (slen > *len_p) |
450 | slen = *len_p; |
451 | m_copydata(m, 4, slen, str); |
452 | m_adj(m, mlen); |
453 | |
454 | str[slen] = '\0'; |
455 | *len_p = slen; |
456 | |
457 | return (m); |
458 | } |
459 | |
460 | |
461 | /* |
462 | * Inet address in RPC messages |
463 | * (Note, really four ints, NOT chars. Blech.) |
464 | */ |
465 | struct xdr_inaddr { |
466 | u_int32_t atype; |
467 | u_int32_t addr[4]; |
468 | }; |
469 | |
470 | struct mbuf * |
471 | xdr_inaddr_encode(struct in_addr *ia) |
472 | /* ia: already in network order */ |
473 | { |
474 | struct mbuf *m; |
475 | struct xdr_inaddr *xi; |
476 | u_int8_t *cp; |
477 | u_int32_t *ip; |
478 | |
479 | m = m_get(M_WAIT, MT_DATA); |
480 | xi = mtod(m, struct xdr_inaddr *); |
481 | m->m_len = sizeof(*xi); |
482 | xi->atype = txdr_unsigned(1); |
483 | ip = xi->addr; |
484 | cp = (u_int8_t *)&ia->s_addr; |
485 | *ip++ = txdr_unsigned(*cp++); |
486 | *ip++ = txdr_unsigned(*cp++); |
487 | *ip++ = txdr_unsigned(*cp++); |
488 | *ip++ = txdr_unsigned(*cp++); |
489 | |
490 | return (m); |
491 | } |
492 | |
493 | struct mbuf * |
494 | xdr_inaddr_decode(struct mbuf *m, struct in_addr *ia) |
495 | /* ia: already in network order */ |
496 | { |
497 | struct xdr_inaddr *xi; |
498 | u_int8_t *cp; |
499 | u_int32_t *ip; |
500 | |
501 | if (m->m_len < sizeof(*xi)) { |
502 | m = m_pullup(m, sizeof(*xi)); |
503 | if (m == NULL) |
504 | return (NULL); |
505 | } |
506 | xi = mtod(m, struct xdr_inaddr *); |
507 | if (xi->atype != txdr_unsigned(1)) { |
508 | ia->s_addr = INADDR_ANY; |
509 | goto out; |
510 | } |
511 | ip = xi->addr; |
512 | cp = (u_int8_t *)&ia->s_addr; |
513 | *cp++ = fxdr_unsigned(u_int8_t, *ip++); |
514 | *cp++ = fxdr_unsigned(u_int8_t, *ip++); |
515 | *cp++ = fxdr_unsigned(u_int8_t, *ip++); |
516 | *cp++ = fxdr_unsigned(u_int8_t, *ip++); |
517 | |
518 | out: |
519 | m_adj(m, sizeof(*xi)); |
520 | return (m); |
521 | } |
522 | |