1 /*        $NetBSD: bootparam.c,v 1.21 2019/04/02 22:25:10 christos Exp $        */
2 
3 /*
4  * Copyright (c) 1995 Gordon W. Ross
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 /*
29  * RPC/bootparams
30  */
31 
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 
35 #include <net/if.h>
36 
37 #include <netinet/in.h>
38 #include <netinet/in_systm.h>
39 
40 #ifdef _STANDALONE
41 #include <lib/libkern/libkern.h>
42 #else
43 #include <string.h>
44 #endif
45 
46 #include "rpcv2.h"
47 
48 #include "stand.h"
49 #include "net.h"
50 #include "rpc.h"
51 #include "bootparam.h"
52 
53 #ifdef DEBUG_RPC
54 #define RPC_PRINTF(a)         printf a
55 #else
56 #define RPC_PRINTF(a)
57 #endif
58 
59 struct in_addr      bp_server_addr;     /* net order */
60 n_short             bp_server_port;     /* net order */
61 
62 uint32_t  hostnamelen;
63 char                domainname[FNAME_SIZE]; /* our DNS domain */
64 uint32_t  domainnamelen;
65 
66 /*
67  * RPC definitions for bootparamd
68  */
69 #define   BOOTPARAM_PROG                100026
70 #define   BOOTPARAM_VERS                1
71 #define BOOTPARAM_WHOAMI      1
72 #define BOOTPARAM_GETFILE     2
73 
74 /*
75  * Inet address in RPC messages
76  * (Note, really four ints, NOT chars.  Blech.)
77  */
78 struct xdr_inaddr {
79           uint32_t  atype;
80           int32_t   addr[4];
81 };
82 
83 int xdr_inaddr_encode(char **, struct in_addr);
84 int xdr_inaddr_decode(char **, struct in_addr *);
85 
86 int xdr_string_encode(char **, char *, uint32_t);
87 int xdr_string_decode(char **, char *, uint32_t *);
88 
89 
90 /*
91  * RPC: bootparam/whoami
92  * Given client IP address, get:
93  *        client name         (hostname)
94  *        domain name (domainname)
95  *        gateway address
96  *
97  * The hostname and domainname are set here for convenience.
98  *
99  * Note - bpsin is initialized to the broadcast address,
100  * and will be replaced with the bootparam server address
101  * after this call is complete.  Have to use PMAP_PROC_CALL
102  * to make sure we get responses only from a servers that
103  * know about us (don't want to broadcast a getport call).
104  */
105 int
bp_whoami(int sockfd)106 bp_whoami(int sockfd)
107 {
108           /* RPC structures for PMAPPROC_CALLIT */
109           struct args {
110                     uint32_t prog;
111                     uint32_t vers;
112                     uint32_t proc;
113                     uint32_t arglen;
114                     struct xdr_inaddr xina;
115           } *args;
116           struct repl {
117                     uint16_t _pad;
118                     uint16_t port;
119                     uint32_t encap_len;
120                     /* encapsulated data here */
121                     n_long  capsule[64];
122           } *repl;
123           struct {
124                     n_long    h[RPC_HEADER_WORDS];
125                     struct args d;
126           } sdata;
127           struct {
128                     n_long    h[RPC_HEADER_WORDS];
129                     struct repl d;
130           } rdata;
131           char *send_tail, *recv_head;
132           struct iodesc *d;
133           uint32_t x;
134           ssize_t len;
135 
136           RPC_PRINTF(("%s: myip=%s\n", __func__, inet_ntoa(myip)));
137 
138           if (!(d = socktodesc(sockfd))) {
139                     RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
140                     return -1;
141           }
142           args = &sdata.d;
143           repl = &rdata.d;
144 
145           /*
146            * Build request args for PMAPPROC_CALLIT.
147            */
148           args->prog = htonl((uint32_t)BOOTPARAM_PROG);
149           args->vers = htonl((uint32_t)BOOTPARAM_VERS);
150           args->proc = htonl((uint32_t)BOOTPARAM_WHOAMI);
151           args->arglen = htonl((uint32_t)sizeof(struct xdr_inaddr));
152           send_tail = (char *)&args->xina;
153 
154           /*
155            * append encapsulated data (client IP address)
156            */
157           if (xdr_inaddr_encode(&send_tail, myip))
158                     return -1;
159 
160           /* RPC: portmap/callit */
161           --rpc_port;
162           d->myport = (uint16_t)htons((uint16_t)rpc_port);
163           d->destip.s_addr = INADDR_BROADCAST;    /* XXX: subnet bcast? */
164           /* rpc_call will set d->destport */
165 
166           len = rpc_call(d, PMAPPROG, PMAPVERS, PMAPPROC_CALLIT,
167                                           args, (size_t)(send_tail - (char *)args),
168                                           repl, sizeof(*repl));
169           if (len < 8) {
170                     printf("%s: 'whoami' call failed\n", __func__);
171                     return -1;
172           }
173 
174           /* Save bootparam server address (from IP header). */
175           rpc_fromaddr(repl, &bp_server_addr, &bp_server_port);
176 
177           /*
178            * Note that bp_server_port is now 111 due to the
179            * indirect call (using PMAPPROC_CALLIT), so get the
180            * actual port number from the reply data.
181            */
182           bp_server_port = repl->port;
183 
184           RPC_PRINTF(("%s: server at %s:%d\n", __func__,
185               inet_ntoa(bp_server_addr), ntohs((uint16_t)bp_server_port)));
186 
187           /* We have just done a portmap call, so cache the portnum. */
188           rpc_pmap_putcache(bp_server_addr,
189                                 BOOTPARAM_PROG,
190                                 BOOTPARAM_VERS,
191                                 (int)ntohs((uint16_t)bp_server_port));
192 
193           /*
194            * Parse the encapsulated results from bootparam/whoami
195            */
196           x = ntohl((uint32_t)repl->encap_len);
197           if (len < x) {
198                     printf("%s: short reply, %zd < %d\n", __func__, len, x);
199                     return -1;
200           }
201           recv_head = (char *)repl->capsule;
202 
203           /* client name */
204           hostnamelen = MAXHOSTNAMELEN-1;
205           if (xdr_string_decode(&recv_head, hostname, &hostnamelen)) {
206                     RPC_PRINTF(("%s: bad hostname\n", __func__));
207                     return -1;
208           }
209 
210           /* domain name */
211           domainnamelen = MAXHOSTNAMELEN-1;
212           if (xdr_string_decode(&recv_head, domainname, &domainnamelen)) {
213                     RPC_PRINTF(("%s: bad domainname\n", __func__));
214                     return -1;
215           }
216 
217           /* gateway address */
218           if (xdr_inaddr_decode(&recv_head, &gateip)) {
219                     RPC_PRINTF(("%s: bad gateway\n", __func__));
220                     return -1;
221           }
222 
223           /* success */
224           return 0;
225 }
226 
227 
228 /*
229  * RPC: bootparam/getfile
230  * Given client name and file "key", get:
231  *        server name
232  *        server IP address
233  *        server pathname
234  */
235 int
bp_getfile(int sockfd,char * key,struct in_addr * serv_addr,char * pathname)236 bp_getfile(int sockfd, char *key, struct in_addr *serv_addr, char *pathname)
237 {
238           struct {
239                     n_long    h[RPC_HEADER_WORDS];
240                     n_long  d[64];
241           } sdata;
242           struct {
243                     n_long    h[RPC_HEADER_WORDS];
244                     n_long  d[128];
245           } rdata;
246           char serv_name[FNAME_SIZE];
247           char *send_tail, *recv_head;
248           /* misc... */
249           struct iodesc *d;
250           uint32_t sn_len, path_len;
251           ssize_t rlen;
252 
253           if (!(d = socktodesc(sockfd))) {
254                     RPC_PRINTF(("%s: bad socket. %d\n", __func__, sockfd));
255                     return -1;
256           }
257 
258           send_tail = (char *)sdata.d;
259           recv_head = (char *)rdata.d;
260 
261           /*
262            * Build request message.
263            */
264 
265           /* client name (hostname) */
266           if (xdr_string_encode(&send_tail, hostname, hostnamelen)) {
267                     RPC_PRINTF(("%s: bad client\n", __func__));
268                     return -1;
269           }
270 
271           /* key name (root or swap) */
272           if (xdr_string_encode(&send_tail, key, (uint32_t)strlen(key))) {
273                     RPC_PRINTF(("%s: bad key\n", __func__));
274                     return -1;
275           }
276 
277           /* RPC: bootparam/getfile */
278           --rpc_port;
279           d->myport = htons((uint16_t)rpc_port);
280           d->destip = bp_server_addr;
281           /* rpc_call will set d->destport */
282 
283           rlen = rpc_call(d,
284                     BOOTPARAM_PROG, BOOTPARAM_VERS, BOOTPARAM_GETFILE,
285                     sdata.d, (size_t)(send_tail - (char *)sdata.d),
286                     rdata.d, sizeof(rdata.d));
287           if (rlen < 4) {
288                     RPC_PRINTF(("%s: short reply\n", __func__));
289                     errno = EBADRPC;
290                     return -1;
291           }
292           recv_head = (char *)rdata.d;
293 
294           /*
295            * Parse result message.
296            */
297 
298           /* server name */
299           sn_len = FNAME_SIZE - 1;
300           if (xdr_string_decode(&recv_head, serv_name, &sn_len)) {
301                     RPC_PRINTF(("%s: bad server name\n", __func__));
302                     return -1;
303           }
304 
305           /* server IP address (mountd/NFS) */
306           if (xdr_inaddr_decode(&recv_head, serv_addr)) {
307                     RPC_PRINTF(("%s: bad server addr\n", __func__));
308                     return -1;
309           }
310 
311           /* server pathname */
312           path_len = MAXPATHLEN - 1;
313           if (xdr_string_decode(&recv_head, pathname, &path_len)) {
314                     RPC_PRINTF(("%s: bad server path\n", __func__));
315                     return -1;
316           }
317 
318           /* success */
319           return 0;
320 }
321 
322 
323 /*
324  * eXternal Data Representation routines.
325  * (but with non-standard args...)
326  */
327 
328 
329 int
xdr_string_encode(char ** pkt,char * str,uint32_t len)330 xdr_string_encode(char **pkt, char *str, uint32_t len)
331 {
332           uint32_t *lenp;
333           char *datap;
334           uint32_t padlen = (len + 3) & ~3U;      /* padded length */
335 
336           /* The data will be int aligned. */
337           lenp = (uint32_t *)*pkt;
338           *pkt += sizeof(*lenp);
339           *lenp = htonl(len);
340 
341           datap = *pkt;
342           *pkt += padlen;
343           (void)memcpy(datap, str, len);
344 
345           return 0;
346 }
347 
348 /* len_p: bufsize - 1 */
349 int
xdr_string_decode(char ** pkt,char * str,uint32_t * len_p)350 xdr_string_decode(char **pkt, char *str, uint32_t *len_p)
351 {
352           uint32_t *lenp;
353           char *datap;
354           uint32_t slen;      /* string length */
355           uint32_t plen;      /* padded length */
356 
357           /* The data will be int aligned. */
358           lenp = (uint32_t *)*pkt;
359           *pkt += sizeof(*lenp);
360           slen = ntohl(*lenp);
361           plen = (slen + 3) & ~3U;
362 
363           if (slen > *len_p)
364                     slen = *len_p;
365           datap = *pkt;
366           *pkt += plen;
367           (void)memcpy(str, datap, slen);
368 
369           str[slen] = '\0';
370           *len_p = slen;
371 
372           return 0;
373 }
374 
375 
376 /* ia: network order */
377 int
xdr_inaddr_encode(char ** pkt,struct in_addr ia)378 xdr_inaddr_encode(char **pkt, struct in_addr ia)
379 {
380           struct xdr_inaddr *xi;
381           u_char *cp;
382           uint32_t *ip;
383           union {
384                     n_long l; /* network order */
385                     u_char c[4];
386           } uia;
387 
388           /* The data will be int aligned. */
389           xi = (struct xdr_inaddr *)*pkt;
390           *pkt += sizeof(*xi);
391           xi->atype = htonl(1);
392           uia.l = ia.s_addr;
393           cp = uia.c;
394           ip = (uint32_t *)xi->addr;
395           /*
396            * Note: the htonl() calls below DO NOT
397            * imply that uia.l is in host order.
398            * In fact this needs it in net order.
399            */
400           *ip++ = htonl((uint32_t)*cp++);
401           *ip++ = htonl((uint32_t)*cp++);
402           *ip++ = htonl((uint32_t)*cp++);
403           *ip++ = htonl((uint32_t)*cp++);
404 
405           return 0;
406 }
407 
408 /* ia: network order */
409 int
xdr_inaddr_decode(char ** pkt,struct in_addr * ia)410 xdr_inaddr_decode(char **pkt, struct in_addr *ia)
411 {
412           struct xdr_inaddr *xi;
413           u_char *cp;
414           uint32_t *ip;
415           union {
416                     n_long l; /* network order */
417                     u_char c[4];
418           } uia;
419 
420           /* The data will be int aligned. */
421           xi = (struct xdr_inaddr *)*pkt;
422           *pkt += sizeof(*xi);
423           if (xi->atype != htonl((uint32_t)1)) {
424                     RPC_PRINTF(("%s: bad addrtype=%d\n", __func__,
425                         ntohl((uint32_t)nxi->atype)));
426                     return -1;
427           }
428 
429           cp = uia.c;
430           ip = (uint32_t *)xi->addr;
431           /*
432            * Note: the ntohl() calls below DO NOT
433            * imply that uia.l is in host order.
434            * In fact this needs it in net order.
435            */
436           *cp++ = (u_char)ntohl(*ip++);
437           *cp++ = (u_char)ntohl(*ip++);
438           *cp++ = (u_char)ntohl(*ip++);
439           *cp++ = (u_char)ntohl(*ip++);
440           ia->s_addr = uia.l;
441 
442           return 0;
443 }
444