ssh_auth_password.c
Go to the documentation of this file.
1 /**
2  * @file ssh_auth_password.c
3  * @brief Password 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_transport.h"
37 #include "ssh/ssh_auth.h"
38 #include "ssh/ssh_auth_password.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_PASSWORD_AUTH_SUPPORT == ENABLED)
45 
46 
47 /**
48  * @brief Send SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message
49  * @param[in] connection Pointer to the SSH connection
50  * @param[in] prompt NULL-terminated string containing the prompt message
51  * @return Error code
52  **/
53 
55  const char_t *prompt)
56 {
57 #if (SSH_SERVER_SUPPORT == ENABLED)
58  error_t error;
59  size_t length;
60  uint8_t *message;
61 
62  //Point to the buffer where to format the message
63  message = connection->buffer + SSH_PACKET_HEADER_SIZE;
64 
65  //Format SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message
66  error = sshFormatUserAuthPasswdChangeReq(connection, prompt, message,
67  &length);
68 
69  //Check status code
70  if(!error)
71  {
72  //Debug message
73  TRACE_INFO("Sending SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message (%" PRIuSIZE " bytes)...\r\n", length);
75 
76  //Send message
77  error = sshSendPacket(connection, message, length);
78  }
79 
80  //Check status code
81  if(!error)
82  {
83  //Wait for an SSH_MSG_USERAUTH_REQUEST message
84  connection->state = SSH_CONN_STATE_USER_AUTH_REQUEST;
85  }
86 
87  //Return status code
88  return error;
89 #else
90  //Server operation mode is not implemented
91  return ERROR_NOT_IMPLEMENTED;
92 #endif
93 }
94 
95 
96 /**
97  * @brief Format "password" method specific fields
98  * @param[in] connection Pointer to the SSH connection
99  * @param[out] p Output stream where to write the method specific fields
100  * @param[out] written Total number of bytes that have been written
101  * @return Error code
102  **/
103 
105  size_t *written)
106 {
107 #if (SSH_CLIENT_SUPPORT == ENABLED)
108  error_t error;
109  size_t n;
110 
111  //Total number of bytes that have been written
112  *written = 0;
113 
114  //Format method name
115  error = sshFormatString("password", p, &n);
116  //Any error to report?
117  if(error)
118  return error;
119 
120  //Point to the first method-specific field
121  p += n;
122  *written += n;
123 
124  //Format flag
125  p[0] = FALSE;
126 
127  //Point to the next field
128  p += sizeof(uint8_t);
129  *written += sizeof(uint8_t);
130 
131  //Format old password
132  error = sshFormatString(connection->context->password, p, &n);
133  //Any error to report?
134  if(error)
135  return error;
136 
137  //Total length of the method specific fields
138  *written += n;
139 
140  //Successful processing
141  return NO_ERROR;
142 #else
143  //Client operation mode is not implemented
144  return ERROR_NOT_IMPLEMENTED;
145 #endif
146 }
147 
148 
149 /**
150  * @brief Format SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message
151  * @param[in] connection Pointer to the SSH connection
152  * @param[in] prompt NULL-terminated string containing the prompt message
153  * @param[out] p Buffer where to format the message
154  * @param[out] length Length of the resulting message, in bytes
155  * @return Error code
156  **/
157 
159  const char_t *prompt, uint8_t *p, size_t *length)
160 {
161 #if (SSH_SERVER_SUPPORT == ENABLED)
162  error_t error;
163  size_t n;
164 
165  //Total length of the message
166  *length = 0;
167 
168  //Set message type
170 
171  //Point to the first field of the message
172  p += sizeof(uint8_t);
173  *length += sizeof(uint8_t);
174 
175  //Format prompt string
176  error = sshFormatString(prompt, p, &n);
177  //Any error to report?
178  if(error)
179  return error;
180 
181  //Point to the next field
182  p += n;
183  *length += n;
184 
185  //Format language tag
186  error = sshFormatString("en", p, &n);
187  //Any error to report?
188  if(error)
189  return error;
190 
191  //Total length of the message
192  *length += n;
193 
194  //Successful processing
195  return NO_ERROR;
196 #else
197  //Server operation mode is not implemented
198  return ERROR_NOT_IMPLEMENTED;
199 #endif
200 }
201 
202 
203 /**
204  * @brief Parse "password" method specific fields
205  * @param[in] connection Pointer to the SSH connection
206  * @param[in] userName Pointer to the user name
207  * @param[in] p Pointer to method specific fields
208  * @param[in] length Length of the method specific fields, in bytes
209  * @return Error code
210  **/
211 
213  const SshString *userName, const uint8_t *p, size_t length)
214 {
215 #if (SSH_SERVER_SUPPORT == ENABLED)
216  error_t error;
217  SshBoolean flag;
218  SshString oldPassword;
219  SshString newPassword;
220  SshAuthStatus status;
221  SshContext *context;
222 
223  //Point to the SSH context
224  context = connection->context;
225 
226  //Malformed message?
227  if(length < sizeof(uint8_t))
228  return ERROR_INVALID_MESSAGE;
229 
230  //Decode flag
231  flag = p[0];
232 
233  //Point to the next field
234  p += sizeof(uint8_t);
235  length -= sizeof(uint8_t);
236 
237  //Decode old password
238  error = sshParseString(p, length, &oldPassword);
239  //Any error to report?
240  if(error)
241  return error;
242 
243  //Point to the next field
244  p += sizeof(uint32_t) + oldPassword.length;
245  length -= sizeof(uint32_t) + oldPassword.length;
246 
247  //Check the value of the flag
248  if(flag)
249  {
250  //Decode new password
251  error = sshParseString(p, length, &newPassword);
252  //Any error to report?
253  if(error)
254  return error;
255 
256  //Point to the next field
257  p += sizeof(uint32_t) + newPassword.length;
258  length -= sizeof(uint32_t) + newPassword.length;
259  }
260  else
261  {
262  //The new password field is not present
263  newPassword.value = NULL;
264  newPassword.length = 0;
265  }
266 
267  //Malformed message?
268  if(length != 0)
269  return ERROR_INVALID_MESSAGE;
270 
271  //Check the length of the user name
272  if(userName->length <= SSH_MAX_USERNAME_LEN)
273  {
274  //Save user name
275  osMemcpy(connection->user, userName->value, userName->length);
276  //Properly terminate the command line with a NULL character
277  connection->user[userName->length] = '\0';
278 
279  //Invoke user-defined callback, if any
280  if(context->passwordAuthCallback != NULL && !flag)
281  {
282  //The user requests password authentication
283  status = context->passwordAuthCallback(connection, connection->user,
284  oldPassword.value, oldPassword.length);
285  }
286  else if(context->passwordChangeCallback != NULL && flag)
287  {
288  //The user requests a password change
289  status = context->passwordChangeCallback(connection,
290  connection->user, oldPassword.value, oldPassword.length,
291  newPassword.value, newPassword.length);
292  }
293  else
294  {
295  //Access is denied
296  status = SSH_AUTH_STATUS_FAILURE;
297  }
298  }
299  else
300  {
301  //Access is denied
302  status = SSH_AUTH_STATUS_FAILURE;
303  }
304 
305  //Successful authentication?
306  if(status == SSH_AUTH_STATUS_SUCCESS)
307  {
308  //When the server accepts authentication, it must respond with an
309  //SSH_MSG_USERAUTH_SUCCESS message
310  error = sshAcceptAuthRequest(connection);
311  }
312  else if(status == SSH_AUTH_STATUS_PASSWORD_EXPIRED)
313  {
314  //Limit the number of authentication attempts
315  if(connection->authAttempts <= SSH_MAX_AUTH_ATTEMPTS)
316  {
317  //If the password has expired, the server should respond with an
318  //SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message
319  error = sshSendUserAuthPasswdChangeReq(connection,
320  connection->passwordChangePrompt);
321  }
322  else
323  {
324  //If the threshold is exceeded, the server should disconnect (refer
325  //to RFC 4252, section 4)
327  "Too many authentication attempts");
328  }
329  }
330  else
331  {
332  //If the server rejects the authentication request, it must respond
333  //with an SSH_MSG_USERAUTH_FAILURE message
334  error = sshRejectAuthRequest(connection);
335  }
336 
337  //Return status code
338  return error;
339 #else
340  //Server operation mode is not implemented
341  return ERROR_NOT_IMPLEMENTED;
342 #endif
343 }
344 
345 
346 /**
347  * @brief Parse SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message
348  * @param[in] connection Pointer to the SSH connection
349  * @param[in] message Pointer to message
350  * @param[in] length Length of the message, in bytes
351  * @return Error code
352  **/
353 
355  const uint8_t *message, size_t length)
356 {
357 #if (SSH_CLIENT_SUPPORT == ENABLED)
358  error_t error;
359  const uint8_t *p;
360  SshString prompt;
361  SshString languageTag;
362 
363  //Debug message
364  TRACE_INFO("SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message received (%" PRIuSIZE " bytes)...\r\n", length);
366 
367  //Check operation mode
368  if(connection->context->mode != SSH_OPERATION_MODE_CLIENT)
370 
371  //Check connection state
372  if(connection->state != SSH_CONN_STATE_SERVER_EXT_INFO_2 &&
373  connection->state != SSH_CONN_STATE_USER_AUTH_REPLY)
374  {
376  }
377 
378  //Sanity check
379  if(length < sizeof(uint8_t))
380  return ERROR_INVALID_MESSAGE;
381 
382  //Point to the first field of the message
383  p = message + sizeof(uint8_t);
384  //Remaining bytes to process
385  length -= sizeof(uint8_t);
386 
387  //Decode prompt string
388  error = sshParseString(p, length, &prompt);
389  //Any error to report?
390  if(error)
391  return error;
392 
393  //Point to the next field
394  p += sizeof(uint32_t) + prompt.length;
395  length -= sizeof(uint32_t) + prompt.length;
396 
397  //Decode language tag
398  error = sshParseString(p, length, &languageTag);
399  //Any error to report?
400  if(error)
401  return error;
402 
403  //Point to the next field
404  p += sizeof(uint32_t) + languageTag.length;
405  length -= sizeof(uint32_t) + languageTag.length;
406 
407  //Malformed message?
408  if(length != 0)
409  return ERROR_INVALID_MESSAGE;
410 
411  //The server sends an SSH_MSG_USERAUTH_PASSWD_CHANGEREQ to indicate that
412  //the password has expired
414 #else
415  //Client operation mode is not implemented
417 #endif
418 }
419 
420 #endif
error_t sshAcceptAuthRequest(SshConnection *connection)
Accept client's authentication request.
Definition: ssh_auth.c:263
error_t sshParseUserAuthPasswdChangeReq(SshConnection *connection, const uint8_t *message, size_t length)
Parse SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message.
SSH user authentication protocol.
@ SSH_CONN_STATE_USER_AUTH_REPLY
Definition: ssh.h:1060
@ SSH_MSG_USERAUTH_PASSWD_CHANGEREQ
Definition: ssh.h:968
@ SSH_AUTH_STATUS_FAILURE
Definition: ssh.h:903
@ 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_AUTH_STATUS_SUCCESS
Definition: ssh.h:904
error_t sshFormatUserAuthPasswdChangeReq(SshConnection *connection, const char_t *prompt, uint8_t *p, size_t *length)
Format SSH_MSG_USERAUTH_PASSWD_CHANGEREQ 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.
size_t length
Definition: ssh_types.h:58
@ ERROR_INVALID_MESSAGE
Definition: error.h:105
#define SSH_PACKET_HEADER_SIZE
Definition: ssh_packet.h:38
error_t sshSendPacket(SshConnection *connection, uint8_t *payload, size_t payloadLen)
Send SSH packet.
Definition: ssh_packet.c:57
Password authentication method.
SshAuthStatus
Authentication status.
Definition: ssh.h:902
@ SSH_DISCONNECT_BY_APPLICATION
Definition: ssh.h:1006
#define FALSE
Definition: os_port.h:46
#define SshContext
Definition: ssh.h:870
#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_OPERATION_MODE_CLIENT
Definition: ssh.h:892
error_t sshSendDisconnect(SshConnection *connection, uint32_t reasonCode, const char_t *description)
Send SSH_MSG_DISCONNECT message.
error_t sshFormatPasswordAuthParams(SshConnection *connection, uint8_t *p, size_t *written)
Format "password" method specific fields.
#define TRACE_INFO(...)
Definition: debug.h:95
uint8_t length
Definition: tcp.h:368
@ SSH_AUTH_STATUS_PASSWORD_EXPIRED
Definition: ssh.h:905
String.
Definition: ssh_types.h:56
bool_t SshBoolean
Boolean.
Definition: ssh_types.h:48
#define SSH_MAX_AUTH_ATTEMPTS
Definition: ssh.h:227
char char_t
Definition: compiler_port.h:48
uint8_t n
#define SshConnection
Definition: ssh.h:874
@ ERROR_AUTHENTICATION_FAILED
Definition: error.h:69
@ SSH_CONN_STATE_USER_AUTH_REQUEST
Definition: ssh.h:1059
error_t sshParsePasswordAuthParams(SshConnection *connection, const SshString *userName, const uint8_t *p, size_t length)
Parse "password" method specific fields.
SSH helper functions.
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 sshSendUserAuthPasswdChangeReq(SshConnection *connection, const char_t *prompt)
Send SSH_MSG_USERAUTH_PASSWD_CHANGEREQ message.
#define PRIuSIZE
Secure Shell (SSH)
error_t sshRejectAuthRequest(SshConnection *connection)
Reject client's authentication request.
Definition: ssh_auth.c:293
#define SSH_MAX_USERNAME_LEN
Definition: ssh.h:255
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125