nfs_clkrpc.c revision 223309
1145516Sdarrenr/*-
2145516Sdarrenr * Copyright (c) 1989, 1993
3145516Sdarrenr *	The Regents of the University of California.  All rights reserved.
4145516Sdarrenr *
5145516Sdarrenr * This code is derived from software contributed to Berkeley by
6145516Sdarrenr * Rick Macklem at The University of Guelph.
7145516Sdarrenr *
8145516Sdarrenr * Redistribution and use in source and binary forms, with or without
9145516Sdarrenr * modification, are permitted provided that the following conditions
10145516Sdarrenr * are met:
11145516Sdarrenr * 1. Redistributions of source code must retain the above copyright
12145516Sdarrenr *    notice, this list of conditions and the following disclaimer.
13145516Sdarrenr * 2. Redistributions in binary form must reproduce the above copyright
14145516Sdarrenr *    notice, this list of conditions and the following disclaimer in the
15145516Sdarrenr *    documentation and/or other materials provided with the distribution.
16145516Sdarrenr * 4. Neither the name of the University nor the names of its contributors
17145516Sdarrenr *    may be used to endorse or promote products derived from this software
18145516Sdarrenr *    without specific prior written permission.
19145516Sdarrenr *
20145516Sdarrenr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21145516Sdarrenr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22145516Sdarrenr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23145516Sdarrenr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24145516Sdarrenr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25145516Sdarrenr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26145516Sdarrenr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27145516Sdarrenr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28145516Sdarrenr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29145516Sdarrenr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30145516Sdarrenr * SUCH DAMAGE.
31145516Sdarrenr *
32145516Sdarrenr */
33145516Sdarrenr
34145516Sdarrenr#include <sys/cdefs.h>
35145516Sdarrenr__FBSDID("$FreeBSD: head/sys/fs/nfsclient/nfs_clkrpc.c 223309 2011-06-19 22:08:55Z rmacklem $");
36145516Sdarrenr
37145516Sdarrenr#include "opt_inet6.h"
38145516Sdarrenr#include "opt_kgssapi.h"
39145516Sdarrenr
40145516Sdarrenr#include <fs/nfs/nfsport.h>
41145516Sdarrenr
42145516Sdarrenr#include <rpc/rpc.h>
43145516Sdarrenr#include <rpc/rpcsec_gss.h>
44145516Sdarrenr#include <rpc/replay.h>
45145516Sdarrenr
46145516Sdarrenr
47145516SdarrenrNFSDLOCKMUTEX;
48145516Sdarrenr
49145516SdarrenrSVCPOOL		*nfscbd_pool;
50145516Sdarrenr
51145516Sdarrenrstatic int nfs_cbproc(struct nfsrv_descript *, u_int32_t);
52145516Sdarrenr
53145516Sdarrenrextern u_long sb_max_adj;
54145516Sdarrenrextern int nfs_numnfscbd;
55145516Sdarrenr
56170268Sdarrenr/*
57145516Sdarrenr * NFS client system calls for handling callbacks.
58145516Sdarrenr */
59145516Sdarrenr
60145516Sdarrenr/*
61145516Sdarrenr * Handles server to client callbacks.
62145516Sdarrenr */
63145516Sdarrenrstatic void
64145516Sdarrenrnfscb_program(struct svc_req *rqst, SVCXPRT *xprt)
65145516Sdarrenr{
66145516Sdarrenr	struct nfsrv_descript nd;
67145516Sdarrenr	int cacherep, credflavor;
68145516Sdarrenr
69145516Sdarrenr	memset(&nd, 0, sizeof(nd));
70145516Sdarrenr	if (rqst->rq_proc != NFSPROC_NULL &&
71145516Sdarrenr	    rqst->rq_proc != NFSV4PROC_CBCOMPOUND) {
72145516Sdarrenr		svcerr_noproc(rqst);
73145516Sdarrenr		svc_freereq(rqst);
74145516Sdarrenr		return;
75145516Sdarrenr	}
76145516Sdarrenr	nd.nd_procnum = rqst->rq_proc;
77145516Sdarrenr	nd.nd_flag = (ND_NFSCB | ND_NFSV4);
78145516Sdarrenr
79145516Sdarrenr	/*
80145516Sdarrenr	 * Note: we want rq_addr, not svc_getrpccaller for nd_nam2 -
81145516Sdarrenr	 * NFS_SRVMAXDATA uses a NULL value for nd_nam2 to detect TCP
82145516Sdarrenr	 * mounts.
83145516Sdarrenr	 */
84145516Sdarrenr	nd.nd_mrep = rqst->rq_args;
85145516Sdarrenr	rqst->rq_args = NULL;
86145516Sdarrenr	newnfs_realign(&nd.nd_mrep);
87145516Sdarrenr	nd.nd_md = nd.nd_mrep;
88145516Sdarrenr	nd.nd_dpos = mtod(nd.nd_md, caddr_t);
89145516Sdarrenr	nd.nd_nam = svc_getrpccaller(rqst);
90145516Sdarrenr	nd.nd_nam2 = rqst->rq_addr;
91145516Sdarrenr	nd.nd_mreq = NULL;
92145516Sdarrenr	nd.nd_cred = NULL;
93145516Sdarrenr
94145516Sdarrenr	if (nd.nd_procnum != NFSPROC_NULL) {
95145516Sdarrenr		if (!svc_getcred(rqst, &nd.nd_cred, &credflavor)) {
96145516Sdarrenr			svcerr_weakauth(rqst);
97145516Sdarrenr			svc_freereq(rqst);
98145516Sdarrenr			m_freem(nd.nd_mrep);
99145516Sdarrenr			return;
100145516Sdarrenr		}
101145516Sdarrenr
102145516Sdarrenr		/* For now, I don't care what credential flavor was used. */
103145516Sdarrenr#ifdef notyet
104145516Sdarrenr#ifdef MAC
105145516Sdarrenr		mac_cred_associate_nfsd(nd.nd_cred);
106170268Sdarrenr#endif
107170268Sdarrenr#endif
108170268Sdarrenr		cacherep = nfs_cbproc(&nd, rqst->rq_xid);
109170268Sdarrenr	} else {
110170268Sdarrenr		NFSMGET(nd.nd_mreq);
111170268Sdarrenr		nd.nd_mreq->m_len = 0;
112147547Sdarrenr		cacherep = RC_REPLY;
113170268Sdarrenr	}
114170268Sdarrenr	if (nd.nd_mrep != NULL)
115170268Sdarrenr		m_freem(nd.nd_mrep);
116170268Sdarrenr
117170268Sdarrenr	if (nd.nd_cred != NULL)
118170268Sdarrenr		crfree(nd.nd_cred);
119170268Sdarrenr
120170268Sdarrenr	if (cacherep == RC_DROPIT) {
121170268Sdarrenr		if (nd.nd_mreq != NULL)
122170268Sdarrenr			m_freem(nd.nd_mreq);
123170268Sdarrenr		svc_freereq(rqst);
124170268Sdarrenr		return;
125170268Sdarrenr	}
126147547Sdarrenr
127145516Sdarrenr	if (nd.nd_mreq == NULL) {
128145516Sdarrenr		svcerr_decode(rqst);
129170268Sdarrenr		svc_freereq(rqst);
130170268Sdarrenr		return;
131170268Sdarrenr	}
132145516Sdarrenr
133145516Sdarrenr	if (nd.nd_repstat & NFSERR_AUTHERR) {
134145516Sdarrenr		svcerr_auth(rqst, nd.nd_repstat & ~NFSERR_AUTHERR);
135170268Sdarrenr		if (nd.nd_mreq != NULL)
136145516Sdarrenr			m_freem(nd.nd_mreq);
137145516Sdarrenr	} else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq)) {
138145516Sdarrenr		svcerr_systemerr(rqst);
139145516Sdarrenr	}
140145516Sdarrenr	svc_freereq(rqst);
141145516Sdarrenr}
142145516Sdarrenr
143145516Sdarrenr/*
144145516Sdarrenr * Check the cache and, optionally, do the RPC.
145145516Sdarrenr * Return the appropriate cache response.
146145516Sdarrenr */
147145516Sdarrenrstatic int
148145516Sdarrenrnfs_cbproc(struct nfsrv_descript *nd, u_int32_t xid)
149145516Sdarrenr{
150153876Sguido	struct thread *td = curthread;
151145516Sdarrenr	int cacherep;
152153876Sguido
153145516Sdarrenr	if (nd->nd_nam2 == NULL)
154145516Sdarrenr		nd->nd_flag |= ND_STREAMSOCK;
155145516Sdarrenr
156170268Sdarrenr	nfscl_docb(nd, td);
157170268Sdarrenr	if (nd->nd_repstat == NFSERR_DONTREPLY)
158170268Sdarrenr		cacherep = RC_DROPIT;
159170268Sdarrenr	else
160170268Sdarrenr		cacherep = RC_REPLY;
161170268Sdarrenr	return (cacherep);
162170268Sdarrenr}
163170268Sdarrenr
164170268Sdarrenr/*
165170268Sdarrenr * Adds a socket to the list for servicing by nfscbds.
166145516Sdarrenr */
167170268Sdarrenrint
168170268Sdarrenrnfscbd_addsock(struct file *fp)
169170268Sdarrenr{
170170268Sdarrenr	int siz;
171145516Sdarrenr	struct socket *so;
172170268Sdarrenr	int error;
173170268Sdarrenr	SVCXPRT *xprt;
174170268Sdarrenr
175170268Sdarrenr	so = fp->f_data;
176170268Sdarrenr
177170268Sdarrenr	siz = sb_max_adj;
178145516Sdarrenr	error = soreserve(so, siz, siz);
179170268Sdarrenr	if (error)
180170268Sdarrenr		return (error);
181145516Sdarrenr
182170268Sdarrenr	/*
183170268Sdarrenr	 * Steal the socket from userland so that it doesn't close
184145516Sdarrenr	 * unexpectedly.
185145516Sdarrenr	 */
186145516Sdarrenr	if (so->so_type == SOCK_DGRAM)
187145516Sdarrenr		xprt = svc_dg_create(nfscbd_pool, so, 0, 0);
188145516Sdarrenr	else
189145516Sdarrenr		xprt = svc_vc_create(nfscbd_pool, so, 0, 0);
190170268Sdarrenr	if (xprt) {
191170268Sdarrenr		fp->f_ops = &badfileops;
192170268Sdarrenr		fp->f_data = NULL;
193145516Sdarrenr		svc_reg(xprt, NFS_CALLBCKPROG, NFSV4_CBVERS, nfscb_program,
194145516Sdarrenr		    NULL);
195145516Sdarrenr		SVC_RELEASE(xprt);
196170268Sdarrenr	}
197145516Sdarrenr
198145516Sdarrenr	return (0);
199145516Sdarrenr}
200170268Sdarrenr
201145516Sdarrenr/*
202145516Sdarrenr * Called by nfssvc() for nfscbds. Just loops around servicing rpc requests
203145516Sdarrenr * until it is killed by a signal.
204145516Sdarrenr *
205170268Sdarrenr * For now, only support callbacks via RPCSEC_GSS if there is a KerberosV
206170268Sdarrenr * keytab entry with a host based entry in it on the client. (I'm not even
207170268Sdarrenr * sure that getting Acceptor credentials for a user principal with a
208145516Sdarrenr * credentials cache is possible, but even if it is, major changes to the
209145516Sdarrenr * kgssapi would be required.)
210145516Sdarrenr * I don't believe that this is a serious limitation since, as of 2009, most
211145516Sdarrenr * NFSv4 servers supporting callbacks are using AUTH_SYS for callbacks even
212145516Sdarrenr * when the client is using RPCSEC_GSS. (This BSD server uses AUTH_SYS
213145516Sdarrenr * for callbacks unless nfsrv_gsscallbackson is set non-zero.)
214145516Sdarrenr */
215145516Sdarrenrint
216170268Sdarrenrnfscbd_nfsd(struct thread *td, struct nfsd_nfscbd_args *args)
217145516Sdarrenr{
218145516Sdarrenr	char principal[128];
219145516Sdarrenr	int error;
220145516Sdarrenr
221170268Sdarrenr	if (args != NULL) {
222170268Sdarrenr		error = copyinstr(args->principal, principal,
223170268Sdarrenr		    sizeof(principal), NULL);
224170268Sdarrenr		if (error)
225170268Sdarrenr			return (error);
226145516Sdarrenr	} else {
227170268Sdarrenr		principal[0] = '\0';
228170268Sdarrenr	}
229170268Sdarrenr
230170268Sdarrenr	/*
231170268Sdarrenr	 * Only the first nfsd actually does any work. The RPC code
232170268Sdarrenr	 * adds threads to it as needed. Any extra processes offered
233170268Sdarrenr	 * by nfsd just exit. If nfsd is new enough, it will call us
234170268Sdarrenr	 * once with a structure that specifies how many threads to
235170268Sdarrenr	 * use.
236170268Sdarrenr	 */
237145516Sdarrenr	NFSD_LOCK();
238145516Sdarrenr	if (nfs_numnfscbd == 0) {
239145516Sdarrenr		nfs_numnfscbd++;
240145516Sdarrenr
241145516Sdarrenr		NFSD_UNLOCK();
242170268Sdarrenr
243170268Sdarrenr		if (principal[0] != '\0')
244170268Sdarrenr			rpc_gss_set_svc_name_call(principal, "kerberosv5",
245170268Sdarrenr			    GSS_C_INDEFINITE, NFS_CALLBCKPROG, NFSV4_CBVERS);
246170268Sdarrenr
247170268Sdarrenr		nfscbd_pool->sp_minthreads = 4;
248170268Sdarrenr		nfscbd_pool->sp_maxthreads = 4;
249170268Sdarrenr
250170268Sdarrenr		svc_run(nfscbd_pool);
251170268Sdarrenr
252170268Sdarrenr		rpc_gss_clear_svc_name_call(NFS_CALLBCKPROG, NFSV4_CBVERS);
253170268Sdarrenr
254170268Sdarrenr		NFSD_LOCK();
255170268Sdarrenr		nfs_numnfscbd--;
256170268Sdarrenr		nfsrvd_cbinit(1);
257170268Sdarrenr	}
258170268Sdarrenr	NFSD_UNLOCK();
259170268Sdarrenr
260170268Sdarrenr	return (0);
261170268Sdarrenr}
262170268Sdarrenr
263170268Sdarrenr/*
264170268Sdarrenr * Initialize the data structures for the server.
265170268Sdarrenr * Handshake with any new nfsds starting up to avoid any chance of
266170268Sdarrenr * corruption.
267170268Sdarrenr */
268170268Sdarrenrvoid
269170268Sdarrenrnfsrvd_cbinit(int terminating)
270170268Sdarrenr{
271170268Sdarrenr
272170268Sdarrenr	NFSD_LOCK_ASSERT();
273170268Sdarrenr
274170268Sdarrenr	if (terminating) {
275170268Sdarrenr		NFSD_UNLOCK();
276170268Sdarrenr		svcpool_destroy(nfscbd_pool);
277170268Sdarrenr		nfscbd_pool = NULL;
278170268Sdarrenr		NFSD_LOCK();
279170268Sdarrenr	}
280170268Sdarrenr
281170268Sdarrenr	NFSD_UNLOCK();
282170268Sdarrenr
283170268Sdarrenr	nfscbd_pool = svcpool_create("nfscbd", NULL);
284170268Sdarrenr	nfscbd_pool->sp_rcache = NULL;
285170268Sdarrenr	nfscbd_pool->sp_assign = NULL;
286170268Sdarrenr	nfscbd_pool->sp_done = NULL;
287170268Sdarrenr
288170268Sdarrenr	NFSD_LOCK();
289145516Sdarrenr}
290145516Sdarrenr
291145516Sdarrenr