Skip to content

Commit a7de103

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 b33f59d commit a7de103

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
@@ -11761,6 +11761,56 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
1176111761
}
1176211762
#endif
1176311763

11764+
/* Puts a global request to the generic callback, which sees the name and
11765+
* the type-specific part to parse itself. Returns 1 when the callback
11766+
* settled the request, with any wanted reply sent and *ret carrying the
11767+
* result, or 0 to leave it to the built-in handling. */
11768+
static int DoGlobalRequestAny(WOLFSSH* ssh, const char* name, int globReqId,
11769+
byte* buf, word32 len, word32 begin, byte wantReply, int* ret)
11770+
{
11771+
int decision, success;
11772+
11773+
if (ssh->ctx->globalReqAnyCb == NULL) {
11774+
return 0;
11775+
}
11776+
11777+
decision = ssh->ctx->globalReqAnyCb(ssh, name, buf + begin, len - begin,
11778+
wantReply, ssh->globalReqCtx);
11779+
if (decision != WOLFSSH_REQ_ACCEPT && decision != WOLFSSH_REQ_REJECT) {
11780+
return 0;
11781+
}
11782+
success = (decision == WOLFSSH_REQ_ACCEPT);
11783+
11784+
#ifdef WOLFSSH_FWD
11785+
/* RFC 4254 7.1: a port-0 request is answered with the port bound,
11786+
* which only the forward callback can report. */
11787+
if (success && globReqId == ID_GLOBREQ_TCPIP_FWD) {
11788+
const byte* bindAddr;
11789+
word32 bindAddrSz, bindPort = 0, peek = begin;
11790+
11791+
if (GetStringRef(&bindAddrSz, &bindAddr, buf, len, &peek)
11792+
!= WS_SUCCESS
11793+
|| GetUint32(&bindPort, buf, len, &peek) != WS_SUCCESS
11794+
|| bindPort == 0) {
11795+
WLOG(WS_LOG_WARN, "DGR: a port-0 forward needs the forward "
11796+
"callback to bind it; rejecting");
11797+
success = 0;
11798+
}
11799+
}
11800+
#else
11801+
(void)globReqId;
11802+
#endif
11803+
11804+
WLOG(WS_LOG_DEBUG, "DGR: global request callback %s",
11805+
success ? "granted" : "refused");
11806+
if (wantReply) {
11807+
*ret = SendRequestSuccess(ssh, success);
11808+
}
11809+
11810+
return 1;
11811+
}
11812+
11813+
1176411814
static int DoGlobalRequest(WOLFSSH* ssh,
1176511815
byte* buf, word32 len, word32* idx)
1176611816
{
@@ -11807,31 +11857,37 @@ static int DoGlobalRequest(WOLFSSH* ssh,
1180711857
}
1180811858
else
1180911859
#endif
11810-
switch (globReqId) {
11860+
if (!DoGlobalRequestAny(ssh, name, globReqId, buf, len, begin,
11861+
wantReply, &ret)) {
11862+
switch (globReqId) {
1181111863
#ifdef WOLFSSH_FWD
11812-
case ID_GLOBREQ_TCPIP_FWD:
11813-
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 0);
11814-
wantReply = 0;
11815-
break;
11816-
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
11817-
ret = DoGlobalRequestFwd(ssh, buf, len, &begin, wantReply, 1);
11818-
wantReply = 0;
11819-
break;
11864+
case ID_GLOBREQ_TCPIP_FWD:
11865+
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
11866+
wantReply, 0);
11867+
wantReply = 0;
11868+
break;
11869+
case ID_GLOBREQ_TCPIP_FWD_CANCEL:
11870+
ret = DoGlobalRequestFwd(ssh, buf, len, &begin,
11871+
wantReply, 1);
11872+
wantReply = 0;
11873+
break;
1182011874
#endif
11821-
default:
11822-
if (ssh->ctx->globalReqCb != NULL) {
11823-
ret = ssh->ctx->globalReqCb(ssh, name, nameSz, wantReply,
11824-
(void *)ssh->globalReqCtx);
11875+
default:
11876+
if (ssh->ctx->globalReqCb != NULL) {
11877+
ret = ssh->ctx->globalReqCb(ssh, name, nameSz,
11878+
wantReply, (void *)ssh->globalReqCtx);
1182511879

11826-
if (wantReply) {
11827-
ret = SendRequestSuccess(ssh, (ret == WS_SUCCESS));
11880+
if (wantReply) {
11881+
ret = SendRequestSuccess(ssh,
11882+
(ret == WS_SUCCESS));
11883+
}
1182811884
}
11829-
}
11830-
else if (wantReply)
11831-
ret = SendRequestSuccess(ssh, 0);
11832-
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
11833-
* IETF:draft-ssh-global-requests */
11834-
break;
11885+
else if (wantReply)
11886+
ret = SendRequestSuccess(ssh, 0);
11887+
/* response SSH_MSG_REQUEST_FAILURE to Keep-Alive.
11888+
* IETF:draft-ssh-global-requests */
11889+
break;
11890+
}
1183511891
}
1183611892
}
1183711893

@@ -12622,9 +12678,10 @@ static void SetTerminalSize(WOLFSSH* ssh, word32 widthChar, word32 heightRows,
1262212678
* kept only if it accepts; a refused request leaves the channel as it was
1262312679
* and the accept loop still waiting, so nothing serves a session the
1262412680
* application turned down. Without a callback the request is accepted,
12625-
* unless the application drives its own channels. */
12681+
* unless the application drives its own channels. A request the generic
12682+
* callback already granted asks no callback. */
1262612683
static int DoChannelRequestSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
12627-
byte sessionType, WS_CallbackChannelReq cb,
12684+
byte sessionType, WS_CallbackChannelReq cb, int granted,
1262812685
byte* buf, word32 len, word32* idx, int* rej)
1262912686
{
1263012687
char* prevCommand = NULL;
@@ -12644,7 +12701,10 @@ static int DoChannelRequestSession(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel,
1264412701

1264512702
if (ret == WS_SUCCESS) {
1264612703
channel->sessionType = sessionType;
12647-
if (cb != NULL) {
12704+
if (granted) {
12705+
*rej = 0;
12706+
}
12707+
else if (cb != NULL) {
1264812708
*rej = cb(channel, ssh->channelReqCtx);
1264912709
}
1265012710
else {
@@ -12681,7 +12741,7 @@ static int DoChannelRequest(WOLFSSH* ssh,
1268112741
word32 typeSz;
1268212742
char type[32];
1268312743
byte wantReply;
12684-
int ret, rej = 0;
12744+
int ret, rej = 0, granted = 0;
1268512745

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

@@ -12710,6 +12770,23 @@ static int DoChannelRequest(WOLFSSH* ssh,
1271012770
WLOG(WS_LOG_DEBUG, " type = %s", type);
1271112771
WLOG(WS_LOG_DEBUG, " wantReply = %u", wantReply);
1271212772

12773+
/* The generic callback sees every request first, with the
12774+
* type-specific part to parse itself. A refusal skips the handling
12775+
* below; a grant runs it with the decision already made. */
12776+
if (ssh->ctx->channelReqAnyCb != NULL) {
12777+
int decision = ssh->ctx->channelReqAnyCb(channel, type,
12778+
buf + begin, len - begin, ssh->channelReqCtx);
12779+
if (decision == WOLFSSH_REQ_REJECT) {
12780+
WLOG(WS_LOG_DEBUG, " channel request callback refused.");
12781+
rej = 1;
12782+
}
12783+
else if (decision == WOLFSSH_REQ_ACCEPT) {
12784+
granted = 1;
12785+
}
12786+
}
12787+
}
12788+
12789+
if (ret == WS_SUCCESS && !rej) {
1271312790
if (ChannelRequestIs(type, typeSz, "env")) {
1271412791
char name[WOLFSSH_MAX_NAMESZ];
1271512792
word32 nameSz;
@@ -12728,16 +12805,18 @@ static int DoChannelRequest(WOLFSSH* ssh,
1272812805
}
1272912806
else if (ChannelRequestIs(type, typeSz, "shell")) {
1273012807
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_SHELL,
12731-
ssh->ctx->channelReqShellCb, buf, len, &begin, &rej);
12808+
ssh->ctx->channelReqShellCb, granted, buf, len, &begin,
12809+
&rej);
1273212810
}
1273312811
else if (ChannelRequestIs(type, typeSz, "exec")) {
1273412812
ret = DoChannelRequestSession(ssh, channel, WOLFSSH_SESSION_EXEC,
12735-
ssh->ctx->channelReqExecCb, buf, len, &begin, &rej);
12813+
ssh->ctx->channelReqExecCb, granted, buf, len, &begin,
12814+
&rej);
1273612815
}
1273712816
else if (ChannelRequestIs(type, typeSz, "subsystem")) {
1273812817
ret = DoChannelRequestSession(ssh, channel,
1273912818
WOLFSSH_SESSION_SUBSYSTEM, ssh->ctx->channelReqSubsysCb,
12740-
buf, len, &begin, &rej);
12819+
granted, buf, len, &begin, &rej);
1274112820
}
1274212821
#ifdef WOLFSSH_TERM
1274312822
else if (ChannelRequestIs(type, typeSz, "pty-req")) {
@@ -12862,6 +12941,9 @@ static int DoChannelRequest(WOLFSSH* ssh,
1286212941
WLOG(WS_LOG_AGENT, "Agent callback not set, not using.");
1286312942
}
1286412943
#endif /* WOLFSSH_AGENT */
12944+
else if (granted) {
12945+
WLOG(WS_LOG_DEBUG, " unknown channel request type, granted.");
12946+
}
1286512947
else {
1286612948
WLOG(WS_LOG_DEBUG, " unknown channel request type, rejecting.");
1286712949
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()");
@@ -4913,6 +4926,20 @@ int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
49134926
}
49144927

49154928

4929+
int wolfSSH_CTX_SetChannelReqCb(WOLFSSH_CTX* ctx,
4930+
WS_CallbackChannelReqAny cb)
4931+
{
4932+
int ret = WS_SSH_CTX_NULL_E;
4933+
4934+
if (ctx != NULL) {
4935+
ctx->channelReqAnyCb = cb;
4936+
ret = WS_SUCCESS;
4937+
}
4938+
4939+
return ret;
4940+
}
4941+
4942+
49164943
int wolfSSH_CTX_SetAppChannels(WOLFSSH_CTX* ctx, byte enable)
49174944
{
49184945
int ret = WS_SSH_CTX_NULL_E;

0 commit comments

Comments
 (0)