ssh_extensions.c
Go to the documentation of this file.
1 /**
2  * @file ssh_extensions.c
3  * @brief SSH extension negotiation
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.4
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_algorithms.h"
37 #include "ssh/ssh_extensions.h"
38 #include "ssh/ssh_transport.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 && SSH_EXT_INFO_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Send SSH_MSG_EXT_INFO message
49  * @param[in] connection Pointer to the SSH connection
50  * @return Error code
51  **/
52 
54 {
55  error_t error;
56  size_t length;
57  uint8_t *message;
58 
59  //Point to the buffer where to format the message
60  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
61 
62  //Format SSH_MSG_EXT_INFO message
63  error = sshFormatExtInfo(connection, message, &length);
64 
65  //Check status code
66  if(!error)
67  {
68  //The implementation may send an SSH_MSG_EXT_INFO message but is not
69  //required to do so (refer to RFC 8308, section 2.2)
71  {
72  //Debug message
73  TRACE_INFO("Sending SSH_MSG_EXT_INFO message (%" PRIuSIZE " bytes)...\r\n", length);
75 
76  //Send message
77  error = sshSendPacket(connection, message, length);
78  }
79  }
80 
81  //Check status code
82  if(!error)
83  {
84  //Check whether SSH operates as a client or a server
85  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
86  {
87  //If a client sends SSH_MSG_EXT_INFO, it must send it as the next
88  //packet following the client's first SSH_MSG_NEWKEYS message
89  connection->state = SSH_CONN_STATE_SERVER_NEW_KEYS;
90  }
91  else
92  {
93  //Check connection state
94  if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1)
95  {
96  //The server may send the SSH_MSG_EXT_INFO message as the next
97  //packet following the server's first SSH_MSG_NEWKEYS message
98  connection->state = SSH_CONN_STATE_CLIENT_NEW_KEYS;
99  }
100  else if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_2)
101  {
102  //The server may send the SSH_MSG_EXT_INFO message immediately
103  //preceding the server's SSH_MSG_USERAUTH_SUCCESS message
104  connection->state = SSH_CONN_STATE_USER_AUTH_SUCCESS;
105  }
106  else
107  {
108  //Just for sanity
109  error = ERROR_WRONG_STATE;
110  }
111  }
112  }
113 
114  //Return status code
115  return error;
116 }
117 
118 
119 /**
120  * @brief Format SSH_MSG_EXT_INFO message
121  * @param[in] connection Pointer to the SSH connection
122  * @param[out] message Buffer where to format the message
123  * @param[out] length Length of the resulting message, in bytes
124  * @return Error code
125  **/
126 
128  size_t *length)
129 {
130  error_t error;
131  size_t n;
132  uint8_t *p;
133  uint32_t nrExtensions;
134 
135  //Point to the first byte of the message
136  p = message;
137  //Total length of the message
138  *length = 0;
139 
140  //Set message type
141  p[0] = SSH_MSG_EXT_INFO;
142 
143  //Skip the nr-extensions field
144  p += sizeof(uint8_t) + sizeof(uint32_t);
145  *length += sizeof(uint8_t) + sizeof(uint32_t);
146 
147  //Calculate the number of extensions
148  nrExtensions = 0;
149 
150 #if (SSH_SERVER_SIG_ALGS_EXT_SUPPORT == ENABLED)
151  //Server operation mode?
152  if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1)
153  {
154  //This extension is sent by the server and contains a list of public key
155  //algorithms that the server is able to process as part of a "publickey"
156  //authentication request
157  error = sshFormatServerSigAlgsExt(connection, p, &n);
158  //Any error to report?
159  if(error)
160  return error;
161 
162  //Point to the next field
163  p += n;
164  *length += n;
165 
166  //The "server-sig-algs" extension is present
167  nrExtensions++;
168  }
169 #endif
170 
171 #if (SSH_GLOBAL_REQ_OK_EXT_SUPPORT == ENABLED)
172  //Client or server operation mode?
173  if(connection->state == SSH_CONN_STATE_CLIENT_EXT_INFO ||
174  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1)
175  {
176  //If a remote party includes this extension in its SSH_MSG_EXT_INFO, then
177  //the remote will handle global requests properly
178  error = sshFormatGlobalRequestsOkExt(connection, p, &n);
179  //Any error to report?
180  if(error)
181  return error;
182 
183  //Point to the next field
184  p += n;
185  *length += n;
186 
187  //The "global-requests-ok" extension is present
188  nrExtensions++;
189  }
190 #endif
191 
192  //The nr-extensions field specifies the number of extensions
193  STORE32BE(nrExtensions, message + 1);
194 
195  //Successful processing
196  return NO_ERROR;
197 }
198 
199 
200 /**
201  * @brief Format "server-sig-algs" extension
202  * @param[in] connection Pointer to the SSH connection
203  * @param[out] p Output stream where to write the extension
204  * @param[out] written Total number of bytes that have been written
205  * @return Error code
206  **/
207 
209  size_t *written)
210 {
211 #if (SSH_SERVER_SUPPORT == ENABLED)
212  error_t error;
213  size_t n;
214 
215  //Total number of bytes that have been written
216  *written = 0;
217 
218  //Format extension name
219  error = sshFormatString("server-sig-algs", p, &n);
220  //Any error to report?
221  if(error)
222  return error;
223 
224  //Point to the next field
225  p += n;
226  *written += n;
227 
228  //In this extension, a server must enumerate all public key algorithms it
229  //might accept during user authentication (refer to RFC 8308, section 3.1)
230  error = sshFormatPublicKeyAlgoList(connection->context, p, &n);
231  //Any error to report?
232  if(error)
233  return error;
234 
235  //Total length of the extension
236  *written += n;
237 
238  //Successful processing
239  return NO_ERROR;
240 #else
241  //Server operation mode is not implemented
242  return ERROR_NOT_IMPLEMENTED;
243 #endif
244 }
245 
246 
247 /**
248  * @brief Format "global-requests-ok" extension
249  * @param[in] connection Pointer to the SSH connection
250  * @param[out] p Output stream where to write the extension
251  * @param[out] written Total number of bytes that have been written
252  * @return Error code
253  **/
254 
256  size_t *written)
257 {
258  error_t error;
259  size_t n;
260 
261  //Total number of bytes that have been written
262  *written = 0;
263 
264  //Format extension name
265  error = sshFormatString("global-requests-ok", p, &n);
266  //Any error to report?
267  if(error)
268  return error;
269 
270  //Point to the next field
271  p += n;
272  *written += n;
273 
274  //The sender must send an empty extension value
275  error = sshFormatString("", p, &n);
276  //Any error to report?
277  if(error)
278  return error;
279 
280  //Total length of the extension
281  *written += n;
282 
283  //Successful processing
284  return NO_ERROR;
285 }
286 
287 
288 /**
289  * @brief Parse SSH_MSG_EXT_INFO message
290  * @param[in] connection Pointer to the SSH connection
291  * @param[in] message Pointer to message
292  * @param[in] length Length of the message, in bytes
293  * @return Error code
294  **/
295 
296 error_t sshParseExtInfo(SshConnection *connection, const uint8_t *message,
297  size_t length)
298 {
299  error_t error;
300  uint32_t i;
301  const uint8_t *p;
302  uint32_t nrExtensions;
303  SshString extensionName;
304  SshString extensionValue;
305 
306  //Debug message
307  TRACE_INFO("SSH_MSG_EXT_INFO message received (%" PRIuSIZE " bytes)...\r\n", length);
309 
310  //Check whether SSH operates as a client or a server
311  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
312  {
313  //Check connection state
314  if(connection->state != SSH_CONN_STATE_SERVER_EXT_INFO_1 &&
315  connection->state != SSH_CONN_STATE_SERVER_EXT_INFO_2)
316  {
317  //Report an error
319  }
320  }
321  else
322  {
323  //Check connection state
324  if(connection->state != SSH_CONN_STATE_CLIENT_EXT_INFO)
325  {
326  //Report an error
328  }
329  }
330 
331  //Sanity check
332  if(length < sizeof(uint8_t))
333  return ERROR_INVALID_MESSAGE;
334 
335  //Point to the first field of the message
336  p = message + sizeof(uint8_t);
337  //Remaining bytes to process
338  length -= sizeof(uint8_t);
339 
340  //Malformed message?
341  if(length < sizeof(uint32_t))
342  return ERROR_INVALID_MESSAGE;
343 
344  //The nr-extensions field specifies the number of extensions
345  nrExtensions = LOAD32BE(p);
346 
347  //Point to the next field
348  p += sizeof(uint32_t);
349  length -= sizeof(uint32_t);
350 
351  //Parse extensions
352  for(i = 0; i < nrExtensions; i++)
353  {
354  //Decode extension name
355  error = sshParseString(p, length, &extensionName);
356  //Any error to report?
357  if(error)
358  return error;
359 
360  //Point to the next field
361  p += sizeof(uint32_t) + extensionName.length;
362  length -= sizeof(uint32_t) + extensionName.length;
363 
364  //Decode extension value
365  error = sshParseString(p, length, &extensionValue);
366  //Any error to report?
367  if(error)
368  return error;
369 
370  //Point to the next field
371  p += sizeof(uint32_t) + extensionValue.length;
372  length -= sizeof(uint32_t) + extensionValue.length;
373 
374 #if (SSH_SERVER_SIG_ALGS_EXT_SUPPORT == ENABLED)
375  //"server-sig-algs" extension?
376  if(sshCompareString(&extensionName, "server-sig-algs"))
377  {
378  //In this extension, a server must enumerate all public key algorithms
379  //it might accept during user authentication (refer to RFC 8308,
380  //section 3.1)
381  error = sshParseServerSigAlgsExt(connection, extensionValue.value,
382  extensionValue.length);
383  }
384  else
385 #endif
386 #if (SSH_GLOBAL_REQ_OK_EXT_SUPPORT == ENABLED)
387  //"global-requests-ok" extension?
388  if(sshCompareString(&extensionName, "global-requests-ok"))
389  {
390  //If a remote party includes this extension in its SSH_MSG_EXT_INFO,
391  //then the remote will handle global requests properly
392  error = sshParseGlobalRequestsOkExt(connection, extensionValue.value,
393  extensionValue.length);
394  }
395  else
396 #endif
397  //Unkown extension?
398  {
399  //Applications must ignore unrecognized extension names (refer to
400  //RFC 8308, section 2.5)
401  error = NO_ERROR;
402  }
403 
404  //Failed to parse extension?
405  if(error)
406  return error;
407  }
408 
409  //Malformed message?
410  if(length != 0)
411  return ERROR_INVALID_MESSAGE;
412 
413  //Check whether SSH operates as a client or a server
414  if(connection->context->mode == SSH_OPERATION_MODE_CLIENT)
415  {
416  //Check connection state
417  if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1)
418  {
419  //The server may send the SSH_MSG_EXT_INFO message as the next packet
420  //following the server's first SSH_MSG_NEWKEYS message
421  connection->state = SSH_CONN_STATE_SERVICE_ACCEPT;
422  }
423  else
424  {
425  //The server may send the SSH_MSG_EXT_INFO message immediately
426  //preceding the server's SSH_MSG_USERAUTH_SUCCESS message
427  connection->state = SSH_CONN_STATE_USER_AUTH_SUCCESS;
428  }
429  }
430  else
431  {
432  //If a client sends SSH_MSG_EXT_INFO, it must send it as the next packet
433  //following the client's first SSH_MSG_NEWKEYS message
434  connection->state = SSH_CONN_STATE_SERVICE_REQUEST;
435  }
436 
437  //Successful processing
438  return NO_ERROR;
439 }
440 
441 
442 /**
443  * @brief Parse "server-sig-algs" extension
444  * @param[in] connection Pointer to the SSH connection
445  * @param[in] p Pointer to extension value
446  * @param[in] length Length of the extension value, in bytes
447  * @return Error code
448  **/
449 
451  size_t length)
452 {
453  error_t error;
454 
455 #if (SSH_CLIENT_SUPPORT == ENABLED)
456  //The "server-sig-algs" extension is sent by the server
457  if(connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_1 ||
458  connection->state == SSH_CONN_STATE_SERVER_EXT_INFO_2)
459  {
460  uint_t i;
461  SshContext *context;
462  SshHostKey *hostKey;
463  SshNameList publicKeyAlgoList;
464 
465  //Point to the SSH context
466  context = connection->context;
467 
468  //In this extension, a server must enumerate all public key algorithms
469  //it might accept during user authentication
470  publicKeyAlgoList.value = p;
471  publicKeyAlgoList.length = length;
472 
473  //Loop through the client's host keys
474  for(i = 0; i < SSH_MAX_HOST_KEYS; i++)
475  {
476  //Point to the current host key
477  hostKey = &context->hostKeys[i];
478 
479  //Valid host key?
480  if(hostKey->keyFormatId != NULL)
481  {
482  //Select the appropriate public key algorithm to use during user
483  //authentication, rather than resorting to trial and error
484  hostKey->publicKeyAlgo = sshSelectPublicKeyAlgo(context,
485  hostKey->keyFormatId, &publicKeyAlgoList);
486  }
487  }
488 
489  //Successful processing
490  error = NO_ERROR;
491  }
492  else
493 #endif
494  {
495  //If a client sends this extension, the server may ignore it and may
496  //disconnect (refer to RFC 8308, section 3.1)
498  "Unexpected extension");
499  }
500 
501  //Return status code
502  return error;
503 }
504 
505 
506 /**
507  * @brief Parse "global-requests-ok" extension
508  * @param[in] connection Pointer to the SSH connection
509  * @param[in] p Pointer to extension value
510  * @param[in] length Length of the extension value, in bytes
511  * @return Error code
512  **/
513 
515  size_t length)
516 {
517  //A receiver must tolerate and ignore non-printable binary characters in the
518  //extension value. Future specifications may define meanings for this value
519  return NO_ERROR;
520 }
521 
522 #endif
#define LOAD32BE(p)
Definition: cpu_endian.h:210
@ ERROR_NOT_IMPLEMENTED
Definition: error.h:66
@ SSH_CONN_STATE_SERVER_EXT_INFO_2
Definition: ssh.h:1055
@ ERROR_UNEXPECTED_MESSAGE
Definition: error.h:194
uint8_t p
Definition: ndp.h:300
uint8_t message[]
Definition: chap.h:154
@ SSH_CONN_STATE_SERVICE_ACCEPT
Definition: ssh.h:1057
error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
Parse a string.
Definition: ssh_misc.c:1152
SSH transport layer protocol.
size_t length
Definition: ssh_types.h:58
error_t sshFormatGlobalRequestsOkExt(SshConnection *connection, uint8_t *p, size_t *written)
Format "global-requests-ok" extension.
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
error_t sshParseServerSigAlgsExt(SshConnection *connection, const char_t *p, size_t length)
Parse "server-sig-algs" extension.
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
@ SSH_CONN_STATE_SERVER_EXT_INFO_1
Definition: ssh.h:1054
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1586
const char_t * sshSelectPublicKeyAlgo(SshContext *context, const char_t *keyFormatId, const SshNameList *peerAlgoList)
Public key algorithm selection.
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
@ ERROR_WRONG_STATE
Definition: error.h:209
error_t sshFormatExtInfo(SshConnection *connection, uint8_t *message, size_t *length)
Format SSH_MSG_EXT_INFO message.
error_t sshFormatPublicKeyAlgoList(SshContext *context, uint8_t *p, size_t *written)
Format the list of public key algorithms.
#define SshContext
Definition: ssh.h:870
const char_t * keyFormatId
Key format identifier.
Definition: ssh.h:1144
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
String containing a comma-separated list of names.
Definition: ssh_types.h:78
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:892
Host key.
Definition: ssh.h:1143
error_t sshParseExtInfo(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_EXT_INFO message.
error_t sshSendDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description)
Send SSH_MSG_DISCONNECT message.
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t length
Definition: tcp.h:368
#define SSH_MAX_HOST_KEYS
Definition: ssh.h:178
@ SSH_CONN_STATE_SERVER_NEW_KEYS
Definition: ssh.h:1052
String.
Definition: ssh_types.h:56
error_t sshSendExtInfo(SshConnection *connection)
Send SSH_MSG_EXT_INFO message.
const char_t * value
Definition: ssh_types.h:79
@ SSH_CONN_STATE_USER_AUTH_SUCCESS
Definition: ssh.h:1061
@ SSH_CONN_STATE_CLIENT_NEW_KEYS
Definition: ssh.h:1051
@ SSH_DISCONNECT_PROTOCOL_ERROR
Definition: ssh.h:997
char char_t
Definition: compiler_port.h:48
const char_t * publicKeyAlgo
Public key algorithm to use during user authentication.
Definition: ssh.h:1151
@ SSH_CONN_STATE_CLIENT_EXT_INFO
Definition: ssh.h:1053
uint8_t n
@ SSH_CONN_STATE_SERVICE_REQUEST
Definition: ssh.h:1056
#define SshConnection
Definition: ssh.h:874
SSH helper functions.
SSH extension negotiation.
size_t length
Definition: ssh_types.h:80
error_t sshParseGlobalRequestsOkExt(SshConnection *connection, const char_t *p, size_t length)
Parse "global-requests-ok" extension.
SSH packet encryption/decryption.
error_t sshFormatString(const char_t *value, uint8_t *p, size_t *written)
Format a string.
Definition: ssh_misc.c:1384
error_t sshFormatServerSigAlgsExt(SshConnection *connection, uint8_t *p, size_t *written)
Format "server-sig-algs" extension.
#define PRIuSIZE
unsigned int uint_t
Definition: compiler_port.h:50
@ SSH_MSG_EXT_INFO
Definition: ssh.h:941
Secure Shell (SSH)
SSH algorithm negotiation.
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
#define SSH_MSG_EXT_INFO_MIN_SIZE
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125