Skip to content

Commit 0c27f30

Browse files
committed
ssh: add generic request callbacks
wolfSSH_CTX_SetChannelReqCb() and wolfSSH_CTX_SetGlobalReqCb() register a callback consulted first for every channel and global request, with the name and the type-specific part to parse. A tri-state answer grants, refuses, or leaves the request to the callbacks and handling already there, so a policy reaches the types with no hook of their own. - a grant still parses and records what the library needs, so a session request granted here commits the session, and the shell, exec and subsystem callbacks are not consulted - a type the library does not know is answered CHANNEL_SUCCESS on a grant - a granted port-0 tcpip-forward is refused, since only the forward callback can report the port bound, per RFC 4254 7.1 - regress.c covers the answers, the data delivered, and which callbacks each answer leaves out
1 parent cf9a26b commit 0c27f30

5 files changed

Lines changed: 564 additions & 28 deletions

File tree

src/internal.c

Lines changed: 110 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11800,6 +11800,56 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
1180011800
}
1180111801
#endif
1180211802

11803+
/* Puts a global request to the generic callback, which sees the name and
11804+
* the type-specific part to parse itself. Returns 1 when the callback
11805+
* settled the request, with any wanted reply sent and *ret carrying the
11806+
* result, or 0 to leave it to the built-in handling. */
11807+
static int DoGlobalRequestAny(WOLFSSH* ssh, const char* name, int globReqId,
11808+
byte* buf, word32 len, word32 begin, byte wantReply, int* ret)
11809+
{
11810+
int decision, success;
11811+
11812+
if (ssh->ctx->globalReqAnyCb == NULL) {
11813+
return 0;
11814+
}
11815+
11816+
decision = ssh->ctx->globalReqAnyCb(ssh, name, buf + begin, len - begin,
11817+
wantReply, ssh->globalReqCtx);
11818+
if (decision != WOLFSSH_REQ_ACCEPT && decision != WOLFSSH_REQ_REJECT) {
11819+
return 0;
11820+
}
11821+
success = (decision == WOLFSSH_REQ_ACCEPT);
11822+
11823+
#ifdef WOLFSSH_FWD
11824+
/* RFC 4254 7.1: a port-0 request is answered with the port bound,
11825+
* which only the forward callback can report. */
11826+
if (success && globReqId == ID_GLOBREQ_TCPIP_FWD) {
11827+
const byte* bindAddr;
11828+
word32 bindAddrSz, bindPort = 0, peek = begin;
11829+
11830+
if (GetStringRef(&bindAddrSz, &bindAddr, buf, len, &peek)
11831+
!= WS_SUCCESS
11832+
|| GetUint32(&bindPort, buf, len, &peek) != WS_SUCCESS
11833+
|| bindPort == 0) {
11834+
WLOG(WS_LOG_WARN, "DGR: a port-0 forward needs the forward "
11835+
"callback to bind it; rejecting");
11836+
success = 0;
11837+
}
11838+
}
11839+
#else
11840+
(void)globReqId;
11841+
#endif
11842+
11843+
WLOG(WS_LOG_DEBUG, "DGR: global request callback %s",
11844+
success ? "granted" : "refused");
11845+
if (wantReply) {
11846+
*ret = SendRequestSuccess(ssh, success);
11847+
}
11848+
11849+
return 1;
11850+
}
11851+
11852+
1180311853
static int DoGlobalRequest(WOLFSSH* ssh,
1180411854
byte* buf, word32 len, word32* idx)
1180511855
{
@@ -11846,31 +11896,37 @@ static int DoGlobalRequest(WOLFSSH* ssh,
1184611896
}
1184711897
else
1184811898
#endif
11849-
switch (globReqId) {
11899+
if (!DoGlobalRequestAny(ssh, name, globReqId, buf, len, begin,
11900+
wantReply, &ret)) {
11901+
switch (globReqId) {
1185011902
#ifdef WOLFSSH_FWD
11851-
case ID_GLOBREQ_TCPIP_FWD:
11852-
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 0);
11853-
wantReply = 0;
11854-
break;
11855-
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
11856-
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 1);
11857-
wantReply = 0;
11858-
break;
11903+
case ID_GLOBREQ_TCPIP_FWD:
11904+
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
11905+
wantReply, 0);
11906+
wantReply = 0;
11907+
break;
11908+
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
11909+
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
11910+
wantReply, 1);
11911+
wantReply = 0;
11912+
break;
1185911913
#endif
11860-
default:
11861-
if (ssh->ctx->globalReqCb != NULL) {
11862-
ret = ssh->ctx->globalReqCb(ssh, name, nameSz, wantReply,
11863-
(void *)ssh->globalReqCtx);
11914+
default:
11915+
if (ssh->ctx->globalReqCb != NULL) {
11916+
ret = ssh->ctx->globalReqCb(ssh, name, nameSz,
11917+
wantReply, (void *)ssh->globalReqCtx);
1186411918

11865-
if (wantReply) {
11866-
ret = SendRequestSuccess(ssh, (ret == WS_SUCCESS));
11919+
if (wantReply) {
11920+
ret = SendRequestSuccess(ssh,
11921+
(ret == WS_SUCCESS));
11922+
}
1186711923
}
11868-
}
11869-
else if (wantReply)
11870-
ret = SendRequestSuccess(ssh, 0);
11871-
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
11872-
* IETF:draft-ssh-global-requests */
11873-
break;
11924+
else if (wantReply)
11925+
ret = SendRequestSuccess(ssh, 0);
11926+
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
11927+
* IETF:draft-ssh-global-requests */
11928+
break;
11929+
}
1187411930
}
1187511931
}
1187611932

@@ -12672,9 +12728,10 @@ static void SetTerminalSize(WOLFSSH* ssh, word32 widthChar, word32 heightRows,
1267212728
* kept only if it accepts; a refused request leaves the channel as it was
1267312729
* and the accept loop still waiting, so nothing serves a session the
1267412730
* application turned down. Without a callback the request is accepted,
12675-
* unless the application drives its own channels. */
12731+
* unless the application drives its own channels. A request the generic
12732+
* callback already granted asks no callback. */
1267612733
static int DoChannelRequestSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
12677-
byte sessionType, WS_CallbackChannelReq cb,
12734+
byte sessionType, WS_CallbackChannelReq cb, int granted,
1267812735
byte* buf, word32 len, word32* idx, int* rej)
1267912736
{
1268012737
char* prevCommand = NULL;
@@ -12694,7 +12751,10 @@ static int DoChannelRequestSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
1269412751

1269512752
if (ret == WS_SUCCESS) {
1269612753
channel->sessionType = sessionType;
12697-
if (cb != NULL) {
12754+
if (granted) {
12755+
*rej = 0;
12756+
}
12757+
else if (cb != NULL) {
1269812758
*rej = cb(channel, ssh->channelReqCtx);
1269912759
}
1270012760
else {
@@ -12731,7 +12791,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
1273112791
word32 typeSz;
1273212792
char type[32];
1273312793
byte wantReply;
12734-
int ret, rej = 0;
12794+
int ret, rej = 0, granted = 0;
1273512795

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

@@ -12760,6 +12820,23 @@ static int DoChannelRequest(WOLFSSH* ssh,
1276012820
WLOG(WS_LOG_DEBUG, " type = %s", type);
1276112821
WLOG(WS_LOG_DEBUG, " wantReply = %u", wantReply);
1276212822

12823+
/* The generic callback sees every request first, with the
12824+
* type-specific part to parse itself. A refusal skips the handling
12825+
* below; a grant runs it with the decision already made. */
12826+
if (ssh->ctx->channelReqAnyCb != NULL) {
12827+
int decision = ssh->ctx->channelReqAnyCb(channel, type,
12828+
buf + begin, len - begin, ssh->channelReqCtx);
12829+
if (decision == WOLFSSH_REQ_REJECT) {
12830+
WLOG(WS_LOG_DEBUG, " channel request callback refused.");
12831+
rej = 1;
12832+
}
12833+
else if (decision == WOLFSSH_REQ_ACCEPT) {
12834+
granted = 1;
12835+
}
12836+
}
12837+
}
12838+
12839+
if (ret == WS_SUCCESS && !rej) {
1276312840
if (ChannelRequestIs(type, typeSz, "env")) {
1276412841
char name[WOLFSSH_MAX_NAMESZ];
1276512842
word32 nameSz;
@@ -12778,16 +12855,18 @@ static int DoChannelRequest(WOLFSSH* ssh,
1277812855
}
1277912856
else if (ChannelRequestIs(type, typeSz, "shell")) {
1278012857
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_SHELL,
12781-
ssh->ctx->channelReqShellCb, buf, len, &begin, &rej);
12858+
ssh->ctx->channelReqShellCb, granted, buf, len, &begin,
12859+
&rej);
1278212860
}
1278312861
else if (ChannelRequestIs(type, typeSz, "exec")) {
1278412862
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_EXEC,
12785-
ssh->ctx->channelReqExecCb, buf, len, &begin, &rej);
12863+
ssh->ctx->channelReqExecCb, granted, buf, len, &begin,
12864+
&rej);
1278612865
}
1278712866
else if (ChannelRequestIs(type, typeSz, "subsystem")) {
1278812867
ret = DoChannelRequestSession(ssh, channel,
1278912868
WOLFSSH_SESSION_SUBSYSTEM, ssh->ctx->channelReqSubsysCb,
12790-
buf, len, &begin, &rej);
12869+
granted, buf, len, &begin, &rej);
1279112870
}
1279212871
#ifdef WOLFSSH_TERM
1279312872
else if (ChannelRequestIs(type, typeSz, "pty-req")) {
@@ -12912,6 +12991,9 @@ static int DoChannelRequest(WOLFSSH* ssh,
1291212991
WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
1291312992
}
1291412993
#endif /* WOLFSSH_AGENT */
12994+
else if (granted) {
12995+
WLOG(WS_LOG_DEBUG, " unknown channel request type, granted.");
12996+
}
1291512997
else {
1291612998
WLOG(WS_LOG_DEBUG, " unknown channel request type, rejecting.");
1291712999
rej = 1;

src/ssh.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,19 @@ void wolfSSH_SetReqFailure(WOLFSSH_CTX *ctx, WS_CallbackReqSuccess cb)
345345
ctx->reqFailureCb = cb;
346346
}
347347

348+
int wolfSSH_CTX_SetGlobalReqCb(WOLFSSH_CTX* ctx, WS_CallbackGlobalReqAny cb)
349+
{
350+
int ret = WS_SSH_CTX_NULL_E;
351+
352+
if (ctx != NULL) {
353+
ctx->globalReqAnyCb = cb;
354+
ret = WS_SUCCESS;
355+
}
356+
357+
return ret;
358+
}
359+
360+
348361
void wolfSSH_SetGlobalReqCtx(WOLFSSH* ssh, void *ctx)
349362
{
350363
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetGlobalReqCtx()");
@@ -4937,6 +4950,20 @@ int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
49374950
}
49384951

49394952

4953+
int wolfSSH_CTX_SetChannelReqCb(WOLFSSH_CTX* ctx,
4954+
WS_CallbackChannelReqAny cb)
4955+
{
4956+
int ret = WS_SSH_CTX_NULL_E;
4957+
4958+
if (ctx != NULL) {
4959+
ctx->channelReqAnyCb = cb;
4960+
ret = WS_SUCCESS;
4961+
}
4962+
4963+
return ret;
4964+
}
4965+
4966+
49404967
int wolfSSH_CTX_SetAppChannels(WOLFSSH_CTX* ctx, byte enable)
49414968
{
49424969
int ret = WS_SSH_CTX_NULL_E;

0 commit comments

Comments
 (0)