mirror of
https://github.com/seregonwar/zftpd.git
synced 2026-10-23 20:22:25 -05:00
Add a lightweight logging subsystem and MIT headers, enable unit tests, and harden session/path handling. Key changes: - Introduce ftp_log.h and src/ftp_log.c for session/command logging and line logging. - Add MIT license blocks and update file dates across many headers/sources. - Makefile: include src/ftp_log.c, add LIB_OBJECTS (build objects without main) and multi-test support, PS5 link libSceNotification, and skip tests on unsupported targets. - Add tests/tests: tests/test_security.c and tests/test_path_security.c and Makefile rules to build/run them. - Extend ftp_session_t with root_path, control RX buffer, auth flags, and rate-limiter fields; initialize realpath(root) and set socket timeouts during init. - Add compile-time config knobs in include/ftp_config.h (control/data timeouts, transfer rate limits and burst size). - Improve RETR/STOR/APPE flows: aggregate bytes sent/received, use new logging hooks, prefer sendfile only when no rate limit, and unify data send path via ftp_session_send_data. - Strengthen PORT/PASV handling: restrict PORT to same client IP, reuse/close previous pasv socket, bind PASV to INADDR_ANY, and robustly determine local IP for PASV response. - Enhance path normalization and resolution (ftp_path.c): safer component parsing, realpath checks, and validation against session root to prevent traversal/symlink escapes. - Add pal API additions in headers (pal_socket_set_timeouts, pal_send_all) and use pal_send_all for control replies. - Add helper utilities in ftp_session.c (wait_fd_ready, monotonic_ns) and other misc cleanups. Overall this commit focuses on observability, testability, and security/robustness of session, path and data handling.
218 lines
6.1 KiB
C
218 lines
6.1 KiB
C
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2026 Seregon
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in all
|
|
copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
SOFTWARE.
|
|
*/
|
|
|
|
/**
|
|
* @file ftp_path.h
|
|
* @brief Secure path validation and normalization
|
|
*
|
|
* @author SeregonWar
|
|
* @version 1.0.0
|
|
* @date 2026-02-13
|
|
*
|
|
* SECURITY: Prevents directory traversal attacks (../, symlinks, etc.)
|
|
* ALGORITHM: Stack-based path component processing
|
|
*
|
|
*/
|
|
|
|
#ifndef FTP_PATH_H
|
|
#define FTP_PATH_H
|
|
|
|
#include "ftp_types.h"
|
|
|
|
/*===========================================================================*
|
|
* PATH VALIDATION
|
|
*===========================================================================*/
|
|
|
|
/**
|
|
* @brief Normalize path (remove .., ., //)
|
|
*
|
|
* SECURITY: Essential for preventing directory traversal attacks
|
|
*
|
|
* ALGORITHM:
|
|
* 1. Split path into components (delimiter: '/')
|
|
* 2. Process each component:
|
|
* - "." -> skip (current directory)
|
|
* - ".." -> pop stack (parent directory)
|
|
* - other -> push to stack
|
|
* 3. Reconstruct path from stack
|
|
*
|
|
* EXAMPLES:
|
|
* "/home/user/../admin" -> "/home/admin"
|
|
* "/home/./user" -> "/home/user"
|
|
* "/home//user" -> "/home/user"
|
|
* "/../etc/passwd" -> "/etc/passwd"
|
|
*
|
|
* @param path Input path (null-terminated)
|
|
* @param output Output buffer for normalized path
|
|
* @param size Size of output buffer
|
|
*
|
|
* @return FTP_OK on success, negative error code on failure
|
|
* @retval FTP_OK Path normalized successfully
|
|
* @retval FTP_ERR_INVALID_PARAM Invalid parameters
|
|
* @retval FTP_ERR_PATH_TOO_LONG Path exceeds buffer size
|
|
*
|
|
* @pre path != NULL
|
|
* @pre output != NULL
|
|
* @pre size >= FTP_PATH_MAX
|
|
*
|
|
* @post output contains normalized path (absolute)
|
|
* @post output is null-terminated
|
|
*
|
|
* @note WCET: O(n) where n = strlen(path)
|
|
* @note Thread-safety: Safe (no shared state)
|
|
*
|
|
* @warning Does not resolve symlinks (use realpath for that)
|
|
*/
|
|
ftp_error_t ftp_path_normalize(const char *path,
|
|
char *output,
|
|
size_t size);
|
|
|
|
/**
|
|
* @brief Resolve path relative to session CWD
|
|
*
|
|
* Converts relative paths to absolute paths:
|
|
* - Absolute path ("/foo") -> keep as-is
|
|
* - Relative path ("foo") -> prepend CWD
|
|
*
|
|
* @param session Client session (for CWD context)
|
|
* @param path User-supplied path
|
|
* @param output Output buffer for resolved path
|
|
* @param size Size of output buffer
|
|
*
|
|
* @return FTP_OK on success, negative error code on failure
|
|
*
|
|
* @pre session != NULL
|
|
* @pre path != NULL
|
|
* @pre output != NULL
|
|
* @pre size >= FTP_PATH_MAX
|
|
*
|
|
* @post output contains absolute, normalized path
|
|
*/
|
|
ftp_error_t ftp_path_resolve(const ftp_session_t *session,
|
|
const char *path,
|
|
char *output,
|
|
size_t size);
|
|
|
|
/**
|
|
* @brief Check if path is within server root
|
|
*
|
|
* SECURITY: Ensures clients cannot escape chroot jail
|
|
*
|
|
* @param path Absolute path to check
|
|
* @param root Server root directory
|
|
*
|
|
* @return 1 if path is within root, 0 if not, negative on error
|
|
*
|
|
* @pre path != NULL
|
|
* @pre root != NULL
|
|
* @pre path is absolute (starts with '/')
|
|
* @pre root is absolute (starts with '/')
|
|
*
|
|
* @note WCET: O(min(strlen(path), strlen(root)))
|
|
*/
|
|
int ftp_path_is_within_root(const char *path, const char *root);
|
|
|
|
/**
|
|
* @brief Validate path safety
|
|
*
|
|
* Checks for:
|
|
* - Null bytes (string injection)
|
|
* - Excessive length
|
|
* - Invalid characters
|
|
*
|
|
* @param path Path to validate
|
|
*
|
|
* @return 1 if safe, 0 if unsafe, negative on error
|
|
*
|
|
* @pre path != NULL
|
|
*/
|
|
int ftp_path_is_safe(const char *path);
|
|
|
|
/**
|
|
* @brief Get basename from path
|
|
*
|
|
* EXAMPLE: "/home/user/file.txt" -> "file.txt"
|
|
*
|
|
* @param path Input path
|
|
* @param basename Output buffer for basename
|
|
* @param size Size of output buffer
|
|
*
|
|
* @return FTP_OK on success, negative error code on failure
|
|
*
|
|
* @pre path != NULL
|
|
* @pre basename != NULL
|
|
* @pre size > 0
|
|
*
|
|
* @post basename contains filename component
|
|
*/
|
|
ftp_error_t ftp_path_basename(const char *path,
|
|
char *basename,
|
|
size_t size);
|
|
|
|
/**
|
|
* @brief Get directory name from path
|
|
*
|
|
* EXAMPLE: "/home/user/file.txt" -> "/home/user"
|
|
*
|
|
* @param path Input path
|
|
* @param dirname Output buffer for directory
|
|
* @param size Size of output buffer
|
|
*
|
|
* @return FTP_OK on success, negative error code on failure
|
|
*
|
|
* @pre path != NULL
|
|
* @pre dirname != NULL
|
|
* @pre size > 0
|
|
*/
|
|
ftp_error_t ftp_path_dirname(const char *path,
|
|
char *dirname,
|
|
size_t size);
|
|
|
|
/**
|
|
* @brief Join two path components
|
|
*
|
|
* EXAMPLE: ("/home/user", "file.txt") -> "/home/user/file.txt"
|
|
*
|
|
* @param base Base path
|
|
* @param append Path to append
|
|
* @param output Output buffer
|
|
* @param size Size of output buffer
|
|
*
|
|
* @return FTP_OK on success, negative error code on failure
|
|
*
|
|
* @pre base != NULL
|
|
* @pre append != NULL
|
|
* @pre output != NULL
|
|
* @pre size >= FTP_PATH_MAX
|
|
*
|
|
* @post output contains joined path
|
|
* @post output is normalized
|
|
*/
|
|
ftp_error_t ftp_path_join(const char *base,
|
|
const char *append,
|
|
char *output,
|
|
size_t size);
|
|
|
|
#endif /* FTP_PATH_H */
|