32 SshSftpOpenCb pc_sftp_open_cb =
nullptr;
33 SshSftpDataCb pc_sftp_data_cb =
nullptr;
36 SshScpOpenCb pc_scp_open_cb =
nullptr;
37 SshScpDataCb pc_scp_data_cb =
nullptr;
48void pc_ssh_channel_set_sftp_open_cb(SshSftpOpenCb cb)
50 s_chcb.pc_sftp_open_cb = cb;
52void pc_ssh_channel_set_sftp_data_cb(SshSftpDataCb cb)
54 s_chcb.pc_sftp_data_cb = cb;
59void pc_ssh_channel_set_scp_open_cb(SshScpOpenCb cb)
61 s_chcb.pc_scp_open_cb = cb;
63void pc_ssh_channel_set_scp_data_cb(SshScpDataCb cb)
65 s_chcb.pc_scp_data_cb = cb;
107static uint32_t rd_u32(
const uint8_t *p)
109 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | ((uint32_t)p[2] << 8) | (uint32_t)p[3];
112static void wr_u32(uint8_t *p, uint32_t v)
114 p[0] = (uint8_t)(v >> 24);
115 p[1] = (uint8_t)(v >> 16);
116 p[2] = (uint8_t)(v >> 8);
121static bool rd_string(
const uint8_t *p,
size_t len,
size_t *off,
const uint8_t **out, uint32_t *slen)
127 uint32_t n = rd_u32(p + *off);
144static SshChannel *chan_by_id(uint8_t i, uint32_t
id)
157static SshChannel *chan_pending_by_id(uint8_t i, uint32_t
id)
169static int chan_alloc(uint8_t i)
187static int build_open_failure(uint8_t *out,
size_t cap, uint32_t sender, uint32_t reason,
size_t *out_len)
194 wr_u32(out + 1, sender);
195 wr_u32(out + 5, reason);
203static int build_open_confirm(
const SshChannel *c, uint8_t *out,
size_t cap,
size_t *out_len)
233 if (!rd_string(payload, len, &off, &name, &name_len))
241 bool want_reply = payload[off++] != 0;
243 bool is_fwd = (name_len == 13 && memcmp(name,
"tcpip-forward", 13) == 0);
244 bool is_cancel = (name_len == 20 && memcmp(name,
"cancel-tcpip-forward", 20) == 0);
246 if (is_fwd || is_cancel)
251 if (!rd_string(payload, len, &off, &addr, &addr_len) || off + 4 > len)
255 uint16_t bind_port = (uint16_t)rd_u32(payload + off);
261 bound = s_chcb.
rfwd_open_cb(i, (
const char *)addr, addr_len, bind_port);
265 bound = s_chcb.
rfwd_cancel_cb(i, (
const char *)addr, addr_len, bind_port);
286 if (is_fwd && bind_port == 0)
293 wr_u32(out + 1, (uint32_t)(uint16_t)bound);
329 uint16_t orig_port, uint8_t *out,
size_t *out_len,
size_t cap)
336 int slot = chan_alloc(i);
342 const char *type =
"forwarded-tcpip";
343 size_t tl = 15, ca = strnlen(conn_addr, cap), oa = strnlen(orig_addr, cap);
346 size_t need = 1 + (4 + tl) + 12 + (4 + ca) + 4 + (4 + oa) + 4;
354 wr_u32(out + off, (uint32_t)tl);
355 memcpy(out + off + 4, type, tl);
357 wr_u32(out + off, (uint32_t)slot);
361 wr_u32(out + off, (uint32_t)ca);
362 memcpy(out + off + 4, conn_addr, ca);
364 wr_u32(out + off, conn_port);
366 wr_u32(out + off, (uint32_t)oa);
367 memcpy(out + off + 4, orig_addr, oa);
369 wr_u32(out + off, orig_port);
393 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
398 c->
peer_id = rd_u32(payload + 5);
417 SshChannel *c = chan_pending_by_id(i, rd_u32(payload + 1));
442 if (!rd_string(payload, len, &off, &type, &type_len))
450 uint32_t sender = rd_u32(payload + off);
451 uint32_t init_window = rd_u32(payload + off + 4);
452 uint32_t max_pkt = rd_u32(payload + off + 8);
455 bool is_session = (type_len == 7 && memcmp(type,
"session", 7) == 0);
456 bool is_dtcpip = (type_len == 12 && memcmp(type,
"direct-tcpip", 12) == 0);
457 if (!is_session && !is_dtcpip)
459 return build_open_failure(out, cap, sender, 3u, out_len);
463 const uint8_t *fhost =
nullptr;
464 uint32_t fhost_len = 0;
470 return build_open_failure(out, cap, sender, 1u, out_len);
472 if (!rd_string(payload, len, &off, &fhost, &fhost_len) || off + 4 > len)
476 fport = (uint16_t)rd_u32(payload + off);
479 int slot = chan_alloc(i);
482 return build_open_failure(out, cap, sender, 4u, out_len);
501 return build_open_failure(out, cap, sender, 2u, out_len);
504 return build_open_confirm(c, out, cap, out_len);
511#if PC_ENABLE_SSH_SFTP || PC_ENABLE_SSH_SCP
517void classify_file_transfer_request(uint8_t i,
SshChannel *c,
const uint8_t *rtype, uint32_t rtype_len,
518 const uint8_t *payload,
size_t len,
size_t *off,
bool *accept)
520#if !PC_ENABLE_SSH_SFTP
523#if PC_ENABLE_SSH_SFTP
525 if (rtype_len == 9 && memcmp(rtype,
"subsystem", 9) == 0)
527 const uint8_t *arg =
nullptr;
528 uint32_t arg_len = 0;
529 if (rd_string(payload, len, off, &arg, &arg_len) && arg_len == 4 && memcmp(arg,
"sftp", 4) == 0)
533 if (s_chcb.pc_sftp_open_cb)
535 s_chcb.pc_sftp_open_cb(i, c->
local_id);
542 if (rtype_len == 4 && memcmp(rtype,
"exec", 4) == 0)
544 const uint8_t *arg =
nullptr;
545 uint32_t arg_len = 0;
546 if (rd_string(payload, len, off, &arg, &arg_len) && arg_len >= 4 && memcmp(arg,
"scp ", 4) == 0)
549 if (s_chcb.pc_scp_open_cb)
551 s_chcb.pc_scp_open_cb(i, c->
local_id, (
const char *)arg, arg_len);
574 uint32_t recipient = rd_u32(payload + off);
576 const uint8_t *rtype;
578 if (!rd_string(payload, len, &off, &rtype, &rtype_len))
586 bool want_reply = payload[off++] != 0;
595 (rtype_len == 5 && memcmp(rtype,
"shell", 5) == 0) || (rtype_len == 4 && memcmp(rtype,
"exec", 4) == 0) ||
596 (rtype_len == 7 && memcmp(rtype,
"pty-req", 7) == 0) || (rtype_len == 3 && memcmp(rtype,
"env", 3) == 0);
598#if PC_ENABLE_SSH_SFTP || PC_ENABLE_SSH_SCP
599 classify_file_transfer_request(i, c, rtype, rtype_len, payload, len, &off, &accept);
634 uint32_t recipient = rd_u32(payload + off);
638 if (!rd_string(payload, len, &off, &data, &dlen))
665#if PC_ENABLE_SSH_SFTP
667 if (s_chcb.pc_sftp_data_cb)
669 s_chcb.pc_sftp_data_cb(i, c->
local_id, data, dlen);
675 if (s_chcb.pc_scp_data_cb)
677 s_chcb.pc_scp_data_cb(i, c->
local_id, data, dlen);
698 wr_u32(out + 5, add);
711 size_t *out_len,
size_t cap)
722 if (cap < 1 + 4 + 4 + len)
729 wr_u32(out + 5, (uint32_t)len);
730 memcpy(out + 9, data, len);
746 SshChannel *c = chan_by_id(i, rd_u32(payload + 1));
751 uint32_t add = rd_u32(payload + 5);
754 c->
peer_window = (w + add < w) ? 0xFFFFFFFFu : (w + add);
764static int build_close_chan(
SshChannel *c, uint8_t *out,
size_t *out_len,
size_t cap)
772 wr_u32(out + 1, peer);
774 wr_u32(out + 6, peer);
786 return build_close_chan(chan_by_id(i, channel), out, out_len, cap);
797 return build_close_chan(chan_by_id(i, rd_u32(payload + 1)), out, out_len, cap);
#define PC_SSH_MAX_CHANNELS
#define SSH_CHAN_WINDOW
Initial receive window the SSH server advertises (RFC 4254 §5.1).
#define SSH_CHAN_MAX_PACKET
Maximum SSH channel data payload the server advertises it can receive per message.
int pc_ssh_channel_build_close(uint8_t i, uint32_t channel, uint8_t *out, size_t *out_len, size_t cap)
Build SSH_MSG_CHANNEL_EOF + SSH_MSG_CHANNEL_CLOSE for channel channel and mark it closed.
int pc_ssh_channel_handle_window_adjust(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_WINDOW_ADJUST (grows the peer window).
int pc_ssh_channel_open_forwarded(uint8_t i, const char *conn_addr, uint16_t conn_port, const char *orig_addr, uint16_t orig_port, uint8_t *out, size_t *out_len, size_t cap)
Open a server-initiated "forwarded-tcpip" channel (RFC 4254 §7.2, ssh -R).
int pc_ssh_channel_handle_close(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle an inbound SSH_MSG_CHANNEL_CLOSE: route to the recipient channel, reply with EOF + CLOSE,...
int pc_ssh_channel_handle_open(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_OPEN and emit CHANNEL_OPEN_CONFIRMATION.
int pc_ssh_channel_handle_data(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_DATA: bounds-check, update the window, and invoke the data callback....
void pc_ssh_channel_set_forward_data_cb(SshForwardDataCb cb)
Install the direct-tcpip forward inbound-data callback.
int ssh_global_request_handle(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_GLOBAL_REQUEST (RFC 4254 §4).
void pc_ssh_channel_init(uint8_t i)
Reset channel state for slot i.
void pc_ssh_channel_set_data_cb(SshChannelDataCb cb)
Install the inbound-data callback (session channels).
void pc_ssh_channel_set_forward_confirm_cb(SshForwardConfirmCb cb)
Install the forwarded-tcpip open-confirmation callback (opt-in, ssh -R).
void pc_ssh_channel_set_rforward_open_cb(SshRemoteForwardOpenCb cb)
Install the remote-forward (ssh -R) open-policy callback (opt-in).
int pc_ssh_channel_build_data(uint8_t i, uint32_t channel, const uint8_t *data, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Build an SSH_MSG_CHANNEL_DATA message carrying data to the client on channel channel (a local channel...
void pc_ssh_channel_set_rforward_cancel_cb(SshRemoteForwardCancelCb cb)
Install the remote-forward (ssh -R) cancel callback (opt-in).
int pc_ssh_channel_handle_open_confirm(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_OPEN_CONFIRMATION for a channel we opened (ssh -R).
SshChannel ssh_chan[MAX_SSH_CONNS][PC_SSH_MAX_CHANNELS]
Channel pool: PC_SSH_MAX_CHANNELS channels per SSH connection (BSS). Owned by this layer; src/ code r...
int pc_ssh_channel_handle_open_failure(uint8_t i, const uint8_t *payload, size_t len)
Handle SSH_MSG_CHANNEL_OPEN_FAILURE for a channel we opened (ssh -R).
int pc_ssh_channel_handle_request(uint8_t i, const uint8_t *payload, size_t len, uint8_t *out, size_t *out_len, size_t cap)
Handle SSH_MSG_CHANNEL_REQUEST.
void pc_ssh_channel_set_forward_open_cb(SshForwardOpenCb cb)
Install the direct-tcpip forward open-policy callback (opt-in).
SSH connection protocol - multiplexed "session" channels (RFC 4254).
int(*)(uint8_t slot, uint32_t channel, const char *host, size_t host_len, uint16_t port) SshForwardOpenCb
"direct-tcpip" forward request: a client asked the server to open a TCP connection to host : port (ss...
int(*)(uint8_t slot, const char *bind_addr, size_t addr_len, uint16_t bind_port) SshRemoteForwardOpenCb
"tcpip-forward" remote-forward request (ssh -R): the client asks the server to listen on bind_addr : ...
@ SSH_CHAN_DIRECT_TCPIP
"direct-tcpip" - client-initiated TCP forward (ssh -L)
@ SSH_CHAN_SESSION
"session" - shell / exec / data
@ SSH_CHAN_FORWARDED_TCPIP
"forwarded-tcpip" - server-initiated TCP forward (ssh -R)
@ SSH_CHAN_SCP
a "session" running an exec "scp …" (PC_ENABLE_SSH_SCP)
@ SSH_CHAN_SFTP
a "session" running the "sftp" subsystem (PC_ENABLE_SSH_SFTP)
int(*)(uint8_t slot, const char *bind_addr, size_t addr_len, uint16_t bind_port) SshRemoteForwardCancelCb
"cancel-tcpip-forward" request (RFC 4254 §7.1): drop a remote forward.
void(*)(uint8_t slot, uint32_t channel, const uint8_t *data, size_t len) SshForwardDataCb
Inbound data on a direct-tcpip channel (the owner writes it to the forwarded TCP socket)....
void(*)(uint8_t slot, uint32_t channel, bool ok) SshForwardConfirmCb
Result of the client's reply to a server-initiated forwarded-tcpip channel: ok = true on CHANNEL_OPEN...
void(*)(uint8_t slot, uint32_t channel, const uint8_t *data, size_t len) SshChannelDataCb
Application callback for inbound channel data (raw bytes), tagged with the channel id it arrived on.
SSH binary packet protocol: framing, AES-256-CTR encryption, HMAC-SHA2-256 integrity,...
#define SSH_MSG_CHANNEL_SUCCESS
#define SSH_MSG_CHANNEL_EOF
#define SSH_MSG_CHANNEL_DATA
#define SSH_MSG_CHANNEL_FAILURE
#define SSH_MSG_CHANNEL_OPEN_CONFIRM
#define SSH_MSG_CHANNEL_OPEN
#define SSH_MSG_CHANNEL_REQUEST
#define SSH_MSG_CHANNEL_OPEN_FAILURE
#define SSH_MSG_REQUEST_SUCCESS
#define SSH_MSG_CHANNEL_CLOSE
#define SSH_MSG_REQUEST_FAILURE
#define SSH_MSG_GLOBAL_REQUEST
#define SSH_MSG_CHANNEL_WINDOW_ADJUST
SshForwardOpenCb forward_open_cb
SshRemoteForwardCancelCb rfwd_cancel_cb
SshForwardConfirmCb forward_confirm_cb
SshForwardDataCb forward_data_cb
SshRemoteForwardOpenCb rfwd_open_cb
Per-connection channel state.
bool open
True once the channel is confirmed open both ways.
uint32_t peer_max_pkt
Client's maximum packet size.
uint32_t peer_window
Bytes we may still send to the client.
SshChanType type
session, direct-tcpip, or forwarded-tcpip.
bool pending
True for a server-initiated channel we opened, awaiting the client's confirmation.
uint32_t local_id
Our channel id (== slot index).
uint32_t local_window
Bytes we may still receive before WINDOW_ADJUST.
uint32_t peer_id
Client's channel id.