Accept RACHECK rc=4 (resource not protected) as allowed - #136
Merged
Conversation
racf_auth() has been answering 0 for a resource with no profile because libc370 set RACHECK flag byte 0x10 believing it meant LOG=NONE, when that bit is DSTYPE=V (libc370 #63). With the flag corrected the same check answers SAF rc 4 -- still "allowed", but visible to callers for the first time. httpd has two consumers of that rc, not one: - auth_gate() (httppc.c) tests != 0 for its RES= route gate. On a system without the profile named by, say, RES=FACILITY:MVSMF.ACCESS, every request to that route would start answering 403. - http_check_auth() publishes the rc to CGI modules through the HTTPX vector, and modules are LINKed at runtime -- a widened "0 or 4" contract would leave every not-yet-rebuilt module denying unprotected resources with no build-time signal to move. So normalize rc 4 to 0 inside http_check_auth(). The published contract "0 == permitted" keeps holding, no module needs rebuilding, and the route gate is correct unchanged. rc 4 never means denied (0 permitted, 4 not protected, 8+ refused), so this cannot soften a denial. Deliberately not the shorthand "rc <= 4": http_check_auth() answers -1 for an unauthenticated request, and <= 4 would read that as allowed and let unauthenticated callers past the gate. The decision therefore lives in httpracf(), which tests the two codes explicitly -- free of httpd.h so the table is covered by a dual host/MVS test, including the -1 cases. A resource with no profile passing the gate is SAF-correct but also the shape of a misconfigured RES= route, so trace it under DEBUG and document it in docs/configuration.md. Safe against both the old and the new libc370, and httpd was the last consumer blocking libc370 #63 (ftpd landed its side in fc26874). Fixes #135
mgrossmann
force-pushed
the
issue-135-racf-rc4-allowed
branch
from
August 7, 2026 06:51
1aff89c to
a86eb9f
Compare
Contributor
Author
|
Correction to the Verification section above: the build does not pass Nothing about this PR's own code changes: it adds no warnings under |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #135. Unblocks mvslovers/libc370#63 — httpd was the last consumer holding it up (ftpd landed its side in
fc26874).The correction to the issue
The issue states httpd "makes no decision on the value — it hands it to CGIs through the HTTPX vector". That is not the case:
auth_gate()insrc/httppc.ctests!= 0for itsRES=route gate, added in9e7014e(per-route auth policy) a month before the issue was written.With the documented example from
docs/configuration.md:on a system where
MVSMF.ACCESShas no profile, RACHECK answers 4 after libc370#63 and every/zosmf/*request gets a 403. So this is an outage in the httpd core, not only a CGI contract question.What was picked, and why
Option 2 from the issue (normalize 4 → 0 in
http_check_auth()), but the deciding argument is not the one the issue gives.The issue argues for option 2 because runtime-loaded modules make a contract change expensive. That is true in principle but currently hypothetical — grepping mvsmf, httplua, httprexx and every in-tree CGI (
httpdsrv.c,httpjes2.c,httpdsl.c) forhttp_check_auth/HTTPCKAUfinds zero consumers. httpd's ownauth_gate()is the only caller today.The load-bearing argument is httpd-specific:
http_check_auth()returns -1 for unauthenticated. It was never a SAF pass-through — the published contract is httpd's, not SAF's. And the widened idiom libc370#63 itself suggests,rc <= 4, would read that -1 as allowed and let unauthenticated requests past the gate. Option 1 would have to renumber -1 as well, breaking existing== -1checks. Option 2 sidesteps it, and because the normalization sits inhttpxauth.c,httppc.cis correct unchanged.rc 4 never means denied (0 permitted, 4 not protected, 8+ refused), so collapsing it into 0 cannot soften a denial.
This also explains why ftpd's opposite choice (
ftpd_racf_allowed(),rc == 0 || rc == 4at the call sites) is not an inconsistency: ftpd callsracf_auth()directly and has no sentinel value in the range.Changes
src/httpracf.c+include/httpracf.h—httpracf(rc), the SAF decision table. Free ofhttpd.h(likehttpbody()) so it unit-tests dual host/MVS; 8-char name, no asm alias needed.src/httpxauth.c—http_check_auth()normalizes 4 → 0, plus the rationale and aDEBUGtrace for the not-protected case (the only place the distinction is still visible).src/httppc.c— comment pinning why!= 0is the whole test and must not be relaxed to<= 4.include/httpd.h,include/httpcgi.h— the return contract documented at both declaration sites.httpcgi.his the one CGI authors read.test/tstracf.c+project.toml—[[test]] TSTRACF, 12 assertions.docs/configuration.md— aRES=resource with no profile permits access; a typo silently disables stage 2 rather than locking the route.Verification
make test-host— 4 tests, 63 assertions, all pass (TSTRACF12/12).httpracf()to the pre-fixrc == 0makesTSTRACFfail on exactly the rc=4 assertion (11 pass / 1 fail), then passes again restored.make(cc370,-Wall -Werror) — clean, all 6 modules link.make deps/make/make test/make lib—make testbuilds the test load modules, it never executes assertions. The 63 passing assertions above are from a localmake test-host.Not verified on the live MVS system: the end-to-end path still needs libc370#63 to land before rc 4 can actually be observed. This change is safe against both the old and the new library, which is why it goes first.
To verify on MVS after libc370#63 lands
A route with
RES=naming an undefined FACILITY profile must serve normally (not 403), andDEBUG 1should show therc=4: resource not protectedtrace for it.Follow-up
The policy question is deliberately not settled here — whether an undefined
RES=profile should mean "everyone passes" is a decision now that httpd can see the difference; today's permissiveness was an accident of the wrong flag bit. Filed separately so this PR stays behaviour-preserving.Noticed, not touched
include/httpd.h:466—extern http_set_env(...)is missing itsintreturn type (implicit int, pre-existing). Out of scope here.