ssh_channel.c
Go to the documentation of this file.
1 /**
2  * @file ssh_channel.c
3  * @brief SSH channel management
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2019-2026 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneSSH Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 2.6.2
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL SSH_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "ssh/ssh_connection.h"
37 #include "ssh/ssh_request.h"
38 #include "ssh/ssh_channel.h"
39 #include "ssh/ssh_packet.h"
40 #include "ssh/ssh_misc.h"
41 #include "debug.h"
42 
43 //Check SSH stack configuration
44 #if (SSH_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Get the channel that matches the specified channel number
49  * @param[in] connection Pointer to the SSH connection
50  * @param[in] localChannelNum Local channel number
51  * @return Handle referencing an SSH channel
52  **/
53 
54 SshChannel *sshGetChannel(SshConnection *connection, uint32_t localChannelNum)
55 {
56  uint_t i;
57  SshContext *context;
58  SshChannel *channel;
59 
60  //Point to the SSH context
61  context = connection->context;
62  //Initialize handle
63  channel = NULL;
64 
65  //Loop through SSH channels
66  for(i = 0; i < context->numChannels; i++)
67  {
68  //Multiple channels can be multiplexed into a single connection
69  if(context->channels[i].state != SSH_CHANNEL_STATE_UNUSED &&
70  context->channels[i].connection == connection)
71  {
72  //Compare channel numbers
73  if(context->channels[i].localChannelNum == localChannelNum)
74  {
75  //The current channel matches the specified channel number
76  channel = &context->channels[i];
77  break;
78  }
79  }
80  }
81 
82  //Return channel handle
83  return channel;
84 }
85 
86 
87 /**
88  * @brief Generate a local channel number
89  * @param[in] connection Pointer to the SSH connection
90  * @return Channel number
91  **/
92 
94 {
95  uint_t i;
96  bool_t valid;
97  SshContext *context;
98  SshChannel *channel;
99 
100  //Point to the SSH context
101  context = connection->context;
102 
103  //When the implementation wish to open a new channel, it allocates a
104  //local number for the channel (refer to RFC 4254, section 5.1)
105  for(valid = FALSE; !valid; )
106  {
107  //Generate a new channel number
108  connection->localChannelNum++;
109 
110  //Loop through SSH channels
111  for(i = 0, valid = TRUE; i < context->numChannels && valid; i++)
112  {
113  //Point to the current SSH channel
114  channel = &context->channels[i];
115 
116  //Multiple channels can be multiplexed into a single connection
117  if(channel->state != SSH_CHANNEL_STATE_UNUSED &&
118  channel->connection == connection)
119  {
120  //Compare channel numbers
121  if(channel->localChannelNum == connection->localChannelNum)
122  {
123  //The channel number is already in use
124  valid = FALSE;
125  }
126  }
127  }
128  }
129 
130  //Return channel number
131  return connection->localChannelNum;
132 }
133 
134 
135 /**
136  * @brief Check remote channel number
137  * @param[in] connection Pointer to the SSH connection
138  * @param[in] remoteChannelNum Remote channel number
139  * @return TRUE if the channel number is valid, else FALSE
140  **/
141 
143  uint32_t remoteChannelNum)
144 {
145  uint_t i;
146  bool_t valid;
147  SshContext *context;
148  SshChannel *channel;
149 
150  //Point to the SSH context
151  context = connection->context;
152 
153  //Loop through SSH channels
154  for(i = 0, valid = TRUE; i < context->numChannels && valid; i++)
155  {
156  //Point to the current SSH channel
157  channel = &context->channels[i];
158 
159  //Check the state of the channel
160  if(channel->state == SSH_CHANNEL_STATE_OPEN)
161  {
162  //Multiple channels can be multiplexed into a single connection
163  if(channel->connection == connection)
164  {
165  //Each side must associate a unique number to the channel
166  if(channel->remoteChannelNum == remoteChannelNum)
167  {
168  //The channel number is already in use
169  valid = FALSE;
170  }
171  }
172  }
173  }
174 
175  //Return TRUE if the channel number is valid
176  return valid;
177 }
178 
179 
180 /**
181  * @brief Register channel events
182  * @param[in] channel Handle referencing an SSH channel
183  * @param[in] eventDesc SSH channel events to be registered
184  **/
185 
187 {
188  //Acquire exclusive access to the SSH context
189  osAcquireMutex(&channel->context->mutex);
190 
191  //Check the state of the channel
192  if(channel->rxWindowSizeInc >= (SSH_CHANNEL_BUFFER_SIZE / 2))
193  {
194  //An SSH_MSG_CHANNEL_WINDOW_ADJUST message is pending for transmission
195  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
196  }
197  else if(channel->txBuffer.length > 0)
198  {
199  //Channels are flow-controlled. No data may be sent to a channel until
200  //a message is received to indicate that window space is available
201  if(channel->txWindowSize > 0)
202  {
203  //An SSH_MSG_CHANNEL_DATA message is pending for transmission
204  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
205  }
206  }
207  else if(channel->eofRequest && !channel->eofSent)
208  {
209  //An SSH_MSG_CHANNEL_EOF message is pending for transmission
210  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
211  }
212  else if(channel->closeRequest && !channel->closeSent)
213  {
214  //An SSH_MSG_CHANNEL_CLOSE message is pending for transmission
215  eventDesc->eventMask = SOCKET_EVENT_TX_READY;
216  }
217  else
218  {
219  //Just for sanity
220  }
221 
222  //Release exclusive access to the SSH context
223  osReleaseMutex(&channel->context->mutex);
224 }
225 
226 
227 /**
228  * @brief Channel event handler
229  * @param[in] channel Handle referencing an SSH channel
230  * @return Error code
231  **/
232 
234 {
235  error_t error;
236 
237  //Initialize status code
238  error = NO_ERROR;
239 
240  //Acquire exclusive access to the SSH context
241  osAcquireMutex(&channel->context->mutex);
242 
243  //Check the state of the channel
244  if(channel->rxWindowSizeInc >= (SSH_CHANNEL_BUFFER_SIZE / 2))
245  {
246  //Update flow-control window
247  channel->rxWindowSize += channel->rxWindowSizeInc;
248 
249  //Send an SSH_MSG_CHANNEL_WINDOW_ADJUST message
250  error = sshSendChannelWindowAdjust(channel, channel->rxWindowSizeInc);
251 
252  //Check status code
253  if(!error)
254  {
255  //Clear window size increment
256  channel->rxWindowSizeInc = 0;
257  }
258  }
259  else if(channel->txBuffer.length > 0)
260  {
261  size_t n;
263 
264  //Point to the transmit buffer
265  txBuffer = &channel->txBuffer;
266 
267  //Get the number of bytes available in the send buffer
268  n = txBuffer->length;
269 
270  //Limit the number of bytes to send at a time
272 
273  //The maximum amount of data allowed is determined by the maximum packet
274  //size for the channel, and the current window size, whichever is smaller
275  //(refer to RFC 4254, section 5.2)
276  n = MIN(n, channel->maxPacketSize);
277  n = MIN(n, channel->txWindowSize);
278 
279  //Channels are flow-controlled. No data may be sent to a channel until
280  //a message is received to indicate that window space is available
281  if(n > 0)
282  {
283  //Send an SSH_MSG_CHANNEL_DATA message
284  error = sshSendChannelData(channel, n);
285 
286  //Check status code
287  if(!error)
288  {
289  //Advance read pointer
290  txBuffer->readPos += n;
291 
292  //Wrap around if necessary
293  if(txBuffer->readPos >= SSH_CHANNEL_BUFFER_SIZE)
294  {
295  txBuffer->readPos -= SSH_CHANNEL_BUFFER_SIZE;
296  }
297 
298  //Update buffer length
299  txBuffer->length -= n;
300  //Update flow-control window
301  channel->txWindowSize -= n;
302 
303  //Update channel related events
304  sshUpdateChannelEvents(channel);
305  }
306  }
307  }
308  else if(channel->eofRequest && !channel->eofSent)
309  {
310  //Send an SSH_MSG_CHANNEL_EOF message
311  error = sshSendChannelEof(channel);
312  }
313  else if(channel->closeRequest && !channel->closeSent)
314  {
315  //When the command terminates, a message can be sent to return the exit
316  //status of the command (refer to RFC 4254, section 6.10)
317  if(channel->exitStatus >= 0 && !channel->exitStatusSent)
318  {
319  SshExitStatusParams requestParams;
320 
321  //A zero 'exit-status' means that the command terminated successfully
322  requestParams.exitStatus = channel->exitStatus;
323 
324  //Send an SSH_MSG_CHANNEL_REQUEST message
325  error = sshSendChannelRequest(channel, "exit-status", &requestParams,
326  FALSE);
327 
328  //Check status code
329  if(!error)
330  {
331  //The channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after
332  //this message
333  channel->exitStatusSent = TRUE;
334  }
335  }
336  else
337  {
338  //Send an SSH_MSG_CHANNEL_CLOSE message
339  error = sshSendChannelClose(channel);
340 
341  //Check status code
342  if(!error)
343  {
344  //Update channel related events
345  sshUpdateChannelEvents(channel);
346  }
347  }
348  }
349  else
350  {
351  //Just for sanity
352  }
353 
354  //Release exclusive access to the SSH context
355  osReleaseMutex(&channel->context->mutex);
356 
357  //Return status code
358  return error;
359 }
360 
361 
362 /**
363  * @brief Wait for a particular SSH channel event
364  * @param[in] channel Pointer to the SSH channel
365  * @param[in] eventMask Logic OR of all the events that will complete the wait
366  * @param[in] timeout Maximum time to wait
367  * @return Logic OR of all the events that satisfied the wait
368  **/
369 
371  systime_t timeout)
372 {
373  uint_t eventFlags = 0;
374 
375  //Valid channel?
376  if(channel != NULL)
377  {
378  //Only one of the events listed here may complete the wait
379  channel->eventMask = eventMask;
380 
381  //Update channel related events
382  sshUpdateChannelEvents(channel);
383 
384  //No event is signaled?
385  if(!channel->eventFlags)
386  {
387  //Reset the event object
388  osResetEvent(&channel->event);
389  //Release exclusive access to the SSH context
390  osReleaseMutex(&channel->context->mutex);
391  //Wait until an event is triggered
392  osWaitForEvent(&channel->event, timeout);
393  //Acquire exclusive access to the SSH context
394  osAcquireMutex(&channel->context->mutex);
395  }
396 
397  //Retrieve the list of events that satisfied the wait
398  eventFlags = channel->eventFlags;
399  }
400 
401  //Return active events
402  return eventFlags;
403 }
404 
405 
406 /**
407  * @brief Update SSH channel related events
408  * @param[in] channel Pointer to the SSH channel
409  **/
410 
412 {
413  //Valid channel?
414  if(channel->state != SSH_CHANNEL_STATE_UNUSED)
415  {
416  //Clear event flags
417  channel->eventFlags = 0;
418 
419  //Check whether the channel is closed
420  if(channel->state == SSH_CHANNEL_STATE_CLOSED)
421  {
422  channel->eventFlags |= SSH_CHANNEL_EVENT_CLOSED;
423  }
424 
425  //Handle TX specific events
426  if(channel->state == SSH_CHANNEL_STATE_RESERVED)
427  {
428  //The channel is not writable
429  }
430  else if(channel->state == SSH_CHANNEL_STATE_OPEN)
431  {
432  //Check whether the send buffer is full or not
433  if(channel->txBuffer.length < SSH_CHANNEL_BUFFER_SIZE)
434  {
435  channel->eventFlags |= SSH_CHANNEL_EVENT_TX_READY;
436  }
437  }
438  else
439  {
440  //Unblock user task if the channel is closed
441  channel->eventFlags |= SSH_CHANNEL_EVENT_TX_READY;
442  }
443 
444  //Handle RX specific events
445  if(channel->state == SSH_CHANNEL_STATE_RESERVED)
446  {
447  //The channel is not readable
448  }
449  else if(channel->state == SSH_CHANNEL_STATE_OPEN)
450  {
451  //Any data pending in the receive buffer?
452  if(channel->rxBuffer.length > channel->rxBuffer.threshold ||
453  channel->eofReceived)
454  {
455  channel->eventFlags |= SSH_CHANNEL_EVENT_RX_READY;
456  }
457  }
458  else
459  {
460  //Unblock user task if the channel is closed
461  channel->eventFlags |= SSH_CHANNEL_EVENT_RX_READY;
462  }
463 
464  //Mask unused events
465  channel->eventFlags &= channel->eventMask;
466 
467  //Any event to signal?
468  if(channel->eventFlags)
469  {
470  //Unblock I/O operations currently in waiting state
471  osSetEvent(&channel->event);
472 
473  //Set user event to signaled state if necessary
474  if(channel->userEvent != NULL)
475  {
476  osSetEvent(channel->userEvent);
477  }
478  }
479  }
480 }
481 
482 
483 /**
484  * @brief Process incoming data
485  * @param[in] channel Pointer to the SSH channel
486  * @param[in] data Pointer to the payload data
487  * @param[in] length Length of the payload data, in bytes
488  * @return Error code
489  **/
490 
491 error_t sshProcessChannelData(SshChannel *channel, const uint8_t *data,
492  size_t length)
493 {
494  error_t error;
496 
497  //Point to the receive buffer
498  rxBuffer = &channel->rxBuffer;
499 
500  //Make sure the receiver is able to accept the data
501  if(length > channel->rxWindowSize)
502  {
503  //Report a flow control error
504  error = ERROR_FLOW_CONTROL;
505  }
506  else if((rxBuffer->length + length) > SSH_CHANNEL_BUFFER_SIZE)
507  {
508  //Report a flow control error
509  error = ERROR_FLOW_CONTROL;
510  }
511  else
512  {
513  //Update channel flow-control window
514  channel->rxWindowSize -= length;
515 
516  //Check whether the specified data crosses channel buffer boundaries
517  if((rxBuffer->writePos + length) <= SSH_CHANNEL_BUFFER_SIZE)
518  {
519  //Copy the data
520  osMemcpy(rxBuffer->data + rxBuffer->writePos, data, length);
521  }
522  else
523  {
524  //Copy the first part of the data
525  osMemcpy(rxBuffer->data + rxBuffer->writePos, data,
526  SSH_CHANNEL_BUFFER_SIZE - rxBuffer->writePos);
527 
528  //Wrap around to the beginning of the circular buffer
530  rxBuffer->writePos + length - SSH_CHANNEL_BUFFER_SIZE);
531  }
532 
533  //Advance write position
534  rxBuffer->writePos += length;
535 
536  //Wrap around if necessary
537  if(rxBuffer->writePos >= SSH_CHANNEL_BUFFER_SIZE)
538  {
539  rxBuffer->writePos -= SSH_CHANNEL_BUFFER_SIZE;
540  }
541 
542  //Update buffer length
543  rxBuffer->length += length;
544 
545  //Update channel related events
546  sshUpdateChannelEvents(channel);
547 
548  //Successful processing
549  error = NO_ERROR;
550  }
551 
552  //Return status code
553  return error;
554 }
555 
556 
557 /**
558  * @brief Process incoming extended data
559  * @param[in] channel Pointer to the SSH channel
560  * @param[in] type Extended data type
561  * @param[in] data Pointer to the extended data
562  * @param[in] length Length of the extended data, in bytes
563  * @return Error code
564  **/
565 
567  const uint8_t *data, size_t length)
568 {
569  error_t error;
570 
571  //Make sure the receiver is able to accept the data
572  if(length > channel->rxWindowSize)
573  {
574  //Report a flow control error
575  error = ERROR_FLOW_CONTROL;
576  }
577  else
578  {
579  //Data sent with SSH_MSG_CHANNEL_EXTENDED_DATA messages consumes the
580  //same window as ordinary data
581  channel->rxWindowSize -= length;
582 
583  //Update flow-control window
584  sshUpdateChannelWindow(channel, length);
585 
586  //Successful processing
587  error = NO_ERROR;
588  }
589 
590  //Return status code
591  return error;
592 }
593 
594 
595 /**
596  * @brief Update channel flow-control window
597  * @param[in] channel Pointer to the SSH channel
598  * @param[in] windowSizeInc Window size increment
599  * @return Error code
600  **/
601 
602 error_t sshUpdateChannelWindow(SshChannel *channel, uint32_t windowSizeInc)
603 {
604  //Update window size increment
605  channel->rxWindowSizeInc += windowSizeInc;
606 
607  //Notify the SSH core that the flow-control window should be updated
608  sshNotifyEvent(channel->context);
609 
610  //Return status code
611  return NO_ERROR;
612 }
613 
614 #endif
SSH channel management.
SshChannel * sshGetChannel(SshConnection *connection, uint32_t localChannelNum)
Get the channel that matches the specified channel number.
Definition: ssh_channel.c:54
int bool_t
Definition: compiler_port.h:63
error_t sshProcessChannelExtendedData(SshChannel *channel, uint32_t type, const uint8_t *data, size_t length)
Process incoming extended data.
Definition: ssh_channel.c:566
void sshUpdateChannelEvents(SshChannel *channel)
Update SSH channel related events.
Definition: ssh_channel.c:411
SSH connection protocol.
#define TRUE
Definition: os_port.h:50
uint8_t data[]
Definition: ethernet.h:224
#define SSH_CHANNEL_DATA_MSG_HEADER_SIZE
Definition: ssh_packet.h:44
uint8_t type
Definition: coap_common.h:176
SSH channel buffer.
Definition: ssh.h:1369
error_t sshSendChannelWindowAdjust(SshChannel *channel, size_t windowSizeInc)
Send SSH_MSG_CHANNEL_WINDOW_ADJUST message.
Structure describing socket events.
Definition: socket.h:433
@ SSH_CHANNEL_EVENT_CLOSED
Definition: ssh.h:1127
@ SSH_CHANNEL_EVENT_TX_READY
Definition: ssh.h:1128
#define FALSE
Definition: os_port.h:46
void osResetEvent(OsEvent *event)
Set the specified event object to the nonsignaled state.
#define SshContext
Definition: ssh.h:892
#define osMemcpy(dest, src, length)
Definition: os_port.h:144
error_t
Error codes.
Definition: error.h:43
@ SOCKET_EVENT_TX_READY
Definition: socket.h:175
error_t sshSendChannelEof(SshChannel *channel)
Send SSH_MSG_CHANNEL_EOF message.
#define txBuffer
uint32_t sshAllocateLocalChannelNum(SshConnection *connection)
Generate a local channel number.
Definition: ssh_channel.c:93
@ ERROR_FLOW_CONTROL
Definition: error.h:279
@ SSH_CHANNEL_STATE_OPEN
Definition: ssh.h:1101
#define SSH_CHANNEL_BUFFER_SIZE
Definition: ssh.h:241
uint8_t length
Definition: tcp.h:375
#define MIN(a, b)
Definition: os_port.h:63
error_t sshProcessChannelData(SshChannel *channel, const uint8_t *data, size_t length)
Process incoming data.
Definition: ssh_channel.c:491
error_t sshProcessChannelEvents(SshChannel *channel)
Channel event handler.
Definition: ssh_channel.c:233
#define rxBuffer
void sshNotifyEvent(SshContext *context)
Notify the SSH context that event is occurring.
Definition: ssh_misc.c:721
uint32_t systime_t
System time.
"exit-status" channel request parameters
Definition: ssh_request.h:172
#define SSH_MAX_PACKET_SIZE
Definition: ssh.h:234
uint8_t n
bool_t osWaitForEvent(OsEvent *event, systime_t timeout)
Wait until the specified event is in the signaled state.
@ SSH_CHANNEL_EVENT_RX_READY
Definition: ssh.h:1132
#define SshConnection
Definition: ssh.h:896
void osAcquireMutex(OsMutex *mutex)
Acquire ownership of the specified mutex object.
void osReleaseMutex(OsMutex *mutex)
Release ownership of the specified mutex object.
@ SSH_CHANNEL_STATE_RESERVED
Definition: ssh.h:1100
SSH helper functions.
error_t sshUpdateChannelWindow(SshChannel *channel, uint32_t windowSizeInc)
Update channel flow-control window.
Definition: ssh_channel.c:602
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
SSH packet encryption/decryption.
void sshRegisterChannelEvents(SshChannel *channel, SocketEventDesc *eventDesc)
Register channel events.
Definition: ssh_channel.c:186
error_t sshSendChannelRequest(SshChannel *channel, const char_t *requestType, const void *requestParams, bool_t wantReply)
Send SSH_MSG_CHANNEL_REQUEST message.
Definition: ssh_request.c:179
unsigned int uint_t
Definition: compiler_port.h:57
@ SSH_CHANNEL_STATE_CLOSED
Definition: ssh.h:1102
Secure Shell (SSH)
@ SSH_CHANNEL_STATE_UNUSED
Definition: ssh.h:1099
Global request and channel request handling.
uint_t eventMask
Requested events.
Definition: socket.h:435
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define SshChannel
Definition: ssh.h:900
bool_t sshCheckRemoteChannelNum(SshConnection *connection, uint32_t remoteChannelNum)
Check remote channel number.
Definition: ssh_channel.c:142
error_t sshSendChannelClose(SshChannel *channel)
Send SSH_MSG_CHANNEL_CLOSE message.
error_t sshSendChannelData(SshChannel *channel, size_t dataLen)
Send SSH_MSG_CHANNEL_DATA message.
uint_t sshWaitForChannelEvents(SshChannel *channel, uint_t eventMask, systime_t timeout)
Wait for a particular SSH channel event.
Definition: ssh_channel.c:370