Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions credentials/include/cred.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,19 @@ credid_enc(CREDID *in, CREDID *out) asm("CREDIDEN");
extern CREDTOK
credtok_gen(CREDID *id) asm("CREDTOKG");

/* logout CRED for this CREDTOK */
extern int
/* logout CRED for this CREDTOK (uses cred_array() -- caller must run in the
GRT where the credential store was initialized, i.e. httpd's own core) */
extern int
credtok_logout(CREDTOK *token) asm("CREDTOKL");

/* credtok_logout_arr() - like credtok_logout() but operates on the caller-
supplied credential array instead of cred_array(). cred_array() reads the
current GRT's WSA, so it is empty/foreign from a CGI's GRT; passing httpd's
own array pointer (cached at cred_init() time) makes logout work from any
execution context. See issue #113 (same GRT/WSA class as #109/#111). */
extern int
credtok_logout_arr(CRED ***array, CREDTOK *token) asm("CREDTKLA");

/* cred_new() - allocate a new CRED struct. all parms can be NULL */
extern CRED *
cred_new(CREDID *id, CREDTOK *token, ACEE *acee, unsigned char flags) asm("CREDNEW");
Expand Down
30 changes: 20 additions & 10 deletions credentials/src/credtokl.c
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
#include "cred.h"

/* logout CRED for this CREDTOK */
int
credtok_logout(CREDTOK *token)
/* logout the CRED matching this CREDTOK in the supplied array. Split out from
credtok_logout() so a CGI can pass httpd's own array pointer: cred_array()
reads the current GRT's WSA, which is empty/foreign from a CGI's GRT, so the
scan there finds nothing and the session is never invalidated (issue #113,
same GRT/WSA class as #109/#111). */
int
credtok_logout_arr(CRED ***array, CREDTOK *token)
{
int rc = 1;
int lockrc;
CRED ***array = cred_array();
unsigned count, n;

if (!token) goto quit;
if (!array) goto quit;

lockrc = lock(array, LOCK_EXC);

count = array_count(array);
for (n=1; n <= count; n++) {
CRED *cred = array_get(array, n);

if (!cred) continue;

if (memcmp(&cred->token, token, sizeof(CREDTOK))==0) {
cred = array_del(array, n);
if (cred) {
Expand All @@ -28,9 +31,16 @@ credtok_logout(CREDTOK *token)
break;
}
}

if (lockrc==0) unlock(array, LOCK_EXC);

quit:
return rc;
}

/* logout CRED for this CREDTOK, using this GRT's credential array */
int
credtok_logout(CREDTOK *token)
{
return credtok_logout_arr(cred_array(), token);
}
3 changes: 2 additions & 1 deletion include/httpd.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,8 @@ struct httpd {
UCHAR cfg_keepalive_max; /* 135 max reqs per connection */
USHRT cfg_session_timeout; /* 136 credential idle TTL (min), 0=off */
CREDKEY *credkey; /* 138 blowfish key ptr (issue #111) */
}; /* 13C */
CRED ***credarr; /* 13C credential array ptr (issue #113) */
}; /* 140 */

/* HTTP variables */
struct httpv {
Expand Down
9 changes: 9 additions & 0 deletions project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ name = "TSTGPWD"
sources = ["test/mvs/tstgpwd.c"]
host = false # blowfish_* + RACF SVCs are MVS-only -> test-mvs

# credtok_logout_arr() + wrapper (issue #113): logout must find, unlink and free
# the CRED in the supplied array so the token stops resolving. Guards the split
# that lets http_logout() reach httpd's store from a CGI's GRT. MVS-only (SHA256
# token + array/lock SVCs); cred_/credtok_ resolve from the [internal] archive.
[[test]]
name = "TSTLOUT"
sources = ["test/mvs/tstlout.c"]
host = false # crent370 SVCs are MVS-only -> test-mvs

# http_new_env() / httpnenv() env-block allocation + layout (guards the M9
# offsetof sizing). Needs the HTTPV struct via httpd.h, so MVS-only; httpnenv
# (HTTPNENV) resolves from the [internal] autocall archive.
Expand Down
7 changes: 7 additions & 0 deletions src/httpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ initialize(int argc, char **argv)
copy) is enough for a CGI running under its own GRT (issue #111). */
httpd->credkey = credkey();

/* Likewise cache the credential array pointer so http_logout() can reach
httpd's real store from a CGI's GRT. cred_array() is GRT-relative too,
so a CGI calling it sees an empty/foreign array and logout invalidates
nothing; caching httpd's array pointer here (in httpd's GRT) fixes it
(issue #113, same class as #111). */
httpd->credarr = cred_array();

/* unlock the httpd */
unlock(httpd,0);

Expand Down
18 changes: 15 additions & 3 deletions src/httpxauth.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,17 +97,29 @@ http_check_auth(HTTPC *httpc, const char *classname, const char *resource, int a

/* http_logout() - end the client's session: drop the CRED (and its ACEE) from
** the credential store by token and clear httpc->cred. Returns 0 on success,
** -1 if there was no session. */
** -1 if there was no session (or no cached store).
**
** We must NOT reach the store via credtok_logout()/cred_array() here:
** cred_array() reads the credential array out of the *current* GRT's WSA, which
** is only populated in httpd's own GRT. A CGI reached through the HTTPX vector
** runs under its own GRT, where that slot is empty -- so the token scan finds
** nothing, the CRED stays in httpd's real store, and the (deterministic) token
** keeps resolving: logout would be a silent no-op (issue #113). Instead we use
** the array pointer httpd cached in the HTTPD block at cred_init() time
** (httpc->httpd->credarr), the same GRT-independent pattern http_get_password()
** uses for the blowfish key (#111). The array lives in the shared address
** space (address-keyed ENQ lock, subpool-0 storage), so mutating/freeing it
** from the CGI's GRT is safe. */
__asm__("\n&FUNC SETC 'HTTPLOUT'");
#undef http_logout
int
http_logout(HTTPC *httpc)
{
int rc;

if (!httpc || !httpc->cred) return -1;
if (!httpc || !httpc->cred || !httpc->httpd) return -1;

rc = credtok_logout(&httpc->cred->token);
rc = credtok_logout_arr(httpc->httpd->credarr, &httpc->cred->token);
httpc->cred = NULL; /* the CRED it pointed at may now be freed */
return rc;
}
Expand Down
109 changes: 109 additions & 0 deletions test/mvs/tstlout.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/* TSTLOUT.C
** Test for credtok_logout_arr() (CREDTKLA, credentials/src/credtokl.c) and its
** credtok_logout() wrapper -- the store side of http_logout() (HTTPLOUT).
**
** Issue #113: http_logout() was a silent no-op when called from a CGI. It
** reached the store via credtok_logout() -> cred_array(), which reads the
** credential array out of the *current* GRT's WSA. A CGI runs under its own
** GRT, where that slot is empty, so the token scan found nothing, the CRED
** stayed in httpd's real store, and the deterministic CREDTOK kept resolving --
** logout returned "success" but never invalidated the session. The fix splits
** out credtok_logout_arr(array, token), which operates on a caller-supplied
** array pointer (httpd caches its own in HTTPD.credarr at cred_init() time), so
** logout reaches httpd's real store from any GRT. Same class as #109/#111.
**
** This test runs single-GRT (a unit test cannot cross the CGI GRT boundary --
** that is verified live via mvsMF #168), so it guards the *refactor*: given an
** explicit array, credtok_logout_arr() must find, unlink and free the matching
** CRED so the token stops resolving; the wrapper must still work via
** cred_array(); and the NULL/absent cases must be no-ops. It needs no RACF and
** no APF: the CREDs carry no ACEE, so cred_free() does no racf_logout().
**
** MVS-only (SHA256 token + array/lock SVCs live in crent370), like tstcred.
*/
#include "cred.h"
#include <mbtcheck.h>

/* build + store a CRED for (userid,addr) and return it; *tok gets its token */
static CRED *
add_cred(unsigned addr, const char *user, CREDTOK *tok)
{
CREDID id;
CRED *cred;

credid_init(&id);
credid_update(&id, &addr, (unsigned char *)user, (unsigned char *)"SYS1");
*tok = credtok_gen(&id);

cred = cred_new(&id, tok, NULL, 0); /* no ACEE -> cred_free needs no RACF */
if (cred) cred_add(cred);
return cred;
}

int main(void)
{
CRED ***array;
CRED *credA;
CRED *credB;
CREDTOK tokA;
CREDTOK tokB;
CREDTOK tokX;
CREDID idX;
unsigned before;
int rc;

printf("=== HTTPD credtok_logout (issue #113) tests ===\n");

rc = cred_init(NULL, 0);
CHECK(rc == 0, "cred_init() succeeds");
if (rc) return mbt_test_summary("TSTLOUT");

array = cred_array();
CHECK(array != NULL, "cred_array() returns the store");
if (!array) return mbt_test_summary("TSTLOUT");

/* --- credtok_logout_arr(): the array-explicit path http_logout() uses --- */
credA = add_cred(0xC0A80101, "USERA", &tokA);
CHECK(credA != NULL, "cred_new/cred_add (USERA) succeeds");
if (credA) {
CHECK(cred_find_by_token(&tokA) == credA,
"token resolves to the CRED before logout");

before = array_count(array);
rc = credtok_logout_arr(array, &tokA);
CHECK(rc == 0, "credtok_logout_arr returns 0 (found + freed)");
CHECK(cred_find_by_token(&tokA) == NULL,
"token no longer resolves after logout (the #113 fix)");
CHECK(array_count(array) == before - 1,
"the CRED was unlinked from the store");
}

/* logging the same (now absent) token out again finds nothing -> rc 1 */
rc = credtok_logout_arr(array, &tokA);
CHECK(rc == 1, "credtok_logout_arr on an absent token is a no-op (rc 1)");

/* a token that was never added -> rc 1 */
credid_init(&idX);
credid_update(&idX, NULL, (unsigned char *)"NOBODY", (unsigned char *)"X");
tokX = credtok_gen(&idX);
rc = credtok_logout_arr(array, &tokX);
CHECK(rc == 1, "credtok_logout_arr on an unknown token is a no-op (rc 1)");

/* NULL guards must not crash and must report "nothing done" (rc 1) */
CHECK(credtok_logout_arr(NULL, &tokA) == 1, "NULL array -> rc 1");
CHECK(credtok_logout_arr(array, NULL) == 1, "NULL token -> rc 1");

/* --- the credtok_logout() wrapper still works via cred_array() --------- */
credB = add_cred(0xC0A80102, "USERB", &tokB);
CHECK(credB != NULL, "cred_new/cred_add (USERB) succeeds");
if (credB) {
CHECK(cred_find_by_token(&tokB) == credB,
"wrapper: token resolves before logout");
rc = credtok_logout(&tokB);
CHECK(rc == 0, "credtok_logout() wrapper returns 0");
CHECK(cred_find_by_token(&tokB) == NULL,
"wrapper: token no longer resolves after logout");
}

return mbt_test_summary("TSTLOUT");
}