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
89 changes: 89 additions & 0 deletions src/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -1731,6 +1731,95 @@ int wolfSSH_AGENT_enable(WOLFSSH* ssh, byte isEnabled)
}


int wolfSSH_AGENT_ChannelOpen(WOLFSSH* ssh)
{
WOLFSSH_AGENT_CTX* newAgent = NULL;
WOLFSSH_CHANNEL* newChannel = NULL;
int ret = WS_SUCCESS;
/* wolfSSH_accept() clears only want-read/want-write/auth-pending, so a
* WS_BAD_ARGUMENT latched by a poll kills the handshake. */
int recordError = 0;

WLOG_ENTER();

if (ssh == NULL)
ret = WS_SSH_NULL_E;
else if (ssh->ctx->side != WOLFSSH_ENDPOINT_SERVER) {
/* Server side only. wolfSSH_connect() sets ssh->agent too, so the
* checks below would report a channel a client never opened. */
ret = WS_BAD_ARGUMENT;
}
else if (SendAfterDisconnect(ssh)) {
/* The session is over, so neither a new open nor the flush of one
* queued before the disconnect may go out. RFC 4253 section 11.1.
* WS_DISCONNECT is in ssh->error, where the rest of the API puts
* it. */
ret = WS_FATAL_ERROR;
}
else if (!ssh->useAgent) {
Comment thread
ejohnstown marked this conversation as resolved.
/* Nothing asked for agent forwarding on this session. */
ret = WS_BAD_ARGUMENT;
}
else if (ssh->agent == NULL) {
Comment thread
ejohnstown marked this conversation as resolved.
/* Nothing else sets ssh->agent, so a NULL one means "not opened
* yet". Idempotent, so a poll cannot open a second channel. */
WLOG(WS_LOG_AGENT, "Starting agent channel");

newAgent = wolfSSH_AGENT_new(ssh->ctx->heap);
if (newAgent == NULL)
ret = WS_MEMORY_E;

if (ret == WS_SUCCESS) {
newChannel = ChannelNew(ssh, ID_CHANTYPE_AUTH_AGENT,
ssh->ctx->windowSz, ssh->ctx->maxPacketSz);
if (newChannel == NULL)
ret = WS_MEMORY_E;
}

if (ret == WS_SUCCESS) {
word32 flushes = ssh->txFlushCount;

recordError = 1;
ret = SendChannelOpenSession(ssh, newChannel);

/* What commits is the open reaching the peer, not the return:
* a highwater callback failing after the flush is not a send
* that never left. */
if (!SendPacketDelivered(ssh, flushes, ret)) {
ChannelDelete(newChannel, ssh->ctx->heap);
}
else {
/* Publish on a queued open too, so a retry takes the
* already-open path rather than opening a second. */
ChannelAppend(ssh, newChannel);
newAgent->channel = newChannel->channel;
ssh->agent = newAgent;
newAgent = NULL;
if (ssh->ctx->agentCb) {
ssh->ctx->agentCb(WOLFSSH_AGENT_LOCAL_SETUP,
ssh->agentCbCtx);
}
}
}

if (newAgent != NULL)
wolfSSH_AGENT_free(newAgent);
}
else if (wolfSSH_OutputPending(ssh)) {
/* Any queued output, not just this open. Flush it rather than
* report a success the peer hasn't seen. */
recordError = 1;
ret = wolfSSH_SendPacket(ssh);
Comment thread
ejohnstown marked this conversation as resolved.
}

if (recordError)
ssh->error = ret;
Comment thread
ejohnstown marked this conversation as resolved.

WLOG_LEAVE(ret);
return ret;
}


int wolfSSH_AGENT_worker(WOLFSSH* ssh)
{
int ret = WS_SUCCESS;
Expand Down
15 changes: 2 additions & 13 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -18059,19 +18059,8 @@ int SendIgnore(WOLFSSH* ssh, const unsigned char* data, word32 dataSz)
return ret;
}

/* Will the packet just framed reach the peer? A completed flush says so; the
* return does not, since the highwater callback runs after the last byte goes
* out and the rekey it starts fails with the same codes a lost send does.
* Comparing the flush count across the send tells those apart.
*
* Short of a flush, WS_WANT_WRITE is the one outcome that keeps the packet
* framed for the next one; an interrupted send is retried inside
* wolfSSH_SendPacket() rather than reported. Anything else counts as not sent,
* which at worst leaves the peer holding a request this side did not register;
* guessing the other way would desync the reply queue for the life of the
* session. Call before anything else runs, since a later send flushes this
* packet and would read as this one's. */
static INLINE int SendPacketDelivered(WOLFSSH* ssh, word32 flushes, int ret)
/* Contract in internal.h. */
int SendPacketDelivered(WOLFSSH* ssh, word32 flushes, int ret)
{
return ssh->txFlushCount != flushes || ret == WS_WANT_WRITE;
}
Expand Down
58 changes: 8 additions & 50 deletions src/ssh.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,10 +567,6 @@ static int DoReceiveHandshake(WOLFSSH* ssh)
#endif /* !NO_WOLFSSH_SERVER || !NO_WOLFSSH_CLIENT */


/* Defined below, ahead of both drivers; either can be the only one built. */
static int SendAfterDisconnect(WOLFSSH* ssh);


#ifndef NO_WOLFSSH_SERVER

const char acceptError[] = "accept error: %s, %d";
Expand Down Expand Up @@ -764,52 +760,17 @@ int wolfSSH_accept(WOLFSSH* ssh)
#endif /* WOLFSSH_SFTP and !NO_WOLFSSH_SERVER */
#ifdef WOLFSSH_AGENT
if (ssh->useAgent) {
WOLFSSH_AGENT_CTX* newAgent;
WOLFSSH_CHANNEL* newChannel;

WLOG(WS_LOG_AGENT, "Starting agent channel");

newAgent = wolfSSH_AGENT_new(ssh->ctx->heap);
if (newAgent == NULL) {
ssh->error = WS_MEMORY_E;
WLOG(WS_LOG_DEBUG, acceptError,
"SERVER_USERAUTH_ACCEPT_DONE", ssh->error);
return WS_ERROR;
}
int agentRet = wolfSSH_AGENT_ChannelOpen(ssh);

newChannel = ChannelNew(ssh, ID_CHANTYPE_AUTH_AGENT,
ssh->ctx->windowSz, ssh->ctx->maxPacketSz);
if (newChannel == NULL) {
wolfSSH_AGENT_free(newAgent);
ssh->error = WS_MEMORY_E;
if (agentRet < WS_SUCCESS) {
/* WS_FATAL_ERROR is the disconnect, which already
* recorded WS_DISCONNECT; keep that. */
if (agentRet != WS_FATAL_ERROR)
ssh->error = agentRet;
WLOG(WS_LOG_DEBUG, acceptError,
"SERVER_USERAUTH_ACCEPT_DONE", ssh->error);
return WS_FATAL_ERROR;
}

ssh->error = SendChannelOpenSession(ssh, newChannel);
if (ssh->error < WS_SUCCESS) {
if (ssh->error == WS_WANT_WRITE ||
ssh->error == WS_WANT_READ) {
ChannelAppend(ssh, newChannel);
}
else {
ChannelDelete(newChannel, ssh->ctx->heap);
wolfSSH_AGENT_free(newAgent);
}
WLOG(WS_LOG_DEBUG, acceptError,
"SERVER_USERAUTH_ACCEPT_DONE", ssh->error);
return WS_FATAL_ERROR;
}
ChannelAppend(ssh, newChannel);
newAgent->channel = newChannel->channel;
if (ssh->ctx->agentCb) {
ssh->ctx->agentCb(WOLFSSH_AGENT_LOCAL_SETUP,
ssh->agentCbCtx);
}
if (ssh->agent != NULL)
wolfSSH_AGENT_free(ssh->agent);
ssh->agent = newAgent;
}
#endif /* WOLFSSH_AGENT */
ssh->acceptState = ACCEPT_CLIENT_SESSION_ESTABLISHED;
Expand Down Expand Up @@ -1134,11 +1095,8 @@ int wolfSSH_connect(WOLFSSH* ssh)
#endif /* NO_WOLFSSH_CLIENT */


/* A disconnect, sent or received, ends the session, so nothing further may
* go out. RFC 4253 section 11.1. Reads are deliberately not gated on this:
* channel data that arrived before the disconnect is still the caller's.
* Call only after ssh has been checked for NULL. */
static int SendAfterDisconnect(WOLFSSH* ssh)
/* See wolfssh/internal.h for the contract. */
int SendAfterDisconnect(WOLFSSH* ssh)
{
if (ssh->disconnected) {
WLOG(WS_LOG_DEBUG, "Send attempted after a disconnect");
Expand Down
Loading
Loading