Skip to content

client: prohibit unprivileged users from setting sgid/suid bits#64356

Merged
vshankar merged 1 commit intoceph:mainfrom
tchaikov:wip-client-suid
Jul 21, 2025
Merged

client: prohibit unprivileged users from setting sgid/suid bits#64356
vshankar merged 1 commit intoceph:mainfrom
tchaikov:wip-client-suid

Conversation

@tchaikov
Copy link
Contributor

@tchaikov tchaikov commented Jul 5, 2025

Prior to fb1b72d, unprivileged users could add mode bits as long as S_ISUID and S_ISGID were not included in the change.

After fb1b72d, unprivileged users were allowed to modify S_ISUID and S_ISGID bits only when no other mode bits were changed in the same operation. This inadvertently permitted unprivileged users to set S_ISUID and/or S_ISGID bits when they were the sole bits being modified.

This behavior should not be allowed. Unprivileged users should be prohibited from setting S_ISUID and/or S_ISGID bits under any circumstances.

This change tightens the permission check to prevent unprivileged users from setting these privileged bits in all cases.

Contribution Guidelines

  • To sign and title your commits, please refer to Submitting Patches to Ceph.

  • If you are submitting a fix for a stable branch (e.g. "quincy"), please refer to Submitting Patches to Ceph - Backports for the proper workflow.

  • When filling out the below checklist, you may click boxes directly in the GitHub web UI. When entering or editing the entire PR message in the GitHub web UI editor, you may also select a checklist item by adding an x between the brackets: [x]. Spaces and capitalization matter when checking off items this way.

Checklist

  • Tracker (select at least one)
    • References tracker ticket
    • Very recent bug; references commit where it was introduced
    • New feature (ticket optional)
    • Doc update (no ticket needed)
    • Code cleanup (no ticket needed)
  • Component impact
    • Affects Dashboard, opened tracker ticket
    • Affects Orchestrator, opened tracker ticket
    • No impact that needs to be tracked
  • Documentation (select at least one)
    • Updates relevant documentation
    • No doc update is appropriate
  • Tests (select at least one)
Show available Jenkins commands

bool clearing_suid_sgid = false;
static const uint16_t SUID_SGID_MASK = S_ISUID | S_ISGID;
if ((in->mode & ~SUID_SGID_MASK) == (stx->stx_mode & ~SUID_SGID_MASK) && // other bits unchanged
(in->mode & SUID_SGID_MASK) > (stx->stx_mode & SUID_SGID_MASK)) { // only clearing
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about using a greater than comparison check with a combined mask initially, but AFAICT that still breaks as one can clear the higher significant bit while setting the lower one, Here is a simplified example of what I mean:

existing mode: b10 == 2 would be bigger than new mode: b01 == 1 while a bit got set for the new one.

I'm not sure what effect that would have in practice, but I think it might still lead to an attack if the group or user (whatever the lower significant bit maps to) of the file is one that has more privileges. And even if not, it would IMO be rather confusing and might lead to issues down the line if this gets copied somewhere else with the assumption it's safe.

While it's less clever and needs more code, it might be the best to check each bit separately, e.g. something like:

if ((in->mode & ~(S_ISUID | S_ISGID)) == (stx->stx_mode & ~(S_ISUID | S_ISGID)) &&  // other bits unchanged
    (in->mode & S_ISUID) > (stx->stx_mode & S_ISUID) && // only clearing SUID
    (in->mode & S_ISGID) > (stx->stx_mode & S_ISGID)) { // only clearing SGID
        clearing_suid_sgid = true;
}

Copy link
Contributor Author

@tchaikov tchaikov Jul 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ThomasLamprecht hi Thomas, i was reading the related linux kernel, and realized that there were chances that only the S_ISUID is cleared, so we might not want to prohibit this case. i updated my change accordingly.

@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 6, 2025

changelog

  • prohibit S_ISGID -> S_ISUID change
  • add more tests

@vshankar
Copy link
Contributor

vshankar commented Jul 7, 2025

changelog

  • prohibit S_ISGID -> S_ISUID change
  • add more tests

Thanks @tchaikov - I will have a look.

Copy link
Contributor

@vshankar vshankar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit. Otherwise LGTM.

I'm trying to run the test locally, but running in issues.

@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 7, 2025

changelog

  • s/old_sid_suid/old_suid_sgid/

@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 7, 2025

jenkins test windows

Copy link
Contributor

@ThomasLamprecht ThomasLamprecht left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your new variant makes the code easier to read, the change itself LGTM too:

Reviewed-by: Thomas Lamprecht t.lamprecht@proxmox.com

@vshankar
Copy link
Contributor

vshankar commented Jul 8, 2025

jenkins test windows

1 similar comment
@vshankar
Copy link
Contributor

vshankar commented Jul 8, 2025

jenkins test windows

@vshankar
Copy link
Contributor

vshankar commented Jul 8, 2025

@tchaikov I think the windows failure is legit.

[2025-07-08T08:47:04.000Z] [isolated][googletest] ceph_test_libcephfs_suidsgid failed. Error: Command returned non-zero code(1): "cmd /c 'C:\ceph\ceph_test_libcephfs_suidsgid.exe --gtest_output=xml:C:\workspace\test_results\out\ceph_test_libcephfs_suidsgid\ceph_test_libcephfs_suidsgid_results.xml --gtest_filter="-LibCephFS.Deleg*" >> C:\workspace\test_results\out\ceph_test_libcephfs_suidsgid\ceph_test_libcephfs_suidsgid_results.log 2>&1'".
[2025-07-08T08:47:04.000Z] [isolated][googletest] ceph_test_libcephfs_vxattr passed.
[2025-07-08T08:47:04.000Z] [isolated][googletest] unittest_admin_socket passed.
ceph_test_libcephfs_suidsgid.SuidsgidTest.WriteClearSetuid
[2025-07-08T08:47:07.000Z] subunit-trace reports failures: Command failed: type C:\workspace\test_results\subunit.out | subunit-trace
One or more test suites have failed
At C:\workspace\repos\ceph-win32-tests\test_host\run_tests.ps1:28 char:9
+         throw "One or more test suites have failed"
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (One or more test suites have failed:String) [], RuntimeException
    + FullyQualifiedErrorId : One or more test suites have failed

Mind having a look please?

Prior to fb1b72d, unprivileged users could add mode bits as long as
S_ISUID and S_ISGID were not included in the change.

After fb1b72d, unprivileged users were allowed to modify S_ISUID and
S_ISGID bits only when no other mode bits were changed in the same
operation. This inadvertently permitted unprivileged users to set
S_ISUID and/or S_ISGID bits when they were the sole bits being modified.

This behavior should not be allowed. Unprivileged users should be
prohibited from setting S_ISUID and/or S_ISGID bits under any
circumstances.

This change tightens the permission check to prevent unprivileged
users from setting these privileged bits in all cases.

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 9, 2025

@vshankar hi Venky, i think that the failures were caused by the new negative tests which intended to chmod by clearing suid / sgid directly with an unprivileged user. these tests failed because of -EPERM. i think this is expected, as unprivileged user should not be allowed chown a directory to clearing its suid / sgid. chown(2) only puts that an executable's suid / sgid should be cleared when the executable is mutated by an unprivileged user. this does not apply to the added negative tests. so i am removing them in the latest revision.

@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 9, 2025

changelog:

  • drop irrelevant negative tests added in previous revision.

@vshankar
Copy link
Contributor

vshankar commented Jul 9, 2025

chown(2) only puts that an executable's suid / sgid should be cleared when the executable is mutated by an unprivileged user. this does not apply to the added negative tests. so i am removing them in the latest revision.

Makes sense. We could however add a test for this specific case, correct? I.e., dropping suid/sgid bits from an executable by an unprivileged user.

@tchaikov
Copy link
Contributor Author

tchaikov commented Jul 9, 2025

chown(2) only puts that an executable's suid / sgid should be cleared when the executable is mutated by an unprivileged user. this does not apply to the added negative tests. so i am removing them in the latest revision.

Makes sense. We could however add a test for this specific case, correct? I.e., dropping suid/sgid bits from an executable by an unprivileged user.

we already have tests for this in the same test case. see

@vshankar
Copy link
Contributor

vshankar commented Jul 9, 2025

we already have tests for this in the same test case. see

Got it. Thx! This change is good to run through tests.

@vshankar
Copy link
Contributor

vshankar commented Jul 9, 2025

jenkins test make check

@tchaikov
Copy link
Contributor Author

jenkins test api

@vshankar
Copy link
Contributor

This PR is under test in https://tracker.ceph.com/issues/72073.

@tchaikov
Copy link
Contributor Author

@vshankar hi Venky, thank you for testing this change. now that this change has been verified at least at https://pulpito.ceph.com/vshankar-2025-07-14_15:05:46-fs-wip-vshankar-testing-20250714.111344-debug-testing-default-smithi/8385861/. and the test passed. is there anything else i can do to move this PR forward?

@vshankar
Copy link
Contributor

@vshankar hi Venky, thank you for testing this change. now that this change has been verified at least at https://pulpito.ceph.com/vshankar-2025-07-14_15:05:46-fs-wip-vshankar-testing-20250714.111344-debug-testing-default-smithi/8385861/. and the test passed. is there anything else i can do to move this PR forward?

This is ready to merge. It's just waiting on me to prepare run wiki which I have been a bit slow due to other stuff needing my attention.

@tchaikov
Copy link
Contributor Author

Ahh, I see. Thanks again!

Copy link
Contributor

@vshankar vshankar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vshankar vshankar merged commit 64f0d78 into ceph:main Jul 21, 2025
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cephfs Ceph File System tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants