[Bug 271704] O_PATH and acl_get_fd_np doesn't work on FreeBSD 13(.2) and causes vfs_zfsacl in Samba to fail

From: <bugzilla-noreply_at_freebsd.org>
Date: Tue, 30 May 2023 21:06:08 UTC
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=271704

--- Comment #9 from Peter Eriksson <pen@lysator.liu.se> ---
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).

-- 
You are receiving this mail because:
You are the assignee for the bug.