Skip to content

Add http_get_password() HTTPX export for token-safe CGI job submit#112

Merged
mgrossmann merged 2 commits into
mainfrom
issue-111-http-get-password
Jul 5, 2026
Merged

Add http_get_password() HTTPX export for token-safe CGI job submit#112
mgrossmann merged 2 commits into
mainfrom
issue-111-http-get-password

Conversation

@mgrossmann

Copy link
Copy Markdown
Contributor

Fixes #111

Summary

Adds an append-only HTTPX export, http_get_password(), so a CGI (mvsMF) can obtain the caller's plaintext password to place a real USER=/PASSWORD= on a JES2 internal-reader JOB card — for both Basic and token authentication.

The password is already retained server-side: cred_login() Blowfish-encrypts it into the persistent CRED (id.password) for the credential's whole lifetime, and a token request resolves to the same CRED via cred_find_by_token(). A CGI cannot decrypt it directly because credid_dec() reads the Blowfish key via credkey()/__wsaget() from the current GRT's WSA, which is only initialized in httpd's own GRT — a CGI runs under its own GRT where that key reads back as zero (the #109 wall).

Changes

  • include/httpd.h — new CREDKEY *credkey field appended to the HTTPD block (0x138); new http_get_password function pointer appended to the HTTPX vector (0x13C, append-only); extern prototype + public macro.
  • src/httpd.c — cache credkey() into httpd->credkey right after cred_init() (runs in httpd's GRT, where credkey() is valid).
  • src/httpxauth.c — implement http_get_password(). It reads the cached key pointer (never credkey()/__wsaget()) and blowfish_decrypt()s httpc->cred->id.password — a pure function of (data, key) valid in any GRT.
  • src/httpx.c — wire the new entry into the static vector.
  • docs/auth-redesign.md — add the export to the §2.4 list.

Behavior

  • NULL-safe like the sibling accessors: returns NULL when unauthenticated (httpc->cred == NULL), when the credential carries no password (token-only session, passflg without CREDID_PASS_ENCRYPT), or when there is no room in out.
  • Returned password is the RACF-canonical (upper-cased) form, since cred_login() folds it before encrypting — correct for a JOB card.

Security

For job submission the plaintext password is unavoidably needed by the CGI (it goes on the JOB card), so the "never hand back the decrypted password" principle can't hold here. The mitigation is to keep the key encapsulated in httpd (the CGI only receives the already-required plaintext) and have the caller memset its buffer immediately after building the JOB card. This is no more exposure than the current Basic-header decode, and it finally covers the token flow.

Verification

  • make compiledb regenerated cleanly (120 entries); no new clangd diagnostics in the changed code (the remaining httpd.c/httpd.h clangd errors are pre-existing trigraph/MVS-builtin artifacts unrelated to this change).
  • The GRT-crossing behavior (a CGI decrypting with the cached pointer) cannot be exercised on the host — it needs live MVS via the mvsMF consumer (Job submission needs the caller's password; the token model can't provide it mvsmf#164).

Consumer

Once merged, mvsMF's process_jobcard() sources PASSWORD= from http_get_password() and the userid from http_get_userid(), drops get_basic_auth_credentials(), and stops parsing the Authorization header — job submit then works identically for Basic and token auth (mvslovers/mvsmf#164).

A CGI submitting a batch job through the JES2 internal reader must place
a real USER=/PASSWORD= on the JOB card. Recovering the password by
re-decoding the request's Authorization: Basic header only works for
Basic auth and has no answer for the token flow, where the request
carries no password.

The password is already retained server-side: cred_login() Blowfish-
encrypts it into the persistent CRED (id.password) for the credential's
whole lifetime, including token-authenticated requests that resolve to
the same CRED. A CGI cannot simply decrypt it, because credid_dec()
reads the Blowfish key via credkey()/__wsaget() from the current GRT's
WSA, which is only initialized in httpd's own GRT. A CGI runs under its
own GRT (the #109 wall), so that key reads back as zero.

Cache the credkey() pointer in the HTTPD control block at cred_init()
time (in httpd's GRT, where credkey() is valid). The key bytes live at a
stable WSA address readable across the shared address space, so caching
the pointer is enough. Add the append-only HTTPX export
http_get_password(), which decrypts id.password with blowfish_decrypt()
using that cached key -- a pure function of (data, key) valid in any
GRT, never calling credkey()/__wsaget() from CGI context.

NULL-safe like the sibling accessors: returns NULL when unauthenticated
or when the credential carries no password (token-only session). The
returned password is RACF-canonical (upper-cased at login).

Fixes #111
Drives the real http_get_password() export on a live HTTPC. Path 1
(no RACF): a CRED with an encrypted CREDID built the way cred_login()
does, read through the HTTPD-block cached key pointer -- covers a 4-char
and an exactly-8-char password round-trip (twice, mirroring a cache
hit), a CRED with no password, no CRED, and a NULL cached key. Path 2
(RACF, skipped without APF): cred_login() with a lower-case password
that RACF folds to upper case, asserting the export returns the folded
form for both the fresh and cache-hit reads.

MVS-only (blowfish + RACF SVCs); runs under make test-mvs.
@mgrossmann

Copy link
Copy Markdown
Contributor Author

make test-mvs — green on live MVS (mvsdev.lan)

Ran TSTGPWD plus the sibling auth tests (TSTGUID, TSTCRED) to also catch any regression from the HTTPD-struct / HTTPX-vector changes:

  TEST       BATCH          TSO
  ---------- -------------- --------------
  TSTCRED    ok CC 0        ok CC 0
  TSTGPWD    ok CC 0        ok CC 0
  TSTGUID    ok CC 0        ok CC 0

  assertions (batch+tso): 84 PASS, 0 FAIL

TSTGPWD's RACF path ran (IBMUSER had APF), so beyond the crypto round-trip and NULL-safety it verified the fold: a lower-case "sys1" login is returned by http_get_password() as the RACF-canonical "SYS1", on both the fresh and cache-hit reads.

Verification boundary (unchanged): the test runs in a single GRT, so it validates the crypto/fold/NULL-safety but not the cross-GRT premise #111 is about (that the cached key pointer is dereferenceable from a CGI's own GRT, where credkey() reads zero). That premise is the same shared-address-space mechanism #109 relied on; the end-to-end proof in a real CGI GRT is the mvsMF consumer, mvslovers/mvsmf#164.

@mgrossmann
mgrossmann merged commit 6b87d82 into main Jul 5, 2026
1 check passed
@mgrossmann
mgrossmann deleted the issue-111-http-get-password branch July 5, 2026 21:23
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.

Add http_get_password() HTTPX export so CGIs can obtain the caller's password (token-safe INTRDR job submit)

1 participant