Skip to content

Mask special bits from peer-supplied SCP receive mode#1032

Open
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_4661
Open

Mask special bits from peer-supplied SCP receive mode#1032
yosuke-wolfssl wants to merge 1 commit into
wolfSSL:masterfrom
yosuke-wolfssl:fix/f_4661

Conversation

@yosuke-wolfssl

Copy link
Copy Markdown
Contributor

Description

The SCP receive path did not mask setuid/setgid/sticky bits out of the
peer-supplied file mode before using it. GetScpFileMode() parsed the octal
mode from a peer-sent C/D record (e.g. D4755 0 dirname) and stored the
raw value into ssh->scpFileMode without masking. That unmasked value then
propagated to:

  • the default wsScpRecvCallback, which passes it straight to WMKDIR() for
    directory creation, and
  • any custom scpRecvCb callback (the documented API contract handed the
    callback an unmasked mode).

The send path already masks with WOLFSSH_MODE_MASK (0777) at
src/wolfscp.c:318 and :355; the receive path had no equivalent. As a
result an authenticated peer could force creation of sticky/setgid
directories inside the SCP base path (the kernel strips setuid/setgid from
mkdir() on Linux but preserves sticky; setgid also survives on BSD/macOS),
and a custom callback that applied the mode via chmod() while running as
root could end up creating setuid files.

Fix

Mask the peer-supplied mode at the single point where it is stored, mirroring
the send path:

/* src/wolfscp.c, GetScpFileMode() */
ssh->scpFileMode = mode & WOLFSSH_MODE_MASK;

Masking at the storage point protects every downstream consumer (scpRecvCb,
the default callback, and WMKDIR) at once. This strips setuid (04000),
setgid (02000), and sticky (01000) while leaving ordinary permission bits
untouched.

Tests

Added test_ScpGetFileMode to tests/unit.c. End-to-end coverage is not
possible because both peers mask the mode on send, so the test drives the
parser directly via a new WOLFSSH_TEST_INTERNAL wrapper
(wolfSSH_TestScpGetFileMode). It covers:

  • Masking: C4755->0755, D2755->0755, D1755->0755,
    D7777->0777, and an ordinary-mode control C0644->0644.
  • Index advancement: asserts idx is moved past the prefix, four mode
    octets, and trailing space.
  • Rejection paths: invalid octal digit (C8755), bad prefix (X4755),
    and a buffer shorter than the mode field (C75) all return
    WS_BAD_ARGUMENT.

Verification

  • Normal build clean; scripts/scp.test passes (regular-file transfers
    unaffected).
  • tests/unit.test passes (exit 0, zero failures), including the new test.
  • Negative controls: reverting the mask makes the masking assertions fail;
    mutating the idx/rejection expectations makes those assertions fail —
    confirming each assertion is real.
  • Built and run under ASan+UBSan (LeakSanitizer disabled on macOS): no
    AddressSanitizer or UBSan reports, including the short-buffer case.

Files changed

  • src/wolfscp.c — mask the mode in GetScpFileMode; add
    wolfSSH_TestScpGetFileMode test wrapper.
  • wolfssh/internal.h — declare the test wrapper (guarded by
    WOLFSSH_TEST_INTERNAL + WOLFSSH_SCP).
  • tests/unit.c — add and register test_ScpGetFileMode.

@yosuke-wolfssl yosuke-wolfssl self-assigned this Jun 16, 2026
Copilot AI review requested due to automatic review settings June 16, 2026 01:19

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the SCP receive path by ensuring peer-supplied file modes cannot propagate setuid/setgid/sticky bits into directory creation or custom receive callbacks, matching the existing behavior on the SCP send path.

Changes:

  • Mask peer-supplied SCP mode (C/D records) with WOLFSSH_MODE_MASK when storing it in ssh->scpFileMode.
  • Add a WOLFSSH_TEST_INTERNAL wrapper to directly exercise the SCP mode parser in unit tests.
  • Add a unit test that validates masking behavior, index advancement, and rejection of invalid inputs.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/wolfscp.c Masks peer-supplied mode on receive and adds an internal test wrapper for the parser.
wolfssh/internal.h Declares the new internal test wrapper behind WOLFSSH_TEST_INTERNAL and WOLFSSH_SCP.
tests/unit.c Adds and registers a unit test covering mode masking and error paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1032

Scan targets checked: wolfssh-bugs, wolfssh-src

No new issues found in the changed files. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants