Seregon/zftpd

Zero-copy FTP/HTTP Daemon compatible with all POSIX systems

C/11.0 KB/No license
include/ftp_path.h
zftpd / include / ftp_path.h
1/*
2MIT License
3 
4Copyright (c) 2026 Seregon
5 
6Permission is hereby granted, free of charge, to any person obtaining a copy
7of this software and associated documentation files (the "Software"), to deal
8in the Software without restriction, including without limitation the rights
9to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10copies of the Software, and to permit persons to whom the Software is
11furnished to do so, subject to the following conditions:
12 
13The above copyright notice and this permission notice shall be included in all
14copies or substantial portions of the Software.
15 
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22SOFTWARE.
23*/
24 
25/**
26 * @file ftp_path.h
27 * @brief Secure path validation and normalization
28 *
29 * @author SeregonWar
30 * @version 1.0.0
31 * @date 2026-02-13
32 *
33 * SECURITY: Prevents directory traversal attacks (../, symlinks, etc.)
34 * ALGORITHM: Stack-based path component processing
35 *
36 */
37 
38#ifndef FTP_PATH_H
39#define FTP_PATH_H
40 
41#include "ftp_types.h"
42 
43/*===========================================================================*
44 * PATH VALIDATION
45 *===========================================================================*/
46 
47/**
48 * @brief Normalize path (remove .., ., //)
49 *
50 * SECURITY: Essential for preventing directory traversal attacks
51 *
52 * ALGORITHM:
53 * 1. Split path into components (delimiter: '/')
54 * 2. Process each component:
55 * - "." -> skip (current directory)
56 * - ".." -> pop stack (parent directory)
57 * - other -> push to stack
58 * 3. Reconstruct path from stack
59 *
60 * EXAMPLES:
61 * "/home/user/../admin" -> "/home/admin"
62 * "/home/./user" -> "/home/user"
63 * "/home//user" -> "/home/user"
64 * "/../etc/passwd" -> "/etc/passwd"
65 *
66 * @param path Input path (null-terminated)
67 * @param output Output buffer for normalized path
68 * @param size Size of output buffer
69 *
70 * @return FTP_OK on success, negative error code on failure
71 * @retval FTP_OK Path normalized successfully
72 * @retval FTP_ERR_INVALID_PARAM Invalid parameters
73 * @retval FTP_ERR_PATH_TOO_LONG Path exceeds buffer size
74 *
75 * @pre path != NULL
76 * @pre output != NULL
77 * @pre size >= FTP_PATH_MAX
78 *
79 * @post output contains normalized path (absolute)
80 * @post output is null-terminated
81 *
82 * @note WCET: O(n) where n = strlen(path)
83 * @note Thread-safety: Safe (no shared state)
84 *
85 * @warning Does not resolve symlinks (use realpath for that)
86 */
87ftp_error_t ftp_path_normalize(const char *path,
88 char *output,
89 size_t size);
90 
91/**
92 * @brief Resolve path relative to session CWD
93 *
94 * Converts relative paths to absolute paths:
95 * - Absolute path ("/foo") -> keep as-is
96 * - Relative path ("foo") -> prepend CWD
97 *
98 * @param session Client session (for CWD context)
99 * @param path User-supplied path
100 * @param output Output buffer for resolved path
101 * @param size Size of output buffer
102 *
103 * @return FTP_OK on success, negative error code on failure
104 *
105 * @pre session != NULL
106 * @pre path != NULL
107 * @pre output != NULL
108 * @pre size >= FTP_PATH_MAX
109 *
110 * @post output contains absolute, normalized path
111 */
112ftp_error_t ftp_path_resolve(const ftp_session_t *session,
113 const char *path,
114 char *output,
115 size_t size);
116 
117/**
118 * @brief Check if path is within server root
119 *
120 * SECURITY: Ensures clients cannot escape chroot jail
121 *
122 * @param path Absolute path to check
123 * @param root Server root directory
124 *
125 * @return 1 if path is within root, 0 if not, negative on error
126 *
127 * @pre path != NULL
128 * @pre root != NULL
129 * @pre path is absolute (starts with '/')
130 * @pre root is absolute (starts with '/')
131 *
132 * @note WCET: O(min(strlen(path), strlen(root)))
133 */
134int ftp_path_is_within_root(const char *path, const char *root);
135 
136/**
137 * @brief Validate path safety
138 *
139 * Checks for:
140 * - Null bytes (string injection)
141 * - Excessive length
142 * - Invalid characters
143 *
144 * @param path Path to validate
145 *
146 * @return 1 if safe, 0 if unsafe, negative on error
147 *
148 * @pre path != NULL
149 */
150int ftp_path_is_safe(const char *path);
151 
152/**
153 * @brief Get basename from path
154 *
155 * EXAMPLE: "/home/user/file.txt" -> "file.txt"
156 *
157 * @param path Input path
158 * @param basename Output buffer for basename
159 * @param size Size of output buffer
160 *
161 * @return FTP_OK on success, negative error code on failure
162 *
163 * @pre path != NULL
164 * @pre basename != NULL
165 * @pre size > 0
166 *
167 * @post basename contains filename component
168 */
169ftp_error_t ftp_path_basename(const char *path,
170 char *basename,
171 size_t size);
172 
173/**
174 * @brief Get directory name from path
175 *
176 * EXAMPLE: "/home/user/file.txt" -> "/home/user"
177 *
178 * @param path Input path
179 * @param dirname Output buffer for directory
180 * @param size Size of output buffer
181 *
182 * @return FTP_OK on success, negative error code on failure
183 *
184 * @pre path != NULL
185 * @pre dirname != NULL
186 * @pre size > 0
187 */
188ftp_error_t ftp_path_dirname(const char *path,
189 char *dirname,
190 size_t size);
191 
192/**
193 * @brief Join two path components
194 *
195 * EXAMPLE: ("/home/user", "file.txt") -> "/home/user/file.txt"
196 *
197 * @param base Base path
198 * @param append Path to append
199 * @param output Output buffer
200 * @param size Size of output buffer
201 *
202 * @return FTP_OK on success, negative error code on failure
203 *
204 * @pre base != NULL
205 * @pre append != NULL
206 * @pre output != NULL
207 * @pre size >= FTP_PATH_MAX
208 *
209 * @post output contains joined path
210 * @post output is normalized
211 */
212ftp_error_t ftp_path_join(const char *base,
213 const char *append,
214 char *output,
215 size_t size);
216 
217#endif /* FTP_PATH_H */
218