mirror of https://github.com/wolfSSL/wolfssh.git
ssh: add generic request callbacks
wolfSSH_CTX_SetChannelReqAnyCb() and wolfSSH_CTX_SetGlobalReqAnyCb() register a callback consulted first for a channel or global request, with the name, the type-specific part, and whether a reply is wanted. A tri-state answer grants, refuses, or leaves it to the handling already there, so a policy reaches the types with no hook of their own. - the name is the one that arrived, since a copy into a buffer truncates a long name and ends it at an embedded NUL, and a policy has to answer on what the peer sent - a grant still parses and records what the library needs, so a granted session request commits the session, the typed callbacks are not consulted, and a granted unknown type is answered CHANNEL_SUCCESS - a port-0 tcpip-forward skips the callback, since only the forward callback can report the port bound and a policy that had bound a listener would then have to be refused, per RFC 4254 7.1 - a client refuses tcpip-forward and cancel-tcpip-forward ahead of any policy, matched on the name that arrived so it holds in a build with no forwarding, where neither name is in the name table - window-change, exit-status and exit-signal are cleared of a reply before the handling runs, so a refusal leaves them unanswered too, per RFC 4254 6.7 and 6.10 - regress.c covers the answers, the data delivered, the names that do not fit a copy, and which callbacks each answer leaves outpull/1251/head
parent
ba8996038d
commit
4477f4744a
230
src/internal.c
230
src/internal.c
|
|
@ -12188,6 +12188,79 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Exact match on a global request name, as ChannelRequestIs() does for a
|
||||
* channel request type. The name is the one that arrived, so the match is
|
||||
* on length and bytes. */
|
||||
static int GlobalRequestIs(const byte* name, word32 nameSz, const char* want)
|
||||
{
|
||||
word32 wantSz = (word32)WSTRLEN(want);
|
||||
|
||||
return (nameSz == wantSz) && (WMEMCMP(name, want, wantSz) == 0);
|
||||
}
|
||||
|
||||
|
||||
/* Whether a tcpip-forward is the built-in handling's to answer rather than
|
||||
* the generic callback's. RFC 4254 7.1: a port-0 request is answered with
|
||||
* the port bound, which only the forward callback can report, and one
|
||||
* whose body does not parse has no port to read. Granting either in the
|
||||
* callback would mean refusing it afterwards, once the callback may
|
||||
* already have bound a listener. */
|
||||
static int GlobalRequestNeedsFwdCb(const byte* name, word32 nameSz,
|
||||
const byte* buf, word32 len, word32 begin)
|
||||
{
|
||||
const byte* bindAddr;
|
||||
word32 bindAddrSz, bindPort = 0, peek = begin;
|
||||
|
||||
if (!GlobalRequestIs(name, nameSz, "tcpip-forward")) {
|
||||
return 0;
|
||||
}
|
||||
if (GetStringRef(&bindAddrSz, &bindAddr, buf, len, &peek) != WS_SUCCESS
|
||||
|| GetUint32(&bindPort, buf, len, &peek) != WS_SUCCESS) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return bindPort == 0;
|
||||
}
|
||||
|
||||
|
||||
/* 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. The name is the one
|
||||
* that arrived, nameSz bytes, not the truncated copy the older callback
|
||||
* is handed. */
|
||||
static int DoGlobalRequestAny(WOLFSSH* ssh, const byte* name, word32 nameSz,
|
||||
byte* buf, word32 len, word32 begin, byte wantReply, int* ret)
|
||||
{
|
||||
int decision, success;
|
||||
|
||||
if (ssh->ctx->globalReqAnyCb == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (GlobalRequestNeedsFwdCb(name, nameSz, buf, len, begin)) {
|
||||
WLOG(WS_LOG_DEBUG, "DGR: a port-0 forward is left to the handling "
|
||||
"that can bind it");
|
||||
return 0;
|
||||
}
|
||||
|
||||
decision = ssh->ctx->globalReqAnyCb(ssh, name, nameSz, buf + begin,
|
||||
len - begin, wantReply, ssh->globalReqCtx);
|
||||
if (decision != WOLFSSH_REQ_ACCEPT && decision != WOLFSSH_REQ_REJECT) {
|
||||
return 0;
|
||||
}
|
||||
success = (decision == WOLFSSH_REQ_ACCEPT);
|
||||
|
||||
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)
|
||||
{
|
||||
|
|
@ -12195,6 +12268,8 @@ static int DoGlobalRequest(WOLFSSH* ssh,
|
|||
int ret = WS_SUCCESS;
|
||||
char name[80];
|
||||
word32 nameSz = (word32)sizeof(name);
|
||||
const byte* wireName;
|
||||
word32 wireNameSz;
|
||||
int globReqId = ID_UNKNOWN;
|
||||
byte wantReply = 0;
|
||||
|
||||
|
|
@ -12207,8 +12282,19 @@ static int DoGlobalRequest(WOLFSSH* ssh,
|
|||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
word32 copyBegin;
|
||||
|
||||
/* Read twice: the generic callback answers on the name as it
|
||||
* arrived, where the callback below it has always been handed a
|
||||
* copy, NUL terminated and truncated to the buffer. */
|
||||
begin = *idx;
|
||||
ret = GetString(name, &nameSz, buf, len, &begin);
|
||||
copyBegin = begin;
|
||||
ret = GetStringRef(&wireNameSz, &wireName, buf, len, &begin);
|
||||
if (ret == WS_SUCCESS) {
|
||||
if (wireName == NULL)
|
||||
wireName = (const byte*)""; /* an empty name parses as NULL */
|
||||
ret = GetString(name, &nameSz, buf, len, ©Begin);
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
|
|
@ -12218,47 +12304,53 @@ static int DoGlobalRequest(WOLFSSH* ssh,
|
|||
}
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
#ifdef WOLFSSH_FWD
|
||||
/* RFC 4254 section 7.1: a remote forward is the client's to ask for,
|
||||
* so a client that receives the request answers a failure rather than
|
||||
* registering a forward on the peer's say-so. Answered here and not
|
||||
* in DoGlobalRequestFwd(), so the request body is never parsed and no
|
||||
* forward state is touched. */
|
||||
if ((globReqId == ID_GLOBREQ_TCPIP_FWD
|
||||
|| globReqId == ID_GLOBREQ_TCPIP_FWD_CANCEL)
|
||||
* forward state is touched. Matched on the name as it arrived rather
|
||||
* than on an ID: the name table carries these two only in a
|
||||
* WOLFSSH_FWD build, while a client answers them in any build. */
|
||||
if ((GlobalRequestIs(wireName, wireNameSz, "tcpip-forward")
|
||||
|| GlobalRequestIs(wireName, wireNameSz,
|
||||
"cancel-tcpip-forward"))
|
||||
&& ssh->ctx->side == WOLFSSH_ENDPOINT_CLIENT) {
|
||||
WLOG(WS_LOG_WARN, "DGR: rejecting %s received by a client", name);
|
||||
if (wantReply) {
|
||||
ret = SendRequestSuccess(ssh, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
switch (globReqId) {
|
||||
else if (!DoGlobalRequestAny(ssh, wireName, wireNameSz,
|
||||
buf, len, begin, wantReply, &ret)) {
|
||||
switch (globReqId) {
|
||||
#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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -13074,12 +13166,14 @@ int wolfSSH_DoModes(const byte* modes, word32 modesSz, int fd)
|
|||
#endif /* !NO_TERMIOS && WOLFSSH_TERM */
|
||||
|
||||
|
||||
/* Exact match on a channel request type, as NameToIdType() does for names. */
|
||||
static int ChannelRequestIs(const char* type, word32 typeSz, const char* name)
|
||||
/* Exact match on a channel request type, as NameToIdType() does for names.
|
||||
* The type is the name as it arrived, so the match is on length and bytes;
|
||||
* a type with an embedded NUL is some other type. */
|
||||
static int ChannelRequestIs(const byte* type, word32 typeSz, const char* name)
|
||||
{
|
||||
word32 nameSz = (word32)WSTRLEN(name);
|
||||
|
||||
return (typeSz == nameSz) && (WSTRNCMP(type, name, nameSz) == 0);
|
||||
return (typeSz == nameSz) && (WMEMCMP(type, name, nameSz) == 0);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -13101,10 +13195,11 @@ static void SetTerminalSize(WOLFSSH* ssh, word32 widthChar, word32 heightRows,
|
|||
|
||||
|
||||
/* Answers a shell, exec, or subsystem request. Sets the session type and
|
||||
* command for the callback to read, and keeps them only if it accepts. */
|
||||
* command for the callback to read, and keeps them only if it accepts.
|
||||
* A request the generic callback already granted asks no callback. */
|
||||
static int DoChannelRequestSession(WOLFSSH* ssh, word32 channelId,
|
||||
WOLFSSH_CHANNEL* channel, byte sessionType, WS_CallbackChannelReq cb,
|
||||
byte* buf, word32 len, word32* idx, int* rej)
|
||||
int granted, byte* buf, word32 len, word32* idx, int* rej)
|
||||
{
|
||||
void* heap = ssh->ctx->heap;
|
||||
byte prevType = channel->sessionType;
|
||||
|
|
@ -13135,7 +13230,9 @@ static int DoChannelRequestSession(WOLFSSH* ssh, word32 channelId,
|
|||
}
|
||||
channel->sessionType = sessionType;
|
||||
|
||||
if (cb != NULL)
|
||||
if (granted)
|
||||
*rej = 0;
|
||||
else if (cb != NULL)
|
||||
*rej = cb(channel, ssh->channelReqCtx);
|
||||
else
|
||||
*rej = ssh->appChannels;
|
||||
|
|
@ -13175,17 +13272,22 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
word32 begin = *idx;
|
||||
word32 channelId;
|
||||
word32 typeSz;
|
||||
char type[32];
|
||||
const byte* type;
|
||||
byte wantReply;
|
||||
int ret, rej = 0;
|
||||
int ret, rej = 0, granted = 0;
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Entering DoChannelRequest()");
|
||||
|
||||
ret = GetUint32(&channelId, buf, len, &begin);
|
||||
|
||||
typeSz = (word32)sizeof(type);
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = GetString(type, &typeSz, buf, len, &begin);
|
||||
/* Taken as it arrived rather than copied into a buffer, which would
|
||||
* truncate a long type and end a type at an embedded NUL. The generic
|
||||
* callback below answers on what the peer actually sent. */
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = GetStringRef(&typeSz, &type, buf, len, &begin);
|
||||
if (ret == WS_SUCCESS && type == NULL)
|
||||
type = (const byte*)""; /* an empty type parses as NULL */
|
||||
}
|
||||
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = GetBoolean(&wantReply, buf, len, &begin);
|
||||
|
|
@ -13202,9 +13304,45 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
|
||||
if (ret == WS_SUCCESS) {
|
||||
WLOG(WS_LOG_DEBUG, " channelId = %u", channelId);
|
||||
WLOG(WS_LOG_DEBUG, " type = %s", type);
|
||||
WLOG(WS_LOG_DEBUG, " type = %.*s", (int)typeSz, 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, typeSz,
|
||||
buf + begin, len - begin, wantReply,
|
||||
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 defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
|
||||
/* RFC 4254 sec 6.10: these are never answered, whatever the handling
|
||||
* below makes of them. Cleared here rather than in each branch, which
|
||||
* a refusal or a freed channel skips. */
|
||||
if (ret == WS_SUCCESS
|
||||
&& (ChannelRequestIs(type, typeSz, "exit-status")
|
||||
|| ChannelRequestIs(type, typeSz, "exit-signal"))) {
|
||||
wantReply = 0;
|
||||
}
|
||||
#endif
|
||||
#if defined(WOLFSSH_SHELL) && defined(WOLFSSH_TERM)
|
||||
/* RFC 4254 sec 6.7: the same for window-change. */
|
||||
if (ret == WS_SUCCESS
|
||||
&& ChannelRequestIs(type, typeSz, "window-change")) {
|
||||
wantReply = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ret == WS_SUCCESS && !rej) {
|
||||
if (ChannelRequestIs(type, typeSz, "env")) {
|
||||
char name[WOLFSSH_MAX_NAMESZ];
|
||||
word32 nameSz;
|
||||
|
|
@ -13229,17 +13367,17 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
else if (ChannelRequestIs(type, typeSz, "shell")) {
|
||||
ret = DoChannelRequestSession(ssh, channelId, channel,
|
||||
WOLFSSH_SESSION_SHELL, ssh->ctx->channelReqShellCb,
|
||||
buf, len, &begin, &rej);
|
||||
granted, buf, len, &begin, &rej);
|
||||
}
|
||||
else if (ChannelRequestIs(type, typeSz, "exec")) {
|
||||
ret = DoChannelRequestSession(ssh, channelId, channel,
|
||||
WOLFSSH_SESSION_EXEC, ssh->ctx->channelReqExecCb,
|
||||
buf, len, &begin, &rej);
|
||||
granted, buf, len, &begin, &rej);
|
||||
}
|
||||
else if (ChannelRequestIs(type, typeSz, "subsystem")) {
|
||||
ret = DoChannelRequestSession(ssh, channelId, channel,
|
||||
WOLFSSH_SESSION_SUBSYSTEM, ssh->ctx->channelReqSubsysCb,
|
||||
buf, len, &begin, &rej);
|
||||
granted, buf, len, &begin, &rej);
|
||||
}
|
||||
#ifdef WOLFSSH_TERM
|
||||
else if (ChannelRequestIs(type, typeSz, "pty-req")) {
|
||||
|
|
@ -13286,7 +13424,6 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
else if (ChannelRequestIs(type, typeSz, "window-change")) {
|
||||
word32 widthChar, heightRows, widthPixels, heightPixels;
|
||||
|
||||
wantReply = 0; /* RFC 4254 sec 6.7: no reply for window-change */
|
||||
ret = GetUint32(&widthChar, buf, len, &begin);
|
||||
if (ret == WS_SUCCESS)
|
||||
ret = GetUint32(&heightRows, buf, len, &begin);
|
||||
|
|
@ -13321,7 +13458,6 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
#endif /* WOLFSSH_SHELL && WOLFSSH_TERM */
|
||||
#if defined(WOLFSSH_TERM) || defined(WOLFSSH_SHELL)
|
||||
else if (ChannelRequestIs(type, typeSz, "exit-status")) {
|
||||
wantReply = 0; /* RFC 4254 sec 6.10: no reply for exit-status */
|
||||
ret = GetUint32(&ssh->exitStatus, buf, len, &begin);
|
||||
WLOG(WS_LOG_AGENT, "Got exit status %u.", ssh->exitStatus);
|
||||
}
|
||||
|
|
@ -13330,7 +13466,6 @@ static int DoChannelRequest(WOLFSSH* ssh,
|
|||
word32 sigSz;
|
||||
byte coreDumped;
|
||||
|
||||
wantReply = 0; /* RFC 4254 sec 6.10: no reply for exit-signal */
|
||||
WLOG(WS_LOG_AGENT, "Got exit signal, remote command terminated");
|
||||
|
||||
sigSz = WOLFSSH_MAX_NAMESZ;
|
||||
|
|
@ -13364,6 +13499,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;
|
||||
|
|
|
|||
27
src/ssh.c
27
src/ssh.c
|
|
@ -397,6 +397,19 @@ void wolfSSH_SetReqFailure(WOLFSSH_CTX *ctx, WS_CallbackReqSuccess cb)
|
|||
ctx->reqFailureCb = cb;
|
||||
}
|
||||
|
||||
int wolfSSH_CTX_SetGlobalReqAnyCb(WOLFSSH_CTX* ctx, WS_CallbackGlobalReqAny cb)
|
||||
{
|
||||
int ret = WS_SSH_CTX_NULL_E;
|
||||
|
||||
if (ctx != NULL) {
|
||||
ctx->globalReqAnyCb = cb;
|
||||
ret = WS_SUCCESS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void wolfSSH_SetGlobalReqCtx(WOLFSSH* ssh, void *ctx)
|
||||
{
|
||||
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_SetGlobalReqCtx()");
|
||||
|
|
@ -5830,6 +5843,20 @@ int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
|
|||
}
|
||||
|
||||
|
||||
int wolfSSH_CTX_SetChannelReqAnyCb(WOLFSSH_CTX* ctx,
|
||||
WS_CallbackChannelReqAny cb)
|
||||
{
|
||||
int ret = WS_SSH_CTX_NULL_E;
|
||||
|
||||
if (ctx != NULL) {
|
||||
ctx->channelReqAnyCb = cb;
|
||||
ret = WS_SUCCESS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int wolfSSH_CTX_SetAppChannels(WOLFSSH_CTX* ctx, byte enable)
|
||||
{
|
||||
int ret = WS_SSH_CTX_NULL_E;
|
||||
|
|
|
|||
962
tests/regress.c
962
tests/regress.c
File diff suppressed because it is too large
Load Diff
|
|
@ -856,6 +856,7 @@ struct WOLFSSH_CTX {
|
|||
WS_CallbackUserAuthResult userAuthResultCb; /* User Authentication Result */
|
||||
WS_CallbackHighwater highwaterCb; /* Data Highwater Mark Callback */
|
||||
WS_CallbackGlobalReq globalReqCb; /* Global Request Callback */
|
||||
WS_CallbackGlobalReqAny globalReqAnyCb; /* Global Request, any name */
|
||||
WS_CallbackReqSuccess reqSuccessCb; /* Global Request Success Callback */
|
||||
WS_CallbackReqSuccess reqFailureCb; /* Global Request Failure Callback */
|
||||
WS_CallbackChannelOpen channelOpenCb; /* Channel Open Requested */
|
||||
|
|
@ -864,6 +865,7 @@ struct WOLFSSH_CTX {
|
|||
WS_CallbackChannelReq channelReqShellCb; /* Channel Request "Shell" */
|
||||
WS_CallbackChannelReq channelReqExecCb; /* Channel Request "Exec" */
|
||||
WS_CallbackChannelReq channelReqSubsysCb; /* Channel Request "Subsystem" */
|
||||
WS_CallbackChannelReqAny channelReqAnyCb; /* Channel Request, any */
|
||||
WS_CallbackChannelEof channelEofCb; /* Channel Eof Callback */
|
||||
WS_CallbackChannelClose channelCloseCb; /* Channel Close Callback */
|
||||
#ifdef WOLFSSH_SCP
|
||||
|
|
|
|||
|
|
@ -475,6 +475,45 @@ WOLFSSH_API int wolfSSH_CTX_SetChannelReqSubsysCb(WOLFSSH_CTX* ctx,
|
|||
WOLFSSH_API int wolfSSH_SetChannelReqCtx(WOLFSSH* ssh, void* ctx);
|
||||
WOLFSSH_API void* wolfSSH_GetChannelReqCtx(WOLFSSH* ssh);
|
||||
|
||||
/* What a request callback decides. UNHANDLED is what a missing callback
|
||||
* answers, and leaves the request to the built-in handling.
|
||||
*
|
||||
* Note that 0 is UNHANDLED here, where the older request callbacks above
|
||||
* read a 0 return as acceptance. A callback of this family returns one of
|
||||
* these three and not WS_SUCCESS: returning 0 out of habit leaves the
|
||||
* request to the handling below, which refuses a type the library does
|
||||
* not know. */
|
||||
typedef enum WS_ReqCbResult {
|
||||
WOLFSSH_REQ_UNHANDLED = 0,
|
||||
WOLFSSH_REQ_ACCEPT,
|
||||
WOLFSSH_REQ_REJECT
|
||||
} WS_ReqCbResult;
|
||||
|
||||
/* Consulted first for every channel request, ahead of the three callbacks
|
||||
* above and of the built-in handling, so a request with no callback of its
|
||||
* own -- env, pty-req, window-change, exit-status, auth-agent-req, or a
|
||||
* type the library does not know -- can be granted or refused by policy.
|
||||
* type is the request name as it arrived, typeSz bytes, and data is the
|
||||
* request's type-specific part, dataSz bytes, for the callback to parse.
|
||||
* Neither is NUL terminated, and a name may hold any byte, so a policy
|
||||
* matches on typeSz bytes rather than with the string functions.
|
||||
* wantReply is what the peer asked for, before the library clears it for
|
||||
* the types RFC 4254 never answers.
|
||||
*
|
||||
* ACCEPT and REJECT settle the request, and the shell, exec and subsystem
|
||||
* callbacks are not consulted. The library still parses and records what
|
||||
* it needs from a request it knows, so a session request accepted here
|
||||
* sets the channel's session type and the modes of an accepted pty-req are
|
||||
* kept; a request that does not fit its type is refused whatever the
|
||||
* callback said. A type the library does not know is answered
|
||||
* CHANNEL_SUCCESS on ACCEPT, where it is otherwise refused. Shares the
|
||||
* channel request context. */
|
||||
typedef int (*WS_CallbackChannelReqAny)(WOLFSSH_CHANNEL* channel,
|
||||
const byte* type, word32 typeSz, const byte* data, word32 dataSz,
|
||||
int wantReply, void* ctx);
|
||||
WOLFSSH_API int wolfSSH_CTX_SetChannelReqAnyCb(WOLFSSH_CTX* ctx,
|
||||
WS_CallbackChannelReqAny cb);
|
||||
|
||||
/* Application-driven channel handling, server side, off by default.
|
||||
*
|
||||
* Off, wolfSSH_accept() runs the session state machine through to an
|
||||
|
|
@ -546,6 +585,40 @@ WOLFSSH_API void wolfSSH_SetGlobalReq(WOLFSSH_CTX* ctx,
|
|||
WS_CallbackGlobalReq cb);
|
||||
WOLFSSH_API void wolfSSH_SetGlobalReqCtx(WOLFSSH* ssh, void* ctx);
|
||||
WOLFSSH_API void *wolfSSH_GetGlobalReqCtx(WOLFSSH* ssh);
|
||||
/* Consulted first for a global request, ahead of the forward callback
|
||||
* that answers tcpip-forward and cancel-tcpip-forward and of the callback
|
||||
* above that answers the rest, but for the two requests named below.
|
||||
* name is the request name as it arrived, nameSz bytes, and data is the
|
||||
* request's type-specific part, dataSz bytes, for the callback to parse,
|
||||
* so a tcpip-forward naming a port can be set up from here without a
|
||||
* forward callback. Neither is NUL terminated, and a name may hold any
|
||||
* byte, so a policy matches on nameSz bytes rather than with the string
|
||||
* functions. UNHANDLED leaves the request to those callbacks. ACCEPT and
|
||||
* REJECT settle it, and no other callback is consulted; the reply, when
|
||||
* one is wanted, is REQUEST_SUCCESS or REQUEST_FAILURE.
|
||||
*
|
||||
* Two requests never reach this callback, both per RFC 4254 7.1. A
|
||||
* tcpip-forward asking for port 0 is answered with the port bound, which
|
||||
* only the forward callback can report, so it is left to the handling
|
||||
* that can bind it; so is one whose body does not parse, having no port
|
||||
* to read. Were a policy asked about either, a grant would have to be
|
||||
* refused once the policy had already bound a listener. And a client
|
||||
* answers tcpip-forward and cancel-tcpip-forward with a failure, in any
|
||||
* build, whatever a policy would make of them.
|
||||
*
|
||||
* Shares the global request context.
|
||||
*
|
||||
* name and data point into the session's input buffer and are good only
|
||||
* for the length of the call, so a callback keeping either copies it.
|
||||
* The packet is still being parsed, so the callback must not re-enter
|
||||
* the receive side of the library on this session -- wolfSSH_worker(),
|
||||
* wolfSSH_stream_read(), wolfSSH_accept(), the SFTP calls -- which may
|
||||
* grow or compact that buffer and leave both pointers behind. */
|
||||
typedef int (*WS_CallbackGlobalReqAny)(WOLFSSH* ssh, const byte* name,
|
||||
word32 nameSz, const byte* data, word32 dataSz, int wantReply,
|
||||
void* ctx);
|
||||
WOLFSSH_API int wolfSSH_CTX_SetGlobalReqAnyCb(WOLFSSH_CTX* ctx,
|
||||
WS_CallbackGlobalReqAny cb);
|
||||
typedef int (*WS_CallbackReqSuccess)(WOLFSSH* ssh, void* buf, word32 sz,
|
||||
void* ctx);
|
||||
WOLFSSH_API void wolfSSH_SetReqSuccess(WOLFSSH_CTX* ctx,
|
||||
|
|
|
|||
Loading…
Reference in New Issue