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