1/*	$OpenBSD: krpc_subr.c,v 1.39 2024/05/01 13:15:59 jsg Exp $	*/
2/*	$NetBSD: krpc_subr.c,v 1.12.4.1 1996/06/07 00:52:26 cgd Exp $	*/
3
4/*
5 * Copyright (c) 1995 Gordon Ross, Adam Glass
6 * Copyright (c) 1992 Regents of the University of California.
7 * All rights reserved.
8 *
9 * This software was developed by the Computer Systems Engineering group
10 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
11 * contributed to Berkeley.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 *    must display the following acknowledgement:
23 *	This product includes software developed by the University of
24 *	California, Lawrence Berkeley Laboratory and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 *    may be used to endorse or promote products derived from this software
27 *    without specific prior written permission.
28 *
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
40 *
41 * partially based on:
42 *      libnetboot/rpc.c
43 *               @(#) Header: rpc.c,v 1.12 93/09/28 08:31:56 leres Exp  (LBL)
44 */
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/proc.h>
49#include <sys/mbuf.h>
50#include <sys/socket.h>
51#include <sys/socketvar.h>
52
53#include <netinet/in.h>
54
55#include <nfs/rpcv2.h>
56#include <nfs/krpc.h>
57#include <nfs/xdr_subs.h>
58#include <crypto/idgen.h>
59
60/*
61 * Kernel support for Sun RPC
62 *
63 * Used currently for bootstrapping in nfs diskless configurations.
64 */
65
66/*
67 * Generic RPC headers
68 */
69
70struct auth_info {
71	u_int32_t 	authtype;	/* auth type */
72	u_int32_t	authlen;	/* auth length */
73};
74
75struct auth_unix {
76	int32_t   ua_time;
77	int32_t   ua_hostname;	/* null */
78	int32_t   ua_uid;
79	int32_t   ua_gid;
80	int32_t   ua_gidlist;	/* null */
81};
82
83struct rpc_call {
84	u_int32_t	rp_xid;		/* request transaction id */
85	int32_t 	rp_direction;	/* call direction (0) */
86	u_int32_t	rp_rpcvers;	/* rpc version (2) */
87	u_int32_t	rp_prog;	/* program */
88	u_int32_t	rp_vers;	/* version */
89	u_int32_t	rp_proc;	/* procedure */
90	struct	auth_info rpc_auth;
91	struct	auth_unix rpc_unix;
92	struct	auth_info rpc_verf;
93};
94
95struct rpc_reply {
96	u_int32_t rp_xid;		/* request transaction id */
97	int32_t   rp_direction;		/* call direction (1) */
98	int32_t   rp_astatus;		/* accept status (0: accepted) */
99	union {
100		u_int32_t rpu_errno;
101		struct {
102			struct auth_info rok_auth;
103			u_int32_t	rok_status;
104		} rpu_rok;
105	} rp_u;
106};
107#define rp_errno  rp_u.rpu_errno
108#define rp_auth   rp_u.rpu_rok.rok_auth
109#define rp_status rp_u.rpu_rok.rok_status
110
111#define MIN_REPLY_HDR 16	/* xid, dir, astat, errno */
112
113u_int32_t krpc_get_xid(void);
114
115/*
116 * Return an unpredictable XID.
117 */
118u_int32_t
119krpc_get_xid(void)
120{
121	static struct idgen32_ctx krpc_xid_ctx;
122	static int called = 0;
123
124	if (!called) {
125		called = 1;
126		idgen32_init(&krpc_xid_ctx);
127	}
128	return idgen32(&krpc_xid_ctx);
129}
130
131/*
132 * What is the longest we will wait before re-sending a request?
133 * Note this is also the frequency of "RPC timeout" messages.
134 * The re-send loop count sup linearly to this maximum, so the
135 * first complaint will happen after (1+2+3+4+5)=15 seconds.
136 */
137#define	MAX_RESEND_DELAY 5	/* seconds */
138
139/*
140 * Call portmap to lookup a port number for a particular rpc program
141 * Returns non-zero error on failure.
142 */
143int
144krpc_portmap(struct sockaddr_in *sin, u_int prog, u_int vers, u_int16_t *portp)
145{
146	struct sdata {
147		u_int32_t prog;		/* call program */
148		u_int32_t vers;		/* call version */
149		u_int32_t proto;	/* call protocol */
150		u_int32_t port;		/* call port (unused) */
151	} *sdata;
152	struct rdata {
153		u_int16_t pad;
154		u_int16_t port;
155	} *rdata;
156	struct mbuf *m;
157	int error;
158
159	/* The portmapper port is fixed. */
160	if (prog == PMAPPROG) {
161		*portp = htons(PMAPPORT);
162		return 0;
163	}
164
165	m = m_get(M_WAIT, MT_DATA);
166	sdata = mtod(m, struct sdata *);
167	m->m_len = sizeof(*sdata);
168
169	/* Do the RPC to get it. */
170	sdata->prog = txdr_unsigned(prog);
171	sdata->vers = txdr_unsigned(vers);
172	sdata->proto = txdr_unsigned(IPPROTO_UDP);
173	sdata->port = 0;
174
175	sin->sin_port = htons(PMAPPORT);
176	error = krpc_call(sin, PMAPPROG, PMAPVERS,
177	    PMAPPROC_GETPORT, &m, NULL, -1);
178	if (error)
179		return error;
180
181	if (m->m_len < sizeof(*rdata)) {
182		m = m_pullup(m, sizeof(*rdata));
183		if (m == NULL)
184			return ENOBUFS;
185	}
186	rdata = mtod(m, struct rdata *);
187	*portp = rdata->port;
188
189	m_freem(m);
190	return 0;
191}
192
193/*
194 * Do a remote procedure call (RPC) and wait for its reply.
195 * If from_p is non-null, then we are doing broadcast, and
196 * the address from whence the response came is saved there.
197 * data:	input/output
198 * from_p:	output
199 */
200int
201krpc_call(struct sockaddr_in *sa, u_int prog, u_int vers, u_int func,
202    struct mbuf **data, struct mbuf **from_p, int retries)
203{
204	struct socket *so;
205	struct sockaddr_in *sin;
206	struct mbuf *m, *nam, *mhead, *from, *mopt;
207	struct rpc_call *call;
208	struct rpc_reply *reply;
209	struct uio auio;
210	int error, rcvflg, timo, secs, len, authlen;
211	static u_int32_t xid = 0;
212	char addr[INET_ADDRSTRLEN];
213	int *ip;
214	struct timeval tv;
215
216	/*
217	 * Validate address family.
218	 * Sorry, this is INET specific...
219	 */
220	if (sa->sin_family != AF_INET)
221		return (EAFNOSUPPORT);
222
223	/* Free at end if not null. */
224	nam = mhead = NULL;
225	from = NULL;
226
227	/*
228	 * Create socket and set its receive timeout.
229	 */
230	if ((error = socreate(AF_INET, &so, SOCK_DGRAM, 0)))
231		goto out;
232
233	m = m_get(M_WAIT, MT_SOOPTS);
234	tv.tv_sec = 1;
235	tv.tv_usec = 0;
236	memcpy(mtod(m, struct timeval *), &tv, sizeof tv);
237	m->m_len = sizeof(tv);
238	error = sosetopt(so, SOL_SOCKET, SO_RCVTIMEO, m);
239	m_freem(m);
240	if (error)
241		goto out;
242
243	/*
244	 * Enable broadcast if necessary.
245	 */
246	if (from_p) {
247		int32_t *on;
248		m = m_get(M_WAIT, MT_SOOPTS);
249		on = mtod(m, int32_t *);
250		m->m_len = sizeof(*on);
251		*on = 1;
252		error = sosetopt(so, SOL_SOCKET, SO_BROADCAST, m);
253		m_freem(m);
254		if (error)
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	MGET(mopt, M_WAIT, MT_SOOPTS);
264	mopt->m_len = sizeof(int);
265	ip = mtod(mopt, int *);
266	*ip = IP_PORTRANGE_LOW;
267	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
268	m_freem(mopt);
269	if (error)
270		goto out;
271
272	MGET(m, M_WAIT, MT_SONAME);
273	sin = mtod(m, struct sockaddr_in *);
274	memset(sin, 0, sizeof(*sin));
275	sin->sin_len = m->m_len = sizeof(struct sockaddr_in);
276	sin->sin_family = AF_INET;
277	sin->sin_addr.s_addr = INADDR_ANY;
278	sin->sin_port = htons(0);
279	solock(so);
280	error = sobind(so, m, &proc0);
281	sounlock(so);
282	m_freem(m);
283	if (error) {
284		printf("bind failed\n");
285		goto out;
286	}
287
288	MGET(mopt, M_WAIT, MT_SOOPTS);
289	mopt->m_len = sizeof(int);
290	ip = mtod(mopt, int *);
291	*ip = IP_PORTRANGE_DEFAULT;
292	error = sosetopt(so, IPPROTO_IP, IP_PORTRANGE, mopt);
293	m_freem(mopt);
294	if (error)
295		goto out;
296
297	/*
298	 * Setup socket address for the server.
299	 */
300	nam = m_get(M_WAIT, MT_SONAME);
301	sin = mtod(nam, struct sockaddr_in *);
302	bcopy(sa, sin, (nam->m_len = sa->sin_len));
303
304	/*
305	 * Prepend RPC message header.
306	 */
307	mhead = m_gethdr(M_WAIT, MT_DATA);
308	mhead->m_next = *data;
309	call = mtod(mhead, struct rpc_call *);
310	mhead->m_len = sizeof(*call);
311	memset(call, 0, sizeof(*call));
312	/* rpc_call part */
313	xid = krpc_get_xid();
314	call->rp_xid = txdr_unsigned(xid);
315	/* call->rp_direction = 0; */
316	call->rp_rpcvers = txdr_unsigned(2);
317	call->rp_prog = txdr_unsigned(prog);
318	call->rp_vers = txdr_unsigned(vers);
319	call->rp_proc = txdr_unsigned(func);
320	/* rpc_auth part (auth_unix as root) */
321	call->rpc_auth.authtype = txdr_unsigned(RPCAUTH_UNIX);
322	call->rpc_auth.authlen  = txdr_unsigned(sizeof(struct auth_unix));
323	/* rpc_verf part (auth_null) */
324	call->rpc_verf.authtype = 0;
325	call->rpc_verf.authlen  = 0;
326
327	/*
328	 * Setup packet header
329	 */
330	m_calchdrlen(mhead);
331	mhead->m_pkthdr.ph_ifidx = 0;
332
333	/*
334	 * Send it, repeatedly, until a reply is received,
335	 * but delay each re-send by an increasing amount.
336	 * If the delay hits the maximum, start complaining.
337	 */
338	for (timo = 0; retries; retries--) {
339		/* Send RPC request (or re-send). */
340		m = m_copym(mhead, 0, M_COPYALL, M_WAIT);
341		if (m == NULL) {
342			error = ENOBUFS;
343			goto out;
344		}
345		error = sosend(so, nam, NULL, m, NULL, 0);
346		if (error) {
347			printf("krpc_call: sosend: %d\n", error);
348			goto out;
349		}
350		m = NULL;
351
352		/* Determine new timeout. */
353		if (timo < MAX_RESEND_DELAY)
354			timo++;
355		else
356			printf("RPC timeout for server %s (0x%x) prog %u\n",
357			    inet_ntop(AF_INET, &sin->sin_addr,
358				addr, sizeof(addr)),
359			    ntohl(sin->sin_addr.s_addr), prog);
360
361		/*
362		 * Wait for up to timo seconds for a reply.
363		 * The socket receive timeout was set to 1 second.
364		 */
365		secs = timo;
366		while (secs > 0) {
367			m_freem(from);
368			from = NULL;
369
370			m_freem(m);
371			m = NULL;
372
373			auio.uio_resid = len = 1<<16;
374			auio.uio_procp = NULL;
375			rcvflg = 0;
376			error = soreceive(so, &from, &auio, &m, NULL, &rcvflg,
377			    0);
378			if (error == EWOULDBLOCK) {
379				secs--;
380				continue;
381			}
382			if (error)
383				goto out;
384			len -= auio.uio_resid;
385
386			/* Does the reply contain at least a header? */
387			if (len < MIN_REPLY_HDR)
388				continue;
389			if (m->m_len < MIN_REPLY_HDR)
390				continue;
391			reply = mtod(m, struct rpc_reply *);
392
393			/* Is it the right reply? */
394			if (reply->rp_direction != txdr_unsigned(RPC_REPLY))
395				continue;
396
397			if (reply->rp_xid != txdr_unsigned(xid))
398				continue;
399
400			/* Was RPC accepted? (authorization OK) */
401			if (reply->rp_astatus != 0) {
402				error = fxdr_unsigned(u_int32_t, reply->rp_errno);
403				printf("rpc denied, error=%d\n", error);
404				continue;
405			}
406
407			/* Did the call succeed? */
408			if (reply->rp_status != 0) {
409				error = fxdr_unsigned(u_int32_t, reply->rp_status);
410				printf("rpc denied, status=%d\n", error);
411				continue;
412			}
413
414			goto gotreply;	/* break two levels */
415
416		} /* while secs */
417	} /* forever send/receive */
418
419	error = ETIMEDOUT;
420	goto out;
421
422 gotreply:
423
424	/*
425	 * Get RPC reply header into first mbuf,
426	 * get its length, then strip it off.
427	 */
428	len = sizeof(*reply);
429	KASSERT(m->m_flags & M_PKTHDR);
430	if (m->m_pkthdr.len < len) {
431		error = EBADRPC;
432		goto out;
433	}
434	if (m->m_len < len) {
435		m = m_pullup(m, len);
436		if (m == NULL) {
437			error = ENOBUFS;
438			goto out;
439		}
440	}
441	reply = mtod(m, struct rpc_reply *);
442	if (reply->rp_auth.authtype != 0) {
443		authlen = fxdr_unsigned(u_int32_t, reply->rp_auth.authlen);
444		if (authlen < 0 || authlen > RPCAUTH_MAXSIZ) {
445			error = EBADRPC;
446			goto out;
447		}
448		len += (authlen + 3) & ~3; /* XXX? */
449	}
450	if (len < 0 || m->m_pkthdr.len < len) {
451		error = EBADRPC;
452		goto out;
453	}
454	m_adj(m, len);
455
456	/* result */
457	*data = m;
458	if (from_p && error == 0) {
459		*from_p = from;
460		from = NULL;
461	}
462
463 out:
464	m_freem(nam);
465	m_freem(mhead);
466	m_freem(from);
467	soclose(so, 0);
468	return error;
469}
470
471/*
472 * eXternal Data Representation routines.
473 * (but with non-standard args...)
474 */
475
476/*
477 * String representation for RPC.
478 */
479struct xdr_string {
480	u_int32_t len;		/* length without null or padding */
481	char data[4];	/* data (longer, of course) */
482    /* data is padded to a long-word boundary */
483};
484
485struct mbuf *
486xdr_string_encode(char *str, int len)
487{
488	struct mbuf *m;
489	struct xdr_string *xs;
490	int dlen;	/* padded string length */
491	int mlen;	/* message length */
492
493	dlen = (len + 3) & ~3;
494	mlen = dlen + 4;
495
496	if (mlen > MCLBYTES)		/* If too big, we just can't do it. */
497		return (NULL);
498
499	m = m_get(M_WAIT, MT_DATA);
500	if (mlen > MLEN) {
501		MCLGET(m, M_WAIT);
502		if ((m->m_flags & M_EXT) == 0) {
503			(void) m_free(m);	/* There can be only one. */
504			return (NULL);
505		}
506	}
507	xs = mtod(m, struct xdr_string *);
508	m->m_len = mlen;
509	xs->len = txdr_unsigned(len);
510	bcopy(str, xs->data, len);
511	return (m);
512}
513
514struct mbuf *
515xdr_string_decode(struct mbuf *m, char *str, int *len_p)
516{
517	struct xdr_string *xs;
518	int mlen;	/* message length */
519	int slen;	/* string length */
520
521	mlen = sizeof(u_int32_t);
522	KASSERT(m->m_flags & M_PKTHDR);
523	if (m->m_pkthdr.len < mlen) {
524		m_freem(m);
525		return (NULL);
526	}
527	if (m->m_len < mlen) {
528		m = m_pullup(m, mlen);
529		if (m == NULL)
530			return (NULL);
531	}
532	xs = mtod(m, struct xdr_string *);
533	slen = fxdr_unsigned(u_int32_t, xs->len);
534	if (slen < 0 || slen > INT_MAX - 3 - mlen) {
535		m_freem(m);
536		return (NULL);
537	}
538	mlen += (slen + 3) & ~3;
539
540	if (slen > *len_p)
541		slen = *len_p;
542	if (m->m_pkthdr.len < mlen) {
543		m_freem(m);
544		return (NULL);
545	}
546	m_copydata(m, 4, slen, str);
547	m_adj(m, mlen);
548
549	str[slen] = '\0';
550	*len_p = slen;
551
552	return (m);
553}
554
555
556/*
557 * Inet address in RPC messages
558 * (Note, really four ints, NOT chars.  Blech.)
559 */
560struct xdr_inaddr {
561	u_int32_t atype;
562	u_int32_t addr[4];
563};
564
565struct mbuf *
566xdr_inaddr_encode(struct in_addr *ia)
567{
568	struct mbuf *m;
569	struct xdr_inaddr *xi;
570	u_int8_t *cp;
571	u_int32_t *ip;
572
573	m = m_get(M_WAIT, MT_DATA);
574	xi = mtod(m, struct xdr_inaddr *);
575	m->m_len = sizeof(*xi);
576	xi->atype = txdr_unsigned(1);
577	ip = xi->addr;
578	cp = (u_int8_t *)&ia->s_addr;
579	*ip++ = txdr_unsigned(*cp++);
580	*ip++ = txdr_unsigned(*cp++);
581	*ip++ = txdr_unsigned(*cp++);
582	*ip++ = txdr_unsigned(*cp++);
583
584	return (m);
585}
586
587struct mbuf *
588xdr_inaddr_decode(struct mbuf *m, struct in_addr *ia)
589{
590	struct xdr_inaddr *xi;
591	u_int8_t *cp;
592	u_int32_t *ip;
593
594	if (m->m_len < sizeof(*xi)) {
595		m = m_pullup(m, sizeof(*xi));
596		if (m == NULL)
597			return (NULL);
598	}
599	xi = mtod(m, struct xdr_inaddr *);
600	if (xi->atype != txdr_unsigned(1)) {
601		ia->s_addr = INADDR_ANY;
602		goto out;
603	}
604	ip = xi->addr;
605	cp = (u_int8_t *)&ia->s_addr;
606	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
607	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
608	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
609	*cp++ = fxdr_unsigned(u_int8_t, *ip++);
610
611out:
612	m_adj(m, sizeof(*xi));
613	return (m);
614}
615