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