CVE-2023-4091: smbd: use open_access_mask for access check in open_file()
[Samba.git] / source3 / lib / util_path.c
blob9c9c6eb580733b38ae1c07fe615c57c8d9e0b5ab
1 /*
2 * Unix SMB/CIFS implementation.
3 * Samba utility functions
4 * Copyright (C) Andrew Tridgell 1992-1998
5 * Copyright (C) Jeremy Allison 2001-2007
6 * Copyright (C) Simo Sorce 2001
7 * Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2003
8 * Copyright (C) James Peach 2006
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 3 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "replace.h"
25 #include <talloc.h>
26 #include "lib/util/debug.h"
27 #include "lib/util/samba_util.h"
28 #include "lib/util_path.h"
30 struct loadparm_substitution;
31 struct share_params;
32 #include "source3/param/param_proto.h"
34 /**
35 * @brief Returns an absolute path to a file concatenating the provided
36 * @a rootpath and @a basename
38 * @param name Filename, relative to @a rootpath
40 * @retval Pointer to a string containing the full path.
41 **/
43 static char *xx_path(TALLOC_CTX *mem_ctx,
44 const char *name,
45 const char *rootpath)
47 char *fname = NULL;
49 fname = talloc_strdup(mem_ctx, rootpath);
50 if (!fname) {
51 return NULL;
53 trim_string(fname,"","/");
55 if (!directory_create_or_exist(fname, 0755)) {
56 return NULL;
59 return talloc_asprintf_append(fname, "/%s", name);
62 /**
63 * @brief Returns an absolute path to a file in the Samba lock directory.
65 * @param name File to find, relative to LOCKDIR.
67 * @retval Pointer to a talloc'ed string containing the full path.
68 **/
70 char *lock_path(TALLOC_CTX *mem_ctx, const char *name)
72 return xx_path(mem_ctx, name, lp_lock_directory());
75 /**
76 * @brief Returns an absolute path to a file in the Samba state directory.
78 * @param name File to find, relative to STATEDIR.
80 * @retval Pointer to a talloc'ed string containing the full path.
81 **/
83 char *state_path(TALLOC_CTX *mem_ctx, const char *name)
85 return xx_path(mem_ctx, name, lp_state_directory());
88 /**
89 * @brief Returns an absolute path to a file in the Samba cache directory.
91 * @param name File to find, relative to CACHEDIR.
93 * @retval Pointer to a talloc'ed string containing the full path.
94 **/
96 char *cache_path(TALLOC_CTX *mem_ctx, const char *name)
98 return xx_path(mem_ctx, name, lp_cache_directory());
102 * @brief Removes any invalid path components in an absolute POSIX path.
104 * @param ctx Talloc context to return string.
106 * @param abs_path Absolute path string to process.
108 * @retval Pointer to a talloc'ed string containing the absolute full path.
111 char *canonicalize_absolute_path(TALLOC_CTX *ctx, const char *pathname_in)
114 * Note we use +2 here so if pathname_in=="" then we
115 * have space to return "/".
117 char *pathname = talloc_array(ctx, char, strlen(pathname_in)+2);
118 const char *s = pathname_in;
119 char *p = pathname;
121 if (pathname == NULL) {
122 return NULL;
125 /* Always start with a '/'. */
126 *p++ = '/';
128 while (*s) {
129 /* Deal with '/' or multiples of '/'. */
130 if (s[0] == '/') {
131 while (s[0] == '/') {
132 /* Eat trailing '/' */
133 s++;
135 /* Update target with one '/' */
136 if (p[-1] != '/') {
137 *p++ = '/';
139 continue;
141 if (p[-1] == '/') {
142 /* Deal with "./" or ".\0" */
143 if (s[0] == '.' &&
144 (s[1] == '/' || s[1] == '\0')) {
145 /* Eat the dot. */
146 s++;
147 while (s[0] == '/') {
148 /* Eat any trailing '/' */
149 s++;
151 /* Don't write anything to target. */
152 continue;
154 /* Deal with "../" or "..\0" */
155 if (s[0] == '.' && s[1] == '.' &&
156 (s[2] == '/' || s[2] == '\0')) {
157 /* Eat the dot dot. */
158 s += 2;
159 while (s[0] == '/') {
160 /* Eat any trailing '/' */
161 s++;
164 * As we're on the slash, we go back
165 * one character to point p at the
166 * slash we just saw.
168 if (p > pathname) {
169 p--;
172 * Now go back to the slash
173 * before the one that p currently points to.
175 while (p > pathname) {
176 p--;
177 if (p[0] == '/') {
178 break;
182 * Step forward one to leave the
183 * last written '/' alone.
185 p++;
187 /* Don't write anything to target. */
188 continue;
191 /* Non-separator character, just copy. */
192 *p++ = *s++;
194 if (p[-1] == '/') {
196 * We finished on a '/'.
197 * Remove the trailing '/', but not if it's
198 * the sole character in the path.
200 if (p > pathname + 1) {
201 p--;
204 /* Terminate and we're done ! */
205 *p++ = '\0';
206 return pathname;
209 static bool find_snapshot_token(
210 const char *filename,
211 char sep,
212 const char **_start,
213 const char **_next_component,
214 NTTIME *twrp)
216 const char *start = NULL;
217 const char *end = NULL;
218 struct tm tm;
219 time_t t;
221 start = strstr_m(filename, "@GMT-");
223 if (start == NULL) {
224 return false;
227 if ((start > filename) && (start[-1] != sep)) {
228 /* the GMT-token does not start a path-component */
229 return false;
232 end = strptime(start, GMT_FORMAT, &tm);
233 if (end == NULL) {
234 /* Not a valid timestring. */
235 return false;
238 if ((end[0] != '\0') && (end[0] != sep)) {
240 * It is not a complete path component, i.e. the path
241 * component continues after the gmt-token.
243 return false;
246 tm.tm_isdst = -1;
247 t = timegm(&tm);
248 unix_to_nt_time(twrp, t);
250 DBG_DEBUG("Extracted @GMT-Timestamp %s\n",
251 nt_time_string(talloc_tos(), *twrp));
253 *_start = start;
255 if (end[0] == sep) {
256 end += 1;
258 *_next_component = end;
260 return true;
263 bool clistr_is_previous_version_path(const char *path)
265 const char *start = NULL;
266 const char *next = NULL;
267 NTTIME twrp;
268 bool ok;
270 ok = find_snapshot_token(path, '\\', &start, &next, &twrp);
271 return ok;
274 static bool extract_snapshot_token_internal(char *fname, NTTIME *twrp, char sep)
276 const char *start = NULL;
277 const char *next = NULL;
278 size_t remaining;
279 bool found;
281 found = find_snapshot_token(fname, sep, &start, &next, twrp);
282 if (!found) {
283 return false;
286 remaining = strlen(next);
287 memmove(discard_const_p(char, start), next, remaining+1);
289 return true;
292 bool extract_snapshot_token(char *fname, NTTIME *twrp)
294 return extract_snapshot_token_internal(fname, twrp, '/');
297 bool clistr_smb2_extract_snapshot_token(char *fname, NTTIME *twrp)
299 return extract_snapshot_token_internal(fname, twrp, '\\');
303 * Take two absolute paths, figure out if "subdir" is a proper
304 * subdirectory of "parent". Return the component relative to the
305 * "parent" without the potential "/". Take care of "parent"
306 * possibly ending in "/".
308 bool subdir_of(const char *parent,
309 size_t parent_len,
310 const char *subdir,
311 const char **_relative)
313 const char *relative = NULL;
314 bool matched;
316 SMB_ASSERT(parent[0] == '/');
317 SMB_ASSERT(subdir[0] == '/');
319 if (parent_len == 1) {
321 * Everything is below "/"
323 *_relative = subdir+1;
324 return true;
327 if (parent[parent_len-1] == '/') {
328 parent_len -= 1;
331 matched = (strncmp(subdir, parent, parent_len) == 0);
332 if (!matched) {
333 return false;
336 relative = &subdir[parent_len];
338 if (relative[0] == '\0') {
339 *_relative = relative; /* nothing left */
340 return true;
343 if (relative[0] == '/') {
344 /* End of parent must match a '/' in subdir. */
345 *_relative = relative+1;
346 return true;
349 return false;