wolfsshd: expand AuthorizedKeysFile %u/%h/%% tokens per user#1064
wolfsshd: expand AuthorizedKeysFile %u/%h/%% tokens per user#1064yosuke-wolfssl wants to merge 1 commit into
Conversation
b3d1fc2 to
5235de8
Compare
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #1064
Scan targets checked: wolfssh-bugs, wolfssh-src
No new issues found in the changed files. ✅
5235de8 to
169ee28
Compare
aidangarske
left a comment
There was a problem hiding this comment.
🐺 Skoll Code Review
Overall recommendation: REQUEST_CHANGES
Findings: 2 total — 2 posted, 0 skipped
Posted findings
- [High] Windows drive-relative AuthorizedKeysFile paths are treated as absolute —
apps/wolfsshd/auth.c:573-610 - [Medium] New path-resolution tests mask missing string termination —
apps/wolfsshd/test/test_configuration.c:1965-1974
Review generated by Skoll.
| else if (path[0] == '\\') { | ||
| ret = 1; | ||
| } | ||
| else if (((path[0] >= 'A' && path[0] <= 'Z') || |
There was a problem hiding this comment.
🟠 [High] Windows drive-relative AuthorizedKeysFile paths are treated as absolute
🚫 BLOCK bug
The PR adds Windows absolute-path detection, but it treats any <letter>: prefix as absolute. On Windows, paths like C:keys\alice or C:alice are drive-relative, not fully qualified. Because ResolveAuthKeysPath() skips the home-directory join when IsAbsoluteAuthKeysPath() returns true (copying the path unchanged for SearchForPubKey to open), an AuthorizedKeysFile C:keys\%u pattern is opened relative to the daemon process's current directory on drive C instead of under the user's home directory. Data flow: config/public setter -> ExpandAuthKeysTokens expands %u -> IsAbsoluteAuthKeysPath returns true solely because path[1] == ':' -> ResolveAuthKeysPath copies unchanged -> SearchForPubKey opens it. Under a misconfigured Windows deployment using X:relative syntax, an attacker with write access to the relevant current-directory tree can influence which authorized_keys file is loaded. This is introduced by the new _WIN32 branch and is not covered by the new Windows test vectors, which only check C:\keys\%u. Severity views differ across modes: review rates this High (BLOCK), security rates the practical impact Low (requires Windows, a non-default/mistyped config, and write access to the current-directory tree); the stricter High is kept.
Recommendation: Require a slash or backslash after the drive colon for fully qualified Windows paths (path[1] == ':' && (path[2] == '\\' || path[2] == '/')), and add Windows test vectors for C:%u and C:keys\%u that expect home-relative resolution (or rejection).
| Log(" Testing scenario: pattern \"%s\" user \"%s\".", | ||
| vectors[i].pattern != NULL ? vectors[i].pattern : "(null)", | ||
| vectors[i].user != NULL ? vectors[i].user : "(null)"); | ||
| WMEMSET(resolved, 0, sizeof(resolved)); |
There was a problem hiding this comment.
🟡 [Medium] New path-resolution tests mask missing string termination
💡 SUGGEST test
The new unit test clears resolved before every success case, so it cannot catch whether ResolveAuthKeysPath() actually writes a terminating NUL. That matters because the relative/default branch currently copies WSTRLEN(suffix) bytes even though the adjacent comment says it copies the NUL terminator. The production caller also zeroes its buffer, so this is mostly a test-quality gap, but the PR newly exposes and tests this helper and should not bake in a caller-side zeroing precondition.
Recommendation: Initialize resolved with a non-zero byte pattern (e.g. 0xA5) for success cases, then assert the expected string. Consider fixing the helper to copy WSTRLEN(suffix) + 1 in the relative/default branch.
wolfsshd: expand AuthorizedKeysFile %u/%h/%% tokens per user
Problem
ResolveAuthKeysPathcopied an absoluteAuthorizedKeysFilepattern into theresolved path verbatim, with no token substitution (a
TODOmarked the gap).A common OpenSSH-migration config such as:
resolved to the literal
/etc/ssh/keys/%ufor every user. If a file existedat that un-substituted path, its keys were accepted for any valid system user,
collapsing per-user key isolation.
Addressed by f_5827.
Fix
ExpandAuthKeysTokens()— expands%u(user),%h(home dir), and%%(literal
%) into a bounds-checked buffer. Unrecognized tokens andover-length expansions fail closed (
WS_FATAL_ERROR) rather than reachingthe filesystem.
ResolveAuthKeysPath()now takes the connecting user and expands the patternbefore the absolute/relative decision, so both branches honor substitution and
each user resolves to a distinct path.
SearchForPubKey()→CheckPublicKeyUnix(Unix)and
CheckPublicKeyWIN(Windows). Both call sites already held it; no newgetpwnam/lookup is introduced and no shared mutable state is added.Token semantics
%u%h%%%A recognized token with no value (e.g.
%uwith a NULL user) and anyunrecognized token fail closed. A trailing lone
%is treated as a literal.Platform notes
Absolute-path detection is
/-rooted on POSIX; on Windows it additionallyrecognizes a
\-root or a drive-letter (X:) root, guarded by#ifdef _WIN32so a POSIX relative pattern beginning with
\or<letter>:is not misread.Testing
test_ResolveAuthKeysPathunit test (13 scenarios): per-user%u(
alicevsbob),%h,%%, relative, default-NULL fallback, trailing%,unrecognized token, NULL-user fail-closed, expansion over-length, relative
home-dir over-length, and platform-aware drive-letter/backslash handling.
MAX_PATH_SZmoved toconfiguration.hso the test buffer tracks the realcontract;
ResolveAuthKeysPathexposed to the tests viaWOLFSSHD_STATIC.tests catch both the verbatim-copy regression and a removed bounds check.
auth.c/configuration.ccompile with no new warnings; default test set(api-test, unit-test, testsuite) passes.
Files changed
apps/wolfsshd/auth.capps/wolfsshd/auth.happs/wolfsshd/configuration.happs/wolfsshd/test/test_configuration.c