/src/samba/source3/smbd/filename.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | filename handling routines |
4 | | Copyright (C) Andrew Tridgell 1992-1998 |
5 | | Copyright (C) Jeremy Allison 1999-2007 |
6 | | Copyright (C) Ying Chen 2000 |
7 | | Copyright (C) Volker Lendecke 2007 |
8 | | |
9 | | This program is free software; you can redistribute it and/or modify |
10 | | it under the terms of the GNU General Public License as published by |
11 | | the Free Software Foundation; either version 3 of the License, or |
12 | | (at your option) any later version. |
13 | | |
14 | | This program is distributed in the hope that it will be useful, |
15 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | GNU General Public License for more details. |
18 | | |
19 | | You should have received a copy of the GNU General Public License |
20 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | /* |
24 | | * New hash table stat cache code added by Ying Chen. |
25 | | */ |
26 | | |
27 | | #include "includes.h" |
28 | | #include "system/filesys.h" |
29 | | #include "fake_file.h" |
30 | | #include "smbd/smbd.h" |
31 | | #include "smbd/globals.h" |
32 | | #include "libcli/smb/reparse.h" |
33 | | #include "source3/smbd/dir.h" |
34 | | |
35 | | uint32_t ucf_flags_from_smb_request(struct smb_request *req) |
36 | 0 | { |
37 | 0 | uint32_t ucf_flags = 0; |
38 | |
|
39 | 0 | if (req == NULL) { |
40 | 0 | return 0; |
41 | 0 | } |
42 | | |
43 | 0 | if (req->posix_pathnames) { |
44 | 0 | ucf_flags |= (UCF_POSIX_PATHNAMES|UCF_LCOMP_LNK_OK); |
45 | 0 | } |
46 | 0 | if (req->flags2 & FLAGS2_DFS_PATHNAMES) { |
47 | 0 | ucf_flags |= UCF_DFS_PATHNAME; |
48 | 0 | } |
49 | 0 | if (req->flags2 & FLAGS2_REPARSE_PATH) { |
50 | 0 | ucf_flags |= UCF_GMT_PATHNAME; |
51 | 0 | } |
52 | |
|
53 | 0 | return ucf_flags; |
54 | 0 | } |
55 | | |
56 | | uint32_t filename_create_ucf_flags(struct smb_request *req, |
57 | | uint32_t create_disposition, |
58 | | uint32_t create_options) |
59 | 0 | { |
60 | 0 | uint32_t ucf_flags = 0; |
61 | |
|
62 | 0 | ucf_flags |= ucf_flags_from_smb_request(req); |
63 | |
|
64 | 0 | switch (create_disposition) { |
65 | 0 | case FILE_OPEN: |
66 | 0 | case FILE_OVERWRITE: |
67 | 0 | break; |
68 | 0 | case FILE_SUPERSEDE: |
69 | 0 | case FILE_CREATE: |
70 | 0 | case FILE_OPEN_IF: |
71 | 0 | case FILE_OVERWRITE_IF: |
72 | 0 | ucf_flags |= UCF_PREP_CREATEFILE; |
73 | 0 | break; |
74 | 0 | } |
75 | | |
76 | 0 | if (create_options & FILE_OPEN_REPARSE_POINT) { |
77 | 0 | ucf_flags |= UCF_LCOMP_LNK_OK; |
78 | 0 | } |
79 | |
|
80 | 0 | return ucf_flags; |
81 | 0 | } |
82 | | |
83 | | /**************************************************************************** |
84 | | Mangle the 2nd name and check if it is then equal to the first name. |
85 | | ****************************************************************************/ |
86 | | |
87 | | static bool mangled_equal(const char *name1, |
88 | | const char *name2, |
89 | | const struct share_params *p) |
90 | 0 | { |
91 | 0 | char mname[13]; |
92 | |
|
93 | 0 | if (!name_to_8_3(name2, mname, False, p)) { |
94 | 0 | return False; |
95 | 0 | } |
96 | 0 | return strequal(name1, mname); |
97 | 0 | } |
98 | | |
99 | | /* |
100 | | * Strip a valid @GMT-token from any incoming filename path, |
101 | | * adding any NTTIME encoded in the pathname into the |
102 | | * twrp field of the passed in smb_fname. |
103 | | * |
104 | | * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS |
105 | | * at the *start* of a pathname component. |
106 | | * |
107 | | * If twrp is passed in then smb_fname->twrp is set to that |
108 | | * value, and the @GMT-token part of the filename is removed |
109 | | * and does not change the stored smb_fname->twrp. |
110 | | * |
111 | | */ |
112 | | |
113 | | NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname, |
114 | | uint32_t ucf_flags, |
115 | | NTTIME twrp) |
116 | 0 | { |
117 | 0 | bool found; |
118 | |
|
119 | 0 | if (twrp != 0) { |
120 | 0 | smb_fname->twrp = twrp; |
121 | 0 | } |
122 | |
|
123 | 0 | if (!(ucf_flags & UCF_GMT_PATHNAME)) { |
124 | 0 | return NT_STATUS_OK; |
125 | 0 | } |
126 | | |
127 | 0 | found = extract_snapshot_token(smb_fname->base_name, &twrp); |
128 | 0 | if (!found) { |
129 | 0 | return NT_STATUS_OK; |
130 | 0 | } |
131 | | |
132 | 0 | if (smb_fname->twrp == 0) { |
133 | 0 | smb_fname->twrp = twrp; |
134 | 0 | } |
135 | |
|
136 | 0 | return NT_STATUS_OK; |
137 | 0 | } |
138 | | |
139 | | static bool strnorm(char *s, int case_default) |
140 | 0 | { |
141 | 0 | if (case_default == CASE_UPPER) |
142 | 0 | return strupper_m(s); |
143 | 0 | else |
144 | 0 | return strlower_m(s); |
145 | 0 | } |
146 | | |
147 | | /* |
148 | | * Utility function to normalize case on an incoming client filename |
149 | | * if required on this connection struct. |
150 | | * Performs an in-place case conversion guaranteed to stay the same size. |
151 | | */ |
152 | | |
153 | | static NTSTATUS normalize_filename_case(connection_struct *conn, |
154 | | char *filename, |
155 | | uint32_t ucf_flags) |
156 | 0 | { |
157 | 0 | bool ok; |
158 | |
|
159 | 0 | if (ucf_flags & UCF_POSIX_PATHNAMES) { |
160 | | /* |
161 | | * POSIX never normalizes filename case. |
162 | | */ |
163 | 0 | return NT_STATUS_OK; |
164 | 0 | } |
165 | 0 | if (!conn->case_sensitive) { |
166 | 0 | return NT_STATUS_OK; |
167 | 0 | } |
168 | 0 | if (conn->case_preserve) { |
169 | 0 | return NT_STATUS_OK; |
170 | 0 | } |
171 | 0 | if (conn->short_case_preserve) { |
172 | 0 | return NT_STATUS_OK; |
173 | 0 | } |
174 | 0 | ok = strnorm(filename, lp_default_case(SNUM(conn))); |
175 | 0 | if (!ok) { |
176 | 0 | return NT_STATUS_INVALID_PARAMETER; |
177 | 0 | } |
178 | 0 | return NT_STATUS_OK; |
179 | 0 | } |
180 | | |
181 | | /**************************************************************************** |
182 | | Check if two filenames are equal. |
183 | | This needs to be careful about whether we are case sensitive. |
184 | | ****************************************************************************/ |
185 | | |
186 | | static bool fname_equal(const char *name1, const char *name2, |
187 | | bool case_sensitive) |
188 | 0 | { |
189 | | /* Normal filename handling */ |
190 | 0 | if (case_sensitive) { |
191 | 0 | return(strcmp(name1,name2) == 0); |
192 | 0 | } |
193 | | |
194 | 0 | return(strequal(name1,name2)); |
195 | 0 | } |
196 | | |
197 | | static bool sname_equal(const char *name1, const char *name2, |
198 | | bool case_sensitive) |
199 | 0 | { |
200 | 0 | bool match; |
201 | 0 | const char *s1 = NULL; |
202 | 0 | const char *s2 = NULL; |
203 | 0 | size_t n1; |
204 | 0 | size_t n2; |
205 | 0 | const char *e1 = NULL; |
206 | 0 | const char *e2 = NULL; |
207 | 0 | char *c1 = NULL; |
208 | 0 | char *c2 = NULL; |
209 | |
|
210 | 0 | match = fname_equal(name1, name2, case_sensitive); |
211 | 0 | if (match) { |
212 | 0 | return true; |
213 | 0 | } |
214 | | |
215 | 0 | if (name1[0] != ':') { |
216 | 0 | return false; |
217 | 0 | } |
218 | 0 | if (name2[0] != ':') { |
219 | 0 | return false; |
220 | 0 | } |
221 | 0 | s1 = &name1[1]; |
222 | 0 | e1 = strchr(s1, ':'); |
223 | 0 | if (e1 == NULL) { |
224 | 0 | n1 = strlen(s1); |
225 | 0 | } else { |
226 | 0 | n1 = PTR_DIFF(e1, s1); |
227 | 0 | } |
228 | 0 | s2 = &name2[1]; |
229 | 0 | e2 = strchr(s2, ':'); |
230 | 0 | if (e2 == NULL) { |
231 | 0 | n2 = strlen(s2); |
232 | 0 | } else { |
233 | 0 | n2 = PTR_DIFF(e2, s2); |
234 | 0 | } |
235 | | |
236 | | /* Normal filename handling */ |
237 | 0 | if (case_sensitive) { |
238 | 0 | return (strncmp(s1, s2, n1) == 0); |
239 | 0 | } |
240 | | |
241 | | /* |
242 | | * We can't use strnequal() here |
243 | | * as it takes the number of codepoints |
244 | | * and not the number of bytes. |
245 | | * |
246 | | * So we make a copy before calling |
247 | | * strequal(). |
248 | | * |
249 | | * Note that we TALLOC_FREE() in reverse order |
250 | | * in order to avoid memory fragmentation. |
251 | | */ |
252 | | |
253 | 0 | c1 = talloc_strndup(talloc_tos(), s1, n1); |
254 | 0 | c2 = talloc_strndup(talloc_tos(), s2, n2); |
255 | 0 | if (c1 == NULL || c2 == NULL) { |
256 | 0 | TALLOC_FREE(c2); |
257 | 0 | TALLOC_FREE(c1); |
258 | 0 | return (strncmp(s1, s2, n1) == 0); |
259 | 0 | } |
260 | | |
261 | 0 | match = strequal(c1, c2); |
262 | 0 | TALLOC_FREE(c2); |
263 | 0 | TALLOC_FREE(c1); |
264 | 0 | return match; |
265 | 0 | } |
266 | | |
267 | | /**************************************************************************** |
268 | | Scan a directory to find a filename, matching without case sensitivity. |
269 | | If the name looks like a mangled name then try via the mangling functions |
270 | | ****************************************************************************/ |
271 | | |
272 | | static NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp, |
273 | | const char *name, |
274 | | bool mangled, |
275 | | TALLOC_CTX *mem_ctx, |
276 | | char **found_name) |
277 | 0 | { |
278 | 0 | struct connection_struct *conn = dirfsp->conn; |
279 | 0 | struct smb_Dir *cur_dir = NULL; |
280 | 0 | const char *dname = NULL; |
281 | 0 | char *talloced = NULL; |
282 | 0 | char *unmangled_name = NULL; |
283 | 0 | NTSTATUS status; |
284 | | |
285 | | /* If we have a case-sensitive filesystem, it doesn't do us any |
286 | | * good to search for a name. If a case variation of the name was |
287 | | * there, then the original stat(2) would have found it. |
288 | | */ |
289 | 0 | if (!mangled && !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) { |
290 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
291 | 0 | } |
292 | | |
293 | | /* |
294 | | * The incoming name can be mangled, and if we de-mangle it |
295 | | * here it will not compare correctly against the filename (name2) |
296 | | * read from the directory and then mangled by the name_to_8_3() |
297 | | * call. We need to mangle both names or neither. |
298 | | * (JRA). |
299 | | * |
300 | | * Fix for bug found by Dina Fine. If in case sensitive mode then |
301 | | * the mangle cache is no good (3 letter extension could be wrong |
302 | | * case - so don't demangle in this case - leave as mangled and |
303 | | * allow the mangling of the directory entry read (which is done |
304 | | * case insensitively) to match instead. This will lead to more |
305 | | * false positive matches but we fail completely without it. JRA. |
306 | | */ |
307 | | |
308 | 0 | if (mangled && !conn->case_sensitive) { |
309 | 0 | mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name, |
310 | 0 | &unmangled_name, |
311 | 0 | conn->params); |
312 | 0 | if (!mangled) { |
313 | | /* Name is now unmangled. */ |
314 | 0 | name = unmangled_name; |
315 | 0 | } |
316 | 0 | } |
317 | | |
318 | | /* open the directory */ |
319 | 0 | status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir); |
320 | 0 | if (!NT_STATUS_IS_OK(status)) { |
321 | 0 | DBG_NOTICE("scan dir didn't open dir [%s]: %s\n", |
322 | 0 | fsp_str_dbg(dirfsp), |
323 | 0 | nt_errstr(status)); |
324 | 0 | TALLOC_FREE(unmangled_name); |
325 | 0 | return status; |
326 | 0 | } |
327 | | |
328 | | /* now scan for matching names */ |
329 | 0 | while ((dname = ReadDirName(cur_dir, &talloced))) { |
330 | |
|
331 | 0 | if (ISDOT(dname) || ISDOTDOT(dname)) { |
332 | 0 | TALLOC_FREE(talloced); |
333 | 0 | continue; |
334 | 0 | } |
335 | | |
336 | | /* |
337 | | * At this point dname is the unmangled name. |
338 | | * name is either mangled or not, depending on the state |
339 | | * of the "mangled" variable. JRA. |
340 | | */ |
341 | | |
342 | | /* |
343 | | * Check mangled name against mangled name, or unmangled name |
344 | | * against unmangled name. |
345 | | */ |
346 | | |
347 | 0 | if ((mangled && mangled_equal(name,dname,conn->params)) || |
348 | 0 | fname_equal(name, dname, conn->case_sensitive)) { |
349 | | /* we've found the file, change it's name and return */ |
350 | 0 | *found_name = talloc_strdup(mem_ctx, dname); |
351 | 0 | TALLOC_FREE(unmangled_name); |
352 | 0 | TALLOC_FREE(cur_dir); |
353 | 0 | if (!*found_name) { |
354 | 0 | TALLOC_FREE(talloced); |
355 | 0 | return NT_STATUS_NO_MEMORY; |
356 | 0 | } |
357 | 0 | TALLOC_FREE(talloced); |
358 | 0 | return NT_STATUS_OK; |
359 | 0 | } |
360 | 0 | TALLOC_FREE(talloced); |
361 | 0 | } |
362 | | |
363 | 0 | TALLOC_FREE(unmangled_name); |
364 | 0 | TALLOC_FREE(cur_dir); |
365 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
366 | 0 | } |
367 | | |
368 | | /**************************************************************************** |
369 | | Wrapper around the vfs get_real_filename and the full directory scan |
370 | | fallback. |
371 | | ****************************************************************************/ |
372 | | |
373 | | NTSTATUS get_real_filename_at(struct files_struct *dirfsp, |
374 | | const char *name, |
375 | | TALLOC_CTX *mem_ctx, |
376 | | char **found_name) |
377 | 0 | { |
378 | 0 | struct connection_struct *conn = dirfsp->conn; |
379 | 0 | NTSTATUS status; |
380 | 0 | bool mangled; |
381 | |
|
382 | 0 | mangled = mangle_is_mangled(name, conn->params); |
383 | |
|
384 | 0 | if (mangled) { |
385 | 0 | status = get_real_filename_full_scan_at( |
386 | 0 | dirfsp, name, mangled, mem_ctx, found_name); |
387 | 0 | return status; |
388 | 0 | } |
389 | | |
390 | | /* Try the vfs first to take advantage of case-insensitive stat. */ |
391 | 0 | status = SMB_VFS_GET_REAL_FILENAME_AT( |
392 | 0 | dirfsp->conn, dirfsp, name, mem_ctx, found_name); |
393 | | |
394 | | /* |
395 | | * If the case-insensitive stat was successful, or returned an error |
396 | | * other than EOPNOTSUPP then there is no need to fall back on the |
397 | | * full directory scan. |
398 | | */ |
399 | 0 | if (NT_STATUS_IS_OK(status) || |
400 | 0 | !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) { |
401 | 0 | return status; |
402 | 0 | } |
403 | | |
404 | 0 | status = get_real_filename_full_scan_at( |
405 | 0 | dirfsp, name, mangled, mem_ctx, found_name); |
406 | 0 | return status; |
407 | 0 | } |
408 | | |
409 | | /* |
410 | | * Lightweight function to just get last component |
411 | | * for rename / enumerate directory calls. |
412 | | */ |
413 | | |
414 | | char *get_original_lcomp(TALLOC_CTX *ctx, |
415 | | connection_struct *conn, |
416 | | const char *filename_in, |
417 | | uint32_t ucf_flags) |
418 | 0 | { |
419 | 0 | char *last_slash = NULL; |
420 | 0 | char *orig_lcomp; |
421 | 0 | NTSTATUS status; |
422 | |
|
423 | 0 | last_slash = strrchr(filename_in, '/'); |
424 | 0 | if (last_slash != NULL) { |
425 | 0 | orig_lcomp = talloc_strdup(ctx, last_slash+1); |
426 | 0 | } else { |
427 | 0 | orig_lcomp = talloc_strdup(ctx, filename_in); |
428 | 0 | } |
429 | 0 | if (orig_lcomp == NULL) { |
430 | 0 | return NULL; |
431 | 0 | } |
432 | 0 | status = normalize_filename_case(conn, orig_lcomp, ucf_flags); |
433 | 0 | if (!NT_STATUS_IS_OK(status)) { |
434 | 0 | TALLOC_FREE(orig_lcomp); |
435 | 0 | return NULL; |
436 | 0 | } |
437 | 0 | return orig_lcomp; |
438 | 0 | } |
439 | | |
440 | | /* |
441 | | * Get the correct capitalized stream name hanging off |
442 | | * base_fsp. Equivalent of get_real_filename(), but for streams. |
443 | | */ |
444 | | static NTSTATUS get_real_stream_name( |
445 | | TALLOC_CTX *mem_ctx, |
446 | | struct files_struct *base_fsp, |
447 | | const char *stream_name, |
448 | | char **_found) |
449 | 0 | { |
450 | 0 | unsigned int i, num_streams = 0; |
451 | 0 | struct stream_struct *streams = NULL; |
452 | 0 | NTSTATUS status; |
453 | |
|
454 | 0 | status = vfs_fstreaminfo( |
455 | 0 | base_fsp, talloc_tos(), &num_streams, &streams); |
456 | 0 | if (!NT_STATUS_IS_OK(status)) { |
457 | 0 | return status; |
458 | 0 | } |
459 | | |
460 | 0 | for (i=0; i<num_streams; i++) { |
461 | 0 | bool equal = sname_equal(stream_name, streams[i].name, false); |
462 | |
|
463 | 0 | DBG_DEBUG("comparing [%s] and [%s]: %sequal\n", |
464 | 0 | stream_name, |
465 | 0 | streams[i].name, |
466 | 0 | equal ? "" : "not "); |
467 | |
|
468 | 0 | if (equal) { |
469 | 0 | *_found = talloc_move(mem_ctx, &streams[i].name); |
470 | 0 | TALLOC_FREE(streams); |
471 | 0 | return NT_STATUS_OK; |
472 | 0 | } |
473 | 0 | } |
474 | | |
475 | 0 | TALLOC_FREE(streams); |
476 | 0 | return NT_STATUS_OBJECT_NAME_NOT_FOUND; |
477 | 0 | } |
478 | | |
479 | | static bool filename_split_lcomp( |
480 | | TALLOC_CTX *mem_ctx, |
481 | | const char *name_in, |
482 | | bool posix, |
483 | | char **_dirname, |
484 | | const char **_fname_rel, |
485 | | const char **_streamname) |
486 | 0 | { |
487 | 0 | const char *lcomp = NULL; |
488 | 0 | const char *fname_rel = NULL; |
489 | 0 | const char *streamname = NULL; |
490 | 0 | char *dirname = NULL; |
491 | |
|
492 | 0 | if (name_in[0] == '\0') { |
493 | 0 | fname_rel = "."; |
494 | 0 | dirname = talloc_strdup(mem_ctx, ""); |
495 | 0 | if (dirname == NULL) { |
496 | 0 | return false; |
497 | 0 | } |
498 | 0 | goto done; |
499 | 0 | } |
500 | | |
501 | 0 | lcomp = strrchr_m(name_in, '/'); |
502 | 0 | if (lcomp != NULL) { |
503 | 0 | fname_rel = lcomp+1; |
504 | 0 | dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in); |
505 | 0 | if (dirname == NULL) { |
506 | 0 | return false; |
507 | 0 | } |
508 | 0 | goto find_stream; |
509 | 0 | } |
510 | | |
511 | | /* |
512 | | * No slash, dir is empty |
513 | | */ |
514 | 0 | dirname = talloc_strdup(mem_ctx, ""); |
515 | 0 | if (dirname == NULL) { |
516 | 0 | return false; |
517 | 0 | } |
518 | | |
519 | 0 | if (!posix && (name_in[0] == ':')) { |
520 | | /* |
521 | | * Special case for stream on root directory |
522 | | */ |
523 | 0 | fname_rel = "."; |
524 | 0 | streamname = name_in; |
525 | 0 | goto done; |
526 | 0 | } |
527 | | |
528 | 0 | fname_rel = name_in; |
529 | |
|
530 | 0 | find_stream: |
531 | 0 | if (!posix) { |
532 | 0 | streamname = strchr_m(fname_rel, ':'); |
533 | |
|
534 | 0 | if (streamname != NULL) { |
535 | 0 | fname_rel = talloc_strndup( |
536 | 0 | mem_ctx, |
537 | 0 | fname_rel, |
538 | 0 | streamname - fname_rel); |
539 | 0 | if (fname_rel == NULL) { |
540 | 0 | TALLOC_FREE(dirname); |
541 | 0 | return false; |
542 | 0 | } |
543 | 0 | } |
544 | 0 | } |
545 | | |
546 | 0 | done: |
547 | 0 | *_dirname = dirname; |
548 | 0 | *_fname_rel = fname_rel; |
549 | 0 | *_streamname = streamname; |
550 | 0 | return true; |
551 | 0 | } |
552 | | |
553 | | /* |
554 | | * Create the correct capitalization of a file name to be created. |
555 | | */ |
556 | | static NTSTATUS filename_convert_normalize_new( |
557 | | TALLOC_CTX *mem_ctx, |
558 | | struct connection_struct *conn, |
559 | | char *name_in, |
560 | | char **_normalized) |
561 | 0 | { |
562 | 0 | char *name = name_in; |
563 | |
|
564 | 0 | *_normalized = NULL; |
565 | |
|
566 | 0 | if (!conn->case_preserve || |
567 | 0 | (mangle_is_8_3(name, false, |
568 | 0 | conn->params) && |
569 | 0 | !conn->short_case_preserve)) { |
570 | |
|
571 | 0 | char *normalized = talloc_strdup(mem_ctx, name); |
572 | 0 | if (normalized == NULL) { |
573 | 0 | return NT_STATUS_NO_MEMORY; |
574 | 0 | } |
575 | | |
576 | 0 | strnorm(normalized, lp_default_case(SNUM(conn))); |
577 | 0 | name = normalized; |
578 | 0 | } |
579 | | |
580 | 0 | if (mangle_is_mangled(name, conn->params)) { |
581 | 0 | bool found; |
582 | 0 | char *unmangled = NULL; |
583 | |
|
584 | 0 | found = mangle_lookup_name_from_8_3( |
585 | 0 | mem_ctx, name, &unmangled, conn->params); |
586 | 0 | if (found) { |
587 | 0 | name = unmangled; |
588 | 0 | } |
589 | 0 | } |
590 | |
|
591 | 0 | if (name != name_in) { |
592 | 0 | *_normalized = name; |
593 | 0 | } |
594 | |
|
595 | 0 | return NT_STATUS_OK; |
596 | 0 | } |
597 | | |
598 | | static NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx, |
599 | | const char *connectpath, |
600 | | const char *dir, |
601 | | const char *target, |
602 | | size_t unparsed, |
603 | | char **_relative) |
604 | 0 | { |
605 | 0 | char *abs_target = NULL; |
606 | 0 | char *abs_target_canon = NULL; |
607 | 0 | const char *relative = NULL; |
608 | 0 | bool in_share; |
609 | 0 | NTSTATUS status = NT_STATUS_NO_MEMORY; |
610 | |
|
611 | 0 | DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n", |
612 | 0 | connectpath, target, unparsed); |
613 | |
|
614 | 0 | if (target[0] == '/') { |
615 | 0 | abs_target = talloc_strdup(mem_ctx, target); |
616 | 0 | } else if (dir == NULL) { |
617 | 0 | abs_target = talloc_asprintf(mem_ctx, |
618 | 0 | "%s/%s", |
619 | 0 | connectpath, |
620 | 0 | target); |
621 | 0 | } else if (dir[0] == '/') { |
622 | 0 | abs_target = talloc_asprintf(mem_ctx, |
623 | 0 | "%s/%s", |
624 | 0 | dir, |
625 | 0 | target); |
626 | 0 | } else { |
627 | 0 | abs_target = talloc_asprintf(mem_ctx, |
628 | 0 | "%s/%s/%s", |
629 | 0 | connectpath, |
630 | 0 | dir, |
631 | 0 | target); |
632 | 0 | } |
633 | 0 | if (abs_target == NULL) { |
634 | 0 | goto fail; |
635 | 0 | } |
636 | | |
637 | 0 | abs_target_canon = canonicalize_absolute_path(abs_target, abs_target); |
638 | 0 | if (abs_target_canon == NULL) { |
639 | 0 | goto fail; |
640 | 0 | } |
641 | | |
642 | 0 | DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon); |
643 | |
|
644 | 0 | in_share = subdir_of( |
645 | 0 | connectpath, strlen(connectpath), abs_target_canon, &relative); |
646 | 0 | if (!in_share) { |
647 | 0 | DBG_DEBUG("wide link to %s\n", abs_target_canon); |
648 | 0 | status = (unparsed != 0) ? NT_STATUS_OBJECT_PATH_NOT_FOUND |
649 | 0 | : NT_STATUS_OBJECT_NAME_NOT_FOUND; |
650 | 0 | goto fail; |
651 | 0 | } |
652 | | |
653 | 0 | *_relative = talloc_strdup(mem_ctx, relative); |
654 | 0 | if (*_relative == NULL) { |
655 | 0 | goto fail; |
656 | 0 | } |
657 | | |
658 | 0 | status = NT_STATUS_OK; |
659 | 0 | fail: |
660 | 0 | TALLOC_FREE(abs_target); |
661 | 0 | return status; |
662 | 0 | } |
663 | | |
664 | | /* |
665 | | * Split up name_in as sent by the client into a directory pathref fsp |
666 | | * and a relative smb_filename. |
667 | | */ |
668 | | NTSTATUS |
669 | | filename_convert_dirfsp_nosymlink(TALLOC_CTX *mem_ctx, |
670 | | connection_struct *conn, |
671 | | struct files_struct *basedir, |
672 | | const char *name_in, |
673 | | uint32_t ucf_flags, |
674 | | NTTIME twrp, |
675 | | struct files_struct **_dirfsp, |
676 | | struct smb_filename **_smb_fname, |
677 | | struct smb_filename **_smb_fname_rel, |
678 | | struct reparse_data_buffer **_symlink_err) |
679 | 0 | { |
680 | 0 | struct smb_filename *smb_dirname = NULL; |
681 | 0 | struct smb_filename *smb_fname_rel = NULL; |
682 | 0 | struct smb_filename *smb_fname = NULL; |
683 | 0 | struct reparse_data_buffer *symlink_err = NULL; |
684 | 0 | const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES); |
685 | 0 | char *dirname = NULL; |
686 | 0 | const char *fname_rel = NULL; |
687 | 0 | const char *streamname = NULL; |
688 | 0 | char *saved_streamname = NULL; |
689 | 0 | struct files_struct *base_fsp = NULL; |
690 | 0 | bool ok; |
691 | 0 | NTSTATUS status = NT_STATUS_UNSUCCESSFUL; |
692 | |
|
693 | 0 | SMB_ASSERT(!(ucf_flags & UCF_DFS_PATHNAME)); |
694 | | |
695 | 0 | if (is_fake_file_path(name_in)) { |
696 | 0 | const struct timespec omit = make_omit_timespec(); |
697 | |
|
698 | 0 | if (posix) { |
699 | | /* |
700 | | * No stream name to look for |
701 | | */ |
702 | 0 | smb_fname = synthetic_smb_fname( |
703 | 0 | mem_ctx, |
704 | 0 | name_in, |
705 | 0 | NULL, |
706 | 0 | NULL, |
707 | 0 | 0, |
708 | 0 | SMB_FILENAME_POSIX_PATH); |
709 | 0 | } else { |
710 | 0 | smb_fname = synthetic_smb_fname_split(mem_ctx, name_in); |
711 | 0 | } |
712 | 0 | if (smb_fname == NULL) { |
713 | 0 | return NT_STATUS_NO_MEMORY; |
714 | 0 | } |
715 | 0 | smb_fname->st = (SMB_STRUCT_STAT){ |
716 | 0 | .st_ex_nlink = 1, |
717 | 0 | .st_ex_mode = S_IFREG | 0644, |
718 | 0 | .st_ex_btime = omit, |
719 | 0 | .st_ex_atime = omit, |
720 | 0 | .st_ex_mtime = omit, |
721 | 0 | .st_ex_ctime = omit, |
722 | 0 | }; |
723 | |
|
724 | 0 | *_dirfsp = basedir; |
725 | 0 | *_smb_fname = smb_fname; |
726 | 0 | return NT_STATUS_OK; |
727 | 0 | } |
728 | | |
729 | | /* |
730 | | * Catch an invalid path of "." before we |
731 | | * call filename_split_lcomp(). We need to |
732 | | * do this as filename_split_lcomp() will |
733 | | * use "." for the missing relative component |
734 | | * when an empty name_in path is sent by |
735 | | * the client. |
736 | | */ |
737 | 0 | if (ISDOT(name_in)) { |
738 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
739 | 0 | goto fail; |
740 | 0 | } |
741 | | |
742 | 0 | ok = filename_split_lcomp( |
743 | 0 | talloc_tos(), |
744 | 0 | name_in, |
745 | 0 | posix, |
746 | 0 | &dirname, |
747 | 0 | &fname_rel, |
748 | 0 | &streamname); |
749 | 0 | if (!ok) { |
750 | 0 | status = NT_STATUS_NO_MEMORY; |
751 | 0 | goto fail; |
752 | 0 | } |
753 | | |
754 | 0 | if ((streamname != NULL) && |
755 | 0 | ((conn->fs_capabilities & FILE_NAMED_STREAMS) == 0)) { |
756 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
757 | 0 | goto fail; |
758 | 0 | } |
759 | | |
760 | 0 | if (!posix) { |
761 | 0 | bool name_has_wild = ms_has_wild(dirname); |
762 | 0 | name_has_wild |= ms_has_wild(fname_rel); |
763 | 0 | if (name_has_wild) { |
764 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
765 | 0 | goto fail; |
766 | 0 | } |
767 | 0 | } |
768 | | |
769 | 0 | if (dirname[0] == '\0') { |
770 | 0 | status = openat_pathref_fsp_dot( |
771 | 0 | mem_ctx, basedir, |
772 | 0 | posix ? SMB_FILENAME_POSIX_PATH : 0, &smb_dirname); |
773 | 0 | } else { |
774 | 0 | status = normalize_filename_case(conn, dirname, ucf_flags); |
775 | 0 | if (!NT_STATUS_IS_OK(status)) { |
776 | 0 | DBG_ERR("normalize_filename_case %s failed: %s\n", |
777 | 0 | dirname, |
778 | 0 | nt_errstr(status)); |
779 | 0 | goto fail; |
780 | 0 | } |
781 | | |
782 | 0 | status = openat_pathref_fsp_nosymlink(mem_ctx, |
783 | 0 | conn, |
784 | 0 | basedir, |
785 | 0 | dirname, |
786 | 0 | twrp, |
787 | 0 | posix, |
788 | 0 | &smb_dirname, |
789 | 0 | &symlink_err); |
790 | |
|
791 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { |
792 | 0 | struct symlink_reparse_struct |
793 | 0 | *lnk = &symlink_err->parsed.lnk; |
794 | 0 | size_t unparsed = lnk->unparsed_path_length; |
795 | 0 | size_t name_in_len, dirname_len; |
796 | |
|
797 | 0 | name_in_len = strlen(name_in); |
798 | 0 | dirname_len = strlen(dirname); |
799 | |
|
800 | 0 | SMB_ASSERT(name_in_len >= dirname_len); |
801 | | |
802 | 0 | unparsed += (name_in_len - dirname_len); |
803 | |
|
804 | 0 | if (unparsed > UINT16_MAX) { |
805 | 0 | status = NT_STATUS_BUFFER_OVERFLOW; |
806 | 0 | goto fail; |
807 | 0 | } |
808 | | |
809 | 0 | lnk->unparsed_path_length = unparsed; |
810 | 0 | goto fail; |
811 | 0 | } |
812 | 0 | } |
813 | | |
814 | 0 | if (!NT_STATUS_IS_OK(status)) { |
815 | 0 | DBG_DEBUG("opening directory %s failed: %s\n", |
816 | 0 | dirname, |
817 | 0 | nt_errstr(status)); |
818 | 0 | TALLOC_FREE(dirname); |
819 | |
|
820 | 0 | if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) { |
821 | | /* |
822 | | * Except ACCESS_DENIED, everything else leads |
823 | | * to PATH_NOT_FOUND. |
824 | | */ |
825 | 0 | status = NT_STATUS_OBJECT_PATH_NOT_FOUND; |
826 | 0 | } |
827 | |
|
828 | 0 | goto fail; |
829 | 0 | } |
830 | | |
831 | 0 | if (!VALID_STAT_OF_DIR(smb_dirname->st)) { |
832 | 0 | status = NT_STATUS_OBJECT_PATH_NOT_FOUND; |
833 | 0 | goto fail; |
834 | 0 | } |
835 | 0 | smb_dirname->fsp->fsp_flags.is_directory = true; |
836 | | |
837 | | /* |
838 | | * Only look at bad last component values |
839 | | * once we know we have a valid directory. That |
840 | | * way we won't confuse error messages from |
841 | | * opening the directory path with error |
842 | | * messages from a bad last component. |
843 | | */ |
844 | | |
845 | | /* Relative filename can't be empty */ |
846 | 0 | if (fname_rel[0] == '\0') { |
847 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
848 | 0 | goto fail; |
849 | 0 | } |
850 | | |
851 | | /* Relative filename can't be ".." */ |
852 | 0 | if (ISDOTDOT(fname_rel)) { |
853 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
854 | 0 | goto fail; |
855 | 0 | } |
856 | | /* Relative name can only be dot if directory is empty. */ |
857 | 0 | if (ISDOT(fname_rel) && dirname[0] != '\0') { |
858 | 0 | status = NT_STATUS_OBJECT_NAME_INVALID; |
859 | 0 | goto fail; |
860 | 0 | } |
861 | | |
862 | 0 | TALLOC_FREE(dirname); |
863 | |
|
864 | 0 | smb_fname_rel = synthetic_smb_fname( |
865 | 0 | mem_ctx, |
866 | 0 | fname_rel, |
867 | 0 | streamname, |
868 | 0 | NULL, |
869 | 0 | twrp, |
870 | 0 | posix ? SMB_FILENAME_POSIX_PATH : 0); |
871 | 0 | if (smb_fname_rel == NULL) { |
872 | 0 | status = NT_STATUS_NO_MEMORY; |
873 | 0 | goto fail; |
874 | 0 | } |
875 | | |
876 | 0 | if ((conn->fs_capabilities & FILE_NAMED_STREAMS) && |
877 | 0 | is_named_stream(smb_fname_rel)) { |
878 | | /* |
879 | | * Find the base_fsp first without the stream. |
880 | | */ |
881 | 0 | saved_streamname = smb_fname_rel->stream_name; |
882 | 0 | smb_fname_rel->stream_name = NULL; |
883 | 0 | } |
884 | |
|
885 | 0 | status = normalize_filename_case( |
886 | 0 | conn, smb_fname_rel->base_name, ucf_flags); |
887 | 0 | if (!NT_STATUS_IS_OK(status)) { |
888 | 0 | DBG_ERR("normalize_filename_case %s failed: %s\n", |
889 | 0 | smb_fname_rel->base_name, |
890 | 0 | nt_errstr(status)); |
891 | 0 | goto fail; |
892 | 0 | } |
893 | | |
894 | 0 | status = openat_pathref_fsp_lcomp(smb_dirname->fsp, |
895 | 0 | smb_fname_rel, |
896 | 0 | ucf_flags); |
897 | |
|
898 | 0 | if (NT_STATUS_IS_OK(status) && S_ISLNK(smb_fname_rel->st.st_ex_mode)) { |
899 | | |
900 | | /* |
901 | | * Upper layers might need the link target. Here we |
902 | | * still have the relname around, get the symlink err. |
903 | | */ |
904 | 0 | status = read_symlink_reparse(mem_ctx, |
905 | 0 | smb_dirname->fsp, |
906 | 0 | smb_fname_rel, |
907 | 0 | &symlink_err); |
908 | 0 | if (!NT_STATUS_IS_OK(status)) { |
909 | 0 | DBG_DEBUG("Could not read symlink for %s: %s\n", |
910 | 0 | smb_fname_str_dbg( |
911 | 0 | smb_fname_rel->fsp->fsp_name), |
912 | 0 | nt_errstr(status)); |
913 | 0 | goto fail; |
914 | 0 | } |
915 | 0 | } |
916 | | |
917 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && |
918 | 0 | !VALID_STAT(smb_fname_rel->st)) { |
919 | |
|
920 | 0 | char *normalized = NULL; |
921 | | |
922 | | /* |
923 | | * Creating a new file |
924 | | */ |
925 | |
|
926 | 0 | status = filename_convert_normalize_new( |
927 | 0 | smb_fname_rel, |
928 | 0 | conn, |
929 | 0 | smb_fname_rel->base_name, |
930 | 0 | &normalized); |
931 | 0 | if (!NT_STATUS_IS_OK(status)) { |
932 | 0 | DBG_DEBUG("filename_convert_normalize_new failed: " |
933 | 0 | "%s\n", |
934 | 0 | nt_errstr(status)); |
935 | 0 | goto fail; |
936 | 0 | } |
937 | 0 | if (normalized != NULL) { |
938 | 0 | smb_fname_rel->base_name = normalized; |
939 | 0 | } |
940 | |
|
941 | 0 | smb_fname_rel->stream_name = saved_streamname; |
942 | |
|
943 | 0 | smb_fname = full_path_from_dirfsp_atname( |
944 | 0 | mem_ctx, smb_dirname->fsp, smb_fname_rel); |
945 | 0 | if (smb_fname == NULL) { |
946 | 0 | status = NT_STATUS_NO_MEMORY; |
947 | 0 | goto fail; |
948 | 0 | } |
949 | 0 | goto done; |
950 | 0 | } |
951 | | |
952 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) { |
953 | | /* A vetoed file, pretend it's not there */ |
954 | 0 | status = NT_STATUS_OBJECT_NAME_NOT_FOUND; |
955 | 0 | } |
956 | 0 | if (!NT_STATUS_IS_OK(status)) { |
957 | 0 | goto fail; |
958 | 0 | } |
959 | | |
960 | 0 | if (saved_streamname == NULL) { |
961 | | /* smb_fname must be allocated off mem_ctx. */ |
962 | 0 | smb_fname = cp_smb_filename(mem_ctx, |
963 | 0 | smb_fname_rel->fsp->fsp_name); |
964 | 0 | if (smb_fname == NULL) { |
965 | 0 | goto fail; |
966 | 0 | } |
967 | 0 | status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel); |
968 | 0 | if (!NT_STATUS_IS_OK(status)) { |
969 | 0 | goto fail; |
970 | 0 | } |
971 | 0 | goto done; |
972 | 0 | } |
973 | | |
974 | 0 | base_fsp = smb_fname_rel->fsp; |
975 | 0 | smb_fname_fsp_unlink(smb_fname_rel); |
976 | 0 | SET_STAT_INVALID(smb_fname_rel->st); |
977 | |
|
978 | 0 | smb_fname_rel->stream_name = saved_streamname; |
979 | |
|
980 | 0 | status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel); |
981 | |
|
982 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) && |
983 | 0 | !conn->case_sensitive) { |
984 | 0 | char *found = NULL; |
985 | |
|
986 | 0 | status = get_real_stream_name( |
987 | 0 | smb_fname_rel, |
988 | 0 | base_fsp, |
989 | 0 | smb_fname_rel->stream_name, |
990 | 0 | &found); |
991 | |
|
992 | 0 | if (NT_STATUS_IS_OK(status)) { |
993 | 0 | smb_fname_rel->stream_name = found; |
994 | 0 | found = NULL; |
995 | 0 | status = open_stream_pathref_fsp( |
996 | 0 | &base_fsp, smb_fname_rel); |
997 | 0 | } |
998 | 0 | } |
999 | |
|
1000 | 0 | if (NT_STATUS_IS_OK(status)) { |
1001 | | /* smb_fname must be allocated off mem_ctx. */ |
1002 | 0 | smb_fname = cp_smb_filename(mem_ctx, |
1003 | 0 | smb_fname_rel->fsp->fsp_name); |
1004 | 0 | if (smb_fname == NULL) { |
1005 | 0 | goto fail; |
1006 | 0 | } |
1007 | 0 | status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel); |
1008 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1009 | 0 | goto fail; |
1010 | 0 | } |
1011 | 0 | goto done; |
1012 | 0 | } |
1013 | | |
1014 | 0 | if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) { |
1015 | | /* |
1016 | | * Creating a new stream |
1017 | | * |
1018 | | * We should save the already-open base fsp for |
1019 | | * create_file_unixpath() somehow. |
1020 | | */ |
1021 | 0 | smb_fname = full_path_from_dirfsp_atname( |
1022 | 0 | mem_ctx, smb_dirname->fsp, smb_fname_rel); |
1023 | 0 | if (smb_fname == NULL) { |
1024 | 0 | status = NT_STATUS_NO_MEMORY; |
1025 | 0 | goto fail; |
1026 | 0 | } |
1027 | | /* |
1028 | | * When open_stream_pathref_fsp() returns |
1029 | | * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp |
1030 | | * has been set to NULL, so we must free base_fsp separately |
1031 | | * to prevent fd-leaks when opening a stream that doesn't |
1032 | | * exist. |
1033 | | */ |
1034 | 0 | fd_close(base_fsp); |
1035 | 0 | file_free(NULL, base_fsp); |
1036 | 0 | base_fsp = NULL; |
1037 | 0 | goto done; |
1038 | 0 | } |
1039 | | |
1040 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1041 | 0 | goto fail; |
1042 | 0 | } |
1043 | | |
1044 | 0 | done: |
1045 | 0 | *_dirfsp = smb_dirname->fsp; |
1046 | 0 | *_smb_fname = smb_fname; |
1047 | 0 | *_symlink_err = symlink_err; |
1048 | |
|
1049 | 0 | smb_fname_fsp_unlink(smb_fname_rel); |
1050 | 0 | *_smb_fname_rel = smb_fname_rel; |
1051 | 0 | return NT_STATUS_OK; |
1052 | | |
1053 | 0 | fail: |
1054 | 0 | *_symlink_err = symlink_err; |
1055 | | |
1056 | | /* |
1057 | | * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp |
1058 | | * has been set to NULL, so we must free base_fsp separately |
1059 | | * to prevent fd-leaks when opening a stream that doesn't |
1060 | | * exist. |
1061 | | */ |
1062 | 0 | if (base_fsp != NULL) { |
1063 | 0 | fd_close(base_fsp); |
1064 | 0 | file_free(NULL, base_fsp); |
1065 | 0 | base_fsp = NULL; |
1066 | 0 | } |
1067 | 0 | TALLOC_FREE(dirname); |
1068 | 0 | TALLOC_FREE(smb_dirname); |
1069 | 0 | TALLOC_FREE(smb_fname_rel); |
1070 | 0 | return status; |
1071 | 0 | } |
1072 | | |
1073 | | NTSTATUS filename_convert_dirfsp_rel(TALLOC_CTX *mem_ctx, |
1074 | | connection_struct *conn, |
1075 | | struct files_struct *basedir, |
1076 | | const char *name_in, |
1077 | | uint32_t ucf_flags, |
1078 | | NTTIME twrp, |
1079 | | struct files_struct **_dirfsp, |
1080 | | struct smb_filename **_smb_fname, |
1081 | | struct smb_filename **_smb_fname_rel) |
1082 | 0 | { |
1083 | 0 | struct reparse_data_buffer *symlink_err = NULL; |
1084 | 0 | struct symlink_reparse_struct *lnk = NULL; |
1085 | 0 | NTSTATUS status; |
1086 | 0 | char *target = NULL; |
1087 | 0 | char *base_name = NULL; |
1088 | 0 | char *safe_target = NULL; |
1089 | 0 | size_t symlink_redirects = 0; |
1090 | 0 | int ret; |
1091 | |
|
1092 | 0 | next: |
1093 | 0 | if (symlink_redirects > 40) { |
1094 | 0 | return NT_STATUS_OBJECT_PATH_NOT_FOUND; |
1095 | 0 | } |
1096 | | |
1097 | 0 | status = filename_convert_dirfsp_nosymlink(mem_ctx, |
1098 | 0 | conn, |
1099 | 0 | basedir, |
1100 | 0 | name_in, |
1101 | 0 | ucf_flags, |
1102 | 0 | twrp, |
1103 | 0 | _dirfsp, |
1104 | 0 | _smb_fname, |
1105 | 0 | _smb_fname_rel, |
1106 | 0 | &symlink_err); |
1107 | |
|
1108 | 0 | if (NT_STATUS_IS_OK(status) && S_ISLNK((*_smb_fname)->st.st_ex_mode)) { |
1109 | | /* |
1110 | | * lcomp is a symlink |
1111 | | */ |
1112 | 0 | if (ucf_flags & UCF_LCOMP_LNK_OK) { |
1113 | 0 | TALLOC_FREE(symlink_err); |
1114 | 0 | return NT_STATUS_OK; |
1115 | 0 | } |
1116 | 0 | close_file_free(NULL, _dirfsp, ERROR_CLOSE); |
1117 | 0 | status = NT_STATUS_STOPPED_ON_SYMLINK; |
1118 | 0 | } |
1119 | | |
1120 | 0 | if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) { |
1121 | 0 | return status; |
1122 | 0 | } |
1123 | 0 | TALLOC_FREE(*_smb_fname_rel); |
1124 | |
|
1125 | 0 | lnk = &symlink_err->parsed.lnk; |
1126 | | |
1127 | | /* |
1128 | | * If we're on an MSDFS share, see if this is |
1129 | | * an MSDFS link. |
1130 | | */ |
1131 | 0 | if (lp_host_msdfs() && lp_msdfs_root(SNUM(conn)) && |
1132 | 0 | strnequal(lnk->substitute_name, "msdfs:", 6)) |
1133 | 0 | { |
1134 | 0 | TALLOC_FREE(*_smb_fname); |
1135 | 0 | TALLOC_FREE(symlink_err); |
1136 | 0 | return NT_STATUS_PATH_NOT_COVERED; |
1137 | 0 | } |
1138 | | |
1139 | 0 | if (!lp_follow_symlinks(SNUM(conn))) { |
1140 | 0 | status = (lnk->unparsed_path_length == 0) |
1141 | 0 | ? NT_STATUS_OBJECT_NAME_NOT_FOUND |
1142 | 0 | : NT_STATUS_OBJECT_PATH_NOT_FOUND; |
1143 | 0 | TALLOC_FREE(symlink_err); |
1144 | 0 | return status; |
1145 | 0 | } |
1146 | | |
1147 | | /* |
1148 | | * Right now, SMB2 and SMB1 always traverse symlinks |
1149 | | * within the share. SMB1+POSIX traverses non-terminal |
1150 | | * symlinks within the share. |
1151 | | * |
1152 | | * When we add SMB2+POSIX we need to return |
1153 | | * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the |
1154 | | * symlink target data read below if SMB2+POSIX has |
1155 | | * UCF_POSIX_PATHNAMES set to cause the client to |
1156 | | * resolve all symlinks locally. |
1157 | | */ |
1158 | | |
1159 | 0 | ret = symlink_target_path(mem_ctx, |
1160 | 0 | name_in, |
1161 | 0 | lnk->unparsed_path_length, |
1162 | 0 | lnk->substitute_name, |
1163 | 0 | lnk->substitute_name[0] != '/', |
1164 | 0 | '/', |
1165 | 0 | &target); |
1166 | 0 | if (ret != 0) { |
1167 | 0 | TALLOC_FREE(symlink_err); |
1168 | 0 | return map_nt_error_from_unix(ret); |
1169 | 0 | } |
1170 | | |
1171 | 0 | if (basedir != conn->cwd_fsp) { |
1172 | 0 | base_name = basedir->fsp_name->base_name; |
1173 | 0 | } |
1174 | |
|
1175 | 0 | status = safe_symlink_target_path(mem_ctx, |
1176 | 0 | conn->connectpath, |
1177 | 0 | base_name, |
1178 | 0 | target, |
1179 | 0 | lnk->unparsed_path_length, |
1180 | 0 | &safe_target); |
1181 | 0 | TALLOC_FREE(symlink_err); |
1182 | 0 | if (!NT_STATUS_IS_OK(status)) { |
1183 | 0 | return status; |
1184 | 0 | } |
1185 | 0 | name_in = safe_target; |
1186 | 0 | basedir = conn->cwd_fsp; |
1187 | |
|
1188 | 0 | symlink_redirects += 1; |
1189 | |
|
1190 | 0 | goto next; |
1191 | 0 | } |
1192 | | |
1193 | | NTSTATUS filename_convert_dirfsp(TALLOC_CTX *ctx, |
1194 | | connection_struct *conn, |
1195 | | const char *name_in, |
1196 | | uint32_t ucf_flags, |
1197 | | NTTIME twrp, |
1198 | | struct files_struct **_dirfsp, |
1199 | | struct smb_filename **_smb_name) |
1200 | 0 | { |
1201 | 0 | struct smb_filename *smb_fname_rel = NULL; |
1202 | 0 | NTSTATUS status; |
1203 | |
|
1204 | 0 | status = filename_convert_dirfsp_rel(ctx, |
1205 | 0 | conn, |
1206 | 0 | conn->cwd_fsp, |
1207 | 0 | name_in, |
1208 | 0 | ucf_flags, |
1209 | 0 | twrp, |
1210 | 0 | _dirfsp, |
1211 | 0 | _smb_name, |
1212 | 0 | &smb_fname_rel); |
1213 | 0 | TALLOC_FREE(smb_fname_rel); |
1214 | 0 | return status; |
1215 | 0 | } |
1216 | | |
1217 | | char *full_path_from_dirfsp_at_basename(TALLOC_CTX *mem_ctx, |
1218 | | const struct files_struct *dirfsp, |
1219 | | const char *at_base_name) |
1220 | 0 | { |
1221 | 0 | char *path = NULL; |
1222 | |
|
1223 | 0 | if (dirfsp == dirfsp->conn->cwd_fsp || |
1224 | 0 | ISDOT(dirfsp->fsp_name->base_name) || at_base_name[0] == '/') { |
1225 | 0 | path = talloc_strdup(mem_ctx, at_base_name); |
1226 | 0 | } else { |
1227 | 0 | path = talloc_asprintf(mem_ctx, |
1228 | 0 | "%s/%s", |
1229 | 0 | dirfsp->fsp_name->base_name, |
1230 | 0 | at_base_name); |
1231 | 0 | } |
1232 | |
|
1233 | 0 | return path; |
1234 | 0 | } |
1235 | | |
1236 | | /* |
1237 | | * Build the full path from a dirfsp and dirfsp relative name |
1238 | | */ |
1239 | | struct smb_filename * |
1240 | | full_path_from_dirfsp_atname(TALLOC_CTX *mem_ctx, |
1241 | | const struct files_struct *dirfsp, |
1242 | | const struct smb_filename *atname) |
1243 | 0 | { |
1244 | 0 | struct smb_filename *fname = NULL; |
1245 | 0 | char *path = NULL; |
1246 | |
|
1247 | 0 | path = full_path_from_dirfsp_at_basename(mem_ctx, |
1248 | 0 | dirfsp, |
1249 | 0 | atname->base_name); |
1250 | 0 | if (path == NULL) { |
1251 | 0 | return NULL; |
1252 | 0 | } |
1253 | | |
1254 | 0 | fname = synthetic_smb_fname(mem_ctx, |
1255 | 0 | path, |
1256 | 0 | atname->stream_name, |
1257 | 0 | &atname->st, |
1258 | 0 | atname->twrp, |
1259 | 0 | atname->flags); |
1260 | 0 | TALLOC_FREE(path); |
1261 | 0 | if (fname == NULL) { |
1262 | 0 | return NULL; |
1263 | 0 | } |
1264 | | |
1265 | 0 | return fname; |
1266 | 0 | } |