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
31 changes: 30 additions & 1 deletion examples/echoserver/echoserver.c
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,8 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
else if (action == WOLFSSH_FWD_REMOTE_SETUP) {
struct sockaddr_in addr;
socklen_t addrSz = 0;
socklen_t boundSz = sizeof(addr);
word32 allocatedPort = 0;

fwdCbCtx->hostName = WSTRDUP(name, NULL, 0);
fwdCbCtx->hostPort = port;
Expand All @@ -553,7 +555,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
}
else {
printf("Not using IPv6 yet.\n");
ret = WS_FWD_SETUP_E;
ret = -1;
}
}

Expand All @@ -566,8 +568,35 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
ret = listen(appCtx->listenFd, 5);
}

if (ret == 0 && port == 0) {
/* The peer requested port 0, so the OS picked the port during
* bind(). Recover it to report back to the caller. */
WMEMSET(&addr, 0, sizeof addr);
if (getsockname(appCtx->listenFd,
(struct sockaddr*)&addr, &boundSz) == 0) {
allocatedPort = (word32)ntohs(addr.sin_port);
/* The library reads a return below WS_FWD_PORT_CHECK as a
* status, not a port, so an allocated port must be reportable.
* An unprivileged OS-chosen port always is; guard anyway. */
if (allocatedPort < WS_FWD_PORT_CHECK) {
printf("Allocated port %u not reportable.\n", allocatedPort);
ret = -1;
}
else {
fwdCbCtx->hostPort = allocatedPort;
}
}
else {
printf("getsockname failed for forwarded port.\n");
ret = -1;
}
}

if (ret == 0) {
appCtx->state = APP_STATE_LISTEN;
/* Report any dynamically allocated port to the library through the
* return value; 0 keeps the port the peer requested. */
ret = (int)allocatedPort;
}
else {
if (fwdCbCtx->hostName != NULL) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,8 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
else if (action == WOLFSSH_FWD_REMOTE_SETUP) {
struct sockaddr_in addr;
socklen_t addrSz = 0;
socklen_t boundSz = sizeof(addr);
word32 allocatedPort = 0;

ctx->hostName = WSTRDUP(name, NULL, 0);
ctx->hostPort = port;
Expand All @@ -545,7 +547,7 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
}
else {
printf("Not using IPv6 yet.\n");
ret = WS_FWD_SETUP_E;
ret = -1;
}
}

Expand All @@ -558,8 +560,35 @@ static int wolfSSH_FwdDefaultActions(WS_FwdCbAction action, void* vCtx,
ret = listen(ctx->listenFd, 5);
}

if (ret == 0 && port == 0) {
/* The peer requested port 0, so the OS picked the port during
* bind(). Recover it to report back to the caller. */
WMEMSET(&addr, 0, sizeof addr);
if (getsockname(ctx->listenFd,
(struct sockaddr*)&addr, &boundSz) == 0) {
allocatedPort = (word32)ntohs(addr.sin_port);
/* The library reads a return below WS_FWD_PORT_CHECK as a
* status, not a port, so an allocated port must be reportable.
* An unprivileged OS-chosen port always is; guard anyway. */
if (allocatedPort < WS_FWD_PORT_CHECK) {
printf("Allocated port %u not reportable.\n", allocatedPort);
ret = -1;
}
else {
ctx->hostPort = allocatedPort;
}
}
else {
printf("getsockname failed for forwarded port.\n");
ret = -1;
}
}

if (ret == 0) {
ctx->state = FWD_STATE_LISTEN;
/* Report any dynamically allocated port to the library through the
* return value; 0 keeps the port the peer requested. */
ret = (int)allocatedPort;
}
else {
if (ctx->hostName != NULL) {
Expand Down
102 changes: 85 additions & 17 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -8957,6 +8957,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
int ret = WS_SUCCESS;
char* bindAddr = NULL;
word32 bindPort;
word32 requestedPort = 0;

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

Expand All @@ -8975,15 +8976,36 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
}

if (ret == WS_SUCCESS) {
requestedPort = bindPort;
WLOG(WS_LOG_INFO, "Requesting forwarding%s for address %s on port %u.",
isCancel ? " cancel" : "", bindAddr, bindPort);
}

if (ret == WS_SUCCESS) {
if (ssh->ctx->fwdCb) {
ret = ssh->ctx->fwdCb(isCancel ? WOLFSSH_FWD_REMOTE_CLEANUP :
int cbRet = ssh->ctx->fwdCb(isCancel ? WOLFSSH_FWD_REMOTE_CLEANUP :
WOLFSSH_FWD_REMOTE_SETUP,
ssh->fwdCbCtx, bindAddr, bindPort);
/* A return at or above WS_FWD_PORT_CHECK is the unprivileged port
* the callback allocated for a remote port-0 request; anything
* below it is a WS_FwdCbError status, where WS_FWD_SUCCESS is
* success and any other value is a rejection. An allocated port is
* only meaningful for a port-0 (dynamic) request and must be a
* valid port number; for a non-zero request the callback should
* return WS_FWD_SUCCESS, so a port-like value is ignored and the
* requested port stands. An out-of-range value for a port-0
* request leaves bindPort unchanged and is rejected by the
* port-0 compliance check below. */
if (!isCancel && cbRet >= WS_FWD_PORT_CHECK) {
if (requestedPort == 0 && cbRet <= 65535) {
bindPort = (word32)cbRet;
}
}
else if (cbRet != WS_FWD_SUCCESS) {
WLOG(WS_LOG_WARN, "Forward callback rejected the request, "
"WS_FwdCbError = %d", cbRet);
ret = WS_RESOURCE_E;
}
}
else {
WLOG(WS_LOG_WARN, "No forwarding callback set, rejecting request. "
Expand All @@ -8992,6 +9014,27 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
}
}

if (ret == WS_SUCCESS && !isCancel) {
/* A remote forward was set up successfully. RFC 4254 7.1 requires a
* port-0 (dynamic) request to be answered with the actual unprivileged
* port allocated. A successful callback that did not report one leaves
* bindPort below WS_FWD_PORT_CHECK, so we cannot comply: undo the setup
* and reject instead of sending a non-compliant success. */
if (requestedPort == 0 && bindPort < WS_FWD_PORT_CHECK) {
WLOG(WS_LOG_WARN, "Forward callback reported no unprivileged port "
"for a port-0 request; rejecting.");
if (ssh->ctx->fwdCb) {
int cleanupRet = ssh->ctx->fwdCb(WOLFSSH_FWD_REMOTE_CLEANUP,
ssh->fwdCbCtx, bindAddr, bindPort);
if (cleanupRet != WS_SUCCESS) {
WLOG(WS_LOG_WARN, "Forward cleanup after rejection failed, "
"ret = %d", cleanupRet);
}
ret = WS_RESOURCE_E;
}
}
}

if (wantReply) {
if (ret == WS_SUCCESS) {
if (isCancel) {
Expand All @@ -9005,7 +9048,7 @@ static int DoGlobalRequestFwd(WOLFSSH* ssh,
ret = SendRequestSuccess(ssh, 0);
}
}
else if (ret == WS_UNIMPLEMENTED_E) {
else if (ret == WS_UNIMPLEMENTED_E || ret == WS_RESOURCE_E) {
/* No reply expected; silently reject without terminating connection. */
ret = WS_SUCCESS;
}
Expand Down Expand Up @@ -9223,23 +9266,45 @@ static int DoChannelOpen(WOLFSSH* ssh,
else {
ChannelUpdatePeer(newChannel, peerChannelId,
peerInitialWindowSz, peerMaxPacketSz);
if (ssh->ctx->channelOpenCb) {
ret = ssh->ctx->channelOpenCb(newChannel, ssh->channelOpenCtx);
#ifdef WOLFSSH_FWD
/* A forwarded-tcpip open is a server-to-client message sent in
* response to a tcpip-forward request, so only a client should
* ever receive one. Reject opens arriving in the wrong direction
* up front, before any policy callback runs or channel state is
* updated. direct-tcpip is intentionally not direction-checked:
* either forwarding side may legitimately request a direct
* forward. */
if (typeId == ID_CHANTYPE_TCPIP_FORWARD &&
ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) {
WLOG(WS_LOG_WARN, "Rejecting forwarded-tcpip channel open "
"received by a server (wrong direction)");
fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED;
ret = WS_ERROR;
}
else {
WLOG(WS_LOG_WARN, "No channel open callback set "
"(call wolfSSH_CTX_SetChannelOpenCb()), accepting "
"channel open by default; typeId=%u, peerChannelId=%u",
(word32)typeId, peerChannelId);
#endif /* WOLFSSH_FWD */
if (ret == WS_SUCCESS) {
if (ssh->ctx->channelOpenCb) {
ret = ssh->ctx->channelOpenCb(newChannel,
ssh->channelOpenCtx);
}
else {
WLOG(WS_LOG_WARN, "No channel open callback set "
"(call wolfSSH_CTX_SetChannelOpenCb()), accepting "
"channel open by default; typeId=%u, "
"peerChannelId=%u",
(word32)typeId, peerChannelId);
}
if (ssh->channelListSz == 0)
ssh->defaultPeerChannelId = peerChannelId;
}
if (ssh->channelListSz == 0)
ssh->defaultPeerChannelId = peerChannelId;
#ifdef WOLFSSH_FWD
if (typeId == ID_CHANTYPE_TCPIP_DIRECT) {
ChannelUpdateForward(newChannel,
host, hostPort, origin, originPort, isDirect);

if (ret == WS_SUCCESS &&
(typeId == ID_CHANTYPE_TCPIP_DIRECT ||
typeId == ID_CHANTYPE_TCPIP_FORWARD)) {
if (ssh->ctx->fwdCb) {
ChannelUpdateForward(newChannel,
host, hostPort, origin, originPort, isDirect);

ret = ssh->ctx->fwdCb(WOLFSSH_FWD_LOCAL_SETUP,
ssh->fwdCbCtx, host, hostPort);
if (ret == WS_SUCCESS) {
Expand All @@ -9248,8 +9313,11 @@ static int DoChannelOpen(WOLFSSH* ssh,
}
}
else {
WLOG(WS_LOG_WARN, "No forward callback set for direct-tcpip channel,"
" failing channel open");
/* Both forwarding channel types require an explicit policy
* callback; without one, fail closed rather than letting
* the default-accept channelOpenCb path admit them. */
WLOG(WS_LOG_WARN, "No forward callback set for forwarding "
"channel, failing channel open");
fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED;
ret = WS_ERROR;
}
Expand Down
Loading
Loading