177943Sdfr/*-
277943Sdfr * Copyright (c) 1989, 1993
377943Sdfr *	The Regents of the University of California.  All rights reserved.
477943Sdfr *
577943Sdfr * This code is derived from software contributed to Berkeley by
677943Sdfr * Rick Macklem at The University of Guelph.
777943Sdfr *
877943Sdfr * Redistribution and use in source and binary forms, with or without
977943Sdfr * modification, are permitted provided that the following conditions
1077943Sdfr * are met:
1177943Sdfr * 1. Redistributions of source code must retain the above copyright
1277943Sdfr *    notice, this list of conditions and the following disclaimer.
1377943Sdfr * 2. Redistributions in binary form must reproduce the above copyright
1477943Sdfr *    notice, this list of conditions and the following disclaimer in the
1577943Sdfr *    documentation and/or other materials provided with the distribution.
1677943Sdfr * 4. Neither the name of the University nor the names of its contributors
1777943Sdfr *    may be used to endorse or promote products derived from this software
1877943Sdfr *    without specific prior written permission.
1977943Sdfr *
2077943Sdfr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2177943Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2277943Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2377943Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2477943Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2577943Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2677943Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2777943Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28113038Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29113038Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3078332Sobrien * SUCH DAMAGE.
3177943Sdfr *
3277943Sdfr */
3377943Sdfr
3483857Sdfr#include <sys/cdefs.h>
3583904Sdfr__FBSDID("$FreeBSD: stable/10/sys/fs/nfsclient/nfs_clkrpc.c 317524 2017-04-27 21:27:20Z rmacklem $");
3683904Sdfr
37108025Smarcel#include "opt_kgssapi.h"
3877943Sdfr
3977943Sdfr#include <fs/nfs/nfsport.h>
4077943Sdfr
4177943Sdfr#include <rpc/rpc.h>
42164010Smarcel#include <rpc/rpcsec_gss.h>
4377943Sdfr#include <rpc/replay.h>
44164010Smarcel
45164010Smarcel
46164010SmarcelNFSDLOCKMUTEX;
47164010Smarcel
4877943Sdfrextern SVCPOOL	*nfscbd_pool;
4977943Sdfr
5077943Sdfrstatic int nfs_cbproc(struct nfsrv_descript *, u_int32_t);
5177943Sdfr
5277943Sdfrextern u_long sb_max_adj;
53164010Smarcelextern int nfs_numnfscbd;
5477943Sdfrextern int nfscl_debuglevel;
5583904Sdfr
5683904Sdfr/*
57107722Smarcel * NFS client system calls for handling callbacks.
58107722Smarcel */
59118346Smarcel
60107722Smarcel/*
61107722Smarcel * Handles server to client callbacks.
62107722Smarcel */
63107722Smarcelstatic void
64107722Smarcelnfscb_program(struct svc_req *rqst, SVCXPRT *xprt)
65107722Smarcel{
66107722Smarcel	struct nfsrv_descript nd;
6783904Sdfr	int cacherep, credflavor;
6883904Sdfr
6983904Sdfr	memset(&nd, 0, sizeof(nd));
7083904Sdfr	if (rqst->rq_proc != NFSPROC_NULL &&
7183904Sdfr	    rqst->rq_proc != NFSV4PROC_CBCOMPOUND) {
7283904Sdfr		svcerr_noproc(rqst);
7383904Sdfr		svc_freereq(rqst);
7483904Sdfr		return;
7583904Sdfr	}
7683904Sdfr	nd.nd_procnum = rqst->rq_proc;
77107733Smarcel	nd.nd_flag = (ND_NFSCB | ND_NFSV4);
78107733Smarcel
7983904Sdfr	/*
8083904Sdfr	 * Note: we want rq_addr, not svc_getrpccaller for nd_nam2 -
8183904Sdfr	 * NFS_SRVMAXDATA uses a NULL value for nd_nam2 to detect TCP
8283904Sdfr	 * mounts.
8383904Sdfr	 */
8483904Sdfr	nd.nd_mrep = rqst->rq_args;
8583904Sdfr	rqst->rq_args = NULL;
8683904Sdfr	newnfs_realign(&nd.nd_mrep, M_WAITOK);
8783904Sdfr	nd.nd_md = nd.nd_mrep;
8883904Sdfr	nd.nd_dpos = mtod(nd.nd_md, caddr_t);
8983904Sdfr	nd.nd_nam = svc_getrpccaller(rqst);
9083904Sdfr	nd.nd_nam2 = rqst->rq_addr;
9183904Sdfr	nd.nd_mreq = NULL;
9283904Sdfr	nd.nd_cred = NULL;
9383904Sdfr
9483904Sdfr	NFSCL_DEBUG(1, "cbproc=%d\n",nd.nd_procnum);
9583904Sdfr	if (nd.nd_procnum != NFSPROC_NULL) {
9683904Sdfr		if (!svc_getcred(rqst, &nd.nd_cred, &credflavor)) {
9783904Sdfr			svcerr_weakauth(rqst);
9883904Sdfr			svc_freereq(rqst);
9983904Sdfr			m_freem(nd.nd_mrep);
10083904Sdfr			return;
10183904Sdfr		}
10283904Sdfr
103202552Smarcel		/* For now, I don't care what credential flavor was used. */
104202552Smarcel#ifdef notyet
105202552Smarcel#ifdef MAC
106202552Smarcel		mac_cred_associate_nfsd(nd.nd_cred);
107202552Smarcel#endif
108202552Smarcel#endif
109202552Smarcel		cacherep = nfs_cbproc(&nd, rqst->rq_xid);
110202552Smarcel	} else {
111202552Smarcel		NFSMGET(nd.nd_mreq);
112202552Smarcel		nd.nd_mreq->m_len = 0;
113202552Smarcel		cacherep = RC_REPLY;
114202552Smarcel	}
115202552Smarcel	if (nd.nd_mrep != NULL)
116202552Smarcel		m_freem(nd.nd_mrep);
117202552Smarcel
118202552Smarcel	if (nd.nd_cred != NULL)
119202552Smarcel		crfree(nd.nd_cred);
120202552Smarcel
121202552Smarcel	if (cacherep == RC_DROPIT) {
122202552Smarcel		if (nd.nd_mreq != NULL)
123202552Smarcel			m_freem(nd.nd_mreq);
124202552Smarcel		svc_freereq(rqst);
125202552Smarcel		return;
126202552Smarcel	}
127202552Smarcel
128202552Smarcel	if (nd.nd_mreq == NULL) {
129202552Smarcel		svcerr_decode(rqst);
130202552Smarcel		svc_freereq(rqst);
131202552Smarcel		return;
132202552Smarcel	}
133202552Smarcel
134202552Smarcel	if (nd.nd_repstat & NFSERR_AUTHERR) {
135202552Smarcel		svcerr_auth(rqst, nd.nd_repstat & ~NFSERR_AUTHERR);
136202552Smarcel		if (nd.nd_mreq != NULL)
137202552Smarcel			m_freem(nd.nd_mreq);
138202552Smarcel	} else if (!svc_sendreply_mbuf(rqst, nd.nd_mreq))
139202552Smarcel		svcerr_systemerr(rqst);
14077943Sdfr	else
141107723Smarcel		NFSCL_DEBUG(1, "cbrep sent\n");
14277943Sdfr	svc_freereq(rqst);
143202552Smarcel}
14493411Smarcel
145202552Smarcel/*
14693411Smarcel * Check the cache and, optionally, do the RPC.
14778332Sobrien * Return the appropriate cache response.
14877943Sdfr */
14977943Sdfrstatic int
15077943Sdfrnfs_cbproc(struct nfsrv_descript *nd, u_int32_t xid)
15177943Sdfr{
15277943Sdfr	struct thread *td = curthread;
15377943Sdfr	int cacherep;
15477943Sdfr
15577943Sdfr	if (nd->nd_nam2 == NULL)
156222799Smarcel		nd->nd_flag |= ND_STREAMSOCK;
157202552Smarcel
15883904Sdfr	nfscl_docb(nd, td);
15983904Sdfr	if (nd->nd_repstat == NFSERR_DONTREPLY)
16077943Sdfr		cacherep = RC_DROPIT;
16177943Sdfr	else
16277943Sdfr		cacherep = RC_REPLY;
16377943Sdfr	return (cacherep);
16477943Sdfr}
16577943Sdfr
16677943Sdfr/*
167107683Smarcel * Adds a socket to the list for servicing by nfscbds.
168107683Smarcel */
169107683Smarcelint
170107683Smarcelnfscbd_addsock(struct file *fp)
171107683Smarcel{
172107683Smarcel	int siz;
173107683Smarcel	struct socket *so;
174107683Smarcel	int error;
175107683Smarcel	SVCXPRT *xprt;
176107683Smarcel
17777943Sdfr	so = fp->f_data;
178202552Smarcel
179202552Smarcel	siz = sb_max_adj;
180202552Smarcel	error = soreserve(so, siz, siz);
181202552Smarcel	if (error)
182295538Ssmh		return (error);
183295538Ssmh
184202552Smarcel	/*
185202552Smarcel	 * Steal the socket from userland so that it doesn't close
186164010Smarcel	 * unexpectedly.
18778332Sobrien	 */
18877943Sdfr	if (so->so_type == SOCK_DGRAM)
189202552Smarcel		xprt = svc_dg_create(nfscbd_pool, so, 0, 0);
190202552Smarcel	else
191202552Smarcel		xprt = svc_vc_create(nfscbd_pool, so, 0, 0);
192202552Smarcel	if (xprt) {
193202552Smarcel		fp->f_ops = &badfileops;
194202552Smarcel		fp->f_data = NULL;
19578332Sobrien		svc_reg(xprt, NFS_CALLBCKPROG, NFSV4_CBVERS, nfscb_program,
196202552Smarcel		    NULL);
197164010Smarcel		SVC_RELEASE(xprt);
198164010Smarcel	}
199164010Smarcel
200220313Smarcel	return (0);
201220313Smarcel}
202220313Smarcel
203164010Smarcel/*
20477943Sdfr * Called by nfssvc() for nfscbds. Just loops around servicing rpc requests
20577943Sdfr * until it is killed by a signal.
20677943Sdfr *
20778332Sobrien * For now, only support callbacks via RPCSEC_GSS if there is a KerberosV
20877943Sdfr * keytab entry with a host based entry in it on the client. (I'm not even
20977943Sdfr * sure that getting Acceptor credentials for a user principal with a
21077943Sdfr * credentials cache is possible, but even if it is, major changes to the
21177943Sdfr * kgssapi would be required.)
21277943Sdfr * I don't believe that this is a serious limitation since, as of 2009, most
21377943Sdfr * NFSv4 servers supporting callbacks are using AUTH_SYS for callbacks even
21477943Sdfr * when the client is using RPCSEC_GSS. (This BSD server uses AUTH_SYS
21578332Sobrien * for callbacks unless nfsrv_gsscallbackson is set non-zero.)
216222799Smarcel */
21778332Sobrienint
21877943Sdfrnfscbd_nfsd(struct thread *td, struct nfsd_nfscbd_args *args)
21983215Sdfr{
220222799Smarcel	char principal[128];
221222799Smarcel	int error;
222222799Smarcel
223222799Smarcel	if (args != NULL) {
224222799Smarcel		error = copyinstr(args->principal, principal,
225222799Smarcel		    sizeof(principal), NULL);
226222799Smarcel		if (error)
227222799Smarcel			return (error);
228222799Smarcel	} else {
229222799Smarcel		principal[0] = '\0';
230222799Smarcel	}
23183215Sdfr
23283215Sdfr	/*
23383215Sdfr	 * Only the first nfsd actually does any work. The RPC code
23483215Sdfr	 * adds threads to it as needed. Any extra processes offered
23583215Sdfr	 * by nfsd just exit. If nfsd is new enough, it will call us
23683215Sdfr	 * once with a structure that specifies how many threads to
23783215Sdfr	 * use.
23883215Sdfr	 */
23983215Sdfr	NFSD_LOCK();
24083215Sdfr	if (nfs_numnfscbd == 0) {
24183215Sdfr		nfs_numnfscbd++;
24283215Sdfr
24383215Sdfr		NFSD_UNLOCK();
24483215Sdfr
24583215Sdfr		if (principal[0] != '\0')
24683215Sdfr			rpc_gss_set_svc_name_call(principal, "kerberosv5",
24783215Sdfr			    GSS_C_INDEFINITE, NFS_CALLBCKPROG, NFSV4_CBVERS);
24883215Sdfr
24983215Sdfr		nfscbd_pool->sp_minthreads = 4;
25083215Sdfr		nfscbd_pool->sp_maxthreads = 4;
25183216Sdfr
25283215Sdfr		svc_run(nfscbd_pool);
25383215Sdfr
25483215Sdfr		rpc_gss_clear_svc_name_call(NFS_CALLBCKPROG, NFSV4_CBVERS);
25583215Sdfr
25683215Sdfr		NFSD_LOCK();
25783215Sdfr		nfs_numnfscbd--;
25883215Sdfr		nfsrvd_cbinit(1);
25983215Sdfr	}
26083215Sdfr	NFSD_UNLOCK();
26183215Sdfr
26283215Sdfr	return (0);
26383857Sdfr}
26483215Sdfr
26583215Sdfr/*
26683215Sdfr * Initialize the data structures for the server.
26783215Sdfr * Handshake with any new nfsds starting up to avoid any chance of
26883215Sdfr * corruption.
26983857Sdfr */
27083215Sdfrvoid
27183215Sdfrnfsrvd_cbinit(int terminating)
27283215Sdfr{
27383216Sdfr
27483215Sdfr	NFSD_LOCK_ASSERT();
27583215Sdfr
27683215Sdfr	if (terminating) {
27783215Sdfr		/* Wait for any xprt registrations to complete. */
27883216Sdfr		while (nfs_numnfscbd > 0)
27983215Sdfr			msleep(&nfs_numnfscbd, NFSDLOCKMUTEXPTR, PZERO,
28083215Sdfr			    "nfscbdt", 0);
28183215Sdfr		if (nfscbd_pool != NULL) {
28283215Sdfr			NFSD_UNLOCK();
28383215Sdfr			svcpool_close(nfscbd_pool);
28483215Sdfr			NFSD_LOCK();
28583215Sdfr		}
28683215Sdfr	}
28783215Sdfr
28883215Sdfr	if (nfscbd_pool == NULL) {
28983215Sdfr		NFSD_UNLOCK();
29083215Sdfr		nfscbd_pool = svcpool_create("nfscbd", NULL);
29183215Sdfr		nfscbd_pool->sp_rcache = NULL;
29283215Sdfr		nfscbd_pool->sp_assign = NULL;
29383215Sdfr		nfscbd_pool->sp_done = NULL;
29483215Sdfr		NFSD_LOCK();
29583215Sdfr	}
29683215Sdfr}
29783215Sdfr
29883215Sdfr