diff --git a/test-refactor/README.md b/test-refactor/README.md index cd37861f9..45ee85273 100644 --- a/test-refactor/README.md +++ b/test-refactor/README.md @@ -87,6 +87,7 @@ Translated tests: | `wh_test_flash_ramsim.c::whTest_Flash_RamSim` | `posix/wh_test_flash_ramsim.c::{whTest_FlashWriteLock, whTest_FlashEraseProgramVerify, whTest_FlashUnitOps}` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | | `wh_test_nvm_flash.c::whTest_NvmFlash` | `posix/wh_test_nvm_flash.c::whTest_NvmAddOverwriteDestroy` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | | `wh_test_posix_threadsafe_stress.c::whTest_ThreadSafeStress` | called directly from `posix/wh_test_posix_main.c` | POSIX port-specific (direct call) | | +| `wh_test_auth.c` (`whTest_AuthMEM` / `whTest_AuthTest` sub-tests) | `client-server/wh_test_auth.c::{whTest_AuthBadArgs, whTest_AuthLogin, whTest_AuthLogout, whTest_AuthAddUser, whTest_AuthDeleteUser, whTest_AuthSetPermissions, whTest_AuthSetCredentials, whTest_AuthRequestAuthorization}` | Client | Under `WOLFHSM_CFG_ENABLE_AUTHENTICATION` the POSIX server installs an auth context + admin user and the client logs in as admin at connect, so the ordinary client tests run authorized; each auth test brackets its own session (logout to start clean, restore admin on exit). Uses the blocking client API; the legacy own-server setup and single-thread manual-pump are dropped. Build with `make AUTH=1`. The TCP/client-only variant (`whTest_AuthTCP`) is not ported | Not yet migrated (still live in `wolfHSM/test/`): @@ -102,7 +103,6 @@ Not yet migrated (still live in `wolfHSM/test/`): | `wh_test_log.c::whTest_Log`, `whTest_LogBackend_RunAll` | `whTest_LogBackend_RunAll` to be reworked to fit the Misc group, likely with a context param. | | `wh_test_she.c::whTest_She` | | | `wh_test_timeout.c::whTest_TimeoutPosix` | | -| `wh_test_auth.c::whTest_AuthMEM`, `whTest_AuthTCP` | | | `wh_test_server_img_mgr.c::whTest_ServerImgMgr` | | | `wh_test_nvmflags.c::whTest_NvmFlags` | | | `wh_test_flash_fault_inject.c` | | diff --git a/test-refactor/client-server/wh_test_auth.c b/test-refactor/client-server/wh_test_auth.c new file mode 100644 index 000000000..34eb55dac --- /dev/null +++ b/test-refactor/client-server/wh_test_auth.c @@ -0,0 +1,1121 @@ +/* + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +/* + * test-refactor/client-server/wh_test_auth.c + * + * Client-side authentication tests. The POSIX server is configured + * with an auth context plus an admin user, and the client logs in as + * admin at connect (see posix/wh_test_posix_server.c and + * wh_test_posix_client.c). Each test here changes the login session to + * exercise a behavior, then restores the admin baseline so the next + * client test stays authorized (the auth gate denies un-logged-in + * requests). The bracketing is done once in _whTest_Auth_RunBracketed. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_ENABLE_AUTHENTICATION) && \ + defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER) + +#include +#include + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_message.h" +#include "wolfhsm/wh_message_auth.h" + +#include "wh_test_common.h" + +#ifndef TEST_ADMIN_USERNAME +#define TEST_ADMIN_USERNAME "admin" +#endif +#ifndef TEST_ADMIN_PIN +#define TEST_ADMIN_PIN "1234" +#endif + + +/* ============================================================================ + * Client op helpers (blocking request/response against the running server) + * ============================================================================ + */ + +static int _whTest_Auth_LoginOp(whClientContext* client, whAuthMethod method, + const char* username, const void* auth_data, + uint16_t auth_data_len, int32_t* out_rc, + whUserId* out_user_id) +{ + return wh_Client_AuthLogin(client, method, username, auth_data, + auth_data_len, out_rc, out_user_id); +} + +static int _whTest_Auth_LogoutOp(whClientContext* client, whUserId user_id, + int32_t* out_rc) +{ + return wh_Client_AuthLogout(client, user_id, out_rc); +} + +static int _whTest_Auth_UserAddOp(whClientContext* client, const char* username, + whAuthPermissions permissions, + whAuthMethod method, const void* credentials, + uint16_t credentials_len, int32_t* out_rc, + whUserId* out_user_id) +{ + return wh_Client_AuthUserAdd(client, username, permissions, method, + credentials, credentials_len, out_rc, + out_user_id); +} + +static int _whTest_Auth_UserDeleteOp(whClientContext* client, whUserId user_id, + int32_t* out_rc) +{ + return wh_Client_AuthUserDelete(client, user_id, out_rc); +} + +static int _whTest_Auth_UserSetPermsOp(whClientContext* client, + whUserId user_id, + whAuthPermissions permissions, + int32_t* out_rc) +{ + return wh_Client_AuthUserSetPermissions(client, user_id, permissions, + out_rc); +} + +static int _whTest_Auth_UserSetCredsOp( + whClientContext* client, whUserId user_id, whAuthMethod method, + const void* current_credentials, uint16_t current_credentials_len, + const void* new_credentials, uint16_t new_credentials_len, int32_t* out_rc) +{ + return wh_Client_AuthUserSetCredentials( + client, user_id, method, current_credentials, current_credentials_len, + new_credentials, new_credentials_len, out_rc); +} + +static int _whTest_Auth_UserGetOp(whClientContext* client, const char* username, + int32_t* out_rc, whUserId* out_user_id, + whAuthPermissions* out_permissions) +{ + return wh_Client_AuthUserGet(client, username, out_rc, out_user_id, + out_permissions); +} + +static void _whTest_Auth_DeleteUserByName(whClientContext* client, + const char* username) +{ + int32_t server_rc = 0; + whUserId user_id = WH_USER_ID_INVALID; + whAuthPermissions perms; + + memset(&perms, 0, sizeof(perms)); + _whTest_Auth_UserGetOp(client, username, &server_rc, &user_id, &perms); + if (server_rc == WH_ERROR_OK && user_id != WH_USER_ID_INVALID) { + _whTest_Auth_UserDeleteOp(client, user_id, &server_rc); + } +} + + +/* ============================================================================ + * Session bracketing + * ============================================================================ + */ + +typedef int (*whTestAuthImplFn)(whClientContext*); + +/* Admin's server-assigned user id, needed to log the baseline session + * out (logout requires the real id, not WH_USER_ID_INVALID). Discovered + * once from the live admin session, which is the baseline at bracket + * entry. */ +static whUserId _adminUserId = WH_USER_ID_INVALID; + +/* + * Run an auth test body with the session bracketed: start from a clean + * logged-out state (the bodies assume logged-out, but the connect-time + * baseline is the admin session), and on return restore the admin + * baseline so the following client tests stay authorized. + */ +static int _whTest_Auth_RunBracketed(whClientContext* client, + whTestAuthImplFn fn) +{ + int ret; + int32_t rc = 0; + whUserId uid = WH_USER_ID_INVALID; + + if (client == NULL) { + return WH_ERROR_BADARGS; + } + + /* Discover admin's id once (admin is logged in at entry). */ + if (_adminUserId == WH_USER_ID_INVALID) { + whAuthPermissions perms; + memset(&perms, 0, sizeof(perms)); + (void)wh_Client_AuthUserGet(client, TEST_ADMIN_USERNAME, &rc, + &_adminUserId, &perms); + } + /* Drop the admin baseline so the body starts logged-out. */ + if (_adminUserId != WH_USER_ID_INVALID) { + (void)wh_Client_AuthLogout(client, _adminUserId, &rc); + } + + ret = fn(client); + + /* Restore the admin baseline for the next test. */ + (void)wh_Client_AuthLogin(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, (uint16_t)strlen(TEST_ADMIN_PIN), + &rc, &uid); + if (rc == WH_ERROR_OK && uid != WH_USER_ID_INVALID) { + _adminUserId = uid; + } + return ret; +} + + +/* ============================================================================ + * Test bodies (assume a logged-out start; bracketed by the wrappers below) + * ============================================================================ + */ + +/* Logout Tests */ +static int _whTest_AuthLogout_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + int32_t login_rc; + whAuthPermissions out_perms; + + /* Test 2: Logout after login */ + WH_TEST_PRINT(" Test: Logout after login\n"); + /* First login */ + memset(&out_perms, 0, sizeof(out_perms)); + login_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_ASSERT_RETURN(client->comm != NULL); + WH_TEST_ASSERT_RETURN(client->comm->initialized == 1); + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &login_rc, &user_id)); + WH_TEST_ASSERT_RETURN(login_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Then logout */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LogoutOp(client, user_id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_PRINT(" Test: Logout before login\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LogoutOp(client, user_id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 3: Logout with invalid user id */ + WH_TEST_PRINT( + " Test: Logout attempt with invalid user ID (should fail)\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LogoutOp(client, WH_USER_ID_INVALID, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + return WH_TEST_SUCCESS; +} + +/* Login Tests */ +static int _whTest_AuthLogin_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + + /* Test 1: Login with invalid credentials */ + WH_TEST_PRINT(" Test: Login with invalid credentials\n"); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + TEST_ADMIN_USERNAME, "wrong", 5, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED || + server_rc != WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID); + + /* Test 2: Login with valid credentials */ + WH_TEST_PRINT(" Test: Login with valid credentials\n"); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Logout for next test */ + _whTest_Auth_LogoutOp(client, user_id, &server_rc); + + /* Test 3: Login with invalid username */ + WH_TEST_PRINT(" Test: Login with invalid username\n"); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "nonexistent", TEST_ADMIN_PIN, + 4, &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED || + server_rc != WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id == WH_USER_ID_INVALID); + + /* Test 4: Login if already logged in */ + WH_TEST_PRINT(" Test: Login if already logged in\n"); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Try to login again without logout */ + server_rc = 0; + whUserId user_id2 = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &user_id2)); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_LOGIN_FAILED || + server_rc != WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID); + + /* Cleanup */ + _whTest_Auth_LogoutOp(client, user_id, &server_rc); + + return WH_TEST_SUCCESS; +} + +/* Add User Tests */ +static int _whTest_AuthAddUser_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + whAuthPermissions perms; + char long_username[34]; /* 33 chars + null terminator */ + int rc; + + /* Login as admin first */ + whAuthPermissions admin_perms; + memset(&admin_perms, 0, sizeof(admin_perms)); + server_rc = 0; + whUserId admin_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Test 1: Add user with invalid username (too long) */ + WH_TEST_PRINT(" Test: Add user with invalid username (too long)\n"); + memset(long_username, 'a', 33); + long_username[33] = '\0'; + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + + /* Expect client-side rejection due to username length */ + rc = wh_Client_AuthUserAddRequest(client, long_username, perms, + WH_AUTH_METHOD_PIN, "test", 4); + WH_TEST_ASSERT_RETURN(rc != WH_ERROR_OK || server_rc != WH_ERROR_OK || + user_id == WH_USER_ID_INVALID); + + /* Test 2: Add user with invalid permissions (keyIdCount > max) */ + WH_TEST_PRINT(" Test: Add user with invalid permissions\n"); + memset(&perms, 0, sizeof(perms)); + perms.keyIdCount = WH_AUTH_MAX_KEY_IDS + 1; /* Invalid: exceeds max */ + server_rc = 0; + user_id = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser1", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &user_id); + /* Should clamp or reject invalid keyIdCount */ + if (server_rc == WH_ERROR_OK) { + /* If it succeeds, keyIdCount should be clamped */ + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + } + + /* Test 3: Add user if already exists */ + WH_TEST_PRINT(" Test: Add user if already exists\n"); + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser2", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &user_id); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Try to add same user again - should fail duplicate username */ + whUserId user_id2 = WH_USER_ID_INVALID; + server_rc = 0; + _whTest_Auth_UserAddOp(client, "testuser2", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &user_id2); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID); + + /* Test 4: Non-admin cannot add admin user */ + WH_TEST_PRINT(" Test: Non-admin cannot add admin user\n"); + { + whAuthPermissions nonadmin_add_perms; + + memset(&nonadmin_add_perms, 0, sizeof(nonadmin_add_perms)); + WH_AUTH_SET_ALLOWED_ACTION(nonadmin_add_perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + WH_AUTH_SET_IS_ADMIN(nonadmin_add_perms, 0); + + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "addadmin_testuser", nonadmin_add_perms, WH_AUTH_METHOD_PIN, + "pass", 4, &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "addadmin_testuser", "pass", + 4, &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Non-admin can add other non-admin users */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id2 = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "other_nonadmin", perms, WH_AUTH_METHOD_PIN, "test", 4, + &server_rc, &user_id2)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id2 != WH_USER_ID_INVALID); + + /* Non-admin cannot add admin user */ + memset(&perms, 0xFF, sizeof(perms)); + perms.keyIdCount = 0; + server_rc = 0; + user_id2 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "wouldbe_admin", perms, + WH_AUTH_METHOD_PIN, "test", 4, &server_rc, + &user_id2); + WH_TEST_ASSERT_RETURN(server_rc == WH_AUTH_PERMISSION_ERROR); + WH_TEST_ASSERT_RETURN(user_id2 == WH_USER_ID_INVALID); + } + + _whTest_Auth_LogoutOp(client, user_id, &server_rc); + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &admin_id)); + + /* Cleanup */ + server_rc = 0; + _whTest_Auth_DeleteUserByName(client, "testuser1"); + _whTest_Auth_DeleteUserByName(client, "testuser2"); + _whTest_Auth_DeleteUserByName(client, "addadmin_testuser"); + _whTest_Auth_DeleteUserByName(client, "other_nonadmin"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + return WH_TEST_SUCCESS; +} + +/* Delete User Tests */ +static int _whTest_AuthDeleteUser_impl(whClientContext* client) +{ + int32_t server_rc; + whAuthPermissions admin_perms; + whUserId admin_id = WH_USER_ID_INVALID; + whAuthPermissions perms; + whAuthPermissions out_perms; + whUserId delete_user_id = WH_USER_ID_INVALID; + + /* Login as admin to perform delete operations */ + memset(&admin_perms, 0, sizeof(admin_perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Test 1: Delete user with invalid user id */ + WH_TEST_PRINT(" Test: Delete user with invalid user ID\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserDeleteOp(client, WH_USER_ID_INVALID, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 2: Delete user that does not exist */ + WH_TEST_PRINT(" Test: Delete user that does not exist\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserDeleteOp(client, 999, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND || + server_rc != WH_ERROR_OK); + + /* Test 2b: Delete existing user (success path) */ + WH_TEST_PRINT(" Test: Delete existing user\n"); + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "deleteuser", perms, + WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &delete_user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(delete_user_id != WH_USER_ID_INVALID); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp( + client, "deleteuser", &server_rc, &delete_user_id, &out_perms)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserDeleteOp(client, delete_user_id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + server_rc = 0; + delete_user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp( + client, "deleteuser", &server_rc, &delete_user_id, &out_perms)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK || + delete_user_id == WH_USER_ID_INVALID); + + /* Test 3: Non-admin user trying to delete another user */ + WH_TEST_PRINT(" Test: Non-admin user trying to delete another user\n"); + { + whUserId nonadmin_id = WH_USER_ID_INVALID; + whUserId target_id = WH_USER_ID_INVALID; + whAuthPermissions nonadmin_perms; + + /* non-admin user with all auth group actions (includes delete) */ + memset(&nonadmin_perms, 0, sizeof(nonadmin_perms)); + WH_AUTH_SET_ALLOWED_GROUP(nonadmin_perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_SET_IS_ADMIN(nonadmin_perms, 0); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "nonadmin", nonadmin_perms, WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &nonadmin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(nonadmin_id != WH_USER_ID_INVALID); + + /* Create a target user to try to delete */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp( + client, "targetuser", perms, WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &target_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(target_id != WH_USER_ID_INVALID); + + /* Logout admin and login as non-admin user */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "nonadmin", "pass", 4, + &server_rc, &nonadmin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Try to delete the target user as non-admin - should fail */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserDeleteOp(client, target_id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + WH_TEST_PRINT(" Non-admin delete attempt correctly denied\n"); + + /* Logout non-admin and login as admin to cleanup */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, nonadmin_id, &server_rc); + + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "admin", "1234", 4, + &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Cleanup - delete both test users */ + _whTest_Auth_UserDeleteOp(client, nonadmin_id, &server_rc); + _whTest_Auth_UserDeleteOp(client, target_id, &server_rc); + } + + /* Test 4: Delete user when not logged in */ + WH_TEST_PRINT(" Test: Delete user when not logged in\n"); + /* Ensure we're logged out */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + /* Try to delete without being logged in */ + server_rc = 0; + _whTest_Auth_UserDeleteOp(client, 1, &server_rc); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + return WH_TEST_SUCCESS; +} + +/* Set User Permissions Tests */ +static int _whTest_AuthSetPermissions_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + whAuthPermissions perms, new_perms; + whAuthPermissions fetched_perms; + whUserId fetched_user_id = WH_USER_ID_INVALID; + int32_t get_rc = 0; + + /* Login as admin first */ + whAuthPermissions admin_perms; + memset(&admin_perms, 0, sizeof(admin_perms)); + server_rc = 0; + whUserId admin_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Create a test user first */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser3", perms, + WH_AUTH_METHOD_PIN, "test", 4, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Test 1: Set user permissions with invalid user id */ + WH_TEST_PRINT(" Test: Set user permissions with invalid user ID\n"); + memset(&new_perms, 0xFF, sizeof(new_perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetPermsOp( + client, WH_USER_ID_INVALID, new_perms, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 2: Set user permissions with invalid permissions */ + WH_TEST_PRINT(" Test: Set user permissions with invalid permissions\n"); + memset(&new_perms, 0, sizeof(new_perms)); + new_perms.keyIdCount = WH_AUTH_MAX_KEY_IDS + 1; /* Invalid */ + server_rc = 0; + _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc); + /* Should clamp or reject invalid keyIdCount */ + if (server_rc == WH_ERROR_OK) { + /* If it succeeds, keyIdCount should be clamped */ + } + + /* Test 2b: Set user permissions success path */ + WH_TEST_PRINT(" Test: Set user permissions success\n"); + memset(&new_perms, 0, sizeof(new_perms)); + WH_AUTH_SET_ALLOWED_ACTION(new_perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + memset(&fetched_perms, 0, sizeof(fetched_perms)); + fetched_user_id = WH_USER_ID_INVALID; + get_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserGetOp( + client, "testuser3", &get_rc, &fetched_user_id, &fetched_perms)); + WH_TEST_ASSERT_RETURN(get_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(fetched_user_id == user_id); + { + /* Compare group permission and all action permission words */ + int groupIndex = (WH_MESSAGE_GROUP_AUTH >> 8) & 0xFF; + int j; + int permissions_match = 1; + WH_TEST_ASSERT_RETURN(fetched_perms.groupPermissions[groupIndex] == + new_perms.groupPermissions[groupIndex]); + for (j = 0; j < WH_AUTH_ACTION_WORDS; j++) { + if (fetched_perms.actionPermissions[groupIndex][j] != + new_perms.actionPermissions[groupIndex][j]) { + permissions_match = 0; + break; + } + } + WH_TEST_ASSERT_RETURN(permissions_match); + } + + /* Test 3: Set user permissions for non-existent user */ + WH_TEST_PRINT(" Test: Set user permissions for non-existent user\n"); + memset(&new_perms, 0, sizeof(new_perms)); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetPermsOp(client, 999, new_perms, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND || + server_rc != WH_ERROR_OK); + + /* Test 4: Set user permissions when not logged in */ + WH_TEST_PRINT(" Test: Set user permissions when not logged in\n"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + memset(&new_perms, 0, sizeof(new_perms)); + server_rc = 0; + _whTest_Auth_UserSetPermsOp(client, user_id, new_perms, &server_rc); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Cleanup */ + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + _whTest_Auth_DeleteUserByName(client, "testuser3"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + return WH_TEST_SUCCESS; +} + +/* Set User Credentials Tests */ +static int _whTest_AuthSetCredentials_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + whAuthPermissions perms; + + /* Login as admin first */ + whAuthPermissions admin_perms; + memset(&admin_perms, 0, sizeof(admin_perms)); + server_rc = 0; + whUserId admin_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Create a test user first */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser4", perms, + WH_AUTH_METHOD_PIN, "test", 4, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Test 1: Set user credentials with invalid user id */ + WH_TEST_PRINT(" Test: Set user credentials with invalid user ID\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetCredsOp( + client, WH_USER_ID_INVALID, WH_AUTH_METHOD_PIN, "test", 4, "newpass", 7, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 2: Set user credentials with invalid method */ + WH_TEST_PRINT(" Test: Set user credentials with invalid method\n"); + server_rc = 0; + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_NONE, "test", 4, + "newpass", 7, &server_rc); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 3: Set user credentials for non-existent user */ + WH_TEST_PRINT(" Test: Set user credentials for non-existent user\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserSetCredsOp( + client, 999, WH_AUTH_METHOD_PIN, NULL, 0, "newpass", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND || + server_rc != WH_ERROR_OK); + + WH_TEST_PRINT(" Test: Admin setting credentials for non-admin user\n"); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserSetCredsOp(client, user_id, WH_AUTH_METHOD_PIN, "test", + 4, "newpass", 7, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Verify new credentials work */ + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + memset(&admin_perms, 0, sizeof(admin_perms)); + server_rc = 0; + whUserId test_user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "testuser4", "newpass", 7, + &server_rc, &test_user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(test_user_id == user_id); + + /* Cleanup: remove the test user (logged in as admin so the delete + * is authorized). */ + _whTest_Auth_LogoutOp(client, test_user_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + _whTest_Auth_DeleteUserByName(client, "testuser4"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + return WH_TEST_SUCCESS; +} + +/* Authorization Checks Tests */ +static int _whTest_AuthRequestAuthorization_impl(whClientContext* client) +{ + int32_t server_rc; + whUserId user_id; + whUserId temp_id3 = WH_USER_ID_INVALID; + whAuthPermissions perms; + + /* Test 1: Operation when not logged in and not allowed */ + WH_TEST_PRINT(" Test: Operation when not logged in and not allowed\n"); + /* Ensure logged out */ + server_rc = 0; + _whTest_Auth_LogoutOp(client, WH_USER_ID_INVALID, &server_rc); + + /* Try an operation that requires auth (e.g., add user) */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + whUserId temp_id = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser5", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 2: Operation when logged in and allowed */ + WH_TEST_PRINT(" Test: Operation when logged in and allowed\n"); + /* Login as admin */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + whUserId admin_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Retry operation after login (admin should be allowed) */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "testuser6", perms, + WH_AUTH_METHOD_PIN, "test", 4, + &server_rc, &user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(user_id != WH_USER_ID_INVALID); + + /* Test 3: Operation when logged in and not allowed */ + WH_TEST_PRINT(" Test: Operation when logged in and not allowed\n"); + /* Create a user with auth group but not USER_ADD action */ + memset(&perms, 0, sizeof(perms)); + WH_AUTH_SET_ALLOWED_GROUP(perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_CLEAR_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + server_rc = 0; + whUserId limited_user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserAddOp(client, "limiteduser", perms, WH_AUTH_METHOD_PIN, + "pass", 4, &server_rc, &limited_user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Logout admin and login as limited user */ + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + whUserId logged_in_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "limiteduser", "pass", 4, + &server_rc, &logged_in_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Try an operation that requires permissions */ + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + whUserId temp_id2 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser7", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id2); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Test 3b: User with auth group cleared cannot add + * (WH_AUTH_CLEAR_ALLOWED_GROUP) */ + WH_TEST_PRINT(" Test: User with no auth group cannot add\n"); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + memset(&perms, 0xFF, sizeof(perms)); + perms.keyIdCount = 0; + WH_AUTH_CLEAR_ALLOWED_GROUP(perms, WH_MESSAGE_GROUP_AUTH); + WH_AUTH_SET_IS_ADMIN(perms, 0); + server_rc = 0; + whUserId noauth_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_UserAddOp(client, "noauthuser", perms, + WH_AUTH_METHOD_PIN, "pass", 4, + &server_rc, &noauth_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "noauthuser", "pass", 4, + &server_rc, &logged_in_id)); + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + temp_id2 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser7b", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id2); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + + /* Test 4: Logged in as different user and allowed */ + WH_TEST_PRINT(" Test: Logged in as different user and allowed\n"); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + + server_rc = 0; + whUserId allowed_user_id = WH_USER_ID_INVALID; + /* Login as admin to create allowed user */ + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + memset(&perms, 0, sizeof(perms)); + WH_AUTH_SET_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_USER_ADD); + /* Free a slot so the adds below stay within WH_AUTH_BASE_MAX_USERS (5) */ + _whTest_Auth_DeleteUserByName(client, "noauthuser"); + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_UserAddOp(client, "alloweduser", perms, WH_AUTH_METHOD_PIN, + "pass", 4, &server_rc, &allowed_user_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + logged_in_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "alloweduser", "pass", 4, + &server_rc, &logged_in_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + server_rc = 0; + temp_id3 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser8", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id3); + /* alloweduser holds the USER_ADD action, so the add is authorized */ + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(temp_id3 != WH_USER_ID_INVALID); + + /* Test 5: Logged in as different user and not allowed */ + WH_TEST_PRINT(" Test: Logged in as different user and not allowed\n"); + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + + memset(&perms, 0, sizeof(perms)); + server_rc = 0; + logged_in_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, + "limiteduser", "pass", 4, + &server_rc, &logged_in_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + server_rc = 0; + temp_id3 = WH_USER_ID_INVALID; + _whTest_Auth_UserAddOp(client, "testuser9", perms, WH_AUTH_METHOD_PIN, + "test", 4, &server_rc, &temp_id3); + WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + + /* Cleanup */ + _whTest_Auth_LogoutOp(client, logged_in_id, &server_rc); + server_rc = 0; + WH_TEST_RETURN_ON_FAIL( + _whTest_Auth_LoginOp(client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, + TEST_ADMIN_PIN, 4, &server_rc, &admin_id)); + _whTest_Auth_DeleteUserByName(client, "limiteduser"); + _whTest_Auth_DeleteUserByName(client, "alloweduser"); + _whTest_Auth_DeleteUserByName(client, "testuser5"); + _whTest_Auth_DeleteUserByName(client, "testuser6"); + _whTest_Auth_DeleteUserByName(client, "testuser7"); + _whTest_Auth_DeleteUserByName(client, "testuser8"); + _whTest_Auth_DeleteUserByName(client, "testuser9"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); + + return WH_TEST_SUCCESS; +} + + +/* ============================================================================ + * Bad-args tests (no live session needed; do not disturb the baseline) + * ============================================================================ + */ + +static int _whTest_Auth_BadArgs(void) +{ + int rc = 0; + int loggedIn = 1; + whAuthContext ctx; + whAuthConfig config; + whAuthPermissions perms; + whUserId user_id = WH_USER_ID_INVALID; + int32_t server_rc = 0; + + memset(&ctx, 0, sizeof(ctx)); + memset(&config, 0, sizeof(config)); + memset(&perms, 0, sizeof(perms)); + + WH_TEST_PRINT(" Test: Auth core bad args\n"); + rc = wh_Auth_Init(NULL, &config); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_Init(&ctx, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_Auth_Cleanup(NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_Cleanup(&ctx); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = + wh_Auth_Login(NULL, 0, WH_AUTH_METHOD_PIN, "user", "pin", 3, &loggedIn); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_Login(&ctx, 0, WH_AUTH_METHOD_PIN, "user", "pin", 3, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthLoginRequest(NULL, WH_AUTH_METHOD_PIN, "user", "pin", 3); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthLoginResponse(NULL, &server_rc, &user_id); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_Auth_Logout(NULL, 1); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_Logout(&ctx, 1); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_Auth_UserAdd(&ctx, "user", &user_id, perms, WH_AUTH_METHOD_PIN, + "pin", 3); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_UserDelete(&ctx, 1); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_UserSetPermissions(&ctx, 1, perms); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_UserGet(&ctx, "user", &user_id, &perms); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_UserSetCredentials(&ctx, 1, WH_AUTH_METHOD_PIN, "pin", 3, + "new", 3); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Auth_Logout(NULL, 999); /* port may not support 999 users */ + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + WH_TEST_PRINT(" Test: Auth client bad args\n"); + rc = wh_Client_AuthLoginRequest(NULL, WH_AUTH_METHOD_PIN, + TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, + (uint16_t)strlen(TEST_ADMIN_PIN)); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthLogoutRequest(NULL, WH_USER_ID_INVALID); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_Auth_CheckRequestAuthorization(NULL, WH_MESSAGE_GROUP_AUTH, + WH_MESSAGE_AUTH_ACTION_LOGIN); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_Client_AuthUserAddRequest(NULL, "baduser", perms, + WH_AUTH_METHOD_NONE, "x", 1); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthUserDeleteRequest(NULL, WH_USER_ID_INVALID); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthUserSetPermissionsRequest(NULL, WH_USER_ID_INVALID, + perms); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthUserSetCredentialsRequest( + NULL, WH_USER_ID_INVALID, WH_AUTH_METHOD_PIN, NULL, 0, "new", 3); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_Client_AuthUserGetRequest(NULL, "missing"); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + return WH_TEST_SUCCESS; +} + +static int _whTest_Auth_MessageBadArgs(void) +{ + int rc = 0; + whMessageAuth_SimpleResponse simple = {0}; + whMessageAuth_LoginRequest login_hdr = {0}; + whMessageAuth_LoginRequest login_out = {0}; + whMessageAuth_UserAddRequest add_hdr = {0}; + whMessageAuth_UserAddRequest add_out = {0}; + whMessageAuth_UserSetCredentialsRequest set_hdr = {0}; + + WH_TEST_PRINT(" Test: Auth message bad args\n"); + rc = wh_MessageAuth_TranslateSimpleResponse(0, NULL, &simple); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_MessageAuth_TranslateSimpleResponse(0, &simple, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + rc = wh_MessageAuth_TranslateLoginRequest(0, NULL, 0, &login_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, 0, &login_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE); + + memset(&login_hdr, 0, sizeof(login_hdr)); + login_hdr.auth_data_len = + (uint16_t)(WH_MESSAGE_AUTH_LOGIN_MAX_AUTH_DATA_LEN + 1); + rc = wh_MessageAuth_TranslateLoginRequest(0, &login_hdr, sizeof(login_hdr), + &login_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE); + + rc = wh_MessageAuth_TranslateUserAddRequest(0, NULL, 0, &add_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + memset(&add_hdr, 0, sizeof(add_hdr)); + add_hdr.credentials_len = + (uint16_t)(WH_MESSAGE_AUTH_USERADD_MAX_CREDENTIALS_LEN + 1); + rc = wh_MessageAuth_TranslateUserAddRequest(0, &add_hdr, sizeof(add_hdr), + &add_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE); + + rc = + wh_MessageAuth_TranslateUserSetCredentialsRequest(0, NULL, 0, &set_hdr); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + memset(&set_hdr, 0, sizeof(set_hdr)); + set_hdr.current_credentials_len = 4; + set_hdr.new_credentials_len = 4; + rc = wh_MessageAuth_TranslateUserSetCredentialsRequest( + 0, &set_hdr, sizeof(set_hdr), &set_hdr); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BUFFER_SIZE); + + return WH_TEST_SUCCESS; +} + + +/* ============================================================================ + * Registered entry points + * ============================================================================ + */ + +int whTest_AuthBadArgs(whClientContext* client) +{ + (void)client; + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_BadArgs()); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_MessageBadArgs()); + return WH_TEST_SUCCESS; +} + +int whTest_AuthLogin(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthLogin_impl); +} + +int whTest_AuthLogout(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthLogout_impl); +} + +int whTest_AuthAddUser(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthAddUser_impl); +} + +int whTest_AuthDeleteUser(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthDeleteUser_impl); +} + +int whTest_AuthSetPermissions(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthSetPermissions_impl); +} + +int whTest_AuthSetCredentials(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, _whTest_AuthSetCredentials_impl); +} + +int whTest_AuthRequestAuthorization(whClientContext* client) +{ + return _whTest_Auth_RunBracketed(client, + _whTest_AuthRequestAuthorization_impl); +} + +#endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION && WOLFHSM_CFG_ENABLE_CLIENT && \ + WOLFHSM_CFG_ENABLE_SERVER */ diff --git a/test-refactor/posix/wh_test_posix_client.c b/test-refactor/posix/wh_test_posix_client.c index 8475a3bad..cbe17eeeb 100644 --- a/test-refactor/posix/wh_test_posix_client.c +++ b/test-refactor/posix/wh_test_posix_client.c @@ -33,10 +33,23 @@ #include "wolfhsm/wh_comm.h" #include "wolfhsm/wh_client.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" +#endif + #include "wh_test_common.h" #include "wh_test_posix_server.h" #include "wh_test_posix_client.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#ifndef TEST_ADMIN_USERNAME +#define TEST_ADMIN_USERNAME "admin" +#endif +#ifndef TEST_ADMIN_PIN +#define TEST_ADMIN_PIN "1234" +#endif +#endif + /* Client-side transport state (buffers are shared with the * server via whTestPosix_Server_GetTransportConfig) */ @@ -80,6 +93,22 @@ int whTestPosix_Client_Init(whClientContext* client) WH_TEST_RETURN_ON_FAIL( wh_Client_CommInit(client, &clientId, &serverId)); +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + /* The server enforces auth, so log in as admin once at connect. This + * is the session baseline every client test inherits; the auth tests + * temporarily change it and restore it. */ + { + int32_t server_rc = 0; + whUserId user_id = WH_USER_ID_INVALID; + WH_TEST_RETURN_ON_FAIL(wh_Client_AuthLogin( + client, WH_AUTH_METHOD_PIN, TEST_ADMIN_USERNAME, TEST_ADMIN_PIN, + (uint16_t)strlen(TEST_ADMIN_PIN), &server_rc, &user_id)); + if (server_rc != WH_ERROR_OK) { + return server_rc; + } + } +#endif + return 0; } diff --git a/test-refactor/posix/wh_test_posix_server.c b/test-refactor/posix/wh_test_posix_server.c index 8ced55d32..77fb0791b 100644 --- a/test-refactor/posix/wh_test_posix_server.c +++ b/test-refactor/posix/wh_test_posix_server.c @@ -37,6 +37,11 @@ #include "wolfhsm/wh_comm.h" #include "wolfhsm/wh_server.h" +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#include "wolfhsm/wh_auth.h" +#include "wolfhsm/wh_auth_base.h" +#endif + #ifndef WOLFHSM_CFG_NO_CRYPTO #include "wolfssl/wolfcrypt/settings.h" #include "wolfssl/wolfcrypt/random.h" @@ -70,6 +75,31 @@ static whNvmContext _nvm; static whServerCryptoContext _crypto; #endif +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION +#ifndef TEST_ADMIN_USERNAME +#define TEST_ADMIN_USERNAME "admin" +#endif +#ifndef TEST_ADMIN_PIN +#define TEST_ADMIN_PIN "1234" +#endif +/* Auth manager backed by the in-memory base backend. The server + * provisions a single admin user with full permissions so the client + * suite can authenticate; the client logs in as admin at connect (see + * whTestPosix_Client_Init). */ +static whAuthCb _authCb = { + .Init = wh_Auth_BaseInit, + .Cleanup = wh_Auth_BaseCleanup, + .Login = wh_Auth_BaseLogin, + .Logout = wh_Auth_BaseLogout, + .UserAdd = wh_Auth_BaseUserAdd, + .UserDelete = wh_Auth_BaseUserDelete, + .UserSetPermissions = wh_Auth_BaseUserSetPermissions, + .UserGet = wh_Auth_BaseUserGet, + .UserSetCredentials = wh_Auth_BaseUserSetCredentials, +}; +static whAuthContext _auth; +#endif + /* Mem transport -- buffers and server-side state. * The client side re-uses these buffers via * whTestPosix_Server_GetTransportConfig. */ @@ -139,6 +169,30 @@ int whTestPosix_Server_Init(whServerContext* server) sCfg.devId = INVALID_DEVID; #endif +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + { + whAuthConfig authCfg = {0}; + whAuthPermissions perms; + whUserId adminId = WH_USER_ID_INVALID; + int i; + + authCfg.cb = &_authCb; + WH_TEST_RETURN_ON_FAIL(wh_Auth_Init(&_auth, &authCfg)); + + /* Admin user: full permissions, no key-id restrictions. */ + memset(&perms, 0xFF, sizeof(perms)); + perms.keyIdCount = 0; + for (i = 0; i < WH_AUTH_MAX_KEY_IDS; i++) { + perms.keyIds[i] = 0; + } + WH_TEST_RETURN_ON_FAIL(wh_Auth_BaseUserAdd( + &_auth, TEST_ADMIN_USERNAME, &adminId, perms, WH_AUTH_METHOD_PIN, + TEST_ADMIN_PIN, (uint16_t)strlen(TEST_ADMIN_PIN))); + + sCfg.auth = &_auth; + } +#endif + return wh_Server_Init(server, &sCfg); } @@ -150,6 +204,9 @@ int whTestPosix_Server_Cleanup(whServerContext* server) } wh_Server_Cleanup(server); +#ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION + wh_Auth_Cleanup(&_auth); +#endif wh_Nvm_Cleanup(&_nvm); #ifndef WOLFHSM_CFG_NO_CRYPTO diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index ac29019a9..446171b37 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -46,6 +46,14 @@ WH_TEST_DECL(whTest_CryptoSha256); WH_TEST_DECL(whTest_Echo); WH_TEST_DECL(whTest_ServerInfo); WH_TEST_DECL(whTest_WolfCryptTest); +WH_TEST_DECL(whTest_AuthBadArgs); +WH_TEST_DECL(whTest_AuthLogin); +WH_TEST_DECL(whTest_AuthLogout); +WH_TEST_DECL(whTest_AuthAddUser); +WH_TEST_DECL(whTest_AuthDeleteUser); +WH_TEST_DECL(whTest_AuthSetPermissions); +WH_TEST_DECL(whTest_AuthSetCredentials); +WH_TEST_DECL(whTest_AuthRequestAuthorization); const whTestCase whTestsMisc[] = { { "whTest_Comm", whTest_Comm }, @@ -71,5 +79,13 @@ const whTestCase whTestsClient[] = { { "whTest_Echo", whTest_Echo }, { "whTest_ServerInfo", whTest_ServerInfo }, { "whTest_WolfCryptTest", whTest_WolfCryptTest }, + { "whTest_AuthBadArgs", whTest_AuthBadArgs }, + { "whTest_AuthLogin", whTest_AuthLogin }, + { "whTest_AuthLogout", whTest_AuthLogout }, + { "whTest_AuthAddUser", whTest_AuthAddUser }, + { "whTest_AuthDeleteUser", whTest_AuthDeleteUser }, + { "whTest_AuthSetPermissions", whTest_AuthSetPermissions }, + { "whTest_AuthSetCredentials", whTest_AuthSetCredentials }, + { "whTest_AuthRequestAuthorization", whTest_AuthRequestAuthorization }, }; const size_t whTestsClientCount = sizeof(whTestsClient) / sizeof(whTestsClient[0]); diff --git a/test/wh_test_auth.c b/test/wh_test_auth.c index cb9e65013..eb6f695c8 100644 --- a/test/wh_test_auth.c +++ b/test/wh_test_auth.c @@ -1142,9 +1142,13 @@ int whTest_AuthSetCredentials(whClientContext* client) WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); WH_TEST_ASSERT_RETURN(test_user_id == user_id); - /* Cleanup */ + /* Cleanup: remove the test user (logged in as admin so the delete + * is authorized). */ _whTest_Auth_LogoutOp(client, test_user_id, &server_rc); + WH_TEST_RETURN_ON_FAIL(_whTest_Auth_LoginOp( + client, WH_AUTH_METHOD_PIN, "admin", "1234", 4, &server_rc, &admin_id)); _whTest_Auth_DeleteUserByName(client, "testuser4"); + _whTest_Auth_LogoutOp(client, admin_id, &server_rc); return WH_TEST_SUCCESS; } @@ -1273,7 +1277,7 @@ int whTest_AuthRequestAuthorization(whClientContext* client) memset(&perms, 0, sizeof(perms)); WH_AUTH_SET_ALLOWED_ACTION(perms, WH_MESSAGE_GROUP_AUTH, WH_MESSAGE_AUTH_ACTION_USER_ADD); - /* Free slot: noauthuser no longer needed (WH_AUTH_BASE_MAX_USERS=5) */ + /* Free a slot so the adds below stay within WH_AUTH_BASE_MAX_USERS (5) */ _whTest_Auth_DeleteUserByName(client, "noauthuser"); WH_TEST_RETURN_ON_FAIL( _whTest_Auth_UserAddOp(client, "alloweduser", perms, WH_AUTH_METHOD_PIN, @@ -1293,7 +1297,9 @@ int whTest_AuthRequestAuthorization(whClientContext* client) temp_id3 = WH_USER_ID_INVALID; _whTest_Auth_UserAddOp(client, "testuser8", perms, WH_AUTH_METHOD_PIN, "test", 4, &server_rc, &temp_id3); - WH_TEST_ASSERT_RETURN(server_rc != WH_ERROR_OK); + /* alloweduser holds the USER_ADD action, so the add is authorized */ + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(temp_id3 != WH_USER_ID_INVALID); /* Test 5: Logged in as different user and not allowed */ WH_TEST_PRINT(" Test: Logged in as different user and not allowed\n");