Created attachment 17601 [details] Example setup to test the bug Overview: Samba always responds with NT_STATUS_ACCESS_DENIED when attempting to write a new file to a share. Steps to reproduce: See attachment. 1. Create a samba configuration with a (public) share. 2. Run SMBD in user mode (non-root). 3. Try to write a new file to the share (e.g. using smbclient). Actual result: NT_STATUS_ACCESS_DENIED opening remote file \test-file.txt Expected result: New file added to share. Build info: MacOS: ProductName: macOS ProductVersion: 12.6 BuildVersion: 21G115 Samba: - 4.17.0rc1 - 4.17.0rc2 - 4.17.0rc3 - 4.17.0 - 4.17.1 Compiled with these options: https://github.com/Homebrew/homebrew-core/blob/HEAD/Formula/samba.rb Additional Builds and Platforms: Doesn't occur on Linux Doesn't occur on <=4.16.5 Additional Information: The conditions for the bug so far are: - SMBD running as a normal user (non-root) - MacOS (Linux is unaffected) - Creating a new file (writing to an existing file works) - Tried: put, scopy, mkdir, mput The bug first came to light when using Qemu's smb feature in the user network. The example configuration is based on the configuration that Qemu produces. For convenience, I've created a HomeBrew Tap to make it easier to install multiple Samba versions side by side. https://github.com/0Rick0/hb-packages
can you check if this is happending with a HFS+ instead of a APFS volume also?
I've formatted a USB disk with MSDOS/FAT32 and with HFS+. In both cases it works with 4.16.5 and it doesn't work with 4.17.0 and 4.17.1.
Created attachment 17612 [details] save and restore errno around unbecome root After some digging I think I found the cause. In vfs_default.c vfswrap_openat() there's a peace of code like this: become_root(); openat(); unbecome_root(); This code is called from open.c non_widelink_open(). Like this: fd = SMB_VFS_OPENAT(...); if (fd == -1) { status = link_errno_convert(errno); } When a file doesn't exist, errno is set to ENOENT. And link_errno_convert sets the correct NT_STATUS for that. But when running smbd as non-root, unbecome_root() fails and sets errno to EPERM. This means that ENOENT would be lost. I've created a patch that saves and restores errno before and after unbecome_root() which solves this specific issue. But maybe this should be part of (un)become root it self? Kind regards, Rick
Well smbd isn't designed to run as non-root, except under very specific test cases where we wrap the become_root()/unbecome_root() functions. This seems somewhat out-of-scope for smbd. Much of smbd functionality depends on root capabilities.
Hmm, I originally came across this issue because of Qemu's userspace networking smb feature. They launch smbd with the same user as the Qemu vm process it self. https://qemu.readthedocs.io/en/latest/system/invocation.html#hxtool-5 Is it a wrong assumption of Qemu that this should work? If so, I think I will create a bug report with the Qemu team. Also, as I read the code, non_widelink_open assumes that errno is set by the SMB_VFS_OPENAT implementation. The vfswrap_openat implementation can have it's errno masked by unbecome_root in some use cases. (currently I know about not being root, but maybe there are others. seteuid for instance can set it.) Shouldn't unbecome_root assure that errno is unmodified? Or should the calling code assume it can be modified?
Well, note that the function signatures for become_root() and unbecome_root() both return void. This means it's just not expected to fail in any way, and isn't checked. Look at this comment in the implementation: void smbd_become_root(void) { /* * no good way to handle push_sec_ctx() failing without changing * the prototype of become_root() */ if (!push_sec_ctx()) { smb_panic("become_root: push_sec_ctx failed"); } push_conn_ctx(); set_root_sec_ctx(); } If QEMU runs smbd as a non-privileged user then they need to wrap these functions to panic on failure I think.