Skip to content
Open
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
19 changes: 19 additions & 0 deletions src/wh_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,25 @@ int wh_Auth_Logout(whAuthContext* context, whUserId user_id)
}


/* returns result of locking, clears context user regardless of lock success. */
int wh_Auth_Reset(whAuthContext* context)
{
int rc;

if (context == NULL) {
return WH_ERROR_BADARGS;
}

/* Best-effort lock, but clear the session regardless */
rc = WH_AUTH_LOCK(context);
memset(&context->user, 0, sizeof(whAuthUser));
if (rc == WH_ERROR_OK) {
(void)WH_AUTH_UNLOCK(context);
}
return rc;
}


/* Check on request authorization and action permissions for current user
* logged in */
int wh_Auth_CheckRequestAuthorization(whAuthContext* context, uint16_t group,
Expand Down
46 changes: 37 additions & 9 deletions src/wh_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,19 @@ int wh_Server_Init(whServerContext* server, whServerConfig* config)
server->nvm = config->nvm;
#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION
server->auth = config->auth;
/* auth context is externally owned; clear any stale session left over from
* a prior connection (Logout first so backend callback runs). */
if (server->auth != NULL &&
server->auth->user.user_id != WH_USER_ID_INVALID) {
whUserId stale_id = server->auth->user.user_id;
int logout_rc = wh_Auth_Logout(server->auth, stale_id);
Comment thread
JacobBarthelmeh marked this conversation as resolved.
if (logout_rc != WH_ERROR_OK) {
WH_LOG(&server->log, WH_LOG_LEVEL_SECEVENT,
"Stale auth session force-cleared during server init after "
"logout failure");
}
(void)wh_Auth_Reset(server->auth);
}
#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */

#ifndef WOLFHSM_CFG_NO_CRYPTO
Expand Down Expand Up @@ -177,6 +190,27 @@ int wh_Server_SetConnected(whServerContext *server, whCommConnected connected)
return WH_ERROR_BADARGS;
}

#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION
/* Log out any active user on disconnect, including abrupt drops where
* COMM_CLOSE never arrives. */
if (connected == WH_COMM_DISCONNECTED &&
server->connected != WH_COMM_DISCONNECTED &&
server->auth != NULL &&
server->auth->user.user_id != WH_USER_ID_INVALID) {
whUserId user_id = server->auth->user.user_id;
int logout_rc = wh_Auth_Logout(server->auth, user_id);
Comment thread
JacobBarthelmeh marked this conversation as resolved.

if (logout_rc != WH_ERROR_OK) {
WH_LOG(&server->log, WH_LOG_LEVEL_SECEVENT,
"Auth session force-cleared on disconnect after logout "
"failure");
}
/* Always reset so a stale identity can never carry over to the next
* connection, even if the backend logout failed. */
(void)wh_Auth_Reset(server->auth);
}
#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */

server->connected = connected;
return WH_ERROR_OK;
}
Expand Down Expand Up @@ -285,15 +319,9 @@ static int _wh_Server_HandleCommRequest(whServerContext* server,
/* No message */
/* Process the close action */

#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION
/* Log out the current user when communication channel closes */
if (server->auth != NULL &&
server->auth->user.user_id != WH_USER_ID_INVALID) {
whUserId user_id = server->auth->user.user_id;
(void)wh_Auth_Logout(server->auth, user_id);
}
#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */

/* wh_Server_SetConnected logs out any active user on the transition to
* the disconnected state, so the graceful-close and abrupt-disconnect
* paths share a single authoritative logout. */
wh_Server_SetConnected(server, WH_COMM_DISCONNECTED);
*out_resp_size = 0;

Expand Down
51 changes: 50 additions & 1 deletion test/wh_test_auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ static whNvmContext nvm[1] = {{0}};
/* Test-specific authorization override callbacks to verify they are invoked */
static int test_checkRequestAuthorizationCalled = 0;
static int test_checkKeyAuthorizationCalled = 0;
static int test_logoutCallCount = 0;

static int test_CheckRequestAuthorization(void* context, int err,
uint16_t user_id, uint16_t group,
Expand All @@ -115,12 +116,20 @@ static int test_CheckKeyAuthorization(void* context, int err, uint16_t user_id,
return err;
}

/* Counts Logout calls for the abrupt-disconnect test. */
static int test_Logout(void* context, uint16_t current_user_id,
uint16_t user_id)
{
test_logoutCallCount++;
return wh_Auth_BaseLogout(context, current_user_id, user_id);
}

/* Auth setup following wh_posix_server pattern */
static whAuthCb default_auth_cb = {
.Init = wh_Auth_BaseInit,
.Cleanup = wh_Auth_BaseCleanup,
.Login = wh_Auth_BaseLogin,
.Logout = wh_Auth_BaseLogout,
.Logout = test_Logout,
.CheckRequestAuthorization = test_CheckRequestAuthorization,
.CheckKeyAuthorization = test_CheckKeyAuthorization,
.UserAdd = wh_Auth_BaseUserAdd,
Expand All @@ -142,6 +151,10 @@ static int _whTest_Auth_SetupMemory(whClientContext** out_client)
uint16_t out_user_id;
int i;

/* Reset so logout-count assertions are self-contained across repeated
* invocations of the suite in a single process. */
test_logoutCallCount = 0;

/* Initialize transport memory config - avoid compound literals for C90 */
tmcf->req = (whTransportMemCsr*)req_buffer;
tmcf->req_size = sizeof(req_buffer);
Expand Down Expand Up @@ -1415,6 +1428,41 @@ int whTest_AuthTCP(whClientConfig* clientCfg)
}


#if !defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) && \
defined(WOLFHSM_CFG_ENABLE_SERVER)
static int AuthAbruptDisconnect(whClientContext* client_ctx)
{
int32_t server_rc = 0;
whUserId user_id = WH_USER_ID_INVALID;
int logouts_before = 0;

WH_TEST_PRINT(" Test: Abrupt disconnect calls wh_Auth_Logout\n");

WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(
client_ctx, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN,
strlen(TEST_ADMIN_PIN), &server_rc, &user_id));
WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK);
WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID);
WH_TEST_ASSERT_RETURN(auth_ctx.user.user_id == user_id);
WH_TEST_ASSERT_RETURN(auth_ctx.user.is_active);

logouts_before = test_logoutCallCount;
WH_TEST_RETURN_ON_FAIL(
wh_Server_SetConnected(server, WH_COMM_DISCONNECTED));
WH_TEST_ASSERT_RETURN(test_logoutCallCount == logouts_before + 1);
WH_TEST_ASSERT_RETURN(auth_ctx.user.user_id == WH_USER_ID_INVALID);
WH_TEST_ASSERT_RETURN(!auth_ctx.user.is_active);

/* Repeat disconnect is a no-op. */
logouts_before = test_logoutCallCount;
WH_TEST_RETURN_ON_FAIL(
wh_Server_SetConnected(server, WH_COMM_DISCONNECTED));
WH_TEST_ASSERT_RETURN(test_logoutCallCount == logouts_before);

return WH_TEST_SUCCESS;
}
#endif /* memory transport */

int whTest_AuthMEM(void)
{
#if !defined(WOLFHSM_CFG_TEST_CLIENT_ONLY_TCP) && \
Expand All @@ -1424,6 +1472,7 @@ int whTest_AuthMEM(void)
/* Memory transport mode */
WH_TEST_RETURN_ON_FAIL(_whTest_Auth_SetupMemory(&client_ctx));
WH_TEST_RETURN_ON_FAIL(whTest_AuthTest(client_ctx));
WH_TEST_RETURN_ON_FAIL(AuthAbruptDisconnect(client_ctx));

/* Verify that authorization callbacks were invoked during tests */
WH_TEST_PRINT(
Expand Down
13 changes: 13 additions & 0 deletions wolfhsm/wh_auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,19 @@ int wh_Auth_Login(whAuthContext* context, uint8_t client_id,
*/
int wh_Auth_Logout(whAuthContext* context, whUserId user_id);

/**
* @brief Force-clear the local session state, dropping any logged-in user.
*
* Unconditionally zeroes only the session (user) state, leaving the
* externally-owned configuration (callbacks, context, lock) intact. Does not
* invoke the backend logout callback.
*
* @param[in] context Pointer to the auth context.
* @return int WH_ERROR_OK, WH_ERROR_BADARGS for a NULL context, or the lock
* error if the lock could not be acquired (session is still cleared).
*/
int wh_Auth_Reset(whAuthContext* context);

/**
* @brief Check authorization for an action.
*
Expand Down
Loading