xref: /openbmc/linux/fs/nfsd/nfs2acl.c (revision fd5e9fccbd504c5179ab57ff695c610bca8809d6)
1  // SPDX-License-Identifier: GPL-2.0
2  /*
3   * Process version 2 NFSACL requests.
4   *
5   * Copyright (C) 2002-2003 Andreas Gruenbacher <agruen@suse.de>
6   */
7  
8  #include "nfsd.h"
9  /* FIXME: nfsacl.h is a broken header */
10  #include <linux/nfsacl.h>
11  #include <linux/gfp.h>
12  #include "cache.h"
13  #include "xdr3.h"
14  #include "vfs.h"
15  
16  #define NFSDDBG_FACILITY		NFSDDBG_PROC
17  
18  /*
19   * NULL call.
20   */
21  static __be32
nfsacld_proc_null(struct svc_rqst * rqstp)22  nfsacld_proc_null(struct svc_rqst *rqstp)
23  {
24  	return rpc_success;
25  }
26  
27  /*
28   * Get the Access and/or Default ACL of a file.
29   */
nfsacld_proc_getacl(struct svc_rqst * rqstp)30  static __be32 nfsacld_proc_getacl(struct svc_rqst *rqstp)
31  {
32  	struct nfsd3_getaclargs *argp = rqstp->rq_argp;
33  	struct nfsd3_getaclres *resp = rqstp->rq_resp;
34  	struct posix_acl *acl;
35  	struct inode *inode;
36  	svc_fh *fh;
37  
38  	dprintk("nfsd: GETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
39  
40  	fh = fh_copy(&resp->fh, &argp->fh);
41  	resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
42  	if (resp->status != nfs_ok)
43  		goto out;
44  
45  	inode = d_inode(fh->fh_dentry);
46  
47  	if (argp->mask & ~NFS_ACL_MASK) {
48  		resp->status = nfserr_inval;
49  		goto out;
50  	}
51  	resp->mask = argp->mask;
52  
53  	resp->status = fh_getattr(fh, &resp->stat);
54  	if (resp->status != nfs_ok)
55  		goto out;
56  
57  	if (resp->mask & (NFS_ACL|NFS_ACLCNT)) {
58  		acl = get_inode_acl(inode, ACL_TYPE_ACCESS);
59  		if (acl == NULL) {
60  			/* Solaris returns the inode's minimum ACL. */
61  			acl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
62  		}
63  		if (IS_ERR(acl)) {
64  			resp->status = nfserrno(PTR_ERR(acl));
65  			goto fail;
66  		}
67  		resp->acl_access = acl;
68  	}
69  	if (resp->mask & (NFS_DFACL|NFS_DFACLCNT)) {
70  		/* Check how Solaris handles requests for the Default ACL
71  		   of a non-directory! */
72  		acl = get_inode_acl(inode, ACL_TYPE_DEFAULT);
73  		if (IS_ERR(acl)) {
74  			resp->status = nfserrno(PTR_ERR(acl));
75  			goto fail;
76  		}
77  		resp->acl_default = acl;
78  	}
79  
80  	/* resp->acl_{access,default} are released in nfssvc_release_getacl. */
81  out:
82  	return rpc_success;
83  
84  fail:
85  	posix_acl_release(resp->acl_access);
86  	posix_acl_release(resp->acl_default);
87  	resp->acl_access = NULL;
88  	resp->acl_default = NULL;
89  	goto out;
90  }
91  
92  /*
93   * Set the Access and/or Default ACL of a file.
94   */
nfsacld_proc_setacl(struct svc_rqst * rqstp)95  static __be32 nfsacld_proc_setacl(struct svc_rqst *rqstp)
96  {
97  	struct nfsd3_setaclargs *argp = rqstp->rq_argp;
98  	struct nfsd_attrstat *resp = rqstp->rq_resp;
99  	struct inode *inode;
100  	svc_fh *fh;
101  	int error;
102  
103  	dprintk("nfsd: SETACL(2acl)   %s\n", SVCFH_fmt(&argp->fh));
104  
105  	fh = fh_copy(&resp->fh, &argp->fh);
106  	resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_SATTR);
107  	if (resp->status != nfs_ok)
108  		goto out;
109  
110  	inode = d_inode(fh->fh_dentry);
111  
112  	error = fh_want_write(fh);
113  	if (error)
114  		goto out_errno;
115  
116  	inode_lock(inode);
117  
118  	error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_ACCESS,
119  			      argp->acl_access);
120  	if (error)
121  		goto out_drop_lock;
122  	error = set_posix_acl(&nop_mnt_idmap, fh->fh_dentry, ACL_TYPE_DEFAULT,
123  			      argp->acl_default);
124  	if (error)
125  		goto out_drop_lock;
126  
127  	inode_unlock(inode);
128  
129  	fh_drop_write(fh);
130  
131  	resp->status = fh_getattr(fh, &resp->stat);
132  
133  out:
134  	/* argp->acl_{access,default} may have been allocated in
135  	   nfssvc_decode_setaclargs. */
136  	posix_acl_release(argp->acl_access);
137  	posix_acl_release(argp->acl_default);
138  	return rpc_success;
139  
140  out_drop_lock:
141  	inode_unlock(inode);
142  	fh_drop_write(fh);
143  out_errno:
144  	resp->status = nfserrno(error);
145  	goto out;
146  }
147  
148  /*
149   * Check file attributes
150   */
nfsacld_proc_getattr(struct svc_rqst * rqstp)151  static __be32 nfsacld_proc_getattr(struct svc_rqst *rqstp)
152  {
153  	struct nfsd_fhandle *argp = rqstp->rq_argp;
154  	struct nfsd_attrstat *resp = rqstp->rq_resp;
155  
156  	dprintk("nfsd: GETATTR  %s\n", SVCFH_fmt(&argp->fh));
157  
158  	fh_copy(&resp->fh, &argp->fh);
159  	resp->status = fh_verify(rqstp, &resp->fh, 0, NFSD_MAY_NOP);
160  	if (resp->status != nfs_ok)
161  		goto out;
162  	resp->status = fh_getattr(&resp->fh, &resp->stat);
163  out:
164  	return rpc_success;
165  }
166  
167  /*
168   * Check file access
169   */
nfsacld_proc_access(struct svc_rqst * rqstp)170  static __be32 nfsacld_proc_access(struct svc_rqst *rqstp)
171  {
172  	struct nfsd3_accessargs *argp = rqstp->rq_argp;
173  	struct nfsd3_accessres *resp = rqstp->rq_resp;
174  
175  	dprintk("nfsd: ACCESS(2acl)   %s 0x%x\n",
176  			SVCFH_fmt(&argp->fh),
177  			argp->access);
178  
179  	fh_copy(&resp->fh, &argp->fh);
180  	resp->access = argp->access;
181  	resp->status = nfsd_access(rqstp, &resp->fh, &resp->access, NULL);
182  	if (resp->status != nfs_ok)
183  		goto out;
184  	resp->status = fh_getattr(&resp->fh, &resp->stat);
185  out:
186  	return rpc_success;
187  }
188  
189  /*
190   * XDR decode functions
191   */
192  
193  static bool
nfsaclsvc_decode_getaclargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)194  nfsaclsvc_decode_getaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
195  {
196  	struct nfsd3_getaclargs *argp = rqstp->rq_argp;
197  
198  	if (!svcxdr_decode_fhandle(xdr, &argp->fh))
199  		return false;
200  	if (xdr_stream_decode_u32(xdr, &argp->mask) < 0)
201  		return false;
202  
203  	return true;
204  }
205  
206  static bool
nfsaclsvc_decode_setaclargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)207  nfsaclsvc_decode_setaclargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
208  {
209  	struct nfsd3_setaclargs *argp = rqstp->rq_argp;
210  
211  	if (!svcxdr_decode_fhandle(xdr, &argp->fh))
212  		return false;
213  	if (xdr_stream_decode_u32(xdr, &argp->mask) < 0)
214  		return false;
215  	if (argp->mask & ~NFS_ACL_MASK)
216  		return false;
217  	if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_ACL) ?
218  				   &argp->acl_access : NULL))
219  		return false;
220  	if (!nfs_stream_decode_acl(xdr, NULL, (argp->mask & NFS_DFACL) ?
221  				   &argp->acl_default : NULL))
222  		return false;
223  
224  	return true;
225  }
226  
227  static bool
nfsaclsvc_decode_accessargs(struct svc_rqst * rqstp,struct xdr_stream * xdr)228  nfsaclsvc_decode_accessargs(struct svc_rqst *rqstp, struct xdr_stream *xdr)
229  {
230  	struct nfsd3_accessargs *args = rqstp->rq_argp;
231  
232  	if (!svcxdr_decode_fhandle(xdr, &args->fh))
233  		return false;
234  	if (xdr_stream_decode_u32(xdr, &args->access) < 0)
235  		return false;
236  
237  	return true;
238  }
239  
240  /*
241   * XDR encode functions
242   */
243  
244  /* GETACL */
245  static bool
nfsaclsvc_encode_getaclres(struct svc_rqst * rqstp,struct xdr_stream * xdr)246  nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
247  {
248  	struct nfsd3_getaclres *resp = rqstp->rq_resp;
249  	struct dentry *dentry = resp->fh.fh_dentry;
250  	struct inode *inode;
251  
252  	if (!svcxdr_encode_stat(xdr, resp->status))
253  		return false;
254  
255  	if (dentry == NULL || d_really_is_negative(dentry))
256  		return true;
257  	inode = d_inode(dentry);
258  
259  	if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
260  		return false;
261  	if (xdr_stream_encode_u32(xdr, resp->mask) < 0)
262  		return false;
263  
264  	if (!nfs_stream_encode_acl(xdr, inode, resp->acl_access,
265  				   resp->mask & NFS_ACL, 0))
266  		return false;
267  	if (!nfs_stream_encode_acl(xdr, inode, resp->acl_default,
268  				   resp->mask & NFS_DFACL, NFS_ACL_DEFAULT))
269  		return false;
270  
271  	return true;
272  }
273  
274  /* ACCESS */
275  static bool
nfsaclsvc_encode_accessres(struct svc_rqst * rqstp,struct xdr_stream * xdr)276  nfsaclsvc_encode_accessres(struct svc_rqst *rqstp, struct xdr_stream *xdr)
277  {
278  	struct nfsd3_accessres *resp = rqstp->rq_resp;
279  
280  	if (!svcxdr_encode_stat(xdr, resp->status))
281  		return false;
282  	switch (resp->status) {
283  	case nfs_ok:
284  		if (!svcxdr_encode_fattr(rqstp, xdr, &resp->fh, &resp->stat))
285  			return false;
286  		if (xdr_stream_encode_u32(xdr, resp->access) < 0)
287  			return false;
288  		break;
289  	}
290  
291  	return true;
292  }
293  
294  /*
295   * XDR release functions
296   */
nfsaclsvc_release_getacl(struct svc_rqst * rqstp)297  static void nfsaclsvc_release_getacl(struct svc_rqst *rqstp)
298  {
299  	struct nfsd3_getaclres *resp = rqstp->rq_resp;
300  
301  	fh_put(&resp->fh);
302  	posix_acl_release(resp->acl_access);
303  	posix_acl_release(resp->acl_default);
304  }
305  
nfsaclsvc_release_access(struct svc_rqst * rqstp)306  static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
307  {
308  	struct nfsd3_accessres *resp = rqstp->rq_resp;
309  
310  	fh_put(&resp->fh);
311  }
312  
313  struct nfsd3_voidargs { int dummy; };
314  
315  #define ST 1		/* status*/
316  #define AT 21		/* attributes */
317  #define pAT (1+AT)	/* post attributes - conditional */
318  #define ACL (1+NFS_ACL_MAX_ENTRIES*3)  /* Access Control List */
319  
320  static const struct svc_procedure nfsd_acl_procedures2[5] = {
321  	[ACLPROC2_NULL] = {
322  		.pc_func = nfsacld_proc_null,
323  		.pc_decode = nfssvc_decode_voidarg,
324  		.pc_encode = nfssvc_encode_voidres,
325  		.pc_argsize = sizeof(struct nfsd_voidargs),
326  		.pc_argzero = sizeof(struct nfsd_voidargs),
327  		.pc_ressize = sizeof(struct nfsd_voidres),
328  		.pc_cachetype = RC_NOCACHE,
329  		.pc_xdrressize = ST,
330  		.pc_name = "NULL",
331  	},
332  	[ACLPROC2_GETACL] = {
333  		.pc_func = nfsacld_proc_getacl,
334  		.pc_decode = nfsaclsvc_decode_getaclargs,
335  		.pc_encode = nfsaclsvc_encode_getaclres,
336  		.pc_release = nfsaclsvc_release_getacl,
337  		.pc_argsize = sizeof(struct nfsd3_getaclargs),
338  		.pc_argzero = sizeof(struct nfsd3_getaclargs),
339  		.pc_ressize = sizeof(struct nfsd3_getaclres),
340  		.pc_cachetype = RC_NOCACHE,
341  		.pc_xdrressize = ST+1+2*(1+ACL),
342  		.pc_name = "GETACL",
343  	},
344  	[ACLPROC2_SETACL] = {
345  		.pc_func = nfsacld_proc_setacl,
346  		.pc_decode = nfsaclsvc_decode_setaclargs,
347  		.pc_encode = nfssvc_encode_attrstatres,
348  		.pc_release = nfssvc_release_attrstat,
349  		.pc_argsize = sizeof(struct nfsd3_setaclargs),
350  		.pc_argzero = sizeof(struct nfsd3_setaclargs),
351  		.pc_ressize = sizeof(struct nfsd_attrstat),
352  		.pc_cachetype = RC_NOCACHE,
353  		.pc_xdrressize = ST+AT,
354  		.pc_name = "SETACL",
355  	},
356  	[ACLPROC2_GETATTR] = {
357  		.pc_func = nfsacld_proc_getattr,
358  		.pc_decode = nfssvc_decode_fhandleargs,
359  		.pc_encode = nfssvc_encode_attrstatres,
360  		.pc_release = nfssvc_release_attrstat,
361  		.pc_argsize = sizeof(struct nfsd_fhandle),
362  		.pc_argzero = sizeof(struct nfsd_fhandle),
363  		.pc_ressize = sizeof(struct nfsd_attrstat),
364  		.pc_cachetype = RC_NOCACHE,
365  		.pc_xdrressize = ST+AT,
366  		.pc_name = "GETATTR",
367  	},
368  	[ACLPROC2_ACCESS] = {
369  		.pc_func = nfsacld_proc_access,
370  		.pc_decode = nfsaclsvc_decode_accessargs,
371  		.pc_encode = nfsaclsvc_encode_accessres,
372  		.pc_release = nfsaclsvc_release_access,
373  		.pc_argsize = sizeof(struct nfsd3_accessargs),
374  		.pc_argzero = sizeof(struct nfsd3_accessargs),
375  		.pc_ressize = sizeof(struct nfsd3_accessres),
376  		.pc_cachetype = RC_NOCACHE,
377  		.pc_xdrressize = ST+AT+1,
378  		.pc_name = "SETATTR",
379  	},
380  };
381  
382  static DEFINE_PER_CPU_ALIGNED(unsigned long,
383  			      nfsd_acl_count2[ARRAY_SIZE(nfsd_acl_procedures2)]);
384  const struct svc_version nfsd_acl_version2 = {
385  	.vs_vers	= 2,
386  	.vs_nproc	= ARRAY_SIZE(nfsd_acl_procedures2),
387  	.vs_proc	= nfsd_acl_procedures2,
388  	.vs_count	= nfsd_acl_count2,
389  	.vs_dispatch	= nfsd_dispatch,
390  	.vs_xdrsize	= NFS3_SVC_XDRSIZE,
391  };
392