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