xref: /netbsd-src/sys/lib/libsa/bootparam.c (revision 94ad267eb1d1735bdf16f6aebb5e9b4ed29887f9)
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