ssh_transport.c
Go to the documentation of this file.
1 /**
2  * @file ssh_transport.c
3  * @brief SSH transport layer protocol
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_transport.h"
37 #include "ssh/ssh_packet.h"
38 #include "ssh/ssh_misc.h"
39 #include "debug.h"
40 
41 //Check SSH stack configuration
42 #if (SSH_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Send identification string
47  * @param[in] connection Pointer to the SSH connection
48  * @return Error code
49  **/
50 
52 {
53  size_t length;
54 
55  //Check whether SSH operates as a client or a server
56  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
57  {
58  //Format V_C (client's identification string)
59  length = osSprintf(connection->clientId, "SSH-2.0-CycloneSSH_%s",
61 
62  //Copy the resulting string
63  osMemcpy(connection->buffer, connection->clientId, length);
64  }
65  else
66  {
67  //Format V_S (server's identification string)
68  length = osSprintf(connection->serverId, "SSH-2.0-CycloneSSH_%s",
70 
71  //Copy the resulting string
72  osMemcpy(connection->buffer, connection->serverId, length);
73  }
74 
75  //The identification string must be terminated by a single CR and a
76  //single LF character (refer to RFC 4253, section 4.2)
77  connection->buffer[length++] = '\r';
78  connection->buffer[length++] = '\n';
79 
80  //Save the length of the identification string
81  connection->txBufferLen = length;
82  connection->txBufferPos = 0;
83 
84  //Check whether SSH operates as a client or a server
85  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
86  {
87  //Debug message
88  TRACE_INFO("Sending client ID string (%" PRIuSIZE " bytes)...\r\n", length);
89  TRACE_INFO(" %s\r\n", connection->clientId);
90 
91  //Wait for the server's identification string
92  connection->state = SSH_CONN_STATE_SERVER_ID;
93  }
94  else
95  {
96  //Debug message
97  TRACE_INFO("Sending server ID string (%" PRIuSIZE " bytes)...\r\n", length);
98  TRACE_INFO(" %s\r\n", connection->serverId);
99 
100  //Wait for the client's identification string
101  connection->state = SSH_CONN_STATE_CLIENT_ID;
102  }
103 
104  //Successful processing
105  return NO_ERROR;
106 }
107 
108 
109 /**
110  * @brief Send SSH_MSG_SERVICE_REQUEST message
111  * @param[in] connection Pointer to the SSH connection
112  * @return Error code
113  **/
114 
116 {
117  error_t error;
118  size_t length;
119  uint8_t *message;
120 
121  //Point to the buffer where to format the message
122  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
123 
124  //Format SSH_MSG_SERVICE_REQUEST message
125  error = sshFormatServiceRequest(connection, message, &length);
126 
127  //Check status code
128  if(!error)
129  {
130  //Debug message
131  TRACE_INFO("Sending SSH_MSG_SERVICE_REQUEST message (%" PRIuSIZE " bytes)...\r\n", length);
133 
134  //Send message
135  error = sshSendPacket(connection, message, length);
136  }
137 
138  //Check status code
139  if(!error)
140  {
141 #if (SSH_EXT_INFO_SUPPORT == ENABLED)
142  //If the client offers "ext-info-c", it must be prepared to accept an
143  //SSH_MSG_EXT_INFO message from the server
144  connection->state = SSH_CONN_STATE_SERVER_EXT_INFO_1;
145 #else
146  //If the server supports the service (and permits the client to use
147  //it), it must respond with an SSH_MSG_SERVICE_ACCEPT message
148  connection->state = SSH_CONN_STATE_SERVICE_ACCEPT;
149 #endif
150  }
151 
152  //Return status code
153  return error;
154 }
155 
156 
157 /**
158  * @brief Send SSH_MSG_SERVICE_ACCEPT message
159  * @param[in] connection Pointer to the SSH connection
160  * @param[in] serviceName NULL-terminating string that contains the service name
161  * @return Error code
162  **/
163 
165  const char_t *serviceName)
166 {
167  error_t error;
168  size_t length;
169  uint8_t *message;
170 
171  //Point to the buffer where to format the message
172  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
173 
174  //Format SSH_MSG_SERVICE_ACCEPT message
175  error = sshFormatServiceAccept(connection, serviceName, message, &length);
176 
177  //Check status code
178  if(!error)
179  {
180  //Debug message
181  TRACE_INFO("Sending SSH_MSG_SERVICE_ACCEPT message (%" PRIuSIZE " bytes)...\r\n", length);
183 
184  //Send message
185  error = sshSendPacket(connection, message, length);
186  }
187 
188  //Check status code
189  if(!error)
190  {
191  //The authentication protocol is intended to be run over the SSH transport
192  //layer protocol (refer to RFC 4252, section 1)
193  connection->state = SSH_CONN_STATE_USER_AUTH_REQUEST;
194  }
195 
196  //Return status code
197  return error;
198 }
199 
200 
201 /**
202  * @brief Send SSH_MSG_DISCONNECT message
203  * @param[in] connection Pointer to the SSH connection
204  * @param[in] reasonCode Reason in a machine-readable format
205  * @param[in] description Specific explanation in a human-readable form
206  * @return Error code
207  **/
208 
210  uint32_t reasonCode, const char_t *description)
211 {
212  error_t error;
213  size_t length;
214  uint8_t *message;
215 
216  //Point to the buffer where to format the message
217  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
218 
219  //Format SSH_MSG_DISCONNECT message
220  error = sshFormatDisconnect(connection, reasonCode, description, message,
221  &length);
222 
223  //Check status code
224  if(!error)
225  {
226  //Debug message
227  TRACE_INFO("Sending SSH_MSG_DISCONNECT message (%" PRIuSIZE " bytes)...\r\n", length);
229 
230  //Send message
231  error = sshSendPacket(connection, message, length);
232  }
233 
234  //Check status code
235  if(!error)
236  {
237  //An SSH_MSG_DISCONNECT message has been successfully sent
238  connection->disconnectSent = TRUE;
239 
240  //This message causes immediate termination of the connection
241  connection->state = SSH_CONN_STATE_DISCONNECT;
242  }
243 
244  //Return status code
245  return error;
246 }
247 
248 
249 /**
250  * @brief Send SSH_MSG_UNIMPLEMENTED message
251  * @param[in] connection Pointer to the SSH connection
252  * @param[in] packetSeqNum Packet sequence number of rejected message
253  * @return Error code
254  **/
255 
257  const uint8_t *packetSeqNum)
258 {
259  error_t error;
260  size_t length;
261  uint8_t *message;
262 
263  //Point to the buffer where to format the message
264  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
265 
266  //Format SSH_MSG_UNIMPLEMENTED message
267  error = sshFormatUnimplemented(connection, packetSeqNum, message, &length);
268 
269  //Check status code
270  if(!error)
271  {
272  //Debug message
273  TRACE_INFO("Sending SSH_MSG_UNIMPLEMENTED message (%" PRIuSIZE " bytes)...\r\n", length);
275 
276  //Send message
277  error = sshSendPacket(connection, message, length);
278  }
279 
280  //Return status code
281  return error;
282 }
283 
284 
285 /**
286  * @brief Format SSH_MSG_SERVICE_REQUEST message
287  * @param[in] connection Pointer to the SSH connection
288  * @param[out] p Buffer where to format the message
289  * @param[out] length Length of the resulting message, in bytes
290  * @return Error code
291  **/
292 
294  size_t *length)
295 {
296  error_t error;
297  size_t n;
298 
299  //Total length of the message
300  *length = 0;
301 
302  //Set message type
304 
305  //Point to the first field of the message
306  p += sizeof(uint8_t);
307  *length += sizeof(uint8_t);
308 
309  //Copy service name
310  error = sshFormatString("ssh-userauth", p, &n);
311  //Any error to report?
312  if(error)
313  return error;
314 
315  //Total length of the message
316  *length += n;
317 
318  //Successful processing
319  return NO_ERROR;
320 }
321 
322 
323 /**
324  * @brief Format SSH_MSG_SERVICE_ACCEPT message
325  * @param[in] connection Pointer to the SSH connection
326  * @param[in] serviceName NULL-terminating string that contains the service name
327  * @param[out] p Buffer where to format the message
328  * @param[out] length Length of the resulting message, in bytes
329  * @return Error code
330  **/
331 
333  const char_t *serviceName, uint8_t *p, size_t *length)
334 {
335  error_t error;
336  size_t n;
337 
338  //Total length of the message
339  *length = 0;
340 
341  //Set message type
343 
344  //Point to the first field of the message
345  p += sizeof(uint8_t);
346  *length += sizeof(uint8_t);
347 
348  //Copy service name
349  error = sshFormatString(serviceName, p, &n);
350  //Any error to report?
351  if(error)
352  return error;
353 
354  //Total length of the message
355  *length += n;
356 
357  //Successful processing
358  return NO_ERROR;
359 }
360 
361 
362 /**
363  * @brief Format SSH_MSG_DISCONNECT message
364  * @param[in] connection Pointer to the SSH connection
365  * @param[in] reasonCode Reason in a machine-readable format
366  * @param[in] description Specific explanation in a human-readable form
367  * @param[out] p Buffer where to format the message
368  * @param[out] length Length of the resulting message, in bytes
369  * @return Error code
370  **/
371 
372 error_t sshFormatDisconnect(SshConnection *connection, uint32_t reasonCode,
373  const char_t *description, uint8_t *p, size_t *length)
374 {
375  error_t error;
376  size_t n;
377 
378  //Total length of the message
379  *length = 0;
380 
381  //Set message type
382  p[0] = SSH_MSG_DISCONNECT;
383 
384  //Point to the first field of the message
385  p += sizeof(uint8_t);
386  *length += sizeof(uint8_t);
387 
388  //Copy the reason code
389  STORE32BE(reasonCode, p);
390 
391  //Point to the next field
392  p += sizeof(uint32_t);
393  *length += sizeof(uint32_t);
394 
395  //Copy description string
396  error = sshFormatString(description, p, &n);
397  //Any error to report?
398  if(error)
399  return error;
400 
401  //Point to the next field
402  p += n;
403  *length += n;
404 
405  //Format language tag
406  error = sshFormatString("en", p, &n);
407  //Any error to report?
408  if(error)
409  return error;
410 
411  //Total length of the message
412  *length += n;
413 
414  //Successful processing
415  return NO_ERROR;
416 }
417 
418 
419 /**
420  * @brief Format SSH_MSG_UNIMPLEMENTED message
421  * @param[in] connection Pointer to the SSH connection
422  * @param[in] packetSeqNum Packet sequence number of rejected message
423  * @param[out] p Buffer where to format the message
424  * @param[out] length Length of the resulting message, in bytes
425  * @return Error code
426  **/
427 
429  const uint8_t *packetSeqNum, uint8_t *p, size_t *length)
430 {
431  //Total length of the message
432  *length = 0;
433 
434  //Set message type
436 
437  //Point to the first field of the message
438  p += sizeof(uint8_t);
439  *length += sizeof(uint8_t);
440 
441  //Copy the packet sequence number of the rejected message
442  osMemcpy(p, packetSeqNum, sizeof(uint32_t));
443 
444  //Total length of the message
445  *length += sizeof(uint32_t);
446 
447  //Successful processing
448  return NO_ERROR;
449 }
450 
451 
452 /**
453  * @brief Parse identification string
454  * @param[in] connection Pointer to the SSH connection
455  * @param[in] id Pointer to the identification string
456  * @param[in] length Length of the identification string
457  * @return Error code
458  **/
459 
460 error_t sshParseIdString(SshConnection *connection, const uint8_t *id,
461  size_t length)
462 {
463  //Check the length of the client's identification string
464  if(length < 2)
465  return ERROR_WRONG_IDENTIFIER;
466 
467  //The identification string must be terminated by a single CR and a single
468  //LF character (refer to RFC 4253, section 4.2). In practice, some SSH 2.0
469  //implementations terminate the string with a LF character only
470  if(id[length - 1] != '\n')
471  return ERROR_WRONG_IDENTIFIER;
472 
473  //Trim the trailing LF character from the string
474  length--;
475 
476  //Trim the trailing CR character from the string
477  if(id[length - 1] == '\r')
478  {
479  length--;
480  }
481 
482  //Sanity check
483  if(length > SSH_MAX_ID_LEN)
484  return ERROR_WRONG_IDENTIFIER;
485 
486  //Check whether SSH operates as a client or a server
487  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
488  {
489  //Copy the server's identification string
490  osMemcpy(connection->serverId, id, length);
491  //Properly terminate the string with a NULL character
492  connection->serverId[length] = '\0';
493 
494  //Debug message
495  TRACE_INFO("Server ID string received (%" PRIuSIZE " bytes)...\r\n", length);
496  TRACE_INFO(" %s\r\n", connection->serverId);
497 
498  //Clients using protocol 2.0 must be able to identify protocol version
499  //"1.99" as identical to "2.0" (refer to RFC 4253, section 5.1)
500  if(osStrncmp(connection->serverId, "SSH-2.0-", 8) &&
501  osStrncmp(connection->serverId, "SSH-1.99-", 9))
502  {
503  //The version advertised by the server is not supported
504  return ERROR_WRONG_IDENTIFIER;
505  }
506 
507  //Key exchange begins by each side sending a KEXINIT message
508  connection->state = SSH_CONN_STATE_CLIENT_KEX_INIT;
509  }
510  else
511  {
512  //Copy the client's identification string
513  osMemcpy(connection->clientId, id, length);
514  //Properly terminate the string with a NULL character
515  connection->clientId[length] = '\0';
516 
517  //Debug message
518  TRACE_INFO("Client ID string received (%" PRIuSIZE " bytes)...\r\n", length);
519  TRACE_INFO(" %s\r\n", connection->clientId);
520 
521  //Check protocol version
522  if(osStrncmp(connection->clientId, "SSH-2.0-", 8) &&
523  osStrncmp(connection->clientId, "SSH-1.99-", 9))
524  {
525  //The version advertised by the client is not supported
526  return ERROR_WRONG_IDENTIFIER;
527  }
528 
529  //Key exchange begins by each side sending a KEXINIT message
530  connection->state = SSH_CONN_STATE_SERVER_KEX_INIT;
531  }
532 
533  //Successful processing
534  return NO_ERROR;
535 }
536 
537 
538 /**
539  * @brief Parse SSH_MSG_SERVICE_REQUEST message
540  * @param[in] connection Pointer to the SSH connection
541  * @param[in] message Pointer to message
542  * @param[in] length Length of the message, in bytes
543  * @return Error code
544  **/
545 
547  size_t length)
548 {
549 #if (SSH_SERVER_SUPPORT == ENABLED)
550  error_t error;
551  const uint8_t *p;
552  SshString serviceName;
553 
554  //Debug message
555  TRACE_INFO("SSH_MSG_SERVICE_REQUEST message received (%" PRIuSIZE " bytes)...\r\n", length);
557 
558  //Check operation mode
559  if(connection->context->mode != SSH_OPERATION_MODE_SERVER)
561 
562  //Check connection state
563  if(connection->state != SSH_CONN_STATE_CLIENT_EXT_INFO &&
564  connection->state != SSH_CONN_STATE_SERVICE_REQUEST)
565  {
567  }
568 
569  //Sanity check
570  if(length < sizeof(uint8_t))
571  return ERROR_INVALID_MESSAGE;
572 
573  //Point to the first field of the message
574  p = message + sizeof(uint8_t);
575  //Remaining bytes to process
576  length -= sizeof(uint8_t);
577 
578  //Decode the service name
579  error = sshParseString(p, length, &serviceName);
580  //Any error to report?
581  if(error)
582  return error;
583 
584  //Point to the next field
585  p += sizeof(uint32_t) + serviceName.length;
586  length -= sizeof(uint32_t) + serviceName.length;
587 
588  //Malformed message?
589  if(length != 0)
590  return ERROR_INVALID_MESSAGE;
591 
592  //The service is identified by a name
593  if(sshCompareString(&serviceName, "ssh-userauth"))
594  {
595  //If the server supports the service (and permits the client to use it),
596  //it must respond with an SSH_MSG_SERVICE_ACCEPT message
597  error = sshSendServiceAccept(connection, "ssh-userauth");
598  }
599  else
600  {
601  //If the server rejects the service request, it should send an
602  //appropriate SSH_MSG_DISCONNECT message and must disconnect
604  "Service not available");
605  }
606 
607  //Return status code
608  return error;
609 #else
610  //Server operation mode is not implemented
611  return ERROR_NOT_IMPLEMENTED;
612 #endif
613 }
614 
615 
616 /**
617  * @brief Parse SSH_MSG_SERVICE_ACCEPT message
618  * @param[in] connection Pointer to the SSH connection
619  * @param[in] message Pointer to message
620  * @param[in] length Length of the message, in bytes
621  * @return Error code
622  **/
623 
625  size_t length)
626 {
627 #if (SSH_CLIENT_SUPPORT == ENABLED)
628  error_t error;
629  const uint8_t *p;
630  SshString serviceName;
631 
632  //Debug message
633  TRACE_INFO("SSH_MSG_SERVICE_ACCEPT message received (%" PRIuSIZE " bytes)...\r\n", length);
635 
636  //Check operation mode
637  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
639 
640  //Check connection state
641  if(connection->state != SSH_CONN_STATE_SERVER_EXT_INFO_1 &&
642  connection->state != SSH_CONN_STATE_SERVICE_ACCEPT)
643  {
645  }
646 
647  //Sanity check
648  if(length < sizeof(uint8_t))
649  return ERROR_INVALID_MESSAGE;
650 
651  //Point to the first field of the message
652  p = message + sizeof(uint8_t);
653  //Remaining bytes to process
654  length -= sizeof(uint8_t);
655 
656  //Decode service name
657  error = sshParseString(p, length, &serviceName);
658  //Any error to report?
659  if(error)
660  return error;
661 
662  //Point to the next field
663  p += sizeof(uint32_t) + serviceName.length;
664  length -= sizeof(uint32_t) + serviceName.length;
665 
666  //Malformed message?
667  if(length != 0)
668  return ERROR_INVALID_MESSAGE;
669 
670  //Check service name
671  if(!sshCompareString(&serviceName, "ssh-userauth"))
672  return ERROR_INVALID_MESSAGE;
673 
674  //The authentication protocol is intended to be run over the SSH transport
675  //layer protocol (refer to RFC 4252, section 1)
676  connection->state = SSH_CONN_STATE_USER_AUTH_REQUEST;
677 
678  //Successful processing
679  return NO_ERROR;
680 #else
681  //Client operation mode is not implemented
682  return ERROR_NOT_IMPLEMENTED;
683 #endif
684 }
685 
686 
687 /**
688  * @brief Parse SSH_MSG_IGNORE message
689  * @param[in] connection Pointer to the SSH connection
690  * @param[in] message Pointer to message
691  * @param[in] length Length of the message, in bytes
692  * @return Error code
693  **/
694 
695 error_t sshParseIgnore(SshConnection *connection, const uint8_t *message,
696  size_t length)
697 {
698  error_t error;
699  const uint8_t *p;
700  SshString data;
701 
702  //Debug message
703  TRACE_DEBUG("SSH_MSG_IGNORE message received (%" PRIuSIZE " bytes)...\r\n", length);
705 
706 #if (SSH_KEX_STRICT_SUPPORT == ENABLED)
707  //Strict key exchange?
708  if(!connection->newKeysReceived && connection->kexStrictReceived)
709  {
710  //During initial KEX, terminate the connection if any unexpected or
711  //out-of-sequence packet is received
713  }
714 #endif
715 
716  //Sanity check
717  if(length < sizeof(uint8_t))
718  return ERROR_INVALID_MESSAGE;
719 
720  //Point to the first field of the message
721  p = message + sizeof(uint8_t);
722  //Remaining bytes to process
723  length -= sizeof(uint8_t);
724 
725  //Decode data field
726  error = sshParseString(p, length, &data);
727  //Any error to report?
728  if(error)
729  return error;
730 
731  //Point to the next field
732  p += sizeof(uint32_t) + data.length;
733  length -= sizeof(uint32_t) + data.length;
734 
735  //Malformed message?
736  if(length != 0)
737  return ERROR_INVALID_MESSAGE;
738 
739  //All implementations must understand (and ignore) this message at any time
740  //after receiving the identification string. This message can be used as an
741  //additional protection measure against advanced traffic analysis techniques
742  return NO_ERROR;
743 }
744 
745 
746 /**
747  * @brief Parse SSH_MSG_DEBUG message
748  * @param[in] connection Pointer to the SSH connection
749  * @param[in] message Pointer to message
750  * @param[in] length Length of the message, in bytes
751  * @return Error code
752  **/
753 
754 error_t sshParseDebug(SshConnection *connection, const uint8_t *message,
755  size_t length)
756 {
757  error_t error;
758  const uint8_t *p;
759  SshBoolean alwaysDisplay;
760  SshString debugMessage;
761  SshString languageTag;
762 
763  //Debug message
764  TRACE_INFO("SSH_MSG_DEBUG message received (%" PRIuSIZE " bytes)...\r\n", length);
766 
767 #if (SSH_KEX_STRICT_SUPPORT == ENABLED)
768  //Strict key exchange?
769  if(!connection->newKeysReceived && connection->kexStrictReceived)
770  {
771  //During initial KEX, terminate the connection if any unexpected or
772  //out-of-sequence packet is received
774  }
775 #endif
776 
777  //Sanity check
778  if(length < sizeof(uint8_t))
779  return ERROR_INVALID_MESSAGE;
780 
781  //Point to the first field of the message
782  p = message + sizeof(uint8_t);
783  //Remaining bytes to process
784  length -= sizeof(uint8_t);
785 
786  //Malformed message?
787  if(length < sizeof(uint8_t))
788  return ERROR_INVALID_MESSAGE;
789 
790  //Decode always_display flag
791  alwaysDisplay = p[0];
792  //The value of this field is not used
793  (void) alwaysDisplay;
794 
795  //Point to the next field
796  p += sizeof(uint8_t);
797  length -= sizeof(uint8_t);
798 
799  //Decode message
800  error = sshParseString(p, length, &debugMessage);
801  //Any error to report?
802  if(error)
803  return error;
804 
805  //Point to the next field
806  p += sizeof(uint32_t) + debugMessage.length;
807  length -= sizeof(uint32_t) + debugMessage.length;
808 
809  //Decode language tag
810  error = sshParseString(p, length, &languageTag);
811  //Any error to report?
812  if(error)
813  return error;
814 
815  //Point to the next field
816  p += sizeof(uint32_t) + languageTag.length;
817  length -= sizeof(uint32_t) + languageTag.length;
818 
819  //Malformed message?
820  if(length != 0)
821  return ERROR_INVALID_MESSAGE;
822 
823  //All implementations must understand this message, but they are allowed
824  //to ignore it. This message is used to transmit information that may help
825  //debugging
826  return NO_ERROR;
827 }
828 
829 
830 /**
831  * @brief Parse SSH_MSG_DISCONNECT message
832  * @param[in] connection Pointer to the SSH connection
833  * @param[in] message Pointer to message
834  * @param[in] length Length of the message, in bytes
835  * @return Error code
836  **/
837 
838 error_t sshParseDisconnect(SshConnection *connection, const uint8_t *message,
839  size_t length)
840 {
841  error_t error;
842  const uint8_t *p;
843  uint32_t reasonCode;
845  SshString languageTag;
846 
847  //Debug message
848  TRACE_INFO("SSH_MSG_DISCONNECT message received (%" PRIuSIZE " bytes)...\r\n", length);
850 
851  //Sanity check
852  if(length < sizeof(uint8_t))
853  return ERROR_INVALID_MESSAGE;
854 
855  //Point to the first field of the message
856  p = message + sizeof(uint8_t);
857  //Remaining bytes to process
858  length -= sizeof(uint8_t);
859 
860  //Malformed message?
861  if(length < sizeof(uint32_t))
862  return ERROR_INVALID_MESSAGE;
863 
864  //Get reason code
865  reasonCode = LOAD32BE(p);
866  //The value of this field is not used
867  (void) reasonCode;
868 
869  //Point to the next field
870  p += sizeof(uint32_t);
871  length -= sizeof(uint32_t);
872 
873  //Decode description string
874  error = sshParseString(p, length, &description);
875  //Any error to report?
876  if(error)
877  return error;
878 
879  //Point to the next field
880  p += sizeof(uint32_t) + description.length;
881  length -= sizeof(uint32_t) + description.length;
882 
883  //Decode language tag
884  error = sshParseString(p, length, &languageTag);
885  //Any error to report?
886  if(error)
887  return error;
888 
889  //Point to the next field
890  p += sizeof(uint32_t) + languageTag.length;
891  length -= sizeof(uint32_t) + languageTag.length;
892 
893  //Malformed message?
894  if(length != 0)
895  return ERROR_INVALID_MESSAGE;
896 
897  //An SSH_MSG_DISCONNECT message has been successfully received
898  connection->disconnectReceived = TRUE;
899 
900  //This message causes immediate termination of the connection
901  connection->state = SSH_CONN_STATE_DISCONNECT;
902 
903  //Successful processing
904  return NO_ERROR;
905 }
906 
907 
908 /**
909  * @brief Parse SSH_MSG_UNIMPLEMENTED message
910  * @param[in] connection Pointer to the SSH connection
911  * @param[in] message Pointer to message
912  * @param[in] length Length of the message, in bytes
913  * @return Error code
914  **/
915 
917  size_t length)
918 {
919  const uint8_t *p;
920  uint32_t packetSeqNum;
921 
922  //Debug message
923  TRACE_INFO("SSH_MSG_UNIMPLEMENTED message received (%" PRIuSIZE " bytes)...\r\n", length);
925 
926  //Sanity check
927  if(length < sizeof(uint8_t))
928  return ERROR_INVALID_MESSAGE;
929 
930  //Point to the first field of the message
931  p = message + sizeof(uint8_t);
932  //Remaining bytes to process
933  length -= sizeof(uint8_t);
934 
935  //Malformed message?
936  if(length != sizeof(uint32_t))
937  return ERROR_INVALID_MESSAGE;
938 
939  //Get the packet sequence number of rejected message
940  packetSeqNum = LOAD32BE(p);
941  //The value of this field is not used
942  (void) packetSeqNum;
943 
944  //Ignore SSH_MSG_UNIMPLEMENTED messages
945  return NO_ERROR;
946 }
947 
948 
949 /**
950  * @brief Parse unrecognized message
951  * @param[in] connection Pointer to the SSH connection
952  * @param[in] message Pointer to message
953  * @param[in] length Length of the message, in bytes
954  * @return Error code
955  **/
956 
957 error_t sshParseUnrecognized(SshConnection *connection, const uint8_t *message,
958  size_t length)
959 {
960  error_t error;
961 
962  //Debug message
963  TRACE_INFO("Unrecognized message received (%" PRIuSIZE " bytes)...\r\n", length);
965 
966  //An implementation must respond to all unrecognized messages with an
967  //SSH_MSG_UNIMPLEMENTED message in the order in which the messages were
968  //received (refer to RFC 4253, section 11.4)
969  error = sshSendUnimplemented(connection, connection->decryptionEngine.seqNum);
970 
971  //Such messages must be otherwise ignored
972  return error;
973 }
974 
975 #endif
uint8_t message[]
Definition: chap.h:154
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
#define LOAD32BE(p)
Definition: cpu_endian.h:210
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_INFO(...)
Definition: debug.h:95
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_WRONG_IDENTIFIER
Definition: error.h:89
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:194
uint8_t data[]
Definition: ethernet.h:222
uint8_t p
Definition: ndp.h:300
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
#define osSprintf(dest,...)
Definition: os_port.h:231
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
#define TRUE
Definition: os_port.h:50
Secure Shell (SSH)
#define CYCLONE_SSH_VERSION_STRING
Definition: ssh.h:77
@ SSH_CONN_STATE_SERVER_ID
Definition: ssh.h:1044
@ SSH_CONN_STATE_CLIENT_EXT_INFO
Definition: ssh.h:1062
@ SSH_CONN_STATE_SERVER_KEX_INIT
Definition: ssh.h:1046
@ SSH_CONN_STATE_USER_AUTH_REQUEST
Definition: ssh.h:1068
@ SSH_CONN_STATE_SERVICE_ACCEPT
Definition: ssh.h:1066
@ SSH_CONN_STATE_DISCONNECT
Definition: ssh.h:1072
@ SSH_CONN_STATE_CLIENT_KEX_INIT
Definition: ssh.h:1045
@ SSH_CONN_STATE_SERVICE_REQUEST
Definition: ssh.h:1065
@ SSH_CONN_STATE_SERVER_EXT_INFO_1
Definition: ssh.h:1063
@ SSH_CONN_STATE_CLIENT_ID
Definition: ssh.h:1043
#define SSH_MAX_ID_LEN
Definition: ssh.h:248
@ SSH_OPERATION_MODE_SERVER
Definition: ssh.h:902
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:901
#define SshConnection
Definition: ssh.h:883
@ SSH_DISCONNECT_SERVICE_NOT_AVAILABLE
Definition: ssh.h:1011
@ SSH_MSG_SERVICE_ACCEPT
Definition: ssh.h:949
@ SSH_MSG_DISCONNECT
Definition: ssh.h:944
@ SSH_MSG_SERVICE_REQUEST
Definition: ssh.h:948
@ SSH_MSG_UNIMPLEMENTED
Definition: ssh.h:946
error_t sshFormatString(const char_t *value, uint8_t *p, size_t *written)
Format a string.
Definition: ssh_misc.c:1384
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1586
error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
Parse a string.
Definition: ssh_misc.c:1152
SSH helper functions.
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
SSH packet encryption/decryption.
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
error_t sshParseIdString(SshConnection *connection, const uint8_t *id, size_t length)
Parse identification string.
error_t sshSendUnimplemented(SshConnection *connection, const uint8_t *packetSeqNum)
Send SSH_MSG_UNIMPLEMENTED message.
error_t sshParseDisconnect(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_DISCONNECT message.
error_t sshSendIdString(SshConnection *connection)
Send identification string.
Definition: ssh_transport.c:51
error_t sshFormatUnimplemented(SshConnection *connection, const uint8_t *packetSeqNum, uint8_t *p, size_t *length)
Format SSH_MSG_UNIMPLEMENTED message.
error_t sshSendServiceAccept(SshConnection *connection, const char_t *serviceName)
Send SSH_MSG_SERVICE_ACCEPT message.
error_t sshParseServiceAccept(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_SERVICE_ACCEPT message.
error_t sshFormatDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description, uint8_t *p, size_t *length)
Format SSH_MSG_DISCONNECT message.
error_t sshParseDebug(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_DEBUG message.
error_t sshFormatServiceAccept(SshConnection *connection, const char_t *serviceName, uint8_t *p, size_t *length)
Format SSH_MSG_SERVICE_ACCEPT message.
error_t sshParseServiceRequest(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_SERVICE_REQUEST message.
error_t sshSendServiceRequest(SshConnection *connection)
Send SSH_MSG_SERVICE_REQUEST message.
error_t sshParseUnrecognized(SshConnection *connection, const uint8_t *message, size_t length)
Parse unrecognized message.
error_t sshParseUnimplemented(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_UNIMPLEMENTED message.
error_t sshParseIgnore(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_IGNORE message.
error_t sshFormatServiceRequest(SshConnection *connection, uint8_t *p, size_t *length)
Format SSH_MSG_SERVICE_REQUEST message.
error_t sshSendDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description)
Send SSH_MSG_DISCONNECT message.
SSH transport layer protocol.
bool_t SshBoolean
Boolean.
Definition: ssh_types.h:48
String.
Definition: ssh_types.h:56
size_t length
Definition: ssh_types.h:58
uint8_t length
Definition: tcp.h:368
uint8_t description
Definition: tls.h:1855