ssh_auth_public_key.c
Go to the documentation of this file.
1 /**
2  * @file ssh_auth_public_key.c
3  * @brief Public key authentication method
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_transport.h"
38 #include "ssh/ssh_auth.h"
40 #include "ssh/ssh_packet.h"
41 #include "ssh/ssh_key_parse.h"
42 #include "ssh/ssh_key_verify.h"
43 #include "ssh/ssh_cert_parse.h"
44 #include "ssh/ssh_cert_verify.h"
45 #include "ssh/ssh_sign_generate.h"
46 #include "ssh/ssh_sign_verify.h"
47 #include "ssh/ssh_misc.h"
48 #include "debug.h"
49 
50 //Check SSH stack configuration
51 #if (SSH_SUPPORT == ENABLED && SSH_PUBLIC_KEY_AUTH_SUPPORT == ENABLED)
52 
53 
54 /**
55  * @brief Send SSH_MSG_USERAUTH_PK_OK message
56  * @param[in] connection Pointer to the SSH connection
57  * @param[in] publicKeyAlgo Public key algorithm name
58  * @param[in] publicKey Public key blob from the authentication request
59  * @return Error code
60  **/
61 
63  const SshString *publicKeyAlgo, const SshBinaryString *publicKey)
64 {
65 #if (SSH_SERVER_SUPPORT == ENABLED)
66  error_t error;
67  size_t length;
68  uint8_t *message;
69 
70  //Point to the buffer where to format the message
71  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
72 
73  //Format SSH_MSG_USERAUTH_PK_OK message
74  error = sshFormatUserAuthPkOk(connection, publicKeyAlgo, publicKey, message,
75  &length);
76 
77  //Check status code
78  if(!error)
79  {
80  //Debug message
81  TRACE_INFO("Sending SSH_MSG_USERAUTH_PK_OK message (%" PRIuSIZE " bytes)...\r\n", length);
83 
84  //Send message
85  error = sshSendPacket(connection, message, length);
86  }
87 
88  //Return status code
89  return error;
90 #else
91  //Server operation mode is not implemented
92  return ERROR_NOT_IMPLEMENTED;
93 #endif
94 }
95 
96 
97 /**
98  * @brief Format "publickey" method specific fields
99  * @param[in] connection Pointer to the SSH connection
100  * @param[in] message Pointer to the message to be signed
101  * @param[out] messageLen Length of the message, in bytes
102  * @param[out] p Output stream where to write the method specific fields
103  * @param[out] written Total number of bytes that have been written
104  * @return Error code
105  **/
106 
108  const uint8_t *message, size_t messageLen, uint8_t *p, size_t *written)
109 {
110 #if (SSH_CLIENT_SUPPORT == ENABLED)
111  error_t error;
112  size_t n;
113  SshHostKey *hostKey;
115  SshBinaryString tbsData;
116 
117  //Total number of bytes that have been written
118  *written = 0;
119 
120  //Format method name
121  error = sshFormatString("publickey", p, &n);
122  //Any error to report?
123  if(error)
124  return error;
125 
126  //Point to the first method-specific field
127  p += n;
128  *written += n;
129 
130  //Format flag
131  p[0] = connection->publicKeyOk ? TRUE : FALSE;
132 
133  //Point to the next field
134  p += sizeof(uint8_t);
135  *written += sizeof(uint8_t);
136 
137  //Get the currently selected host key
138  hostKey = sshGetHostKey(connection);
139  //Invalid host key?
140  if(hostKey == NULL)
141  return ERROR_INVALID_KEY;
142 
143  //Make sure the public key algorithm is valid
144  if(hostKey->publicKeyAlgo == NULL)
145  return ERROR_INVALID_KEY;
146 
147  //Format public key algorithm name
148  error = sshFormatString(hostKey->publicKeyAlgo, p, &n);
149  //Any error to report?
150  if(error)
151  return error;
152 
153  //Total length of the message
154  p += n;
155  *written += n;
156 
157  //Format public key blob
158  error = sshFormatHostKey(connection, p + sizeof(uint32_t), &n);
159  //Any error to report?
160  if(error)
161  return error;
162 
163  //The octet string value is preceded by a uint32 containing its length
164  STORE32BE(n, p);
165 
166  //Point to the next field
167  p += sizeof(uint32_t) + n;
168  *written += sizeof(uint32_t) + n;
169 
170  //If the host key is acceptable, then the client can send a signature
171  //generated using the private key
172  if(connection->publicKeyOk)
173  {
174  //Point to the session identifier
175  sessionId.value = connection->sessionId;
176  sessionId.length = connection->sessionIdLen;
177 
178  //Point to the message to be signed
179  tbsData.value = message;
180  tbsData.length = messageLen + *written;
181 
182  //Compute the signature using the private key
183  error = sshGenerateSignature(connection, hostKey->publicKeyAlgo,
184  hostKey, &sessionId, &tbsData, p + sizeof(uint32_t), &n);
185  //Any error to report?
186  if(error)
187  return error;
188 
189  //The octet string value is preceded by a uint32 containing its length
190  STORE32BE(n, p);
191 
192  //Total length of the method specific fields
193  *written += sizeof(uint32_t) + n;
194  }
195 
196  //Successful processing
197  return NO_ERROR;
198 #else
199  //Client operation mode is not implemented
200  return ERROR_NOT_IMPLEMENTED;
201 #endif
202 }
203 
204 
205 /**
206  * @brief Format SSH_MSG_USERAUTH_PK_OK message
207  * @param[in] connection Pointer to the SSH connection
208  * @param[in] publicKeyAlgo Public key algorithm name
209  * @param[in] publicKey Public key blob from the authentication request
210  * @param[out] p Buffer where to format the message
211  * @param[out] length Length of the resulting message, in bytes
212  * @return Error code
213  **/
214 
216  const SshString *publicKeyAlgo, const SshBinaryString *publicKey,
217  uint8_t *p, size_t *length)
218 {
219 #if (SSH_SERVER_SUPPORT == ENABLED)
220  error_t error;
221  size_t n;
222 
223  //Total length of the message
224  *length = 0;
225 
226  //Set message type
228 
229  //Point to the first field of the message
230  p += sizeof(uint8_t);
231  *length += sizeof(uint8_t);
232 
233  //The public key algorithm name is copied from the request
234  error = sshFormatBinaryString(publicKeyAlgo->value, publicKeyAlgo->length,
235  p, &n);
236  //Any error to report?
237  if(error)
238  return error;
239 
240  //Point to the next field
241  p += n;
242  *length += n;
243 
244  //The public key blob is copied from the request
245  error = sshFormatBinaryString(publicKey->value, publicKey->length, p, &n);
246  //Any error to report?
247  if(error)
248  return error;
249 
250  //Total length of the message
251  *length += n;
252 
253  //Successful processing
254  return NO_ERROR;
255 #else
256  //Server operation mode is not implemented
257  return ERROR_NOT_IMPLEMENTED;
258 #endif
259 }
260 
261 
262 /**
263  * @brief Parse "publickey" method specific fields
264  * @param[in] connection Pointer to the SSH connection
265  * @param[in] userName Pointer to the user name
266  * @param[in] message Pointer to the SSH_MSG_USERAUTH_REQUEST message
267  * @param[in] p Pointer to method specific fields
268  * @param[in] length Length of the method specific fields, in bytes
269  * @return Error code
270  **/
271 
273  const SshString *userName, const uint8_t *message, const uint8_t *p,
274  size_t length)
275 {
276 #if (SSH_SERVER_SUPPORT == ENABLED)
277  error_t error;
278  SshBoolean flag;
279  SshString publicKeyAlgo;
280  SshBinaryString publicKey;
282  SshBinaryString tbsData;
283  SshBinaryString signature;
284 
285  //Malformed message?
286  if(length < sizeof(uint8_t))
287  return ERROR_INVALID_MESSAGE;
288 
289  //Decode flag
290  flag = p[0];
291 
292  //Point to the next field
293  p += sizeof(uint8_t);
294  length -= sizeof(uint8_t);
295 
296  //Decode public key algorithm name
297  error = sshParseString(p, length, &publicKeyAlgo);
298  //Any error to report?
299  if(error)
300  return error;
301 
302  //Point to the next field
303  p += sizeof(uint32_t) + publicKeyAlgo.length;
304  length -= sizeof(uint32_t) + publicKeyAlgo.length;
305 
306  //Decode public key blob
307  error = sshParseBinaryString(p, length, &publicKey);
308  //Any error to report?
309  if(error)
310  return error;
311 
312  //Point to the next field
313  p += sizeof(uint32_t) + publicKey.length;
314  length -= sizeof(uint32_t) + publicKey.length;
315 
316  //Point to the message whose signature is to be verified
317  tbsData.value = message;
318  tbsData.length = p - message;
319 
320  //Check the value of the flag
321  if(flag)
322  {
323  //Decode signature
324  error = sshParseBinaryString(p, length, &signature);
325  //Any error to report?
326  if(error)
327  return error;
328 
329  //Point to the next field
330  p += sizeof(uint32_t) + signature.length;
331  length -= sizeof(uint32_t) + signature.length;
332  }
333  else
334  {
335  //The signature field is not present
336  signature.value = NULL;
337  signature.length = 0;
338  }
339 
340  //Malformed message?
341  if(length != 0)
342  return ERROR_INVALID_MESSAGE;
343 
344  //When the server receives this message, it must check whether the supplied
345  //key is acceptable for authentication (refer to RFC 4252, section 7)
346  if(userName->length <= SSH_MAX_USERNAME_LEN)
347  {
348  //Save user name
349  osMemcpy(connection->user, userName->value, userName->length);
350  //Properly terminate the command line with a NULL character
351  connection->user[userName->length] = '\0';
352 
353 #if (SSH_CERT_SUPPORT == ENABLED)
354  //Certificate-based authentication?
355  if(sshIsCertPublicKeyAlgo(&publicKeyAlgo))
356  {
357  //Verify client's certificate
358  error = sshVerifyClientCertificate(connection, &publicKeyAlgo,
359  &publicKey, flag);
360  }
361  else
362 #endif
363  {
364  //Verify client's host key
365  error = sshVerifyClientHostKey(connection, &publicKeyAlgo,
366  &publicKey);
367  }
368  }
369  else
370  {
371  //Report an error
372  error = ERROR_INVALID_KEY;
373  }
374 
375  //Valid host key?
376  if(!error)
377  {
378  //Check whether the signature is present
379  if(flag)
380  {
381  //Point to the session identifier
382  sessionId.value = connection->sessionId;
383  sessionId.length = connection->sessionIdLen;
384 
385  //If the supplied key is acceptable for authentication, the server
386  //must check whether the signature is correct
387  error = sshVerifySignature(connection, &publicKeyAlgo, &publicKey,
388  &sessionId, &tbsData, &signature);
389  }
390  }
391 
392  //Check status code
393  if(!error)
394  {
395  //Check whether the signature is present
396  if(flag)
397  {
398  //When the server accepts authentication, it must respond with a
399  //SSH_MSG_USERAUTH_SUCCESS message
400  error = sshAcceptAuthRequest(connection);
401  }
402  else
403  {
404  //Limit the number of authentication attempts
405  if(connection->authAttempts <= SSH_MAX_AUTH_ATTEMPTS)
406  {
407  //The supplied key is acceptable for authentication
408  error = sshSendUserAuthPkOk(connection, &publicKeyAlgo, &publicKey);
409  }
410  else
411  {
412  //If the threshold is exceeded, the server should disconnect (refer
413  //to RFC 4252, section 4)
415  "Too many authentication attempts");
416  }
417  }
418  }
419  else
420  {
421  //If the server rejects the authentication request, it must respond with
422  //an SSH_MSG_USERAUTH_FAILURE message
423  error = sshRejectAuthRequest(connection);
424  }
425 
426  //Return status code
427  return error;
428 #else
429  //Server operation mode is not implemented
430  return ERROR_NOT_IMPLEMENTED;
431 #endif
432 }
433 
434 
435 /**
436  * @brief Parse SSH_MSG_USERAUTH_PK_OK message
437  * @param[in] connection Pointer to the SSH connection
438  * @param[in] message Pointer to message
439  * @param[in] length Length of the message, in bytes
440  * @return Error code
441  **/
442 
444  const uint8_t *message, size_t length)
445 {
446 #if (SSH_CLIENT_SUPPORT == ENABLED)
447  error_t error;
448  const uint8_t *p;
449  SshString publicKeyAlgo;
450  SshBinaryString publicKey;
451 
452  //Debug message
453  TRACE_INFO("SSH_MSG_USERAUTH_PK_OK message received (%" PRIuSIZE " bytes)...\r\n", length);
455 
456  //Check operation mode
457  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
459 
460  //Check connection state
461  if(connection->state != SSH_CONN_STATE_SERVER_EXT_INFO_2 &&
462  connection->state != SSH_CONN_STATE_USER_AUTH_REPLY)
463  {
465  }
466 
467  //Sanity check
468  if(length < sizeof(uint8_t))
469  return ERROR_INVALID_MESSAGE;
470 
471  //Point to the first field of the message
472  p = message + sizeof(uint8_t);
473  //Remaining bytes to process
474  length -= sizeof(uint8_t);
475 
476  //Decode public key algorithm name
477  error = sshParseString(p, length, &publicKeyAlgo);
478  //Any error to report?
479  if(error)
480  return error;
481 
482  //Point to the next field
483  p += sizeof(uint32_t) + publicKeyAlgo.length;
484  length -= sizeof(uint32_t) + publicKeyAlgo.length;
485 
486  //Decode public key blob
487  error = sshParseBinaryString(p, length, &publicKey);
488  //Any error to report?
489  if(error)
490  return error;
491 
492  //Point to the next field
493  p += sizeof(uint32_t) + publicKey.length;
494  length -= sizeof(uint32_t) + publicKey.length;
495 
496  //Malformed message?
497  if(length != 0)
498  return ERROR_INVALID_MESSAGE;
499 
500  //Sanity check
501  if(connection->publicKeyOk)
503 
504 #if (SSH_CERT_SUPPORT == ENABLED)
505  //Certificate-based authentication?
506  if(sshIsCertPublicKeyAlgo(&publicKeyAlgo))
507  {
508  SshCertificate cert;
509 
510  //Check the syntax of the certificate structure
511  error = sshParseCertificate(publicKey.value, publicKey.length, &cert);
512  }
513  else
514 #endif
515  {
516  SshString keyFormatId;
517 
518  //Check the syntax of the host key structure
519  error = sshParseHostKey(publicKey.value, publicKey.length, &keyFormatId);
520  }
521 
522  //Any error to report?
523  if(error)
524  return error;
525 
526  //The provided host key is acceptable
527  connection->publicKeyOk = TRUE;
528 
529  //Now that the public key is acceptable, the client can perform the signing
530  //operation by sending an SSH_MSG_USERAUTH_REQUEST message to the server
531  connection->state = SSH_CONN_STATE_USER_AUTH_REQUEST;
532 
533  //Successful processing
534  return NO_ERROR;
535 #else
536  //Client operation mode is not implemented
538 #endif
539 }
540 
541 #endif
error_t sshAcceptAuthRequest(SshConnection *connection)
Accept client's authentication request.
Definition: ssh_auth.c:263
uint8_t sessionId[]
Definition: tls.h:1773
SSH user authentication protocol.
@ SSH_CONN_STATE_USER_AUTH_REPLY
Definition: ssh.h:1060
SSH host key verification.
Binary string.
Definition: ssh_types.h:67
@ 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
#define TRUE
Definition: os_port.h:50
error_t sshParseUserAuthPkOk(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_USERAUTH_PK_OK message.
error_t sshParseString(const uint8_t *p, size_t length, SshString *string)
Parse a string.
Definition: ssh_misc.c:1152
SSH transport layer protocol.
error_t sshVerifyClientHostKey(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey)
Verify client's host key.
SSH certificate verification.
size_t length
Definition: ssh_types.h:58
SSH key parsing.
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
Public key authentication method.
RSA/DSA/ECDSA/EdDSA signature generation.
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
error_t sshParsePublicKeyAuthParams(SshConnection *connection, const SshString *userName, const uint8_t *message, const uint8_t *p, size_t length)
Parse "publickey" method specific fields.
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
bool_t sshIsCertPublicKeyAlgo(const SshString *publicKeyAlgo)
Test if the specified public key algorithm is using certificates.
size_t length
Definition: ssh_types.h:69
error_t sshFormatPublicKeyAuthParams(SshConnection *connection, const uint8_t *message, size_t messageLen, uint8_t *p, size_t *written)
Format "publickey" method specific fields.
@ SSH_DISCONNECT_BY_APPLICATION
Definition: ssh.h:1006
error_t sshFormatBinaryString(const void *value, size_t valueLen, uint8_t *p, size_t *written)
Format a binary string.
Definition: ssh_misc.c:1415
#define FALSE
Definition: os_port.h:46
error_t sshGenerateSignature(SshConnection *connection, const char_t *publicKeyAlgo, const SshHostKey *hostKey, const SshBinaryString *sessionId, const SshBinaryString *message, uint8_t *p, size_t *written)
Signature generation.
#define osMemcpy(dest, src, length)
Definition: os_port.h:141
const char_t * value
Definition: ssh_types.h:57
error_t
Error codes.
Definition: error.h:43
@ SSH_MSG_USERAUTH_PK_OK
Definition: ssh.h:967
error_t sshVerifySignature(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *publicKeyBlob, const SshBinaryString *sessionId, const SshBinaryString *message, const SshBinaryString *signature)
Signature verification.
@ SSH_OPERATION_MODE_CLIENT
Definition: ssh.h:892
Host key.
Definition: ssh.h:1143
SSH certificate parsing.
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
String.
Definition: ssh_types.h:56
const uint8_t * value
Definition: ssh_types.h:68
bool_t SshBoolean
Boolean.
Definition: ssh_types.h:48
#define SSH_MAX_AUTH_ATTEMPTS
Definition: ssh.h:227
const char_t * publicKeyAlgo
Public key algorithm to use during user authentication.
Definition: ssh.h:1151
uint8_t n
error_t sshVerifyClientCertificate(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *hostKey, bool_t flag)
Verify client's certificate.
#define SshConnection
Definition: ssh.h:874
SshHostKey * sshGetHostKey(SshConnection *connection)
Get the currently selected host key.
Definition: ssh_misc.c:722
error_t sshParseCertificate(const uint8_t *data, size_t length, SshCertificate *cert)
Parse SSH certificate.
@ SSH_CONN_STATE_USER_AUTH_REQUEST
Definition: ssh.h:1059
SSH helper functions.
error_t sshFormatHostKey(SshConnection *connection, uint8_t *p, size_t *written)
Format host key structure.
Definition: ssh_misc.c:863
error_t sshFormatUserAuthPkOk(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *publicKey, uint8_t *p, size_t *length)
Format SSH_MSG_USERAUTH_PK_OK message.
SSH packet encryption/decryption.
error_t sshSendUserAuthPkOk(SshConnection *connection, const SshString *publicKeyAlgo, const SshBinaryString *publicKey)
Send SSH_MSG_USERAUTH_PK_OK message.
error_t sshParseBinaryString(const uint8_t *p, size_t length, SshBinaryString *string)
Parse a binary string.
Definition: ssh_misc.c:1189
error_t sshFormatString(const char_t *value, uint8_t *p, size_t *written)
Format a string.
Definition: ssh_misc.c:1384
error_t sshParseHostKey(const uint8_t *data, size_t length, SshString *keyFormatId)
Parse host key structure.
Definition: ssh_key_parse.c:53
#define PRIuSIZE
Secure Shell (SSH)
SSH algorithm negotiation.
error_t sshRejectAuthRequest(SshConnection *connection)
Reject client's authentication request.
Definition: ssh_auth.c:293
#define SSH_MAX_USERNAME_LEN
Definition: ssh.h:255
RSA/DSA/ECDSA/EdDSA signature verification.
#define STORE32BE(a, p)
Definition: cpu_endian.h:286
SSH certificate (OpenSSH format)
@ ERROR_INVALID_KEY
Definition: error.h:106
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125