sftp_server_misc.c
Go to the documentation of this file.
1 /**
2  * @file sftp_server_misc.c
3  * @brief Helper functions for 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.0
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 "ssh/ssh_request.h"
37 #include "ssh/ssh_misc.h"
38 #include "sftp/sftp_server.h"
39 #include "sftp/sftp_server_file.h"
42 #include "sftp/sftp_server_misc.h"
43 #include "str.h"
44 #include "path.h"
45 #include "debug.h"
46 
47 //Check SSH stack configuration
48 #if (SFTP_SERVER_SUPPORT == ENABLED)
49 
50 
51 /**
52  * @brief Handle periodic operations
53  * @param[in] context Pointer to the SFTP server context
54  **/
55 
57 {
58 }
59 
60 
61 /**
62  * @brief SSH channel request callback
63  * @param[in] channel Handle referencing an SSH channel
64  * @param[in] type Request type
65  * @param[in] data Request-specific data
66  * @param[in] length Length of the request-specific data, in bytes
67  * @param[in] param Pointer to the SFTP server context
68  * @return Error code
69  **/
70 
72  const SshString *type, const uint8_t *data, size_t length,
73  void *param)
74 {
75  error_t error;
76  SftpAccessStatus status;
77  SftpServerContext *context;
78  SftpServerSession *session;
79 
80  //Debug message
81  TRACE_INFO("SFTP server: SSH channel request callback...\r\n");
82 
83  //Initialize status code
84  error = NO_ERROR;
85 
86  //Point to the SFTP server context
87  context = (SftpServerContext *) param;
88 
89  //Check request type
90  if(sshCompareString(type, "subsystem"))
91  {
92  SshSubsystemParams requestParams;
93 
94  //This message executes a predefined subsystem
95  error = sshParseSubsystemParams(data, length, &requestParams);
96  //Any error to report?
97  if(error)
98  return error;
99 
100  //Check subsystem name
101  if(sshCompareString(&requestParams.subsystemName, "sftp"))
102  {
103  //Retrieve the SFTP session that matches the channel number
104  session = sftpServerFindSession(context, channel);
105 
106  //Any active session found?
107  if(session != NULL)
108  {
109  //Only one of the "shell", "exec" and "subsystem" requests can
110  //succeed per channel (refer to RFC 4254, section 6.5)
111  return ERROR_WRONG_STATE;
112  }
113  else
114  {
115  //Open a new SFTP session
116  session = sftpServerOpenSession(context, channel);
117  //Check whether the session table runs out of resources
118  if(session == NULL)
119  return ERROR_OUT_OF_RESOURCES;
120 
121  //Invoke user-defined callback, if any
122  if(context->checkUserCallback != NULL)
123  {
124  //Check user name
125  status = context->checkUserCallback(session,
126  channel->connection->user);
127 
128  //Access denied?
129  if(status != SFTP_ACCESS_ALLOWED)
130  return ERROR_ACCESS_DENIED;
131  }
132 
133  //Force the channel to operate in non-blocking mode
134  error = sshSetChannelTimeout(channel, 0);
135  //Any error to report?
136  if(error)
137  return error;
138 
139  //Set initial session state
140  session->state = SFTP_SERVER_SESSION_STATE_RECEIVING;
141 
142  //Notify the SFTP server that the session is ready to accept data
143  osSetEvent(&session->context->event);
144  }
145  }
146  }
147  else
148  {
149  //The request is not supported
150  return ERROR_UNKNOWN_REQUEST;
151  }
152 
153  //Successful processing
154  return NO_ERROR;
155 }
156 
157 
158 /**
159  * @brief Find the SFTP session that matches a given SSH channel
160  * @param[in] context Pointer to the SFTP server context
161  * @param[in] channel Handle referencing an SSH channel
162  * @return Pointer to the matching SFTP session
163  **/
164 
166  SshChannel *channel)
167 {
168  uint_t i;
169  SftpServerSession *session;
170 
171  //Loop through SFTP sessions
172  for(i = 0; i < context->numSessions; i++)
173  {
174  //Point to the current session
175  session = &context->sessions[i];
176 
177  //Active session?
178  if(session->state != SFTP_SERVER_SESSION_STATE_CLOSED)
179  {
180  //Matching channel found?
181  if(session->channel == channel)
182  {
183  return session;
184  }
185  }
186  }
187 
188  //The channel number does not match any active session
189  return NULL;
190 }
191 
192 
193 /**
194  * @brief Open a new SFTP session
195  * @param[in] context Pointer to the SFTP server context
196  * @param[in] channel Handle referencing an SSH channel
197  * @return Pointer to the newly created SFTP session
198  **/
199 
201  SshChannel *channel)
202 {
203  uint_t i;
204  SftpServerSession *session;
205 
206  //Loop through SFTP sessions
207  for(i = 0; i < context->numSessions; i++)
208  {
209  //Point to the current session
210  session = &context->sessions[i];
211 
212  //Check whether the current session is free
213  if(session->state == SFTP_SERVER_SESSION_STATE_CLOSED)
214  {
215  //Initialize session parameters
216  osMemset(session, 0, sizeof(SftpServerSession));
217 
218  //Attach SFTP server context
219  session->context = context;
220  //Attach SSH channel
221  session->channel = channel;
222 
223  //Set default user's root directory
224  pathCopy(session->rootDir, context->rootDir,
226 
227  //Set default user's home directory
228  pathCopy(session->homeDir, context->rootDir,
230 
231  //Return session handle
232  return session;
233  }
234  }
235 
236  //The session table runs out of space
237  return NULL;
238 }
239 
240 
241 /**
242  * @brief Close an SFTP session
243  * @param[in] session Handle referencing an SFTP session
244  **/
245 
247 {
248  uint_t i;
249  SftpServerContext *context;
250  SftpFileObject *fileObject;
251 
252  //Debug message
253  TRACE_INFO("Closing SFTP session...\r\n");
254 
255  //Point to the SFTP server context
256  context = session->context;
257 
258  //Loop through file objects
259  for(i = 0; i < context->numFileObjects; i++)
260  {
261  //Point to the current file object
262  fileObject = &context->fileObjects[i];
263 
264  //Check whether the file object is currently in use
265  if(fileObject->type != SSH_FILEXFER_TYPE_INVALID &&
266  fileObject->session == session)
267  {
268  //Close file, if any
269  if(fileObject->file != NULL)
270  {
271  fsCloseFile(fileObject->file);
272  fileObject->file = NULL;
273  }
274 
275  //Close directory, if any
276  if(fileObject->dir != NULL)
277  {
278  fsCloseDir(fileObject->dir);
279  fileObject->dir = NULL;
280  }
281 
282  //Mark the entry as free
283  fileObject->type = SSH_FILEXFER_TYPE_INVALID;
284  }
285  }
286 
287  //Close SSH channel
288  sshCloseChannel(session->channel);
289  session->channel = NULL;
290 
291  //Mark the current session as closed
292  session->state = SFTP_SERVER_SESSION_STATE_CLOSED;
293 }
294 
295 
296 /**
297  * @brief Register session events
298  * @param[in] session Handle referencing an SFTP session
299  * @param[in] eventDesc SSH channel events to be registered
300  **/
301 
303  SshChannelEventDesc *eventDesc)
304 {
305  //Check the state of the SFTP session
306  if(session->state == SFTP_SERVER_SESSION_STATE_RECEIVING)
307  {
308  //Any data left to read?
309  if(session->bufferPos < sizeof(SftpPacketHeader))
310  {
311  //Wait for the channel to be readable
312  eventDesc->channel = session->channel;
314  }
315  else if(session->bufferPos < session->bufferLen)
316  {
317  //Wait for the channel to be readable
318  eventDesc->channel = session->channel;
320  }
321  else
322  {
323  //The read operation is complete
325  }
326  }
327  else if(session->state == SFTP_SERVER_SESSION_STATE_SENDING)
328  {
329  //Any data left to write?
330  if(session->bufferPos < session->bufferLen)
331  {
332  //Wait for the channel to be writable
333  eventDesc->channel = session->channel;
335  }
336  else
337  {
338  //The write operation is complete
340  }
341  }
342  else if(session->state == SFTP_SERVER_SESSION_STATE_RECEIVING_DATA)
343  {
344  //Any data left to read?
345  if(session->bufferPos < session->bufferLen)
346  {
347  //Wait for the channel to be readable
348  eventDesc->channel = session->channel;
350  }
351  else
352  {
353  //The read operation is complete
355  }
356  }
357  else if(session->state == SFTP_SERVER_SESSION_STATE_SENDING_DATA)
358  {
359  //Any data left to write?
360  if(session->bufferPos < session->bufferLen)
361  {
362  //Wait for the channel to be writable
363  eventDesc->channel = session->channel;
365  }
366  else
367  {
368  //The write operation is complete
370  }
371  }
372  else
373  {
374  //Just for sanity
375  }
376 }
377 
378 
379 /**
380  * @brief Session event handler
381  * @param[in] session Handle referencing an SFTP session
382  **/
383 
385 {
386  error_t error;
387  size_t n;
388  SshChannel *channel;
389 
390  //Initialize status code
391  error = NO_ERROR;
392 
393  //Point to the SSH channel
394  channel = session->channel;
395 
396  //Check the state of the SFTP session
397  if(session->state == SFTP_SERVER_SESSION_STATE_RECEIVING)
398  {
399  //Receive SFTP packet
400  if(session->bufferPos < sizeof(SftpPacketHeader))
401  {
402  //Receive more data
403  error = sshReadChannel(channel, session->buffer + session->bufferPos,
404  sizeof(SftpPacketHeader) - session->bufferPos, &n, 0);
405 
406  //Check status code
407  if(!error)
408  {
409  //Advance data pointer
410  session->bufferPos += n;
411 
412  //SFTP packet header successfully received?
413  if(session->bufferPos >= sizeof(SftpPacketHeader))
414  {
415  //Parse SFTP packet header
416  error = sftpServerParsePacketLength(session, session->buffer);
417  }
418  }
419  }
420  else if(session->bufferPos < session->bufferLen)
421  {
422  //Receive more data
423  error = sshReadChannel(channel, session->buffer + session->bufferPos,
424  session->bufferLen - session->bufferPos, &n, 0);
425 
426  //Check status code
427  if(!error)
428  {
429  //Advance data pointer
430  session->bufferPos += n;
431  }
432  }
433  else
434  {
435  //Process SFTP packet
436  error = sftpServerParsePacket(session, session->buffer,
437  session->bufferLen, session->totalLen);
438  }
439  }
440  else if(session->state == SFTP_SERVER_SESSION_STATE_SENDING)
441  {
442  //Send SFTP packet
443  if(session->bufferPos < session->bufferLen)
444  {
445  //Send more data
446  error = sshWriteChannel(channel, session->buffer + session->bufferPos,
447  session->bufferLen - session->bufferPos, &n, 0);
448 
449  //Check status code
450  if(error == NO_ERROR || error == ERROR_TIMEOUT)
451  {
452  //Advance data pointer
453  session->bufferPos += n;
454  }
455  }
456  else
457  {
458  //Flush receive buffer
459  session->bufferLen = 0;
460  session->bufferPos = 0;
461 
462  //Wait for the next SFTP packet
463  session->state = SFTP_SERVER_SESSION_STATE_RECEIVING;
464  }
465  }
466  else if(session->state == SFTP_SERVER_SESSION_STATE_RECEIVING_DATA)
467  {
468  //Receive data payload
469  if(session->bufferPos < session->bufferLen)
470  {
471  //Receive more data
472  error = sshReadChannel(channel, session->buffer + session->bufferPos,
473  session->bufferLen - session->bufferPos, &n, 0);
474 
475  //Check status code
476  if(!error)
477  {
478  //Advance data pointer
479  session->bufferPos += n;
480  }
481  }
482  else
483  {
484  //Write the data to the specified file
485  error = sftpServerWriteData(session);
486 
487  //Check status code
488  if(!error)
489  {
490  //Check whether the data transfer is complete
491  if(session->dataLen == 0)
492  {
493  //The server responds to a write request with an SSH_FXP_STATUS
494  //message
495  if(session->requestStatus == NO_ERROR)
496  {
497  //Successful write operation
498  error = sftpFormatFxpStatus(session, session->requestId,
499  SSH_FX_OK, "Success");
500  }
501  else if(session->requestStatus == ERROR_INVALID_HANDLE)
502  {
503  //The supplied handle is not valid
504  error = sftpFormatFxpStatus(session, session->requestId,
505  SSH_FX_FAILURE, "Invalid handle");
506  }
507  else
508  {
509  //Generic error
510  error = sftpFormatFxpStatus(session, session->requestId,
511  SSH_FX_FAILURE, "Failed to write data");
512  }
513  }
514  }
515  }
516  }
517  else if(session->state == SFTP_SERVER_SESSION_STATE_SENDING_DATA)
518  {
519  //Send data payload
520  if(session->bufferPos < session->bufferLen)
521  {
522  //Send more data
523  error = sshWriteChannel(channel, session->buffer + session->bufferPos,
524  session->bufferLen - session->bufferPos, &n, 0);
525 
526  //Check status code
527  if(error == NO_ERROR || error == ERROR_TIMEOUT)
528  {
529  //Advance data pointer
530  session->bufferPos += n;
531  }
532  }
533  else
534  {
535  //Flush receive buffer
536  session->bufferLen = 0;
537  session->bufferPos = 0;
538 
539  //Check whether the data transfer is in progress?
540  if(session->dataLen > 0)
541  {
542  //Read data from the specified file
543  error = sftpServerReadData(session);
544  }
545  else
546  {
547  //The data transfer is complete
548  session->state = SFTP_SERVER_SESSION_STATE_RECEIVING;
549  }
550  }
551  }
552  else
553  {
554  //Invalid state
555  error = ERROR_WRONG_STATE;
556  }
557 
558  //Any communication error?
559  if(error != NO_ERROR && error != ERROR_TIMEOUT)
560  {
561  //Close the SSH connection
562  sftpServerCloseSession(session);
563  }
564 }
565 
566 
567 /**
568  * @brief Retrieve the length of an incoming SFTP packet
569  * @param[in] session Handle referencing an SFTP session
570  * @param[in] packet Pointer to received SFTP packet
571  * @return Error code
572  **/
573 
575  const uint8_t *packet)
576 {
577  error_t error;
578  const SftpPacketHeader *header;
579 
580  //Initialize status code
581  error = NO_ERROR;
582 
583  //Point to the SSH packet header
584  header = (SftpPacketHeader *) packet;
585 
586  //Convert the packet length to host byte order
587  session->totalLen = ntohl(header->length);
588  //The length of the packet does not include the packet_length field itself
589  session->totalLen += sizeof(uint32_t);
590 
591  //Sanity check
592  if(session->totalLen > ntohl(header->length))
593  {
594  //SSH_FXP_WRITE packet received?
595  if(header->type == SSH_FXP_WRITE)
596  {
597  //Read as much data as possible
598  session->bufferLen = MIN(session->totalLen, SFTP_SERVER_BUFFER_SIZE);
599  }
600  else
601  {
602  //Check the length of the packet
603  if(session->totalLen <= SFTP_SERVER_BUFFER_SIZE)
604  {
605  //Save the total length of the packet
606  session->bufferLen = session->totalLen;
607  }
608  else
609  {
610  //Report an error
611  error = ERROR_INVALID_LENGTH;
612  }
613  }
614  }
615  else
616  {
617  //Report an error
618  error = ERROR_INVALID_LENGTH;
619  }
620 
621  //Return status code
622  return error;
623 }
624 
625 
626 /**
627  * @brief SFTP packet processing
628  * @param[in] session Handle referencing an SFTP session
629  * @param[in] packet Pointer to the received SFTP packet
630  * @param[in] fragLen Number of bytes available on hand
631  * @param[in] totalLen Total length of the packet, in bytes
632  * @return Error code
633  **/
634 
636  const uint8_t *packet, size_t fragLen, size_t totalLen)
637 {
638  error_t error;
639  const SftpPacketHeader *header;
640 
641  //Debug message
642  TRACE_DEBUG("SFTP packet received (%" PRIuSIZE " bytes)...\r\n", totalLen);
643  TRACE_VERBOSE_ARRAY(" ", packet, fragLen);
644 
645  //Check the length of the received packet
646  if(fragLen >= sizeof(SftpPacketHeader) && fragLen <= totalLen)
647  {
648  //Point to the SSH packet header
649  header = (SftpPacketHeader *) packet;
650 
651  //Retrieve the length of the payload
652  fragLen -= sizeof(SftpPacketHeader);
653  totalLen -= sizeof(SftpPacketHeader);
654 
655  //When the file transfer protocol starts, the client first sends a
656  //SSH_FXP_INIT (including its version number) packet to the server
657  if(session->version == 0)
658  {
659  //Check message type
660  if(header->type == SSH_FXP_INIT)
661  {
662  //The SSH_FXP_INIT message contains the client's version number
663  error = sftpServerParseFxpInit(session, header->payload, fragLen);
664  }
665  else
666  {
667  //Report an error
668  error = ERROR_INVALID_TYPE;
669  }
670  }
671  else
672  {
673  //Check message type
674  if(header->type == SSH_FXP_OPEN)
675  {
676  //Files are opened and created using the SSH_FXP_OPEN message
677  error = sftpServerParseFxpOpen(session, header->payload, fragLen);
678  }
679  else if(header->type == SSH_FXP_CLOSE)
680  {
681  //A file is closed by using the SSH_FXP_CLOSE request
682  error = sftpServerParseFxpClose(session, header->payload, fragLen);
683  }
684  else if(header->type == SSH_FXP_READ)
685  {
686  //Once a file has been opened, it can be read using the SSH_FXP_READ
687  //message
688  error = sftpServerParseFxpRead(session, header->payload, fragLen);
689  }
690  else if(header->type == SSH_FXP_WRITE)
691  {
692  //Writing to a file is achieved using the SSH_FXP_WRITE message
693  error = sftpServerParseFxpWrite(session, header->payload, fragLen,
694  totalLen);
695  }
696  else if(header->type == SSH_FXP_LSTAT)
697  {
698  //The SSH_FXP_LSTAT request can be used retrieve the attributes
699  //for a named file (SSH_FXP_LSTAT does not follow symbolic links)
700  error = sftpServerParseFxpStat(session, header->payload, fragLen);
701  }
702  else if(header->type == SSH_FXP_FSTAT)
703  {
704  //SSH_FXP_FSTAT differs from SSH_FXP_STAT and SSH_FXP_LSTAT in that
705  //it returns status information for an open file (identified by the
706  //file handle)
707  error = sftpServerParseFxpFstat(session, header->payload, fragLen);
708  }
709  else if(header->type == SSH_FXP_SETSTAT)
710  {
711  //File attributes may be modified using the SSH_FXP_SETSTAT request
712  error = sftpServerParseFxpSetStat(session, header->payload, fragLen);
713  }
714  else if(header->type == SSH_FXP_FSETSTAT)
715  {
716  //The SSH_FXP_FSETSTAT request modifies the attributes of a file
717  //which is already open
718  error = sftpServerParseFxpSetFstat(session, header->payload, fragLen);
719  }
720  else if(header->type == SSH_FXP_OPENDIR)
721  {
722  //The SSH_FXP_OPENDIR opens a directory for reading
723  error = sftpServerParseFxpOpenDir(session, header->payload, fragLen);
724  }
725  else if(header->type == SSH_FXP_READDIR)
726  {
727  //A directory can be listed using SSH_FXP_READDIR requests
728  error = sftpServerParseFxpReadDir(session, header->payload, fragLen);
729  }
730  else if(header->type == SSH_FXP_REMOVE)
731  {
732  //Files can be removed using the SSH_FXP_REMOVE message
733  error = sftpServerParseFxpRemove(session, header->payload, fragLen);
734  }
735  else if(header->type == SSH_FXP_MKDIR)
736  {
737  //New directories can be created using the SSH_FXP_MKDIR request
738  error = sftpServerParseFxpMkDir(session, header->payload, fragLen);
739  }
740  else if(header->type == SSH_FXP_RMDIR)
741  {
742  //Directories can be removed using the SSH_FXP_RMDIR request
743  error = sftpServerParseFxpRmDir(session, header->payload, fragLen);
744  }
745  else if(header->type == SSH_FXP_REALPATH)
746  {
747  //The SSH_FXP_REALPATH request can be used to have the server
748  //canonicalize any given path name to an absolute path
749  error = sftpServerParseFxpRealPath(session, header->payload, fragLen);
750  }
751  else if(header->type == SSH_FXP_STAT)
752  {
753  //The SSH_FXP_STAT request can be used retrieve the attributes
754  //for a named file (SSH_FXP_STAT follows symbolic links)
755  error = sftpServerParseFxpStat(session, header->payload, fragLen);
756  }
757  else if(header->type == SSH_FXP_RENAME)
758  {
759  //Files (and directories) can be renamed using the SSH_FXP_RENAME
760  //message
761  error = sftpServerParseFxpRename(session, header->payload, fragLen);
762  }
763  else if(header->type == SSH_FXP_EXTENDED)
764  {
765  //The SSH_FXP_EXTENDED request provides a generic extension
766  //mechanism for adding vendor-specific commands
767  error = sftpServerParseFxpExtended(session, header->payload, fragLen);
768  }
769  else
770  {
771  //Debug message
772  TRACE_WARNING("Unknown SFTP packet type!\r\n");
773  //Unknown packet type
774  error = ERROR_INVALID_TYPE;
775  }
776  }
777  }
778  else
779  {
780  //Malformed SFTP packet
781  error = ERROR_INVALID_LENGTH;
782  }
783 
784  //Any error to report?
785  if(error)
786  {
787  //Flush buffer
788  session->bufferPos = 0;
789  session->bufferLen = 0;
790  }
791 
792  //Return status code
793  return error;
794 }
795 
796 
797 /**
798  * @brief Generate a unique handle
799  * @param[in] session Handle referencing an SFTP session
800  * @return Handle value
801  **/
802 
804 {
805  uint_t i;
806  bool_t valid;
807  SftpServerContext *context;
808  SftpFileObject *fileObject;
809 
810  //Point to the SFTP server context
811  context = session->context;
812 
813  //SSH_FXP_OPEN and SSH_FXP_OPENDIR requests return a handle which may be
814  //used to access the file or the directory later
815  for(valid = FALSE; !valid; )
816  {
817  //Generate a new handle value
818  session->handle++;
819 
820  //Loop through file objects
821  for(i = 0, valid = TRUE; i < context->numFileObjects && valid; i++)
822  {
823  //Point to the current file object
824  fileObject = &context->fileObjects[i];
825 
826  //The handle can identify a file or a directory
827  if(fileObject->type != SSH_FILEXFER_TYPE_INVALID &&
828  fileObject->session == session)
829  {
830  //Compare handle values
831  if(fileObject->handle == session->handle)
832  {
833  //The handle value is already in use
834  valid = FALSE;
835  }
836  }
837  }
838  }
839 
840  //Return handle value
841  return session->handle;
842 }
843 
844 
845 /**
846  * @brief Get permissions for the specified file or directory
847  * @param[in] session Handle referencing an SFTP session
848  * @param[in] path Canonical path of the file
849  * @return Access rights for the specified file
850  **/
851 
853  const char_t *path)
854 {
855  size_t n;
856  uint_t perm;
857  SftpServerContext *context;
858 
859  //Point to the SFTP server context
860  context = session->context;
861 
862  //Calculate the length of the root directory
863  n = osStrlen(session->rootDir);
864 
865  //Make sure the pathname is valid
866  if(!osStrncmp(path, session->rootDir, n))
867  {
868  //Strip root directory from the pathname
869  path = sftpServerStripRootDir(session, path);
870 
871  //Invoke user-defined callback, if any
872  if(context->getFilePermCallback != NULL)
873  {
874  //Retrieve access rights for the specified file
875  perm = context->getFilePermCallback(session,
876  session->channel->connection->user, path);
877  }
878  else
879  {
880  //Use default access rights
883  }
884  }
885  else
886  {
887  //The specified pathname is not valid
888  perm = 0;
889  }
890 
891  //Return access rights
892  return perm;
893 }
894 
895 
896 /**
897  * @brief Retrieve the full pathname
898  * @param[in] session Handle referencing an SFTP session
899  * @param[in] path Relative or absolute path
900  * @param[out] fullPath Resulting full path
901  * @param[in] maxLen Maximum acceptable path length
902  * @return Error code
903  **/
904 
906  char_t *fullPath, size_t maxLen)
907 {
908  size_t n;
909 
910  //Relative or absolute path?
911  if(path->length > 0 && (path->value[0] == '/' || path->value[0] == '\\'))
912  {
913  //Check the length of the root directory
914  if(osStrlen(session->rootDir) > maxLen)
915  return ERROR_FAILURE;
916 
917  //Copy the root directory
918  osStrcpy(fullPath, session->rootDir);
919  }
920  else
921  {
922  //Check the length of the home directory
923  if(osStrlen(session->homeDir) > maxLen)
924  return ERROR_FAILURE;
925 
926  //Copy the home directory
927  osStrcpy(fullPath, session->homeDir);
928  }
929 
930  //Append a slash character to the root directory
931  if(fullPath[0] != '\0')
932  pathAddSlash(fullPath, maxLen);
933 
934  //Retrieve the length of the path name
935  n = osStrlen(fullPath);
936 
937  //Check the length of the full path name
938  if((n + path->length) > maxLen)
939  return ERROR_FAILURE;
940 
941  //Append the specified path
942  osStrncpy(fullPath + n, path->value, path->length);
943  //Properly terminate the string with a NULL character
944  fullPath[n + path->length] = '\0';
945 
946  //Clean the resulting path
947  pathCanonicalize(fullPath);
948  pathRemoveSlash(fullPath);
949 
950  //Calculate the length of the home directory
951  n = osStrlen(session->rootDir);
952 
953  //If the server implementation limits access to certain parts of the file
954  //system, it must be extra careful in parsing file names when enforcing
955  //such restrictions
956  if(osStrncmp(fullPath, session->rootDir, n))
957  return ERROR_INVALID_PATH;
958 
959  //Successful processing
960  return NO_ERROR;
961 }
962 
963 
964 /**
965  * @brief Strip root dir from specified pathname
966  * @param[in] session Handle referencing an SFTP session
967  * @param[in] path input pathname
968  * @return Resulting pathname with root dir stripped
969  **/
970 
972  const char_t *path)
973 {
974  //Default directory
975  static const char_t defaultDir[] = "/";
976 
977  //Local variables
978  size_t m;
979  size_t n;
980 
981  //Retrieve the length of the root directory
982  n = osStrlen(session->rootDir);
983  //Retrieve the length of the specified pathname
984  m = osStrlen(path);
985 
986  //Strip the root dir from the specified pathname
987  if(n <= 1)
988  {
989  return path;
990  }
991  else if(n < m)
992  {
993  return path + n;
994  }
995  else
996  {
997  return defaultDir;
998  }
999 }
1000 
1001 #endif
uint8_t type
Definition: coap_common.h:176
unsigned int uint_t
Definition: compiler_port.h:50
#define PRIuSIZE
char char_t
Definition: compiler_port.h:48
int bool_t
Definition: compiler_port.h:53
#define ntohl(value)
Definition: cpu_endian.h:422
Debugging facilities.
#define TRACE_DEBUG(...)
Definition: debug.h:107
#define TRACE_WARNING(...)
Definition: debug.h:85
#define TRACE_INFO(...)
Definition: debug.h:95
#define TRACE_VERBOSE_ARRAY(p, a, n)
Definition: debug.h:125
uint8_t n
error_t
Error codes.
Definition: error.h:43
@ ERROR_INVALID_TYPE
Definition: error.h:115
@ ERROR_TIMEOUT
Definition: error.h:95
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
@ ERROR_INVALID_PATH
Definition: error.h:146
@ ERROR_INVALID_HANDLE
Definition: error.h:280
@ ERROR_ACCESS_DENIED
Definition: error.h:148
@ ERROR_UNKNOWN_REQUEST
Definition: error.h:276
@ NO_ERROR
Success.
Definition: error.h:44
@ ERROR_WRONG_STATE
Definition: error.h:209
@ ERROR_INVALID_LENGTH
Definition: error.h:111
@ ERROR_FAILURE
Generic error code.
Definition: error.h:45
uint8_t data[]
Definition: ethernet.h:222
void fsCloseFile(FsFile *file)
Close a file.
void fsCloseDir(FsDir *dir)
Close a directory stream.
uint8_t m
Definition: ndp.h:304
#define osMemset(p, value, length)
Definition: os_port.h:135
#define MIN(a, b)
Definition: os_port.h:63
#define osStrlen(s)
Definition: os_port.h:165
#define osStrncmp(s1, s2, length)
Definition: os_port.h:177
#define TRUE
Definition: os_port.h:50
#define FALSE
Definition: os_port.h:46
#define osStrcpy(s1, s2)
Definition: os_port.h:207
#define osStrncpy(s1, s2, length)
Definition: os_port.h:213
void osSetEvent(OsEvent *event)
Set the specified event object to the signaled state.
void pathCanonicalize(char_t *path)
Simplify a path.
Definition: path.c:150
void pathCopy(char_t *dest, const char_t *src, size_t maxLen)
Copy a path.
Definition: path.c:129
void pathAddSlash(char_t *path, size_t maxLen)
Add a slash to the end of a string.
Definition: path.c:312
void pathRemoveSlash(char_t *path)
Remove the trailing slash from a given path.
Definition: path.c:340
Path manipulation helper functions.
@ SSH_FILEXFER_TYPE_INVALID
Definition: sftp_common.h:189
@ SSH_FX_OK
Definition: sftp_common.h:171
@ SSH_FX_FAILURE
Definition: sftp_common.h:175
@ SSH_FXP_SETSTAT
Definition: sftp_common.h:143
@ SSH_FXP_RENAME
Definition: sftp_common.h:152
@ SSH_FXP_READ
Definition: sftp_common.h:139
@ SSH_FXP_FSETSTAT
Definition: sftp_common.h:144
@ SSH_FXP_MKDIR
Definition: sftp_common.h:148
@ SSH_FXP_EXTENDED
Definition: sftp_common.h:160
@ SSH_FXP_STAT
Definition: sftp_common.h:151
@ SSH_FXP_FSTAT
Definition: sftp_common.h:142
@ SSH_FXP_REMOVE
Definition: sftp_common.h:147
@ SSH_FXP_CLOSE
Definition: sftp_common.h:138
@ SSH_FXP_OPENDIR
Definition: sftp_common.h:145
@ SSH_FXP_LSTAT
Definition: sftp_common.h:141
@ SSH_FXP_RMDIR
Definition: sftp_common.h:149
@ SSH_FXP_REALPATH
Definition: sftp_common.h:150
@ SSH_FXP_INIT
Definition: sftp_common.h:135
@ SSH_FXP_OPEN
Definition: sftp_common.h:137
@ SSH_FXP_READDIR
Definition: sftp_common.h:146
@ SSH_FXP_WRITE
Definition: sftp_common.h:140
SftpPacketHeader
Definition: sftp_common.h:219
SFTP server.
SftpAccessStatus
Access status.
Definition: sftp_server.h:133
@ SFTP_ACCESS_ALLOWED
Definition: sftp_server.h:135
#define SFTP_SERVER_MAX_ROOT_DIR_LEN
Definition: sftp_server.h:95
@ SFTP_FILE_PERM_LIST
Definition: sftp_server.h:145
@ SFTP_FILE_PERM_READ
Definition: sftp_server.h:146
@ SFTP_FILE_PERM_WRITE
Definition: sftp_server.h:147
#define SftpServerSession
Definition: sftp_server.h:120
#define SftpServerContext
Definition: sftp_server.h:116
@ SFTP_SERVER_SESSION_STATE_RECEIVING_DATA
Definition: sftp_server.h:160
@ SFTP_SERVER_SESSION_STATE_SENDING_DATA
Definition: sftp_server.h:161
@ SFTP_SERVER_SESSION_STATE_RECEIVING
Definition: sftp_server.h:158
@ SFTP_SERVER_SESSION_STATE_CLOSED
Definition: sftp_server.h:157
@ SFTP_SERVER_SESSION_STATE_SENDING
Definition: sftp_server.h:159
#define SFTP_SERVER_MAX_HOME_DIR_LEN
Definition: sftp_server.h:102
#define SFTP_SERVER_BUFFER_SIZE
Definition: sftp_server.h:88
Directory operations.
error_t sftpServerReadData(SftpServerSession *session)
Read data from the specified file.
error_t sftpServerWriteData(SftpServerSession *session)
Write data to the specified file.
File operations.
void sftpServerCloseSession(SftpServerSession *session)
Close an SFTP session.
void sftpServerProcessSessionEvents(SftpServerSession *session)
Session event handler.
error_t sftpServerChannelRequestCallback(SshChannel *channel, const SshString *type, const uint8_t *data, size_t length, void *param)
SSH channel request callback.
void sftpServerTick(SftpServerContext *context)
Handle periodic operations.
uint_t sftpServerGetFilePermissions(SftpServerSession *session, const char_t *path)
Get permissions for the specified file or directory.
error_t sftpServerParsePacket(SftpServerSession *session, const uint8_t *packet, size_t fragLen, size_t totalLen)
SFTP packet processing.
uint32_t sftpServerGenerateHandle(SftpServerSession *session)
Generate a unique handle.
void sftpServerRegisterSessionEvents(SftpServerSession *session, SshChannelEventDesc *eventDesc)
Register session events.
error_t sftpServerParsePacketLength(SftpServerSession *session, const uint8_t *packet)
Retrieve the length of an incoming SFTP packet.
SftpServerSession * sftpServerOpenSession(SftpServerContext *context, SshChannel *channel)
Open a new SFTP session.
const char_t * sftpServerStripRootDir(SftpServerSession *session, const char_t *path)
Strip root dir from specified pathname.
SftpServerSession * sftpServerFindSession(SftpServerContext *context, SshChannel *channel)
Find the SFTP session that matches a given SSH channel.
error_t sftpServerGetPath(SftpServerSession *session, const SshString *path, char_t *fullPath, size_t maxLen)
Retrieve the full pathname.
Helper functions for SFTP server.
error_t sftpServerParseFxpExtended(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_EXTENDED packet.
error_t sftpServerParseFxpOpenDir(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_OPENDIR packet.
error_t sftpServerParseFxpRead(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_READ packet.
error_t sftpServerParseFxpWrite(SftpServerSession *session, const uint8_t *packet, size_t fragLen, size_t totalLen)
Parse SSH_FXP_WRITE packet.
error_t sftpServerParseFxpSetFstat(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_FSETSTAT packet.
error_t sftpServerParseFxpSetStat(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_SETSTAT packet.
error_t sftpServerParseFxpRemove(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_REMOVE packet.
error_t sftpServerParseFxpClose(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_CLOSE packet.
error_t sftpServerParseFxpRename(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_RENAME packet.
error_t sftpServerParseFxpRmDir(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_RMDIR packet.
error_t sftpServerParseFxpFstat(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_FSTAT packet.
error_t sftpFormatFxpStatus(SftpServerSession *session, uint32_t id, uint32_t statusCode, const char_t *message)
Format SSH_FXP_STATUS message.
error_t sftpServerParseFxpInit(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_INIT packet.
error_t sftpServerParseFxpOpen(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_OPEN packet.
error_t sftpServerParseFxpStat(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_STAT packet.
error_t sftpServerParseFxpReadDir(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_READDIR packet.
error_t sftpServerParseFxpMkDir(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_MKDIR packet.
error_t sftpServerParseFxpRealPath(SftpServerSession *session, const uint8_t *packet, size_t length)
Parse SSH_FXP_REALPATH packet.
SFTP packet parsing and formatting.
error_t sshSetChannelTimeout(SshChannel *channel, systime_t timeout)
Set timeout for read/write operations.
Definition: ssh.c:2027
error_t sshCloseChannel(SshChannel *channel)
Close channel.
Definition: ssh.c:2465
error_t sshWriteChannel(SshChannel *channel, const void *data, size_t length, size_t *written, uint_t flags)
Write data to the specified channel.
Definition: ssh.c:2051
error_t sshReadChannel(SshChannel *channel, void *data, size_t size, size_t *received, uint_t flags)
Receive data from the specified channel.
Definition: ssh.c:2180
Secure Shell (SSH)
#define SshChannel
Definition: ssh.h:887
@ SSH_CHANNEL_EVENT_TX_READY
Definition: ssh.h:1111
@ SSH_CHANNEL_EVENT_RX_READY
Definition: ssh.h:1115
bool_t sshCompareString(const SshString *string, const char_t *value)
Compare a binary string against the supplied value.
Definition: ssh_misc.c:1586
SSH helper functions.
error_t sshParseSubsystemParams(const uint8_t *p, size_t length, SshSubsystemParams *params)
Parse "subsystem" channel request parameters.
Definition: ssh_request.c:1590
Global request and channel request handling.
String manipulation helper functions.
File or directory object.
Definition: sftp_server.h:186
FsFile * file
File pointer.
Definition: sftp_server.h:193
SftpFileType type
File type.
Definition: sftp_server.h:187
FsDir * dir
Directory pointer.
Definition: sftp_server.h:194
uint32_t handle
Opaque value that identifies the file.
Definition: sftp_server.h:190
SftpServerSession * session
Pointer to the SFTP session.
Definition: sftp_server.h:188
Structure describing channel events.
Definition: ssh.h:1560
uint_t eventMask
Requested events.
Definition: ssh.h:1562
SshChannel * channel
Handle to a channel to monitor.
Definition: ssh.h:1561
uint_t eventFlags
Returned events.
Definition: ssh.h:1563
String.
Definition: ssh_types.h:56
const char_t * value
Definition: ssh_types.h:57
size_t length
Definition: ssh_types.h:58
"subsystem" channel request parameters
Definition: ssh_request.h:129
SshString subsystemName
Definition: ssh_request.h:130
uint8_t length
Definition: tcp.h:368