2024-05-02 20:01:08

by Stephen Smalley

[permalink] [raw]
Subject: [PATCH v2] nfsd: set security label during create operations

When security labeling is enabled, the client can pass a file security
label as part of a create operation for the new file, similar to mode
and other attributes. At present, the security label is received by nfsd
and passed down to nfsd_create_setattr(), but nfsd_setattr() is never
called and therefore the label is never set on the new file. I couldn't
tell if this has always been broken or broke at some point in time. Looking
at nfsd_setattr() I am uncertain as to whether the same issue presents for
file ACLs and therefore requires a similar fix for those. I am not overly
confident that this is the right solution.

An alternative approach would be to introduce a new LSM hook to set the
"create SID" of the current task prior to the actual file creation, which
would atomically label the new inode at creation time. This would be better
for SELinux and a similar approach has been used previously
(see security_dentry_create_files_as) but perhaps not usable by other LSMs.

Reproducer:
1. Install a Linux distro with SELinux - Fedora is easiest
2. git clone https://github.com/SELinuxProject/selinux-testsuite
3. Install the requisite dependencies per selinux-testsuite/README.md
4. Run something like the following script:
MOUNT=$HOME/selinux-testsuite
sudo systemctl start nfs-server
sudo exportfs -o rw,no_root_squash,security_label localhost:$MOUNT
sudo mkdir -p /mnt/selinux-testsuite
sudo mount -t nfs -o vers=4.2 localhost:$MOUNT /mnt/selinux-testsuite
pushd /mnt/selinux-testsuite/
sudo make -C policy load
pushd tests/filesystem
sudo runcon -t test_filesystem_t ./create_file -f trans_test_file \
-e test_filesystem_filetranscon_t -v
sudo rm -f trans_test_file
popd
sudo make -C policy unload
popd
sudo umount /mnt/selinux-testsuite
sudo exportfs -u localhost:$MOUNT
sudo rmdir /mnt/selinux-testsuite
sudo systemctl stop nfs-server

Expected output:
<eliding noise from commands run prior to or after the test itself>
Process context:
unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
Created file: trans_test_file
File context: unconfined_u:object_r:test_filesystem_filetranscon_t:s0
File context is correct

Actual output:
<eliding noise from commands run prior to or after the test itself>
Process context:
unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
Created file: trans_test_file
File context: system_u:object_r:test_file_t:s0
File context error, expected:
test_filesystem_filetranscon_t
got:
test_file_t

Signed-off-by: Stephen Smalley <[email protected]>
---
v2 introduces a nfsd_attrs_valid() helper and uses it as suggested by
Jeffrey Layton <[email protected]>.

fs/nfsd/nfsproc.c | 2 +-
fs/nfsd/vfs.c | 2 +-
fs/nfsd/vfs.h | 8 ++++++++
3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
index 36370b957b63..3e438159f561 100644
--- a/fs/nfsd/nfsproc.c
+++ b/fs/nfsd/nfsproc.c
@@ -389,7 +389,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
* open(..., O_CREAT|O_TRUNC|O_WRONLY).
*/
attr->ia_valid &= ATTR_SIZE;
- if (attr->ia_valid)
+ if (nfsd_attrs_valid(attr))
resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
NULL);
}
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 2e41eb4c3cec..29b1f3613800 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1422,7 +1422,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
* Callers expect new file metadata to be committed even
* if the attributes have not changed.
*/
- if (iap->ia_valid)
+ if (nfsd_attrs_valid(attrs))
status = nfsd_setattr(rqstp, resfhp, attrs, NULL);
else
status = nfserrno(commit_metadata(resfhp));
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index c60fdb6200fd..57cd70062048 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -60,6 +60,14 @@ static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
posix_acl_release(attrs->na_dpacl);
}

+static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
+{
+ struct iattr *iap = attrs->na_iattr;
+
+ return (iap->ia_valid || (attrs->na_seclabel &&
+ attrs->na_seclabel->len));
+}
+
__be32 nfserrno (int errno);
int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
struct svc_export **expp);
--
2.40.1



2024-05-02 20:17:48

by Chuck Lever

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: set security label during create operations

On Thu, May 02, 2024 at 03:58:01PM -0400, Stephen Smalley wrote:
> When security labeling is enabled, the client can pass a file security
> label as part of a create operation for the new file, similar to mode
> and other attributes. At present, the security label is received by nfsd
> and passed down to nfsd_create_setattr(), but nfsd_setattr() is never
> called and therefore the label is never set on the new file. I couldn't
> tell if this has always been broken or broke at some point in time.

Might have been introduced on or around commit d6a97d3f589a ("NFSD:
add security label to struct nfsd_attrs"). Neil, can you spare an
eyeball or two for Stephen's patch?


> Looking
> at nfsd_setattr() I am uncertain as to whether the same issue presents for
> file ACLs and therefore requires a similar fix for those. I am not overly
> confident that this is the right solution.
>
> An alternative approach would be to introduce a new LSM hook to set the
> "create SID" of the current task prior to the actual file creation, which
> would atomically label the new inode at creation time. This would be better
> for SELinux and a similar approach has been used previously
> (see security_dentry_create_files_as) but perhaps not usable by other LSMs.
>
> Reproducer:
> 1. Install a Linux distro with SELinux - Fedora is easiest
> 2. git clone https://github.com/SELinuxProject/selinux-testsuite
> 3. Install the requisite dependencies per selinux-testsuite/README.md
> 4. Run something like the following script:
> MOUNT=$HOME/selinux-testsuite
> sudo systemctl start nfs-server
> sudo exportfs -o rw,no_root_squash,security_label localhost:$MOUNT
> sudo mkdir -p /mnt/selinux-testsuite
> sudo mount -t nfs -o vers=4.2 localhost:$MOUNT /mnt/selinux-testsuite
> pushd /mnt/selinux-testsuite/
> sudo make -C policy load
> pushd tests/filesystem
> sudo runcon -t test_filesystem_t ./create_file -f trans_test_file \
> -e test_filesystem_filetranscon_t -v
> sudo rm -f trans_test_file
> popd
> sudo make -C policy unload
> popd
> sudo umount /mnt/selinux-testsuite
> sudo exportfs -u localhost:$MOUNT
> sudo rmdir /mnt/selinux-testsuite
> sudo systemctl stop nfs-server
>
> Expected output:
> <eliding noise from commands run prior to or after the test itself>
> Process context:
> unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> Created file: trans_test_file
> File context: unconfined_u:object_r:test_filesystem_filetranscon_t:s0
> File context is correct
>
> Actual output:
> <eliding noise from commands run prior to or after the test itself>
> Process context:
> unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> Created file: trans_test_file
> File context: system_u:object_r:test_file_t:s0
> File context error, expected:
> test_filesystem_filetranscon_t
> got:
> test_file_t
>
> Signed-off-by: Stephen Smalley <[email protected]>
> ---
> v2 introduces a nfsd_attrs_valid() helper and uses it as suggested by
> Jeffrey Layton <[email protected]>.
>
> fs/nfsd/nfsproc.c | 2 +-
> fs/nfsd/vfs.c | 2 +-
> fs/nfsd/vfs.h | 8 ++++++++
> 3 files changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
> index 36370b957b63..3e438159f561 100644
> --- a/fs/nfsd/nfsproc.c
> +++ b/fs/nfsd/nfsproc.c
> @@ -389,7 +389,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
> * open(..., O_CREAT|O_TRUNC|O_WRONLY).
> */
> attr->ia_valid &= ATTR_SIZE;
> - if (attr->ia_valid)
> + if (nfsd_attrs_valid(attr))
> resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
> NULL);
> }
> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 2e41eb4c3cec..29b1f3613800 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1422,7 +1422,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
> * Callers expect new file metadata to be committed even
> * if the attributes have not changed.
> */
> - if (iap->ia_valid)
> + if (nfsd_attrs_valid(attrs))
> status = nfsd_setattr(rqstp, resfhp, attrs, NULL);
> else
> status = nfserrno(commit_metadata(resfhp));
> diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
> index c60fdb6200fd..57cd70062048 100644
> --- a/fs/nfsd/vfs.h
> +++ b/fs/nfsd/vfs.h
> @@ -60,6 +60,14 @@ static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
> posix_acl_release(attrs->na_dpacl);
> }
>
> +static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
> +{
> + struct iattr *iap = attrs->na_iattr;
> +
> + return (iap->ia_valid || (attrs->na_seclabel &&
> + attrs->na_seclabel->len));
> +}
> +
> __be32 nfserrno (int errno);
> int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
> struct svc_export **expp);
> --
> 2.40.1
>

--
Chuck Lever

2024-05-02 22:35:43

by Jeff Layton

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: set security label during create operations

On Thu, 2024-05-02 at 15:58 -0400, Stephen Smalley wrote:
> When security labeling is enabled, the client can pass a file security
> label as part of a create operation for the new file, similar to mode
> and other attributes. At present, the security label is received by nfsd
> and passed down to nfsd_create_setattr(), but nfsd_setattr() is never
> called and therefore the label is never set on the new file. I couldn't
> tell if this has always been broken or broke at some point in time. Looking
> at nfsd_setattr() I am uncertain as to whether the same issue presents for
> file ACLs and therefore requires a similar fix for those. I am not overly
> confident that this is the right solution.
>
> An alternative approach would be to introduce a new LSM hook to set the
> "create SID" of the current task prior to the actual file creation, which
> would atomically label the new inode at creation time. This would be better
> for SELinux and a similar approach has been used previously
> (see security_dentry_create_files_as) but perhaps not usable by other LSMs.
>
> Reproducer:
> 1. Install a Linux distro with SELinux - Fedora is easiest
> 2. git clone https://github.com/SELinuxProject/selinux-testsuite
> 3. Install the requisite dependencies per selinux-testsuite/README.md
> 4. Run something like the following script:
> MOUNT=$HOME/selinux-testsuite
> sudo systemctl start nfs-server
> sudo exportfs -o rw,no_root_squash,security_label localhost:$MOUNT
> sudo mkdir -p /mnt/selinux-testsuite
> sudo mount -t nfs -o vers=4.2 localhost:$MOUNT /mnt/selinux-testsuite
> pushd /mnt/selinux-testsuite/
> sudo make -C policy load
> pushd tests/filesystem
> sudo runcon -t test_filesystem_t ./create_file -f trans_test_file \
> -e test_filesystem_filetranscon_t -v
> sudo rm -f trans_test_file
> popd
> sudo make -C policy unload
> popd
> sudo umount /mnt/selinux-testsuite
> sudo exportfs -u localhost:$MOUNT
> sudo rmdir /mnt/selinux-testsuite
> sudo systemctl stop nfs-server
>
> Expected output:
> <eliding noise from commands run prior to or after the test itself>
> Process context:
> unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> Created file: trans_test_file
> File context: unconfined_u:object_r:test_filesystem_filetranscon_t:s0
> File context is correct
>
> Actual output:
> <eliding noise from commands run prior to or after the test itself>
> Process context:
> unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> Created file: trans_test_file
> File context: system_u:object_r:test_file_t:s0
> File context error, expected:
> test_filesystem_filetranscon_t
> got:
> test_file_t
>
> Signed-off-by: Stephen Smalley <[email protected]>
> ---
> v2 introduces a nfsd_attrs_valid() helper and uses it as suggested by
> Jeffrey Layton <[email protected]>.
>
> fs/nfsd/nfsproc.c | 2 +-
> fs/nfsd/vfs.c | 2 +-
> fs/nfsd/vfs.h | 8 ++++++++
> 3 files changed, 10 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
> index 36370b957b63..3e438159f561 100644
> --- a/fs/nfsd/nfsproc.c
> +++ b/fs/nfsd/nfsproc.c
> @@ -389,7 +389,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
> * open(..., O_CREAT|O_TRUNC|O_WRONLY).
> */
> attr->ia_valid &= ATTR_SIZE;
> - if (attr->ia_valid)
> + if (nfsd_attrs_valid(attr))
> resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
> NULL);
> }

This function is for NFSv2, which doesn't support any inode attributes
that aren't represented in ia_valid. We could leave this as-is, but
this is fine too.

> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
> index 2e41eb4c3cec..29b1f3613800 100644
> --- a/fs/nfsd/vfs.c
> +++ b/fs/nfsd/vfs.c
> @@ -1422,7 +1422,7 @@ nfsd_create_setattr(struct svc_rqst *rqstp, struct svc_fh *fhp,
> * Callers expect new file metadata to be committed even
> * if the attributes have not changed.
> */
> - if (iap->ia_valid)
> + if (nfsd_attrs_valid(attrs))
> status = nfsd_setattr(rqstp, resfhp, attrs, NULL);
> else
> status = nfserrno(commit_metadata(resfhp));
> diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
> index c60fdb6200fd..57cd70062048 100644
> --- a/fs/nfsd/vfs.h
> +++ b/fs/nfsd/vfs.h
> @@ -60,6 +60,14 @@ static inline void nfsd_attrs_free(struct nfsd_attrs *attrs)
> posix_acl_release(attrs->na_dpacl);
> }
>
> +static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
> +{
> + struct iattr *iap = attrs->na_iattr;
> +
> + return (iap->ia_valid || (attrs->na_seclabel &&
> + attrs->na_seclabel->len));
> +}
> +
> __be32 nfserrno (int errno);
> int nfsd_cross_mnt(struct svc_rqst *rqstp, struct dentry **dpp,
> struct svc_export **expp);

Reviewed-by: Jeffrey Layton <[email protected]>

2024-05-03 07:32:28

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: set security label during create operations

Hi Stephen,

kernel test robot noticed the following build errors:

[auto build test ERROR on linus/master]
[also build test ERROR on v6.9-rc6 next-20240502]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url: https://github.com/intel-lab-lkp/linux/commits/Stephen-Smalley/nfsd-set-security-label-during-create-operations/20240503-040242
base: linus/master
patch link: https://lore.kernel.org/r/20240502195800.3252-1-stephen.smalley.work%40gmail.com
patch subject: [PATCH v2] nfsd: set security label during create operations
config: arm64-randconfig-r123-20240503 (https://download.01.org/0day-ci/archive/20240503/[email protected]/config)
compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 37ae4ad0eef338776c7e2cffb3896153d43dcd90)
reproduce: (https://download.01.org/0day-ci/archive/20240503/[email protected]/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/

All errors (new ones prefixed by >>):

In file included from fs/nfsd/nfsproc.c:10:
In file included from fs/nfsd/cache.h:12:
In file included from include/linux/sunrpc/svc.h:17:
In file included from include/linux/sunrpc/xdr.h:17:
In file included from include/linux/scatterlist.h:8:
In file included from include/linux/mm.h:2210:
include/linux/vmstat.h:508:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
508 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
509 | item];
| ~~~~
include/linux/vmstat.h:515:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
515 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
516 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
522 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
| ~~~~~~~~~~~ ^ ~~~
include/linux/vmstat.h:527:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
527 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
528 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
include/linux/vmstat.h:536:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
536 | return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~ ^
537 | NR_VM_NUMA_EVENT_ITEMS +
| ~~~~~~~~~~~~~~~~~~~~~~
>> fs/nfsd/nfsproc.c:392:24: error: incompatible pointer types passing 'struct iattr *' to parameter of type 'struct nfsd_attrs *' [-Werror,-Wincompatible-pointer-types]
392 | if (nfsd_attrs_valid(attr))
| ^~~~
fs/nfsd/vfs.h:63:56: note: passing argument to parameter 'attrs' here
63 | static inline bool nfsd_attrs_valid(struct nfsd_attrs *attrs)
| ^
5 warnings and 1 error generated.


vim +392 fs/nfsd/nfsproc.c

240
241 /*
242 * CREATE processing is complicated. The keyword here is `overloaded.'
243 * The parent directory is kept locked between the check for existence
244 * and the actual create() call in compliance with VFS protocols.
245 * N.B. After this call _both_ argp->fh and resp->fh need an fh_put
246 */
247 static __be32
248 nfsd_proc_create(struct svc_rqst *rqstp)
249 {
250 struct nfsd_createargs *argp = rqstp->rq_argp;
251 struct nfsd_diropres *resp = rqstp->rq_resp;
252 svc_fh *dirfhp = &argp->fh;
253 svc_fh *newfhp = &resp->fh;
254 struct iattr *attr = &argp->attrs;
255 struct nfsd_attrs attrs = {
256 .na_iattr = attr,
257 };
258 struct inode *inode;
259 struct dentry *dchild;
260 int type, mode;
261 int hosterr;
262 dev_t rdev = 0, wanted = new_decode_dev(attr->ia_size);
263
264 dprintk("nfsd: CREATE %s %.*s\n",
265 SVCFH_fmt(dirfhp), argp->len, argp->name);
266
267 /* First verify the parent file handle */
268 resp->status = fh_verify(rqstp, dirfhp, S_IFDIR, NFSD_MAY_EXEC);
269 if (resp->status != nfs_ok)
270 goto done; /* must fh_put dirfhp even on error */
271
272 /* Check for NFSD_MAY_WRITE in nfsd_create if necessary */
273
274 resp->status = nfserr_exist;
275 if (isdotent(argp->name, argp->len))
276 goto done;
277 hosterr = fh_want_write(dirfhp);
278 if (hosterr) {
279 resp->status = nfserrno(hosterr);
280 goto done;
281 }
282
283 inode_lock_nested(dirfhp->fh_dentry->d_inode, I_MUTEX_PARENT);
284 dchild = lookup_one_len(argp->name, dirfhp->fh_dentry, argp->len);
285 if (IS_ERR(dchild)) {
286 resp->status = nfserrno(PTR_ERR(dchild));
287 goto out_unlock;
288 }
289 fh_init(newfhp, NFS_FHSIZE);
290 resp->status = fh_compose(newfhp, dirfhp->fh_export, dchild, dirfhp);
291 if (!resp->status && d_really_is_negative(dchild))
292 resp->status = nfserr_noent;
293 dput(dchild);
294 if (resp->status) {
295 if (resp->status != nfserr_noent)
296 goto out_unlock;
297 /*
298 * If the new file handle wasn't verified, we can't tell
299 * whether the file exists or not. Time to bail ...
300 */
301 resp->status = nfserr_acces;
302 if (!newfhp->fh_dentry) {
303 printk(KERN_WARNING
304 "nfsd_proc_create: file handle not verified\n");
305 goto out_unlock;
306 }
307 }
308
309 inode = d_inode(newfhp->fh_dentry);
310
311 /* Unfudge the mode bits */
312 if (attr->ia_valid & ATTR_MODE) {
313 type = attr->ia_mode & S_IFMT;
314 mode = attr->ia_mode & ~S_IFMT;
315 if (!type) {
316 /* no type, so if target exists, assume same as that,
317 * else assume a file */
318 if (inode) {
319 type = inode->i_mode & S_IFMT;
320 switch(type) {
321 case S_IFCHR:
322 case S_IFBLK:
323 /* reserve rdev for later checking */
324 rdev = inode->i_rdev;
325 attr->ia_valid |= ATTR_SIZE;
326
327 fallthrough;
328 case S_IFIFO:
329 /* this is probably a permission check..
330 * at least IRIX implements perm checking on
331 * echo thing > device-special-file-or-pipe
332 * by doing a CREATE with type==0
333 */
334 resp->status = nfsd_permission(rqstp,
335 newfhp->fh_export,
336 newfhp->fh_dentry,
337 NFSD_MAY_WRITE|NFSD_MAY_LOCAL_ACCESS);
338 if (resp->status && resp->status != nfserr_rofs)
339 goto out_unlock;
340 }
341 } else
342 type = S_IFREG;
343 }
344 } else if (inode) {
345 type = inode->i_mode & S_IFMT;
346 mode = inode->i_mode & ~S_IFMT;
347 } else {
348 type = S_IFREG;
349 mode = 0; /* ??? */
350 }
351
352 attr->ia_valid |= ATTR_MODE;
353 attr->ia_mode = mode;
354
355 /* Special treatment for non-regular files according to the
356 * gospel of sun micro
357 */
358 if (type != S_IFREG) {
359 if (type != S_IFBLK && type != S_IFCHR) {
360 rdev = 0;
361 } else if (type == S_IFCHR && !(attr->ia_valid & ATTR_SIZE)) {
362 /* If you think you've seen the worst, grok this. */
363 type = S_IFIFO;
364 } else {
365 /* Okay, char or block special */
366 if (!rdev)
367 rdev = wanted;
368 }
369
370 /* we've used the SIZE information, so discard it */
371 attr->ia_valid &= ~ATTR_SIZE;
372
373 /* Make sure the type and device matches */
374 resp->status = nfserr_exist;
375 if (inode && inode_wrong_type(inode, type))
376 goto out_unlock;
377 }
378
379 resp->status = nfs_ok;
380 if (!inode) {
381 /* File doesn't exist. Create it and set attrs */
382 resp->status = nfsd_create_locked(rqstp, dirfhp, &attrs, type,
383 rdev, newfhp);
384 } else if (type == S_IFREG) {
385 dprintk("nfsd: existing %s, valid=%x, size=%ld\n",
386 argp->name, attr->ia_valid, (long) attr->ia_size);
387 /* File already exists. We ignore all attributes except
388 * size, so that creat() behaves exactly like
389 * open(..., O_CREAT|O_TRUNC|O_WRONLY).
390 */
391 attr->ia_valid &= ATTR_SIZE;
> 392 if (nfsd_attrs_valid(attr))
393 resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
394 NULL);
395 }
396
397 out_unlock:
398 inode_unlock(dirfhp->fh_dentry->d_inode);
399 fh_drop_write(dirfhp);
400 done:
401 fh_put(dirfhp);
402 if (resp->status != nfs_ok)
403 goto out;
404 resp->status = fh_getattr(&resp->fh, &resp->stat);
405 out:
406 return rpc_success;
407 }
408

--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

2024-05-03 12:49:04

by Stephen Smalley

[permalink] [raw]
Subject: Re: [PATCH v2] nfsd: set security label during create operations

On Thu, May 2, 2024 at 6:34 PM Jeffrey Layton <[email protected]> wrote:
>
> On Thu, 2024-05-02 at 15:58 -0400, Stephen Smalley wrote:
> > When security labeling is enabled, the client can pass a file security
> > label as part of a create operation for the new file, similar to mode
> > and other attributes. At present, the security label is received by nfsd
> > and passed down to nfsd_create_setattr(), but nfsd_setattr() is never
> > called and therefore the label is never set on the new file. I couldn't
> > tell if this has always been broken or broke at some point in time. Looking
> > at nfsd_setattr() I am uncertain as to whether the same issue presents for
> > file ACLs and therefore requires a similar fix for those. I am not overly
> > confident that this is the right solution.
> >
> > An alternative approach would be to introduce a new LSM hook to set the
> > "create SID" of the current task prior to the actual file creation, which
> > would atomically label the new inode at creation time. This would be better
> > for SELinux and a similar approach has been used previously
> > (see security_dentry_create_files_as) but perhaps not usable by other LSMs.
> >
> > Reproducer:
> > 1. Install a Linux distro with SELinux - Fedora is easiest
> > 2. git clone https://github.com/SELinuxProject/selinux-testsuite
> > 3. Install the requisite dependencies per selinux-testsuite/README.md
> > 4. Run something like the following script:
> > MOUNT=$HOME/selinux-testsuite
> > sudo systemctl start nfs-server
> > sudo exportfs -o rw,no_root_squash,security_label localhost:$MOUNT
> > sudo mkdir -p /mnt/selinux-testsuite
> > sudo mount -t nfs -o vers=4.2 localhost:$MOUNT /mnt/selinux-testsuite
> > pushd /mnt/selinux-testsuite/
> > sudo make -C policy load
> > pushd tests/filesystem
> > sudo runcon -t test_filesystem_t ./create_file -f trans_test_file \
> > -e test_filesystem_filetranscon_t -v
> > sudo rm -f trans_test_file
> > popd
> > sudo make -C policy unload
> > popd
> > sudo umount /mnt/selinux-testsuite
> > sudo exportfs -u localhost:$MOUNT
> > sudo rmdir /mnt/selinux-testsuite
> > sudo systemctl stop nfs-server
> >
> > Expected output:
> > <eliding noise from commands run prior to or after the test itself>
> > Process context:
> > unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> > Created file: trans_test_file
> > File context: unconfined_u:object_r:test_filesystem_filetranscon_t:s0
> > File context is correct
> >
> > Actual output:
> > <eliding noise from commands run prior to or after the test itself>
> > Process context:
> > unconfined_u:unconfined_r:test_filesystem_t:s0-s0:c0.c1023
> > Created file: trans_test_file
> > File context: system_u:object_r:test_file_t:s0
> > File context error, expected:
> > test_filesystem_filetranscon_t
> > got:
> > test_file_t
> >
> > Signed-off-by: Stephen Smalley <[email protected]>
> > ---
> > v2 introduces a nfsd_attrs_valid() helper and uses it as suggested by
> > Jeffrey Layton <[email protected]>.
> >
> > fs/nfsd/nfsproc.c | 2 +-
> > fs/nfsd/vfs.c | 2 +-
> > fs/nfsd/vfs.h | 8 ++++++++
> > 3 files changed, 10 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/nfsd/nfsproc.c b/fs/nfsd/nfsproc.c
> > index 36370b957b63..3e438159f561 100644
> > --- a/fs/nfsd/nfsproc.c
> > +++ b/fs/nfsd/nfsproc.c
> > @@ -389,7 +389,7 @@ nfsd_proc_create(struct svc_rqst *rqstp)
> > * open(..., O_CREAT|O_TRUNC|O_WRONLY).
> > */
> > attr->ia_valid &= ATTR_SIZE;
> > - if (attr->ia_valid)
> > + if (nfsd_attrs_valid(attr))
> > resp->status = nfsd_setattr(rqstp, newfhp, &attrs,
> > NULL);
> > }
>
> This function is for NFSv2, which doesn't support any inode attributes
> that aren't represented in ia_valid. We could leave this as-is, but
> this is fine too.

Sorry, I got over-eager with trying to fix all ia_valid checks. It's
actually wrong so I'll send a 3rd version without it.