Bug 15215 - Can't create files to MacOS Host
Summary: Can't create files to MacOS Host
Alias: None
Product: Samba 4.1 and newer
Classification: Unclassified
Component: File services (show other bugs)
Version: 4.17.0
Hardware: All Mac OS X
: P5 regression (vote)
Target Milestone: ---
Assignee: Samba QA Contact
QA Contact: Samba QA Contact
Depends on:
Reported: 2022-10-25 08:17 UTC by Rick
Modified: 2022-11-03 16:58 UTC (History)
1 user (show)

See Also:

Example setup to test the bug (1017 bytes, application/x-gzip)
2022-10-25 08:17 UTC, Rick
no flags Details
save and restore errno around unbecome root (377 bytes, patch)
2022-11-02 13:59 UTC, Rick
Rick.Rongen: review?
Rick.Rongen: ci-passed?

Note You need to log in before you can comment on or make changes to this bug.
Description Rick 2022-10-25 08:17:25 UTC
Created attachment 17601 [details]
Example setup to test the bug

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:
ProductName:	macOS
ProductVersion:	12.6
BuildVersion:	21G115
 - 4.17.0rc1
 - 4.17.0rc2
 - 4.17.0rc3
 - 4.17.0
 - 4.17.1
Compiled with these options:

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.
Comment 1 Björn Jacke 2022-10-25 09:53:56 UTC
can you check if this is happending with a HFS+ instead of a APFS volume also?
Comment 2 Rick 2022-10-25 10:04:45 UTC
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.
Comment 3 Rick 2022-11-02 13:59:28 UTC
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:

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,
Comment 4 Jeremy Allison 2022-11-02 16:22:52 UTC
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.
Comment 5 Rick 2022-11-03 08:05:45 UTC
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.

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?
Comment 6 Jeremy Allison 2022-11-03 16:58:07 UTC
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");

If QEMU runs smbd as a non-privileged user then they need to wrap these functions to panic on failure I think.