sftp_server.c
Go to the documentation of this file.
1 /**
2  * @file sftp_server.c
3  * @brief SFTP server
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 SFTP_TRACE_LEVEL
33 
34 //Dependencies
35 #include "ssh/ssh.h"
36 #include "sftp/sftp_server.h"
37 #include "sftp/sftp_server_misc.h"
38 #include "path.h"
39 #include "debug.h"
40 
41 //Check SSH stack configuration
42 #if (SFTP_SERVER_SUPPORT == ENABLED)
43 
44 
45 /**
46  * @brief Initialize settings with default values
47  * @param[out] settings Structure that contains SFTP server settings
48  **/
49 
51 {
52  //Default task parameters
53  settings->task = OS_TASK_DEFAULT_PARAMS;
56 
57  //SSH server context
58  settings->sshServerContext = NULL;
59 
60  //SFTP sessions
61  settings->numSessions = 0;
62  settings->sessions = NULL;
63 
64  //File objects
65  settings->numFileObjects = 0;
66  settings->fileObjects = NULL;
67 
68  //Root directory
69  settings->rootDir = NULL;
70 
71  //User verification callback function
72  settings->checkUserCallback = NULL;
73  //Callback used to retrieve file permissions
74  settings->getFilePermCallback = NULL;
75 }
76 
77 
78 /**
79  * @brief Initialize SFTP server context
80  * @param[in] context Pointer to the SFTP server context
81  * @param[in] settings SFTP server specific settings
82  * @return Error code
83  **/
84 
86  const SftpServerSettings *settings)
87 {
88  error_t error;
89  uint_t i;
90 
91  //Debug message
92  TRACE_INFO("Initializing SFTP server...\r\n");
93 
94  //Ensure the parameters are valid
95  if(context == NULL || settings == NULL)
97 
98  //Invalid SFTP sessions?
99  if(settings->sessions == NULL || settings->numSessions < 1 ||
101  {
103  }
104 
105  //Invalid file objects?
106  if(settings->fileObjects == NULL || settings->numFileObjects < 1)
107  {
109  }
110 
111  //Invalid root directory?
112  if(settings->rootDir == NULL ||
114  {
116  }
117 
118  //Initialize status code
119  error = NO_ERROR;
120 
121  //Clear SFTP server context
122  osMemset(context, 0, sizeof(SftpServerContext));
123 
124  //Initialize task parameters
125  context->taskParams = settings->task;
126  context->taskId = OS_INVALID_TASK_ID;
127 
128  //Save user settings
129  context->sshServerContext = settings->sshServerContext;
130  context->numSessions = settings->numSessions;
131  context->sessions = settings->sessions;
132  context->numFileObjects = settings->numFileObjects;
133  context->fileObjects = settings->fileObjects;
134  context->checkUserCallback = settings->checkUserCallback;
135  context->getFilePermCallback = settings->getFilePermCallback;
136 
137  //Set root directory
138  osStrcpy(context->rootDir, settings->rootDir);
139 
140  //Clean the root directory path
141  pathCanonicalize(context->rootDir);
142  pathRemoveSlash(context->rootDir);
143 
144  //Loop through SFTP sessions
145  for(i = 0; i < context->numSessions; i++)
146  {
147  //Initialize the structure representing the SFTP session
148  osMemset(&context->sessions[i], 0, sizeof(SftpServerSession));
149  }
150 
151  //Loop through file objects
152  for(i = 0; i < context->numFileObjects; i++)
153  {
154  //Initialize the structure representing a file object
155  osMemset(&context->fileObjects[i], 0, sizeof(SftpFileObject));
156  }
157 
158  //Create an event object to poll the state of channels
159  if(!osCreateEvent(&context->event))
160  {
161  //Report an error
162  error = ERROR_OUT_OF_RESOURCES;
163  }
164 
165  //Check status code
166  if(error)
167  {
168  //Clean up side effects
169  sftpServerDeinit(context);
170  }
171 
172  //Return status code
173  return error;
174 }
175 
176 
177 /**
178  * @brief Start SFTP server
179  * @param[in] context Pointer to the SFTP server context
180  * @return Error code
181  **/
182 
184 {
185  error_t error;
186 
187  //Make sure the SFTP server context is valid
188  if(context == NULL)
190 
191  //Debug message
192  TRACE_INFO("Starting SFTP server...\r\n");
193 
194  //Make sure the SFTP server is not already running
195  if(context->running)
196  return ERROR_ALREADY_RUNNING;
197 
198  //Register channel request processing callback
199  error = sshServerRegisterChannelRequestCallback(context->sshServerContext,
201 
202  //Check status code
203  if(!error)
204  {
205  //Start the SFTP server
206  context->stop = FALSE;
207  context->running = TRUE;
208 
209  //Create a task
210  context->taskId = osCreateTask("SFTP Server", (OsTaskCode) sftpServerTask,
211  context, &context->taskParams);
212 
213  //Failed to create task?
214  if(context->taskId == OS_INVALID_TASK_ID)
215  {
216  error = ERROR_OUT_OF_RESOURCES;
217  }
218  }
219 
220  //Any error to report?
221  if(error)
222  {
223  //Clean up side effects
224  context->running = FALSE;
225 
226  //Unregister channel request processing callback
227  sshServerUnregisterChannelRequestCallback(context->sshServerContext,
229  }
230 
231  //Return status code
232  return error;
233 }
234 
235 
236 /**
237  * @brief Stop SFTP server
238  * @param[in] context Pointer to the SFTP server context
239  * @return Error code
240  **/
241 
243 {
244  uint_t i;
245 
246  //Make sure the SFTP server context is valid
247  if(context == NULL)
249 
250  //Debug message
251  TRACE_INFO("Stopping SFTP server...\r\n");
252 
253  //Check whether the SFTP server is running
254  if(context->running)
255  {
256  //Unregister channel request processing callback
257  sshServerUnregisterChannelRequestCallback(context->sshServerContext,
259 
260 #if (NET_RTOS_SUPPORT == ENABLED)
261  //Stop the SFTP server
262  context->stop = TRUE;
263  //Send a signal to the task to abort any blocking operation
264  osSetEvent(&context->event);
265 
266  //Wait for the task to terminate
267  while(context->running)
268  {
269  osDelayTask(1);
270  }
271 #endif
272 
273  //Loop through SFTP sessions
274  for(i = 0; i < context->numSessions; i++)
275  {
276  //Active session?
277  if(context->sessions[i].state != SFTP_SERVER_SESSION_STATE_CLOSED)
278  {
279  //Close SFTP session
280  sftpServerCloseSession(&context->sessions[i]);
281  }
282  }
283  }
284 
285  //Successful processing
286  return NO_ERROR;
287 }
288 
289 
290 /**
291  * @brief Set user's root directory
292  * @param[in] session Handle referencing an SFTP session
293  * @param[in] rootDir NULL-terminated string specifying the root directory
294  * @return Error code
295  **/
296 
298 {
299  SftpServerContext *context;
300 
301  //Check parameters
302  if(session == NULL || rootDir == NULL)
304 
305  //Point to the SFTP server context
306  context = session->context;
307 
308  //Set user's root directory
309  pathCopy(session->rootDir, context->rootDir, SFTP_SERVER_MAX_ROOT_DIR_LEN);
310  pathCombine(session->rootDir, rootDir, SFTP_SERVER_MAX_ROOT_DIR_LEN);
311 
312  //Clean the resulting path
313  pathCanonicalize(session->rootDir);
314  pathRemoveSlash(session->rootDir);
315 
316  //Set default user's home directory
317  pathCopy(session->homeDir, session->rootDir, SFTP_SERVER_MAX_HOME_DIR_LEN);
318 
319  //Successful processing
320  return NO_ERROR;
321 }
322 
323 
324 /**
325  * @brief Set user's home directory
326  * @param[in] session Handle referencing an SFTP session
327  * @param[in] homeDir NULL-terminated string specifying the home directory
328  * @return Error code
329  **/
330 
332 {
333  SftpServerContext *context;
334 
335  //Check parameters
336  if(session == NULL || homeDir == NULL)
338 
339  //Point to the SFTP server context
340  context = session->context;
341 
342  //Set user's home directory
343  pathCopy(session->homeDir, context->rootDir, SFTP_SERVER_MAX_HOME_DIR_LEN);
344  pathCombine(session->homeDir, homeDir, SFTP_SERVER_MAX_HOME_DIR_LEN);
345 
346  //Clean the resulting path
347  pathCanonicalize(session->homeDir);
348  pathRemoveSlash(session->homeDir);
349 
350  //Successful processing
351  return NO_ERROR;
352 }
353 
354 
355 /**
356  * @brief SFTP server task
357  * @param[in] param Pointer to the SFTP server context
358  **/
359 
360 void sftpServerTask(void *param)
361 {
362  error_t error;
363  uint_t i;
364  systime_t timeout;
365  SftpServerContext *context;
366  SftpServerSession *session;
367 
368  //Point to the SFTP server context
369  context = (SftpServerContext *) param;
370 
371 #if (NET_RTOS_SUPPORT == ENABLED)
372  //Task prologue
373  osEnterTask();
374 
375  //Process events
376  while(1)
377  {
378 #endif
379  //Set polling timeout
380  timeout = SFTP_SERVER_TICK_INTERVAL;
381 
382  //Clear event descriptor set
383  osMemset(context->eventDesc, 0, sizeof(context->eventDesc));
384 
385  //Loop through SFTP sessions
386  for(i = 0; i < context->numSessions; i++)
387  {
388  //Point to the structure describing the current session
389  session = &context->sessions[i];
390 
391  //Active session?
392  if(session->state != SFTP_SERVER_SESSION_STATE_CLOSED)
393  {
394  //Register session events
395  sftpServerRegisterSessionEvents(session, &context->eventDesc[i]);
396 
397  //Check whether the channel is ready for I/O operation
398  if(context->eventDesc[i].eventFlags != 0)
399  {
400  //No need to poll the underlying channel for incoming traffic
401  timeout = 0;
402  }
403  }
404  }
405 
406  //Wait for one of the set of channels to become ready to perform I/O
407  error = sshPollChannels(context->eventDesc, context->numSessions,
408  &context->event, timeout);
409 
410  //Check status code
411  if(error == NO_ERROR || error == ERROR_TIMEOUT)
412  {
413  //Stop request?
414  if(context->stop)
415  {
416  //Stop SFTP server operation
417  context->running = FALSE;
418  //Task epilogue
419  osExitTask();
420  //Kill ourselves
422  }
423 
424  //Loop through SFTP sessions
425  for(i = 0; i < context->numSessions; i++)
426  {
427  //Point to the structure describing the current session
428  session = &context->sessions[i];
429 
430  //Active session?
431  if(session->state != SFTP_SERVER_SESSION_STATE_CLOSED)
432  {
433  //Check whether the channel is ready to perform I/O
434  if(context->eventDesc[i].eventFlags != 0)
435  {
436  //Session event handler
438  }
439  }
440  }
441  }
442 
443  //Handle periodic operations
444  sftpServerTick(context);
445 
446 #if (NET_RTOS_SUPPORT == ENABLED)
447  }
448 #endif
449 }
450 
451 
452 /**
453  * @brief Release SFTP server context
454  * @param[in] context Pointer to the SFTP server context
455  **/
456 
458 {
459  //Make sure the SFTP server context is valid
460  if(context != NULL)
461  {
462  //Free previously allocated resources
463  osDeleteEvent(&context->event);
464 
465  //Clear SFTP server context
466  osMemset(context, 0, sizeof(SftpServerContext));
467  }
468 }
469 
470 #endif
uint_t numSessions
Maximum number of SFTP sessions.
Definition: sftp_server.h:206
OsTaskId osCreateTask(const char_t *name, OsTaskCode taskCode, void *arg, const OsTaskParameters *params)
Create a task.
Path manipulation helper functions.
void sftpServerCloseSession(SftpServerSession *session)
Close an SFTP session.
#define osExitTask()
OsTaskParameters task
Task parameters.
Definition: sftp_server.h:204
SftpServerCheckUserCallback checkUserCallback
User verification callback function.
Definition: sftp_server.h:211
#define TRUE
Definition: os_port.h:50
#define OS_INVALID_TASK_ID
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
error_t sshServerRegisterChannelRequestCallback(SshServerContext *context, SshChannelReqCallback callback, void *param)
Register channel request callback function.
Definition: ssh_server.c:360
SshServerContext * sshServerContext
SSH server context.
Definition: sftp_server.h:205
error_t sftpServerSetHomeDir(SftpServerSession *session, const char_t *homeDir)
Set user's home directory.
Definition: sftp_server.c:331
error_t sshServerUnregisterChannelRequestCallback(SshServerContext *context, SshChannelReqCallback callback)
Unregister channel request callback function.
Definition: ssh_server.c:376
#define SFTP_SERVER_PRIORITY
Definition: sftp_server.h:55
#define osStrlen(s)
Definition: os_port.h:165
void sftpServerTask(void *param)
SFTP server task.
Definition: sftp_server.c:360
SFTP server.
#define OS_SELF_TASK_ID
SFTP server settings.
Definition: sftp_server.h:203
void sftpServerRegisterSessionEvents(SftpServerSession *session, SshChannelEventDesc *eventDesc)
Register session events.
error_t sftpServerChannelRequestCallback(SshChannel *channel, const SshString *type, const uint8_t *data, size_t length, void *param)
SSH channel request callback.
error_t sftpServerStart(SftpServerContext *context)
Start SFTP server.
Definition: sftp_server.c:183
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:158
void osDeleteTask(OsTaskId taskId)
Delete a task.
#define FALSE
Definition: os_port.h:46
@ ERROR_INVALID_PARAMETER
Invalid parameter.
Definition: error.h:47
error_t
Error codes.
Definition: error.h:43
const char_t * rootDir
Root directory.
Definition: sftp_server.h:210
void(* OsTaskCode)(void *arg)
Task routine.
void sftpServerProcessSessionEvents(SftpServerSession *session)
Session event handler.
#define SFTP_SERVER_MAX_ROOT_DIR_LEN
Definition: sftp_server.h:95
SftpServerSession * sessions
SFTP sessions.
Definition: sftp_server.h:207
void osDeleteEvent(OsEvent *event)
Delete an event object.
const OsTaskParameters OS_TASK_DEFAULT_PARAMS
error_t sshPollChannels(SshChannelEventDesc *eventDesc, uint_t size, OsEvent *extEvent, systime_t timeout)
Wait for one of a set of channels to become ready to perform I/O.
Definition: ssh.c:2376
void sftpServerDeinit(SftpServerContext *context)
Release SFTP server context.
Definition: sftp_server.c:457
void sftpServerGetDefaultSettings(SftpServerSettings *settings)
Initialize settings with default values.
Definition: sftp_server.c:50
#define TRACE_INFO(...)
Definition: debug.h:95
#define osEnterTask()
File or directory object.
Definition: sftp_server.h:186
error_t sftpServerStop(SftpServerContext *context)
Stop SFTP server.
Definition: sftp_server.c:242
uint32_t systime_t
System time.
@ SFTP_SERVER_SESSION_STATE_CLOSED
Definition: sftp_server.h:157
@ ERROR_TIMEOUT
Definition: error.h:95
char char_t
Definition: compiler_port.h:48
#define SFTP_SERVER_MAX_SESSIONS
Definition: sftp_server.h:60
SftpFileObject * fileObjects
File objects.
Definition: sftp_server.h:209
error_t sftpServerInit(SftpServerContext *context, const SftpServerSettings *settings)
Initialize SFTP server context.
Definition: sftp_server.c:85
Helper functions for SFTP server.
bool_t osCreateEvent(OsEvent *event)
Create an event object.
#define SFTP_SERVER_TICK_INTERVAL
Definition: sftp_server.h:67
void osDelayTask(systime_t delay)
Delay routine.
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:360
SftpServerGetFilePermCallback getFilePermCallback
Callback used to retrieve file permissions.
Definition: sftp_server.h:212
error_t sftpServerSetRootDir(SftpServerSession *session, const char_t *rootDir)
Set user's root directory.
Definition: sftp_server.c:297
#define SftpServerSession
Definition: sftp_server.h:120
unsigned int uint_t
Definition: compiler_port.h:50
#define osMemset(p, value, length)
Definition: os_port.h:135
Secure Shell (SSH)
#define SftpServerContext
Definition: sftp_server.h:116
#define osStrcpy(s1, s2)
Definition: os_port.h:207
uint_t numFileObjects
Maximum number of file objects.
Definition: sftp_server.h:208
#define SFTP_SERVER_STACK_SIZE
Definition: sftp_server.h:48
void sftpServerTick(SftpServerContext *context)
Handle periodic operations.
@ ERROR_ALREADY_RUNNING
Definition: error.h:293
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:137
#define SFTP_SERVER_MAX_HOME_DIR_LEN
Definition: sftp_server.h:102
void pathCombine(char_t *path, const char_t *more, size_t maxLen)
Concatenate two paths.
Definition: path.c:394