Skip to content
Draft
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
85 changes: 85 additions & 0 deletions src/agent.c
Original file line number Diff line number Diff line change
Expand Up @@ -1731,6 +1731,91 @@ 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) {
/* Nothing asked for agent forwarding on this session. */
ret = WS_BAD_ARGUMENT;
}
else if (ssh->agent == NULL) {
/* 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) {
recordError = 1;
ret = SendChannelOpenSession(ssh, newChannel);

if (ret < WS_SUCCESS
&& ret != WS_WANT_WRITE && ret != WS_WANT_READ) {
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);
}

if (recordError)
ssh->error = ret;

WLOG_LEAVE(ret);
return ret;
}


int wolfSSH_AGENT_worker(WOLFSSH* ssh)
{
int ret = WS_SUCCESS;
Expand Down
214 changes: 168 additions & 46 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1672,6 +1672,7 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx)
ssh->highwaterMark = ctx->highwaterMark;
ssh->msgHighwaterMark = ctx->msgHighwaterMark;
ssh->maxAuthAttempts = ctx->maxAuthAttempts;
ssh->appChannels = ctx->appChannels;
ssh->highwaterCtx = (void*)ssh;
ssh->reqSuccessCtx = (void*)ssh;
ssh->fs = NULL;
Expand Down Expand Up @@ -11799,6 +11800,56 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
}
#endif

/* Puts a global request to the generic callback, which sees the name and
* the type-specific part to parse itself. Returns 1 when the callback
* settled the request, with any wanted reply sent and *ret carrying the
* result, or 0 to leave it to the built-in handling. */
static int DoGlobalRequestAny(WOLFSSH* ssh, const char* name, int globReqId,
byte* buf, word32 len, word32 begin, byte wantReply, int* ret)
{
int decision, success;

if (ssh->ctx->globalReqAnyCb == NULL) {
return 0;
}

decision = ssh->ctx->globalReqAnyCb(ssh, name, buf + begin, len - begin,
wantReply, ssh->globalReqCtx);
if (decision != WOLFSSH_REQ_ACCEPT && decision != WOLFSSH_REQ_REJECT) {
return 0;
}
success = (decision == WOLFSSH_REQ_ACCEPT);

#ifdef WOLFSSH_FWD
/* RFC 4254 7.1: a port-0 request is answered with the port bound,
* which only the forward callback can report. */
if (success && globReqId == ID_GLOBREQ_TCPIP_FWD) {
const byte* bindAddr;
word32 bindAddrSz, bindPort = 0, peek = begin;

if (GetStringRef(&bindAddrSz, &bindAddr, buf, len, &peek)
!= WS_SUCCESS
|| GetUint32(&bindPort, buf, len, &peek) != WS_SUCCESS
|| bindPort == 0) {
WLOG(WS_LOG_WARN, "DGR: a port-0 forward needs the forward "
"callback to bind it; rejecting");
success = 0;
}
}
#else
(void)globReqId;
#endif

WLOG(WS_LOG_DEBUG, "DGR: global request callback %s",
success ? "granted" : "refused");
if (wantReply) {
*ret = SendRequestSuccess(ssh, success);
}

return 1;
}


static int DoGlobalRequest(WOLFSSH* ssh,
byte* buf, word32 len, word32* idx)
{
Expand Down Expand Up @@ -11845,31 +11896,37 @@ static int DoGlobalRequest(WOLFSSH* ssh,
}
else
#endif
switch (globReqId) {
if (!DoGlobalRequestAny(ssh, name, globReqId, buf, len, begin,
wantReply, &ret)) {
switch (globReqId) {
Comment on lines +11899 to +11901
#ifdef WOLFSSH_FWD
case ID_GLOBREQ_TCPIP_FWD:
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 0);
wantReply = 0;
break;
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 1);
wantReply = 0;
break;
case ID_GLOBREQ_TCPIP_FWD:
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
wantReply, 0);
wantReply = 0;
break;
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
wantReply, 1);
wantReply = 0;
break;
#endif
default:
if (ssh->ctx->globalReqCb != NULL) {
ret = ssh->ctx->globalReqCb(ssh, name, nameSz, wantReply,
(void *)ssh->globalReqCtx);
default:
if (ssh->ctx->globalReqCb != NULL) {
ret = ssh->ctx->globalReqCb(ssh, name, nameSz,
wantReply, (void *)ssh->globalReqCtx);

if (wantReply) {
ret = SendRequestSuccess(ssh, (ret == WS_SUCCESS));
if (wantReply) {
ret = SendRequestSuccess(ssh,
(ret == WS_SUCCESS));
}
}
}
else if (wantReply)
ret = SendRequestSuccess(ssh, 0);
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
* IETF:draft-ssh-global-requests */
break;
else if (wantReply)
ret = SendRequestSuccess(ssh, 0);
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
* IETF:draft-ssh-global-requests */
break;
}
}
}

Expand Down Expand Up @@ -12666,6 +12723,65 @@ static void SetTerminalSize(WOLFSSH* ssh, word32 widthChar, word32 heightRows,
#endif /* WOLFSSH_TERM */


/* Answers a shell, exec, or subsystem request. The session type, and the
* command for the two that carry one, are set for the callback to read and
* kept only if it accepts; a refused request leaves the channel as it was
* and the accept loop still waiting, so nothing serves a session the
* application turned down. Without a callback the request is accepted,
* unless the application drives its own channels. A request the generic
* callback already granted asks no callback. */
static int DoChannelRequestSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
byte sessionType, WS_CallbackChannelReq cb, int granted,
byte* buf, word32 len, word32* idx, int* rej)
{
char* prevCommand = NULL;
byte prevType = channel->sessionType;
byte hasCommand = (sessionType != WOLFSSH_SESSION_SHELL);
int ret = WS_SUCCESS;

if (hasCommand) {
prevCommand = channel->command;
channel->command = NULL;
ret = GetStringAlloc(ssh->ctx->heap, &channel->command, NULL,
buf, len, idx);
if (ret == WS_SUCCESS) {
WLOG(WS_LOG_DEBUG, " command = %s", channel->command);
}
}

if (ret == WS_SUCCESS) {
channel->sessionType = sessionType;
if (granted) {
*rej = 0;
}
else if (cb != NULL) {
*rej = cb(channel, ssh->channelReqCtx);
}
else {
*rej = ssh->appChannels;
}
}

if (ret == WS_SUCCESS && !*rej) {
if (prevCommand != NULL) {
WFREE(prevCommand, ssh->ctx->heap, DYNTYPE_STRING);
}
ssh->clientState = CLIENT_DONE;
}
else {
if (hasCommand) {
if (channel->command != NULL) {
WFREE(channel->command, ssh->ctx->heap, DYNTYPE_STRING);
}
channel->command = prevCommand;
}
channel->sessionType = prevType;
}

return ret;
}


static int DoChannelRequest(WOLFSSH* ssh,
byte* buf, word32 len, word32* idx)
{
Expand All @@ -12675,7 +12791,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
word32 typeSz;
char type[32];
byte wantReply;
int ret, rej = 0;
int ret, rej = 0, granted = 0;

WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");

Expand Down Expand Up @@ -12704,6 +12820,23 @@ static int DoChannelRequest(WOLFSSH* ssh,
WLOG(WS_LOG_DEBUG, " type = %s", type);
WLOG(WS_LOG_DEBUG, " wantReply = %u", wantReply);

/* The generic callback sees every request first, with the
* type-specific part to parse itself. A refusal skips the handling
* below; a grant runs it with the decision already made. */
if (ssh->ctx->channelReqAnyCb != NULL) {
int decision = ssh->ctx->channelReqAnyCb(channel, type,
buf + begin, len - begin, ssh->channelReqCtx);
if (decision == WOLFSSH_REQ_REJECT) {
WLOG(WS_LOG_DEBUG, " channel request callback refused.");
rej = 1;
}
else if (decision == WOLFSSH_REQ_ACCEPT) {
granted = 1;
}
}
}

if (ret == WS_SUCCESS && !rej) {
if (ChannelRequestIs(type, typeSz, "env")) {
char name[WOLFSSH_MAX_NAMESZ];
word32 nameSz;
Expand All @@ -12721,33 +12854,19 @@ static int DoChannelRequest(WOLFSSH* ssh,
WLOG(WS_LOG_DEBUG, " %s = %s", name, value);
}
else if (ChannelRequestIs(type, typeSz, "shell")) {
channel->sessionType = WOLFSSH_SESSION_SHELL;
if (ssh->ctx->channelReqShellCb) {
rej = ssh->ctx->channelReqShellCb(channel, ssh->channelReqCtx);
}
ssh->clientState = CLIENT_DONE;
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_SHELL,
ssh->ctx->channelReqShellCb, granted, buf, len, &begin,
&rej);
}
else if (ChannelRequestIs(type, typeSz, "exec")) {
ret = GetStringAlloc(ssh->ctx->heap, &channel->command, NULL,
buf, len, &begin);
channel->sessionType = WOLFSSH_SESSION_EXEC;
if (ssh->ctx->channelReqExecCb) {
rej = ssh->ctx->channelReqExecCb(channel, ssh->channelReqCtx);
}
ssh->clientState = CLIENT_DONE;

WLOG(WS_LOG_DEBUG, " command = %s", channel->command);
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_EXEC,
ssh->ctx->channelReqExecCb, granted, buf, len, &begin,
&rej);
}
else if (ChannelRequestIs(type, typeSz, "subsystem")) {
ret = GetStringAlloc(ssh->ctx->heap, &channel->command, NULL,
buf, len, &begin);
channel->sessionType = WOLFSSH_SESSION_SUBSYSTEM;
if (ssh->ctx->channelReqSubsysCb) {
rej = ssh->ctx->channelReqSubsysCb(channel, ssh->channelReqCtx);
}
ssh->clientState = CLIENT_DONE;

WLOG(WS_LOG_DEBUG, " subsystem = %s", channel->command);
ret = DoChannelRequestSession(ssh, channel,
WOLFSSH_SESSION_SUBSYSTEM, ssh->ctx->channelReqSubsysCb,
granted, buf, len, &begin, &rej);
}
#ifdef WOLFSSH_TERM
else if (ChannelRequestIs(type, typeSz, "pty-req")) {
Expand Down Expand Up @@ -12872,6 +12991,9 @@ static int DoChannelRequest(WOLFSSH* ssh,
WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
}
#endif /* WOLFSSH_AGENT */
else if (granted) {
WLOG(WS_LOG_DEBUG, " unknown channel request type, granted.");
}
else {
WLOG(WS_LOG_DEBUG, " unknown channel request type, rejecting.");
rej = 1;
Expand All @@ -12886,7 +13008,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
int replyRet;

if (rej) {
WLOG(WS_LOG_DEBUG, "Callback rejecting channel request.");
WLOG(WS_LOG_DEBUG, "Rejecting channel request.");
}
replyRet = SendChannelSuccess(ssh, channelId,
(ret == WS_SUCCESS && !rej));
Expand Down
Loading
Loading