diff --git a/src/agent.c b/src/agent.c index 04c71382c..5c95d32c0 100644 --- a/src/agent.c +++ b/src/agent.c @@ -1340,6 +1340,8 @@ static int DoMessage(WOLFSSH_AGENT_CTX* agent, if (agent == NULL) ret = WS_SSH_NULL_E; /* WS_AGENT_NULL_E */ + else + agent->lastMsgId = 0; if (ret == WS_SUCCESS) { if (buf == NULL || idx == NULL || len == 0) @@ -1371,6 +1373,7 @@ static int DoMessage(WOLFSSH_AGENT_CTX* agent, if (ret == WS_SUCCESS) { msg = buf[begin++]; + agent->lastMsgId = msg; payloadIdx = 0; switch (msg) { case MSGID_AGENT_FAILURE: @@ -1793,6 +1796,11 @@ int wolfSSH_AGENT_SignRequest(WOLFSSH* ssh, if (ret == WS_SUCCESS) { agent = ssh->agent; + agent->requestFailure = 0; + agent->requestSuccess = 0; + agent->msg = NULL; + agent->msgSz = 0; + agent->lastMsgId = 0; if (ssh->ctx->agentCb) ret = ssh->ctx->agentCb(WOLFSSH_AGENT_LOCAL_SETUP, ssh->agentCbCtx); } @@ -1801,11 +1809,16 @@ int wolfSSH_AGENT_SignRequest(WOLFSSH* ssh, ret = SendSignRequest(agent, digest, digestSz, keyBlob, keyBlobSz, flags); - if (ret == WS_SUCCESS) - ret = ssh->ctx->agentIoCb(WOLFSSH_AGENT_IO_WRITE, - agent->msg, agent->msgSz, ssh->agentCbCtx); + if (ret == WS_SUCCESS) { + int wrote; - if (ret > 0) ret = WS_SUCCESS; + wrote = ssh->ctx->agentIoCb(WOLFSSH_AGENT_IO_WRITE, + agent->msg, agent->msgSz, ssh->agentCbCtx); + if (wrote != (int)agent->msgSz) { + WLOG(WS_LOG_AGENT, "agent write incomplete"); + ret = WS_AGENT_CXN_FAIL; + } + } if (agent != NULL && agent->msg != NULL) { WFREE(ssh->agent->msg, ssh->agent->heap, DYNTYPE_AGENT_BUFFER); @@ -1818,18 +1831,41 @@ int wolfSSH_AGENT_SignRequest(WOLFSSH* ssh, rxBuf, sizeof(rxBuf), ssh->agentCbCtx); if (rxSz > 0) { ret = DoMessage(ssh->agent, rxBuf, rxSz, &idx); - if (ssh->agent->requestFailure) { - ssh->agent->requestFailure = 0; - ret = WS_AGENT_NO_KEY_E; - } - else { - WMEMCPY(sig, ssh->agent->msg, ssh->agent->msgSz); - *sigSz = ssh->agent->msgSz; + if (ret == WS_SUCCESS) { + if (ssh->agent->lastMsgId != MSGID_AGENT_SIGN_RESPONSE) { + WLOG(WS_LOG_AGENT, + "agent response was not a signature message"); + ret = WS_AGENT_NO_KEY_E; + } + else { + if (ssh->agent->requestFailure || + ssh->agent->msg == NULL || + ssh->agent->msgSz == 0) { + ssh->agent->requestFailure = 0; + ret = WS_AGENT_NO_KEY_E; + } + else { + word32 maxSigSz = *sigSz; + + if (ssh->agent->msgSz > maxSigSz) { + WLOG(WS_LOG_AGENT, + "agent signature too large for caller buffer"); + ret = WS_BUFFER_E; + } + else { + WMEMCPY(sig, ssh->agent->msg, ssh->agent->msgSz); + *sigSz = ssh->agent->msgSz; + } + } + } } } else ret = WS_AGENT_NO_KEY_E; } + if (ret != WS_SUCCESS && sigSz != NULL) + *sigSz = 0; + if (agent != NULL) { agent->msg = NULL; agent->msgSz = 0; diff --git a/src/wolfscp.c b/src/wolfscp.c index 9a7dfc881..38893be9b 100644 --- a/src/wolfscp.c +++ b/src/wolfscp.c @@ -966,6 +966,7 @@ static int GetScpFileName(WOLFSSH* ssh, byte* buf, word32 bufSz, { int ret = WS_SUCCESS; word32 idx, len; + const char* fileName; if (ssh == NULL || buf == NULL || inOutIdx == NULL) return WS_BAD_ARGUMENT; @@ -977,6 +978,31 @@ static int GetScpFileName(WOLFSSH* ssh, byte* buf, word32 bufSz, ret = WS_SCP_CMD_E; if (ret == WS_SUCCESS) { + word32 i; + + fileName = (const char*)(buf + idx); + + if (len == 0 || + (len == 1 && fileName[0] == '.') || + (len == 2 && fileName[0] == '.' && fileName[1] == '.')) { + WLOG(WS_LOG_ERROR, "scp: invalid file name component received"); + wolfSSH_SetScpErrorMsg(ssh, "invalid file name"); + return WS_SCP_BAD_MSG_E; + } + + for (i = 0; i < len; i++) { + char c = fileName[i]; + + if (c == '/' || c == '\\' +#if defined(USE_WINDOWS_API) || defined(WOLFSSL_NUCLEUS) + || c == ':' +#endif + ) { + WLOG(WS_LOG_ERROR, "scp: invalid file name component received"); + wolfSSH_SetScpErrorMsg(ssh, "invalid file name"); + return WS_SCP_BAD_MSG_E; + } + } if (ssh->scpFileName != NULL) { WFREE(ssh->scpFileName, ssh->ctx->heap, DYNTYPE_STRING); @@ -1277,11 +1303,13 @@ int ParseScpCommand(WOLFSSH* ssh) ssh->scpBasePath = ssh->scpBasePathDynamic; WMEMCPY(ssh->scpBasePathDynamic, cmd + idx, cmdSz - idx); - ret = ParseBasePathHelper(ssh, cmdSz); - if (ret == WS_SUCCESS && - wolfSSH_CleanPath(ssh, - ssh->scpBasePathDynamic) < 0) + if (wolfSSH_CleanPath(ssh, + ssh->scpBasePathDynamic) < 0) { ret = WS_FATAL_ERROR; + } + else { + ret = ParseBasePathHelper(ssh, cmdSz); + } } break; @@ -3090,4 +3118,3 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, #endif /* WOLFSSH_SCP_USER_CALLBACKS */ #endif /* WOLFSSH_SCP */ - diff --git a/tests/api.c b/tests/api.c index 9da02849b..a82edff81 100644 --- a/tests/api.c +++ b/tests/api.c @@ -31,11 +31,15 @@ #include #include +#include #include #include #ifdef WOLFSSH_SCP #include #endif +#ifdef WOLFSSH_AGENT + #include +#endif #ifdef WOLFSSH_SFTP #define WOLFSSH_TEST_LOCKING @@ -873,6 +877,210 @@ static void test_wolfSSH_SCP_CB(void) static void test_wolfSSH_SCP_CB(void) { ; } #endif /* WOLFSSH_SCP */ +#ifdef WOLFSSH_AGENT +typedef struct AgentTestCtx { + int partialWrite; + byte response[128]; + word32 responseSz; + int writeCalls; + int readCalls; +} AgentTestCtx; + +static int test_agent_cb(WS_AgentCbAction action, void* ctx) +{ + (void)ctx; + + if (action == WOLFSSH_AGENT_LOCAL_SETUP || + action == WOLFSSH_AGENT_LOCAL_CLEANUP) { + return WS_AGENT_SUCCESS; + } + + return WS_AGENT_INVALID_ACTION; +} + +static void put_uint32(byte* dst, word32 value) +{ + dst[0] = (byte)((value >> 24) & 0xff); + dst[1] = (byte)((value >> 16) & 0xff); + dst[2] = (byte)((value >> 8) & 0xff); + dst[3] = (byte)(value & 0xff); +} + +static void build_agent_message(byte* out, word32* outSz, byte id, + const byte* body, word32 bodySz) +{ + word32 payloadSz = 1 + bodySz; + + put_uint32(out, payloadSz); + out[4] = id; + if (bodySz > 0) + memcpy(out + 5, body, bodySz); + *outSz = payloadSz + LENGTH_SZ; +} + +static void build_sign_response(AgentTestCtx* ctx, const byte* sig, + word32 sigSz) +{ + byte body[4 + 64]; + + AssertTrue(sigSz <= 64); + put_uint32(body, sigSz); + if (sigSz > 0) + memcpy(body + LENGTH_SZ, sig, sigSz); + build_agent_message(ctx->response, &ctx->responseSz, + MSGID_AGENT_SIGN_RESPONSE, body, LENGTH_SZ + sigSz); +} + +static void build_simple_response(AgentTestCtx* ctx, byte id) +{ + build_agent_message(ctx->response, &ctx->responseSz, id, NULL, 0); +} + +static int test_agent_io_cb(WS_AgentIoCbAction action, void* buf, word32 bufSz, + void* ctx) +{ + AgentTestCtx* io = (AgentTestCtx*)ctx; + + if (action == WOLFSSH_AGENT_IO_WRITE) { + io->writeCalls++; + if (io->partialWrite && bufSz > 0) { + io->partialWrite = 0; + return (int)(bufSz - 1); + } + return (int)bufSz; + } + + io->readCalls++; + if (io->responseSz == 0 || bufSz < io->responseSz) + return 0; + memcpy(buf, io->response, io->responseSz); + return (int)io->responseSz; +} + +static void setup_agent_test(WOLFSSH_CTX** ctx, WOLFSSH** ssh, AgentTestCtx* io) +{ + AssertNotNull(*ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL)); + AssertIntEQ(wolfSSH_CTX_AGENT_enable(*ctx, 1), WS_SUCCESS); + AssertIntEQ(wolfSSH_CTX_set_agent_cb(*ctx, test_agent_cb, + test_agent_io_cb), WS_SUCCESS); + AssertNotNull(*ssh = wolfSSH_new(*ctx)); + AssertNotNull((*ssh)->agent = wolfSSH_AGENT_new((*ctx)->heap)); + AssertIntEQ(wolfSSH_set_agent_cb_ctx(*ssh, io), WS_SUCCESS); + AssertIntEQ(wolfSSH_AGENT_enable(*ssh, 1), WS_SUCCESS); +} + +static void cleanup_agent_test(WOLFSSH_CTX* ctx, WOLFSSH* ssh) +{ + wolfSSH_free(ssh); + wolfSSH_CTX_free(ctx); +} + +static void test_wolfSSH_agent_signrequest_partial_write(void) +{ + WOLFSSH_CTX* ctx; + WOLFSSH* ssh; + AgentTestCtx io; + byte digest[16] = {0}; + byte keyBlob[8] = {0}; + byte sig[8]; + word32 sigSz = sizeof(sig); + int ret; + + memset(&io, 0, sizeof(io)); + io.partialWrite = 1; + setup_agent_test(&ctx, &ssh, &io); + + ret = wolfSSH_AGENT_SignRequest(ssh, digest, sizeof(digest), + sig, &sigSz, keyBlob, sizeof(keyBlob), 0); + AssertIntEQ(ret, WS_AGENT_CXN_FAIL); + AssertIntEQ(sigSz, 0); + AssertIntEQ(io.writeCalls, 1); + AssertIntEQ(io.readCalls, 0); + + cleanup_agent_test(ctx, ssh); +} + +static void test_wolfSSH_agent_signrequest_wrong_message(void) +{ + WOLFSSH_CTX* ctx; + WOLFSSH* ssh; + AgentTestCtx io; + byte digest[16] = {0}; + byte keyBlob[8] = {0}; + byte sig[16]; + word32 sigSz = sizeof(sig); + int ret; + + memset(&io, 0, sizeof(io)); + build_simple_response(&io, MSGID_AGENT_SUCCESS); + setup_agent_test(&ctx, &ssh, &io); + + ret = wolfSSH_AGENT_SignRequest(ssh, digest, sizeof(digest), + sig, &sigSz, keyBlob, sizeof(keyBlob), 0); + AssertIntEQ(ret, WS_AGENT_NO_KEY_E); + AssertIntEQ(sigSz, 0); + AssertIntEQ(io.writeCalls, 1); + AssertIntEQ(io.readCalls, 1); + + cleanup_agent_test(ctx, ssh); +} + +static void test_wolfSSH_agent_signrequest_signature_too_large(void) +{ + WOLFSSH_CTX* ctx; + WOLFSSH* ssh; + AgentTestCtx io; + byte digest[16] = {0}; + byte keyBlob[8] = {0}; + byte signatureData[12]; + byte sig[8]; + word32 sigSz = sizeof(sig); + int ret; + + memset(signatureData, 0x5a, sizeof(signatureData)); + memset(&io, 0, sizeof(io)); + build_sign_response(&io, signatureData, sizeof(signatureData)); + setup_agent_test(&ctx, &ssh, &io); + + ret = wolfSSH_AGENT_SignRequest(ssh, digest, sizeof(digest), + sig, &sigSz, keyBlob, sizeof(keyBlob), 0); + AssertIntEQ(ret, WS_BUFFER_E); + AssertIntEQ(sigSz, 0); + AssertIntEQ(io.writeCalls, 1); + AssertIntEQ(io.readCalls, 1); + + cleanup_agent_test(ctx, ssh); +} + +static void test_wolfSSH_agent_signrequest_success(void) +{ + WOLFSSH_CTX* ctx; + WOLFSSH* ssh; + AgentTestCtx io; + byte digest[16] = {0}; + byte keyBlob[8] = {0}; + byte signatureData[8]; + byte sig[16]; + word32 sigSz = sizeof(sig); + int ret; + + memset(signatureData, 0xa5, sizeof(signatureData)); + memset(&io, 0, sizeof(io)); + build_sign_response(&io, signatureData, sizeof(signatureData)); + setup_agent_test(&ctx, &ssh, &io); + + ret = wolfSSH_AGENT_SignRequest(ssh, digest, sizeof(digest), + sig, &sigSz, keyBlob, sizeof(keyBlob), 0); + AssertIntEQ(ret, WS_SUCCESS); + AssertIntEQ(sigSz, sizeof(signatureData)); + AssertTrue(memcmp(sig, signatureData, sizeof(signatureData)) == 0); + AssertIntEQ(io.writeCalls, 1); + AssertIntEQ(io.readCalls, 1); + + cleanup_agent_test(ctx, ssh); +} +#endif /* WOLFSSH_AGENT */ + #if defined(WOLFSSH_SFTP) && !defined(NO_WOLFSSH_CLIENT) && \ !defined(SINGLE_THREADED) @@ -1779,6 +1987,12 @@ int wolfSSH_ApiTest(int argc, char** argv) test_wolfSSH_ReadKey(); test_wolfSSH_QueryAlgoList(); test_wolfSSH_SetAlgoList(); +#ifdef WOLFSSH_AGENT + test_wolfSSH_agent_signrequest_partial_write(); + test_wolfSSH_agent_signrequest_wrong_message(); + test_wolfSSH_agent_signrequest_signature_too_large(); + test_wolfSSH_agent_signrequest_success(); +#endif #ifdef WOLFSSH_KEYBOARD_INTERACTIVE test_wolfSSH_KeyboardInteractive(); #endif diff --git a/wolfssh/agent.h b/wolfssh/agent.h index c3d6412aa..a133b0903 100644 --- a/wolfssh/agent.h +++ b/wolfssh/agent.h @@ -140,6 +140,7 @@ struct WOLFSSH_AGENT_CTX { enum AgentStates state; int requestSuccess; int requestFailure; + byte lastMsgId; }; typedef struct WOLFSSH_AGENT_CTX WOLFSSH_AGENT_CTX; @@ -190,4 +191,3 @@ WOLFSSH_API int wolfSSH_AGENT_SignRequest(WOLFSSH*, const byte*, word32, #endif #endif /* _WOLFSSH_AGENT_H_ */ -