Bug 271704 - O_PATH and acl_get_fd_np doesn't work on FreeBSD 13(.2) and causes vfs_zfsacl in Samba to fail
Summary: O_PATH and acl_get_fd_np doesn't work on FreeBSD 13(.2) and causes vfs_zfsacl...
Status: Open
Alias: None
Product: Base System
Classification: Unclassified
Component: kern (show other bugs)
Version: 13.2-RELEASE
Hardware: Any Any
: --- Affects Many People
Assignee: freebsd-bugs (Nobody)
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2023-05-29 12:25 UTC by Peter Eriksson
Modified: 2023-07-19 10:42 UTC (History)
5 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Peter Eriksson 2023-05-29 12:25:40 UTC
The new O_PATH support in FreeBSD 13 doesn't seem to work with trying to read ZFS ACLs via acl_get_fd_np().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/acl.h>
#include <sys/stat.h>


int
main(int argc,
     char *argv[]) {
  int i, j, fd;
  acl_t a;
  char *path = ".";
  int flags = O_PATH;
  struct stat sb;
  
  
  for (i = 1; i < argc && argv[i][0] == '-'; i++) {
    for (j = 1; argv[i][j]; j++) {
      switch (argv[i][j]) {
      case 'n':
	flags = 0;
	break;
      }
    }
  }
  
  if (i < argc)
    path = argv[i];
  
  fd = open(path, O_RDONLY|flags);
  if (fd < 0) {
    fprintf(stderr, "%s: Error: %s: open: %s\n", argv[0], path, strerror(errno));
    exit(1);
  }

  if (fstat(fd, &sb) < 0) {
    fprintf(stderr, "%s: Error: %s: fstat: %s\n", argv[0], path, strerror(errno));
  }

  a = acl_get_fd_np(fd, ACL_TYPE_NFS4);
  if (!a) {
    fprintf(stderr, "%s: Error: %s: acl_get_fd_np: %s\n", argv[0], path, strerror(errno));
  }

  return 0;
}


root@runur00:~Lpeter86 # ./t 
./t: Error: .: acl_get_fd_np: Bad file descriptor

root@runur00:~Lpeter86 # ./t -n

root@runur00:~Lpeter86 # getfacl .
# file: .
# owner: Lpeter86
# group: Ladmins
            owner@:rwxp--aARWcCos:-------:allow
            group@:------a-R-c--s:-------:allow
         everyone@:------a-R-c--s:-------:allow



This seems to cause atleast the Samba "zfsacl" module to fail.
Comment 1 Konstantin Belousov freebsd_committer freebsd_triage 2023-05-29 15:12:27 UTC
https://reviews.freebsd.org/D40318
Comment 2 Mark Johnston freebsd_committer freebsd_triage 2023-05-29 15:34:23 UTC
(In reply to Peter Eriksson from comment #0)
Does it appear that Samba also wants to set/modify ACLs using O_PATH fds?
Comment 3 Peter Eriksson 2023-05-29 18:09:22 UTC
(In reply to Mark Johnston from comment #2)

Good question. I don't know yet... (I'm not really that familiar in all the inner workings on Sambas new VFS layer where this magic is done). I have opened a case in the Samba bugzilla too btw. No response so far though.

https://bugzilla.samba.org/show_bug.cgi?id=15376

But anyway I think it would be nice if FreeBSD allowed reading the ACL list at least
(if the parent directory access allows it?)

As a workaround in Samba I replaced a couple of #ifdef O_PATH with #if defined(O_PATH) && defined(LINUX) so another code path in Samba is used (for systems without O_PATH, but since the O_PATH-path is more efficient it would be nice if it could be used :-)
Comment 4 commit-hook freebsd_committer freebsd_triage 2023-05-30 06:03:25 UTC
A commit in branch main references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=7a292504bad8467915f072f0576b2a07c76de1f5

commit 7a292504bad8467915f072f0576b2a07c76de1f5
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2023-05-29 15:07:18 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2023-05-30 05:53:56 +0000

    __acl_get_fd(2), __acl_aclcheck_fd(2): enable for O_PATH filedescriptors

    PR:     271704
    Reported by:    Peter Eriksson  <pen@lysator.liu.se>
    Reviewed by:    markj
    Sponsored by:   The FreeBSD Foundation
    MFC after:      1 week
    Differential revision:  https://reviews.freebsd.org/D40318

 lib/libc/sys/open.2 | 3 ++-
 sys/kern/vfs_acl.c  | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)
Comment 5 Peter Eriksson 2023-05-30 08:46:57 UTC
Did a quick test with Samba and a FreeBSD 13.2-RELEASE kernel modified with the fix for acl_get_fd and now I can create new directories and read the ACL of it. But attempting to modify the ACL from a Windows 10 client fails with:

> An error occured while applying security information to:
> 
> T:\}foo
> 
> The handle is invalid.

As a test I modified the kernel a bit more and made similar changes to acl_set_fd and acl_delete_fd and now it works to update the ACL from the Windows client.

So as the Samba code works today it looks like it needs to be update ACLs via O_PATH-opened file descriptors.
Comment 6 Mark Johnston freebsd_committer freebsd_triage 2023-05-30 15:13:26 UTC
(In reply to Peter Eriksson from comment #5)
I think something doesn't quite add up here: from my reading of the latest Linux sources, the syscalls used to get/set ACLs (fgetxattr(2), fsetxattr(2)) don't operate on O_PATH fds.  Samba must be doing something else there for Linux.
Comment 7 Peter Eriksson 2023-05-30 16:59:29 UTC
> Samba must be doing something else there for Linux.

Yes, Linux doesn't support accessing local real ZFS ACLs at all... 

I think you might sort of work around that by NFSv4-exporting a ZFS filesystem and then loopback mount it back onto the same host, since ZFS-on-Linux internally does support the full ACLs (and exposes them via NFS as 'xattrs' so you can modify them via that interface - there just isn't a working kernel - userland interface to access/modify them directly. There is some half-baked PosixACL emulation support in ZFS-on-Linux but that only supports a subset of the full access rights.

So the Samba vfs_zfsacl module only works on FreeBSD (and Solaris-derivates)

One (out of many, but that one is a showstopper for us) big reason we use FreeBSD and not Linux as the OS for our ZFS-based fileservers :-)

ZFS on Linux isn't that big either so people using Samba on Linux are normally using other filesystems that doesn't have "full" ZFS/NFS ACLs either and then they emulate the ACLs using other means (but that makes it hard to share data between SMB and NFS for example since Samba will validate ACLs one way and then the kernel/NFS will do it differently). It's big headache.
Comment 8 Konstantin Belousov freebsd_committer freebsd_triage 2023-05-30 20:43:49 UTC
(In reply to Peter Eriksson from comment #7)
But what is the API Linux uses to modify ACLs over NFS?  Does it end up as
modification syscalls over O_PATH fds?
Comment 9 Peter Eriksson 2023-05-30 21:06:08 UTC
Linux uses the getxattr/setxattr calls for NFS stuff.

Well, the generic way Samba handles O_PATH stuff on Linux for syscalls that fails is apparently to use /proc/self/fd/%d (where %d is the fd for the O_PATH descriptor) and then call the path-based variants of the syscall using that path instead of using the fd

       if (!fsp->fsp_flags.is_pathref) {
                result = fchmod(fsp_get_io_fd(fsp), mode);
                END_PROFILE(syscall_fchmod);
                return result;
        }

        if (fsp->fsp_flags.have_proc_fds) {
                int fd = fsp_get_pathref_fd(fsp);
		const char *p = NULL;
                char buf[PATH_MAX];

                p = sys_proc_fd_path(fd, buf, sizeof(buf));
                if (p != NULL) {
                        result = chmod(p, mode);
                } else {
                        result = -1;
                }
                END_PROFILE(syscall_fchmod);
                return result;
        }

        /*                                                                                                                                                      
         * This is no longer a handle based call.                                                                                                               
         */
        result = chmod(fsp->fsp_name->base_name, mode);

(The last line of code is the reason most calls still work for a default-compiled Samba on FreeBSD 13 - it falls back to using the (insecure) full path based functionality... But that code was missing in the vfs_zfsacl module).

For FreeBSD I think using openat(fd, "", O_EMPTY_PATH) is a cleaner way to get an fd that you can use... Something like this:

        if (!fsp->fsp_flags.is_pathref) {
                rv = facl(fsp_get_io_fd(fsp), ACE_SETACL, naces, acebuf);
        } else {
#if defined(HAVE_OPENAT) && defined(O_EMPTY_PATH)
                fd = fsp_get_pathref_fd(fsp);

                /* First try this for versions of FreeBSD that allows facl() on O_PATH fd's */
                rv = facl(fd, ACE_SETACL, naces, acebuf);
                if (rv < 0 && errno == EBADF) {
                        /* Fall back to getting a real fd via openat() */
                        int saved_errno, real_fd;

                        real_fd = openat(fd, "", O_EMPTY_PATH);
                        if (real_fd < 0) {
                                errno = EBADF;
                                return false;
                        }

                        rv = facl(real_fd, ACE_SETACL, naces, acebuf);
                        saved_errno = errno;
                        close(real_fd);
                        errno = saved_errno;
                }
#else
                /* Last ditch fallback */
                rv = acl(fsp->fsp_name->base_name, ACE_SETACL, naces, acebuf);
#endif
        }


(facl is a helper function in libsunacl that calls the right acl_get_fd_np/acl_get_file functions that Samba uses for compatibility with Solaris).
Comment 10 Konstantin Belousov freebsd_committer freebsd_triage 2023-05-30 21:42:42 UTC
Of course openat(fd, O_EMPTY_PATH) is cleaner.  Doesn't Linux have the same
flag?

But my question was, are some Linux equivalents of our __acl_set_fd() etc
work over O_PATH fds for NFS.  This would be an argument pro or contra enabling
that syscalls for O_PATH fds, as discussed with Mark.
Comment 11 commit-hook freebsd_committer freebsd_triage 2023-06-05 08:37:16 UTC
A commit in branch stable/13 references this bug:

URL: https://cgit.FreeBSD.org/src/commit/?id=1e26f443cd2b8775ba50207aa9645ec12ead9e8a

commit 1e26f443cd2b8775ba50207aa9645ec12ead9e8a
Author:     Konstantin Belousov <kib@FreeBSD.org>
AuthorDate: 2023-05-29 15:07:18 +0000
Commit:     Konstantin Belousov <kib@FreeBSD.org>
CommitDate: 2023-06-05 08:35:01 +0000

    __acl_get_fd(2), __acl_aclcheck_fd(2): enable for O_PATH filedescriptors

    PR:     271704

    (cherry picked from commit 7a292504bad8467915f072f0576b2a07c76de1f5)

 lib/libc/sys/open.2 | 3 ++-
 sys/kern/vfs_acl.c  | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)