Line data Source code
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 "lib/util/memcache.h"
33 :
34 15075 : uint32_t ucf_flags_from_smb_request(struct smb_request *req)
35 : {
36 15075 : uint32_t ucf_flags = 0;
37 :
38 15075 : if (req == NULL) {
39 0 : return 0;
40 : }
41 :
42 15075 : if (req->posix_pathnames) {
43 0 : ucf_flags |= UCF_POSIX_PATHNAMES;
44 :
45 0 : if (!req->sconn->using_smb2) {
46 0 : ucf_flags |= UCF_LCOMP_LNK_OK;
47 : }
48 : }
49 15075 : if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
50 1048 : ucf_flags |= UCF_DFS_PATHNAME;
51 : }
52 15075 : if (req->flags2 & FLAGS2_REPARSE_PATH) {
53 0 : ucf_flags |= UCF_GMT_PATHNAME;
54 : }
55 :
56 15075 : return ucf_flags;
57 : }
58 :
59 15050 : uint32_t filename_create_ucf_flags(struct smb_request *req, uint32_t create_disposition)
60 : {
61 15050 : uint32_t ucf_flags = 0;
62 :
63 15050 : ucf_flags |= ucf_flags_from_smb_request(req);
64 :
65 15050 : switch (create_disposition) {
66 13556 : case FILE_OPEN:
67 : case FILE_OVERWRITE:
68 13556 : break;
69 1492 : case FILE_SUPERSEDE:
70 : case FILE_CREATE:
71 : case FILE_OPEN_IF:
72 : case FILE_OVERWRITE_IF:
73 1492 : ucf_flags |= UCF_PREP_CREATEFILE;
74 1492 : break;
75 : }
76 :
77 15050 : return ucf_flags;
78 : }
79 :
80 : /****************************************************************************
81 : Mangle the 2nd name and check if it is then equal to the first name.
82 : ****************************************************************************/
83 :
84 0 : static bool mangled_equal(const char *name1,
85 : const char *name2,
86 : const struct share_params *p)
87 : {
88 : char mname[13];
89 :
90 0 : if (!name_to_8_3(name2, mname, False, p)) {
91 0 : return False;
92 : }
93 0 : return strequal(name1, mname);
94 : }
95 :
96 : /*
97 : * Strip a valid @GMT-token from any incoming filename path,
98 : * adding any NTTIME encoded in the pathname into the
99 : * twrp field of the passed in smb_fname.
100 : *
101 : * Valid @GMT-tokens look like @GMT-YYYY-MM-DD-HH-MM-SS
102 : * at the *start* of a pathname component.
103 : *
104 : * If twrp is passed in then smb_fname->twrp is set to that
105 : * value, and the @GMT-token part of the filename is removed
106 : * and does not change the stored smb_fname->twrp.
107 : *
108 : */
109 :
110 0 : NTSTATUS canonicalize_snapshot_path(struct smb_filename *smb_fname,
111 : uint32_t ucf_flags,
112 : NTTIME twrp)
113 : {
114 : bool found;
115 :
116 0 : if (twrp != 0) {
117 0 : smb_fname->twrp = twrp;
118 : }
119 :
120 0 : if (!(ucf_flags & UCF_GMT_PATHNAME)) {
121 0 : return NT_STATUS_OK;
122 : }
123 :
124 0 : found = extract_snapshot_token(smb_fname->base_name, &twrp);
125 0 : if (!found) {
126 0 : return NT_STATUS_OK;
127 : }
128 :
129 0 : if (smb_fname->twrp == 0) {
130 0 : smb_fname->twrp = twrp;
131 : }
132 :
133 0 : return NT_STATUS_OK;
134 : }
135 :
136 0 : static bool strnorm(char *s, int case_default)
137 : {
138 0 : if (case_default == CASE_UPPER)
139 0 : return strupper_m(s);
140 : else
141 0 : return strlower_m(s);
142 : }
143 :
144 : /*
145 : * Utility function to normalize case on an incoming client filename
146 : * if required on this connection struct.
147 : * Performs an in-place case conversion guaranteed to stay the same size.
148 : */
149 :
150 35683 : static NTSTATUS normalize_filename_case(connection_struct *conn,
151 : char *filename,
152 : uint32_t ucf_flags)
153 : {
154 : bool ok;
155 :
156 35683 : if (ucf_flags & UCF_POSIX_PATHNAMES) {
157 : /*
158 : * POSIX never normalizes filename case.
159 : */
160 0 : return NT_STATUS_OK;
161 : }
162 35683 : if (!conn->case_sensitive) {
163 35683 : return NT_STATUS_OK;
164 : }
165 0 : if (conn->case_preserve) {
166 0 : return NT_STATUS_OK;
167 : }
168 0 : if (conn->short_case_preserve) {
169 0 : return NT_STATUS_OK;
170 : }
171 0 : ok = strnorm(filename, lp_default_case(SNUM(conn)));
172 0 : if (!ok) {
173 0 : return NT_STATUS_INVALID_PARAMETER;
174 : }
175 0 : return NT_STATUS_OK;
176 : }
177 :
178 : /****************************************************************************
179 : Check if two filenames are equal.
180 : This needs to be careful about whether we are case sensitive.
181 : ****************************************************************************/
182 :
183 12060 : static bool fname_equal(const char *name1, const char *name2,
184 : bool case_sensitive)
185 : {
186 : /* Normal filename handling */
187 12060 : if (case_sensitive) {
188 0 : return(strcmp(name1,name2) == 0);
189 : }
190 :
191 12060 : return(strequal(name1,name2));
192 : }
193 :
194 0 : static bool sname_equal(const char *name1, const char *name2,
195 : bool case_sensitive)
196 : {
197 : bool match;
198 0 : const char *s1 = NULL;
199 0 : const char *s2 = NULL;
200 : size_t n1;
201 : size_t n2;
202 0 : const char *e1 = NULL;
203 0 : const char *e2 = NULL;
204 0 : char *c1 = NULL;
205 0 : char *c2 = NULL;
206 :
207 0 : match = fname_equal(name1, name2, case_sensitive);
208 0 : if (match) {
209 0 : return true;
210 : }
211 :
212 0 : if (name1[0] != ':') {
213 0 : return false;
214 : }
215 0 : if (name2[0] != ':') {
216 0 : return false;
217 : }
218 0 : s1 = &name1[1];
219 0 : e1 = strchr(s1, ':');
220 0 : if (e1 == NULL) {
221 0 : n1 = strlen(s1);
222 : } else {
223 0 : n1 = PTR_DIFF(e1, s1);
224 : }
225 0 : s2 = &name2[1];
226 0 : e2 = strchr(s2, ':');
227 0 : if (e2 == NULL) {
228 0 : n2 = strlen(s2);
229 : } else {
230 0 : n2 = PTR_DIFF(e2, s2);
231 : }
232 :
233 : /* Normal filename handling */
234 0 : if (case_sensitive) {
235 0 : return (strncmp(s1, s2, n1) == 0);
236 : }
237 :
238 : /*
239 : * We can't use strnequal() here
240 : * as it takes the number of codepoints
241 : * and not the number of bytes.
242 : *
243 : * So we make a copy before calling
244 : * strequal().
245 : *
246 : * Note that we TALLOC_FREE() in reverse order
247 : * in order to avoid memory fragmentation.
248 : */
249 :
250 0 : c1 = talloc_strndup(talloc_tos(), s1, n1);
251 0 : c2 = talloc_strndup(talloc_tos(), s2, n2);
252 0 : if (c1 == NULL || c2 == NULL) {
253 0 : TALLOC_FREE(c2);
254 0 : TALLOC_FREE(c1);
255 0 : return (strncmp(s1, s2, n1) == 0);
256 : }
257 :
258 0 : match = strequal(c1, c2);
259 0 : TALLOC_FREE(c2);
260 0 : TALLOC_FREE(c1);
261 0 : return match;
262 : }
263 :
264 : /****************************************************************************
265 : Scan a directory to find a filename, matching without case sensitivity.
266 : If the name looks like a mangled name then try via the mangling functions
267 : ****************************************************************************/
268 :
269 3380 : NTSTATUS get_real_filename_full_scan_at(struct files_struct *dirfsp,
270 : const char *name,
271 : bool mangled,
272 : TALLOC_CTX *mem_ctx,
273 : char **found_name)
274 : {
275 3380 : struct connection_struct *conn = dirfsp->conn;
276 3380 : struct smb_Dir *cur_dir = NULL;
277 3380 : const char *dname = NULL;
278 3380 : char *talloced = NULL;
279 3380 : char *unmangled_name = NULL;
280 : long curpos;
281 : NTSTATUS status;
282 :
283 : /* If we have a case-sensitive filesystem, it doesn't do us any
284 : * good to search for a name. If a case variation of the name was
285 : * there, then the original stat(2) would have found it.
286 : */
287 3380 : if (!mangled && !(conn->fs_capabilities & FILE_CASE_SENSITIVE_SEARCH)) {
288 0 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
289 : }
290 :
291 : /*
292 : * The incoming name can be mangled, and if we de-mangle it
293 : * here it will not compare correctly against the filename (name2)
294 : * read from the directory and then mangled by the name_to_8_3()
295 : * call. We need to mangle both names or neither.
296 : * (JRA).
297 : *
298 : * Fix for bug found by Dina Fine. If in case sensitive mode then
299 : * the mangle cache is no good (3 letter extension could be wrong
300 : * case - so don't demangle in this case - leave as mangled and
301 : * allow the mangling of the directory entry read (which is done
302 : * case insensitively) to match instead. This will lead to more
303 : * false positive matches but we fail completely without it. JRA.
304 : */
305 :
306 3380 : if (mangled && !conn->case_sensitive) {
307 8 : mangled = !mangle_lookup_name_from_8_3(talloc_tos(), name,
308 : &unmangled_name,
309 8 : conn->params);
310 8 : if (!mangled) {
311 : /* Name is now unmangled. */
312 8 : name = unmangled_name;
313 : }
314 : }
315 :
316 : /* open the directory */
317 3380 : status = OpenDir_from_pathref(talloc_tos(), dirfsp, NULL, 0, &cur_dir);
318 3380 : if (!NT_STATUS_IS_OK(status)) {
319 0 : DBG_NOTICE("scan dir didn't open dir [%s]: %s\n",
320 : fsp_str_dbg(dirfsp),
321 : nt_errstr(status));
322 0 : TALLOC_FREE(unmangled_name);
323 0 : return status;
324 : }
325 :
326 : /* now scan for matching names */
327 3380 : curpos = 0;
328 20697 : while ((dname = ReadDirName(cur_dir, &curpos, NULL, &talloced))) {
329 :
330 : /* Is it dot or dot dot. */
331 18820 : if (ISDOT(dname) || ISDOTDOT(dname)) {
332 6760 : TALLOC_FREE(talloced);
333 6760 : continue;
334 : }
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 24120 : if ((mangled && mangled_equal(name,dname,conn->params)) ||
348 12060 : fname_equal(name, dname, conn->case_sensitive)) {
349 : /* we've found the file, change it's name and return */
350 1503 : *found_name = talloc_strdup(mem_ctx, dname);
351 1503 : TALLOC_FREE(unmangled_name);
352 1503 : TALLOC_FREE(cur_dir);
353 1503 : if (!*found_name) {
354 0 : TALLOC_FREE(talloced);
355 0 : return NT_STATUS_NO_MEMORY;
356 : }
357 1503 : TALLOC_FREE(talloced);
358 1503 : return NT_STATUS_OK;
359 : }
360 10557 : TALLOC_FREE(talloced);
361 : }
362 :
363 1877 : TALLOC_FREE(unmangled_name);
364 1877 : TALLOC_FREE(cur_dir);
365 1877 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
366 : }
367 :
368 : /****************************************************************************
369 : Wrapper around the vfs get_real_filename and the full directory scan
370 : fallback.
371 : ****************************************************************************/
372 :
373 3380 : NTSTATUS get_real_filename_at(struct files_struct *dirfsp,
374 : const char *name,
375 : TALLOC_CTX *mem_ctx,
376 : char **found_name)
377 : {
378 3380 : struct connection_struct *conn = dirfsp->conn;
379 : NTSTATUS status;
380 : bool mangled;
381 :
382 3380 : mangled = mangle_is_mangled(name, conn->params);
383 :
384 3380 : if (mangled) {
385 8 : status = get_real_filename_full_scan_at(
386 : dirfsp, name, mangled, mem_ctx, found_name);
387 8 : return status;
388 : }
389 :
390 : /* Try the vfs first to take advantage of case-insensitive stat. */
391 3372 : status = SMB_VFS_GET_REAL_FILENAME_AT(
392 : 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 3372 : if (NT_STATUS_IS_OK(status) ||
400 3372 : !NT_STATUS_EQUAL(status, NT_STATUS_NOT_SUPPORTED)) {
401 0 : return status;
402 : }
403 :
404 3372 : status = get_real_filename_full_scan_at(
405 : dirfsp, name, mangled, mem_ctx, found_name);
406 3372 : return status;
407 : }
408 :
409 : /*
410 : * Create the memcache-key for GETREALFILENAME_CACHE: This supplements
411 : * the stat cache for the last component to be looked up. Cache
412 : * contents is the correctly capitalized translation of the parameter
413 : * "name" as it exists on disk. This is indexed by inode of the dirfsp
414 : * and name, and contrary to stat_cahce_lookup() it does not
415 : * vfs_stat() the last component. This will be taken care of by an
416 : * attempt to do a openat_pathref_fsp().
417 : */
418 1910 : static bool get_real_filename_cache_key(
419 : TALLOC_CTX *mem_ctx,
420 : struct files_struct *dirfsp,
421 : const char *name,
422 : DATA_BLOB *_key)
423 : {
424 1910 : struct file_id fid = vfs_file_id_from_sbuf(
425 1910 : dirfsp->conn, &dirfsp->fsp_name->st);
426 1910 : char *upper = NULL;
427 1910 : uint8_t *key = NULL;
428 : size_t namelen, keylen;
429 :
430 1910 : upper = talloc_strdup_upper(mem_ctx, name);
431 1910 : if (upper == NULL) {
432 0 : return false;
433 : }
434 1910 : namelen = talloc_get_size(upper);
435 :
436 1910 : keylen = namelen + sizeof(fid);
437 1910 : if (keylen < sizeof(fid)) {
438 0 : TALLOC_FREE(upper);
439 0 : return false;
440 : }
441 :
442 1910 : key = talloc_size(mem_ctx, keylen);
443 1910 : if (key == NULL) {
444 0 : TALLOC_FREE(upper);
445 0 : return false;
446 : }
447 :
448 1910 : memcpy(key, &fid, sizeof(fid));
449 1910 : memcpy(key + sizeof(fid), upper, namelen);
450 1910 : TALLOC_FREE(upper);
451 :
452 1910 : *_key = (DATA_BLOB) { .data = key, .length = keylen, };
453 1910 : return true;
454 : }
455 :
456 : /*
457 : * Lightweight function to just get last component
458 : * for rename / enumerate directory calls.
459 : */
460 :
461 277 : char *get_original_lcomp(TALLOC_CTX *ctx,
462 : connection_struct *conn,
463 : const char *filename_in,
464 : uint32_t ucf_flags)
465 : {
466 277 : char *last_slash = NULL;
467 : char *orig_lcomp;
468 : NTSTATUS status;
469 :
470 277 : last_slash = strrchr(filename_in, '/');
471 277 : if (last_slash != NULL) {
472 4 : orig_lcomp = talloc_strdup(ctx, last_slash+1);
473 : } else {
474 273 : orig_lcomp = talloc_strdup(ctx, filename_in);
475 : }
476 277 : if (orig_lcomp == NULL) {
477 0 : return NULL;
478 : }
479 277 : status = normalize_filename_case(conn, orig_lcomp, ucf_flags);
480 277 : if (!NT_STATUS_IS_OK(status)) {
481 0 : TALLOC_FREE(orig_lcomp);
482 0 : return NULL;
483 : }
484 277 : return orig_lcomp;
485 : }
486 :
487 : /*
488 : * Deal with the SMB1 semantics of sending a pathname with a
489 : * wildcard as the terminal component for a SMB1search or
490 : * trans2 findfirst.
491 : */
492 :
493 0 : NTSTATUS filename_convert_smb1_search_path(TALLOC_CTX *ctx,
494 : connection_struct *conn,
495 : char *name_in,
496 : uint32_t ucf_flags,
497 : struct files_struct **_dirfsp,
498 : struct smb_filename **_smb_fname_out,
499 : char **_mask_out)
500 : {
501 : NTSTATUS status;
502 0 : char *p = NULL;
503 0 : char *mask = NULL;
504 0 : struct smb_filename *smb_fname = NULL;
505 0 : NTTIME twrp = 0;
506 :
507 0 : *_smb_fname_out = NULL;
508 0 : *_dirfsp = NULL;
509 0 : *_mask_out = NULL;
510 :
511 0 : DBG_DEBUG("name_in: %s\n", name_in);
512 :
513 0 : if (ucf_flags & UCF_GMT_PATHNAME) {
514 0 : extract_snapshot_token(name_in, &twrp);
515 0 : ucf_flags &= ~UCF_GMT_PATHNAME;
516 : }
517 :
518 : /* Get the original lcomp. */
519 0 : mask = get_original_lcomp(ctx,
520 : conn,
521 : name_in,
522 : ucf_flags);
523 0 : if (mask == NULL) {
524 0 : return NT_STATUS_NO_MEMORY;
525 : }
526 :
527 0 : if (mask[0] == '\0') {
528 : /* Windows and OS/2 systems treat search on the root as * */
529 0 : TALLOC_FREE(mask);
530 0 : mask = talloc_strdup(ctx, "*");
531 0 : if (mask == NULL) {
532 0 : return NT_STATUS_NO_MEMORY;
533 : }
534 : }
535 :
536 0 : DBG_DEBUG("mask = %s\n", mask);
537 :
538 : /*
539 : * Remove the terminal component so
540 : * filename_convert_dirfsp never sees the mask.
541 : */
542 0 : p = strrchr_m(name_in,'/');
543 0 : if (p == NULL) {
544 : /* filename_convert_dirfsp handles a '\0' name. */
545 0 : name_in[0] = '\0';
546 : } else {
547 0 : *p = '\0';
548 : }
549 :
550 0 : DBG_DEBUG("For filename_convert_dirfsp: name_in = %s\n",
551 : name_in);
552 :
553 : /* Convert the parent directory path. */
554 0 : status = filename_convert_dirfsp(ctx,
555 : conn,
556 : name_in,
557 : ucf_flags,
558 : twrp,
559 : _dirfsp,
560 : &smb_fname);
561 :
562 0 : if (!NT_STATUS_IS_OK(status)) {
563 0 : DBG_DEBUG("filename_convert error for %s: %s\n",
564 : name_in,
565 : nt_errstr(status));
566 : }
567 :
568 0 : *_smb_fname_out = talloc_move(ctx, &smb_fname);
569 0 : *_mask_out = talloc_move(ctx, &mask);
570 :
571 0 : return status;
572 : }
573 :
574 : /*
575 : * Get the correct capitalized stream name hanging off
576 : * base_fsp. Equivalent of get_real_filename(), but for streams.
577 : */
578 10 : static NTSTATUS get_real_stream_name(
579 : TALLOC_CTX *mem_ctx,
580 : struct files_struct *base_fsp,
581 : const char *stream_name,
582 : char **_found)
583 : {
584 10 : unsigned int i, num_streams = 0;
585 10 : struct stream_struct *streams = NULL;
586 : NTSTATUS status;
587 :
588 10 : status = vfs_fstreaminfo(
589 : base_fsp, talloc_tos(), &num_streams, &streams);
590 10 : if (!NT_STATUS_IS_OK(status)) {
591 0 : return status;
592 : }
593 :
594 10 : for (i=0; i<num_streams; i++) {
595 0 : bool equal = sname_equal(stream_name, streams[i].name, false);
596 :
597 0 : DBG_DEBUG("comparing [%s] and [%s]: %sequal\n",
598 : stream_name,
599 : streams[i].name,
600 : equal ? "" : "not ");
601 :
602 0 : if (equal) {
603 0 : *_found = talloc_move(mem_ctx, &streams[i].name);
604 0 : TALLOC_FREE(streams);
605 0 : return NT_STATUS_OK;
606 : }
607 : }
608 :
609 10 : TALLOC_FREE(streams);
610 10 : return NT_STATUS_OBJECT_NAME_NOT_FOUND;
611 : }
612 :
613 28856 : static bool filename_split_lcomp(
614 : TALLOC_CTX *mem_ctx,
615 : const char *name_in,
616 : bool posix,
617 : char **_dirname,
618 : const char **_fname_rel,
619 : const char **_streamname)
620 : {
621 28856 : const char *lcomp = NULL;
622 28856 : const char *fname_rel = NULL;
623 28856 : const char *streamname = NULL;
624 28856 : char *dirname = NULL;
625 :
626 28856 : if (name_in[0] == '\0') {
627 6967 : fname_rel = ".";
628 6967 : dirname = talloc_strdup(mem_ctx, "");
629 6967 : if (dirname == NULL) {
630 0 : return false;
631 : }
632 6967 : goto done;
633 : }
634 :
635 21889 : lcomp = strrchr_m(name_in, '/');
636 21889 : if (lcomp != NULL) {
637 19495 : fname_rel = lcomp+1;
638 19495 : dirname = talloc_strndup(mem_ctx, name_in, lcomp - name_in);
639 19495 : if (dirname == NULL) {
640 0 : return false;
641 : }
642 19495 : goto find_stream;
643 : }
644 :
645 : /*
646 : * No slash, dir is emtpy
647 : */
648 2394 : dirname = talloc_strdup(mem_ctx, "");
649 2394 : if (dirname == NULL) {
650 0 : return false;
651 : }
652 :
653 2394 : if (!posix && (name_in[0] == ':')) {
654 : /*
655 : * Special case for stream on root directory
656 : */
657 8 : fname_rel = ".";
658 8 : streamname = name_in;
659 8 : goto done;
660 : }
661 :
662 2386 : fname_rel = name_in;
663 :
664 21881 : find_stream:
665 21881 : if (!posix) {
666 21881 : streamname = strchr_m(fname_rel, ':');
667 :
668 21881 : if (streamname != NULL) {
669 40 : fname_rel = talloc_strndup(
670 : mem_ctx,
671 : fname_rel,
672 40 : streamname - fname_rel);
673 40 : if (fname_rel == NULL) {
674 0 : TALLOC_FREE(dirname);
675 0 : return false;
676 : }
677 : }
678 : }
679 :
680 21881 : done:
681 28856 : *_dirname = dirname;
682 28856 : *_fname_rel = fname_rel;
683 28856 : *_streamname = streamname;
684 28856 : return true;
685 : }
686 :
687 : /*
688 : * Create the correct capitalization of a file name to be created.
689 : */
690 1826 : static NTSTATUS filename_convert_normalize_new(
691 : TALLOC_CTX *mem_ctx,
692 : struct connection_struct *conn,
693 : char *name_in,
694 : char **_normalized)
695 : {
696 1826 : char *name = name_in;
697 :
698 1826 : *_normalized = NULL;
699 :
700 3652 : if (!conn->case_preserve ||
701 1826 : (mangle_is_8_3(name, false,
702 1826 : conn->params) &&
703 988 : !conn->short_case_preserve)) {
704 :
705 0 : char *normalized = talloc_strdup(mem_ctx, name);
706 0 : if (normalized == NULL) {
707 0 : return NT_STATUS_NO_MEMORY;
708 : }
709 :
710 0 : strnorm(normalized, lp_default_case(SNUM(conn)));
711 0 : name = normalized;
712 : }
713 :
714 1826 : if (mangle_is_mangled(name, conn->params)) {
715 : bool found;
716 0 : char *unmangled = NULL;
717 :
718 0 : found = mangle_lookup_name_from_8_3(
719 0 : mem_ctx, name, &unmangled, conn->params);
720 0 : if (found) {
721 0 : name = unmangled;
722 : }
723 : }
724 :
725 1826 : if (name != name_in) {
726 0 : *_normalized = name;
727 : }
728 :
729 1826 : return NT_STATUS_OK;
730 : }
731 :
732 : /*
733 : * Open smb_fname_rel->fsp as a pathref fsp with a case insensitive
734 : * fallback using GETREALFILENAME_CACHE and get_real_filename_at() if
735 : * the first attempt based on the filename sent by the client gives
736 : * ENOENT.
737 : */
738 15911 : static NTSTATUS openat_pathref_fsp_case_insensitive(
739 : struct files_struct *dirfsp,
740 : struct smb_filename *smb_fname_rel,
741 : uint32_t ucf_flags)
742 : {
743 15911 : const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
744 15911 : DATA_BLOB cache_key = { .data = NULL, };
745 15911 : char *found_name = NULL;
746 : NTSTATUS status;
747 : bool ok;
748 :
749 15911 : SET_STAT_INVALID(smb_fname_rel->st);
750 :
751 : /* Check veto files - only looks at last component. */
752 15911 : if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
753 0 : DBG_DEBUG("veto files rejecting last component %s\n",
754 : smb_fname_str_dbg(smb_fname_rel));
755 0 : return NT_STATUS_NETWORK_OPEN_RESTRICTION;
756 : }
757 :
758 15911 : status = openat_pathref_fsp(dirfsp, smb_fname_rel);
759 :
760 15911 : if (NT_STATUS_IS_OK(status)) {
761 13101 : return NT_STATUS_OK;
762 : }
763 :
764 2810 : if (VALID_STAT(smb_fname_rel->st)) {
765 : /*
766 : * We got an error although the object existed. Might
767 : * be a symlink we don't want.
768 : */
769 900 : return status;
770 : }
771 :
772 1910 : if (!NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
773 : /*
774 : * Only retry on ENOENT
775 : */
776 0 : return status;
777 : }
778 :
779 1910 : if (posix || dirfsp->conn->case_sensitive) {
780 : /*
781 : * Only return case insensitive if required
782 : */
783 0 : return status;
784 : }
785 :
786 1910 : if (lp_stat_cache()) {
787 1910 : char *base_name = smb_fname_rel->base_name;
788 1910 : char *original_relname = NULL;
789 1910 : DATA_BLOB value = { .data = NULL };
790 :
791 1910 : ok = get_real_filename_cache_key(
792 : talloc_tos(), dirfsp, base_name, &cache_key);
793 1910 : if (!ok) {
794 : /*
795 : * probably ENOMEM, just bail
796 : */
797 8 : return status;
798 : }
799 :
800 1910 : DO_PROFILE_INC(statcache_lookups);
801 :
802 1910 : ok = memcache_lookup(
803 : NULL, GETREALFILENAME_CACHE, cache_key, &value);
804 1910 : if (!ok) {
805 1902 : DO_PROFILE_INC(statcache_misses);
806 1902 : goto lookup;
807 : }
808 8 : DO_PROFILE_INC(statcache_hits);
809 :
810 : /*
811 : * For the "new filename" case we need to preserve the
812 : * capitalization the client sent us, see
813 : * https://bugzilla.samba.org/show_bug.cgi?id=15481
814 : */
815 8 : original_relname = smb_fname_rel->base_name;
816 :
817 8 : smb_fname_rel->base_name = talloc_memdup(
818 : smb_fname_rel, value.data, value.length);
819 8 : if (smb_fname_rel->base_name == NULL) {
820 0 : TALLOC_FREE(cache_key.data);
821 0 : return NT_STATUS_NO_MEMORY;
822 : }
823 :
824 8 : if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
825 0 : DBG_DEBUG("veto files rejecting last component %s\n",
826 : smb_fname_str_dbg(smb_fname_rel));
827 0 : TALLOC_FREE(cache_key.data);
828 0 : return NT_STATUS_NETWORK_OPEN_RESTRICTION;
829 : }
830 :
831 8 : status = openat_pathref_fsp(dirfsp, smb_fname_rel);
832 8 : if (NT_STATUS_IS_OK(status)) {
833 8 : TALLOC_FREE(cache_key.data);
834 8 : TALLOC_FREE(original_relname);
835 8 : return NT_STATUS_OK;
836 : }
837 :
838 0 : memcache_delete(NULL, GETREALFILENAME_CACHE, cache_key);
839 0 : TALLOC_FREE(smb_fname_rel->base_name);
840 0 : smb_fname_rel->base_name = original_relname;
841 : }
842 :
843 0 : lookup:
844 1902 : status = get_real_filename_at(
845 1902 : dirfsp, smb_fname_rel->base_name, smb_fname_rel, &found_name);
846 1902 : if (NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED) &&
847 0 : (ucf_flags & UCF_PREP_CREATEFILE)) {
848 : /*
849 : * dropbox
850 : */
851 0 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
852 : }
853 :
854 1902 : if (NT_STATUS_IS_OK(status)) {
855 76 : TALLOC_FREE(smb_fname_rel->base_name);
856 76 : smb_fname_rel->base_name = found_name;
857 :
858 76 : if (IS_VETO_PATH(dirfsp->conn, smb_fname_rel->base_name)) {
859 0 : DBG_DEBUG("veto files rejecting last component %s\n",
860 : smb_fname_str_dbg(smb_fname_rel));
861 0 : return NT_STATUS_NETWORK_OPEN_RESTRICTION;
862 : }
863 :
864 76 : status = openat_pathref_fsp(dirfsp, smb_fname_rel);
865 : }
866 :
867 1902 : if (NT_STATUS_IS_OK(status) && (cache_key.data != NULL)) {
868 76 : DATA_BLOB value = {
869 76 : .data = (uint8_t *)smb_fname_rel->base_name,
870 76 : .length = strlen(smb_fname_rel->base_name) + 1,
871 : };
872 :
873 76 : memcache_add(NULL, GETREALFILENAME_CACHE, cache_key, value);
874 : }
875 :
876 1902 : TALLOC_FREE(cache_key.data);
877 :
878 1902 : return status;
879 : }
880 :
881 0 : static const char *previous_slash(const char *name_in, const char *slash)
882 : {
883 0 : const char *prev = name_in;
884 :
885 0 : while (true) {
886 0 : const char *next = strchr_m(prev, '/');
887 :
888 0 : SMB_ASSERT(next != NULL); /* we have at least one slash */
889 :
890 0 : if (next == slash) {
891 0 : break;
892 : }
893 :
894 0 : prev = next+1;
895 : };
896 :
897 0 : if (prev == name_in) {
898 : /* no previous slash */
899 0 : return NULL;
900 : }
901 :
902 0 : return prev;
903 : }
904 :
905 3444 : static char *symlink_target_path(
906 : TALLOC_CTX *mem_ctx,
907 : const char *name_in,
908 : const char *substitute,
909 : size_t unparsed)
910 : {
911 3444 : size_t name_in_len = strlen(name_in);
912 3444 : const char *p_unparsed = NULL;
913 3444 : const char *parent = NULL;
914 : char *ret;
915 :
916 3444 : SMB_ASSERT(unparsed <= name_in_len);
917 :
918 3444 : p_unparsed = name_in + (name_in_len - unparsed);
919 :
920 3444 : if (substitute[0] == '/') {
921 3444 : ret = talloc_asprintf(mem_ctx, "%s%s", substitute, p_unparsed);
922 3444 : return ret;
923 : }
924 :
925 0 : if (unparsed == 0) {
926 0 : parent = strrchr_m(name_in, '/');
927 : } else {
928 0 : parent = previous_slash(name_in, p_unparsed);
929 : }
930 :
931 0 : if (parent == NULL) {
932 : /* no previous slash */
933 0 : parent = name_in;
934 : }
935 :
936 0 : ret = talloc_asprintf(
937 : mem_ctx,
938 : "%.*s%s%s",
939 0 : (int)(parent - name_in),
940 : name_in,
941 : substitute,
942 : p_unparsed);
943 0 : return ret;
944 : }
945 :
946 5336 : NTSTATUS safe_symlink_target_path(TALLOC_CTX *mem_ctx,
947 : const char *connectpath,
948 : const char *dir,
949 : const char *target,
950 : size_t unparsed,
951 : char **_relative)
952 : {
953 5336 : char *abs_target = NULL;
954 5336 : char *abs_target_canon = NULL;
955 5336 : const char *relative = NULL;
956 : bool in_share;
957 5336 : NTSTATUS status = NT_STATUS_NO_MEMORY;
958 :
959 5336 : DBG_DEBUG("connectpath [%s] target [%s] unparsed [%zu]\n",
960 : connectpath, target, unparsed);
961 :
962 5336 : if (target[0] == '/') {
963 4368 : abs_target = talloc_strdup(mem_ctx, target);
964 968 : } else if (dir == NULL) {
965 0 : abs_target = talloc_asprintf(mem_ctx,
966 : "%s/%s",
967 : connectpath,
968 : target);
969 968 : } else if (dir[0] == '/') {
970 0 : abs_target = talloc_asprintf(mem_ctx,
971 : "%s/%s",
972 : dir,
973 : target);
974 : } else {
975 968 : abs_target = talloc_asprintf(mem_ctx,
976 : "%s/%s/%s",
977 : connectpath,
978 : dir,
979 : target);
980 : }
981 5336 : if (abs_target == NULL) {
982 0 : goto fail;
983 : }
984 :
985 5336 : abs_target_canon = canonicalize_absolute_path(abs_target, abs_target);
986 5336 : if (abs_target_canon == NULL) {
987 0 : goto fail;
988 : }
989 :
990 5336 : DBG_DEBUG("abs_target_canon=%s\n", abs_target_canon);
991 :
992 5336 : in_share = subdir_of(
993 : connectpath, strlen(connectpath), abs_target_canon, &relative);
994 5336 : if (!in_share) {
995 12 : DBG_DEBUG("wide link to %s\n", abs_target_canon);
996 12 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
997 12 : goto fail;
998 : }
999 :
1000 5324 : *_relative = talloc_strdup(mem_ctx, relative);
1001 5324 : if (*_relative == NULL) {
1002 0 : goto fail;
1003 : }
1004 :
1005 5324 : status = NT_STATUS_OK;
1006 5336 : fail:
1007 5336 : TALLOC_FREE(abs_target);
1008 5336 : return status;
1009 : }
1010 :
1011 : /*
1012 : * Split up name_in as sent by the client into a directory pathref fsp
1013 : * and a relative smb_filename.
1014 : */
1015 28857 : static NTSTATUS filename_convert_dirfsp_nosymlink(
1016 : TALLOC_CTX *mem_ctx,
1017 : connection_struct *conn,
1018 : const char *name_in,
1019 : uint32_t ucf_flags,
1020 : NTTIME twrp,
1021 : struct files_struct **_dirfsp,
1022 : struct smb_filename **_smb_fname,
1023 : char **_substitute,
1024 : size_t *_unparsed)
1025 : {
1026 28857 : struct smb_filename *smb_dirname = NULL;
1027 28857 : struct smb_filename *smb_fname_rel = NULL;
1028 28857 : struct smb_filename *smb_fname = NULL;
1029 28857 : const bool posix = (ucf_flags & UCF_POSIX_PATHNAMES);
1030 28857 : char *dirname = NULL;
1031 28857 : const char *fname_rel = NULL;
1032 28857 : const char *streamname = NULL;
1033 28857 : char *saved_streamname = NULL;
1034 28857 : struct files_struct *base_fsp = NULL;
1035 : bool ok;
1036 28857 : NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
1037 :
1038 28857 : if (ucf_flags & UCF_DFS_PATHNAME) {
1039 : /*
1040 : * We've been given a raw DFS pathname.
1041 : */
1042 1048 : char *pathname = NULL;
1043 1048 : DBG_DEBUG("Before dfs_filename_convert name_in: %s\n",
1044 : name_in);
1045 1048 : status = dfs_filename_convert(mem_ctx,
1046 : conn,
1047 : ucf_flags,
1048 : name_in,
1049 : &pathname);
1050 1048 : if (!NT_STATUS_IS_OK(status)) {
1051 0 : DBG_DEBUG("dfs_filename_convert "
1052 : "failed for name %s with %s\n",
1053 : name_in,
1054 : nt_errstr(status));
1055 0 : return status;
1056 : }
1057 1048 : ucf_flags &= ~UCF_DFS_PATHNAME;
1058 1048 : name_in = pathname;
1059 1048 : DBG_DEBUG("After dfs_filename_convert name_in: %s\n",
1060 : name_in);
1061 : }
1062 :
1063 28857 : if (is_fake_file_path(name_in)) {
1064 1 : smb_fname = synthetic_smb_fname_split(mem_ctx, name_in, posix);
1065 1 : if (smb_fname == NULL) {
1066 0 : return NT_STATUS_NO_MEMORY;
1067 : }
1068 1 : smb_fname->st = (SMB_STRUCT_STAT) { .st_ex_nlink = 1 };
1069 1 : smb_fname->st.st_ex_btime =
1070 : (struct timespec){0, SAMBA_UTIME_OMIT};
1071 1 : smb_fname->st.st_ex_atime =
1072 : (struct timespec){0, SAMBA_UTIME_OMIT};
1073 1 : smb_fname->st.st_ex_mtime =
1074 : (struct timespec){0, SAMBA_UTIME_OMIT};
1075 1 : smb_fname->st.st_ex_ctime =
1076 : (struct timespec){0, SAMBA_UTIME_OMIT};
1077 :
1078 1 : *_dirfsp = conn->cwd_fsp;
1079 1 : *_smb_fname = smb_fname;
1080 1 : return NT_STATUS_OK;
1081 : }
1082 :
1083 : /*
1084 : * Catch an invalid path of "." before we
1085 : * call filename_split_lcomp(). We need to
1086 : * do this as filename_split_lcomp() will
1087 : * use "." for the missing relative component
1088 : * when an empty name_in path is sent by
1089 : * the client.
1090 : */
1091 28856 : if (ISDOT(name_in)) {
1092 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1093 0 : goto fail;
1094 : }
1095 :
1096 28856 : ok = filename_split_lcomp(
1097 : talloc_tos(),
1098 : name_in,
1099 : posix,
1100 : &dirname,
1101 : &fname_rel,
1102 : &streamname);
1103 28856 : if (!ok) {
1104 0 : status = NT_STATUS_NO_MEMORY;
1105 0 : goto fail;
1106 : }
1107 :
1108 28856 : if ((streamname != NULL) &&
1109 48 : ((conn->fs_capabilities & FILE_NAMED_STREAMS) == 0)) {
1110 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1111 0 : goto fail;
1112 : }
1113 :
1114 28856 : if (!posix) {
1115 28856 : bool name_has_wild = ms_has_wild(dirname);
1116 28856 : name_has_wild |= ms_has_wild(fname_rel);
1117 28856 : if (name_has_wild) {
1118 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1119 0 : goto fail;
1120 : }
1121 : }
1122 :
1123 28856 : if (dirname[0] == '\0') {
1124 9361 : status = synthetic_pathref(
1125 : mem_ctx,
1126 : conn->cwd_fsp,
1127 : ".",
1128 : NULL,
1129 : NULL,
1130 : 0,
1131 : posix ? SMB_FILENAME_POSIX_PATH : 0,
1132 : &smb_dirname);
1133 : } else {
1134 19495 : char *substitute = NULL;
1135 19495 : size_t unparsed = 0;
1136 :
1137 19495 : status = normalize_filename_case(conn, dirname, ucf_flags);
1138 19495 : if (!NT_STATUS_IS_OK(status)) {
1139 0 : DBG_ERR("normalize_filename_case %s failed: %s\n",
1140 : dirname,
1141 : nt_errstr(status));
1142 3444 : goto fail;
1143 : }
1144 :
1145 19495 : status = openat_pathref_dirfsp_nosymlink(
1146 : mem_ctx,
1147 : conn,
1148 : dirname,
1149 : twrp,
1150 : posix,
1151 : &smb_dirname,
1152 : &unparsed,
1153 : &substitute);
1154 :
1155 19495 : if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1156 :
1157 3444 : size_t name_in_len = strlen(name_in);
1158 3444 : size_t dirname_len = strlen(dirname);
1159 :
1160 3444 : SMB_ASSERT(name_in_len >= dirname_len);
1161 :
1162 3444 : *_substitute = substitute;
1163 3444 : *_unparsed = unparsed + (name_in_len - dirname_len);
1164 :
1165 3444 : goto fail;
1166 : }
1167 : }
1168 :
1169 25412 : if (!NT_STATUS_IS_OK(status)) {
1170 9501 : DBG_DEBUG("opening directory %s failed: %s\n",
1171 : dirname,
1172 : nt_errstr(status));
1173 9501 : TALLOC_FREE(dirname);
1174 :
1175 9501 : if (NT_STATUS_EQUAL(status, NT_STATUS_PATH_NOT_COVERED)) {
1176 : /* MS-DFS error must propagate back out. */
1177 9450 : goto fail;
1178 : }
1179 :
1180 51 : if (!NT_STATUS_EQUAL(status, NT_STATUS_ACCESS_DENIED)) {
1181 : /*
1182 : * Except ACCESS_DENIED, everything else leads
1183 : * to PATH_NOT_FOUND.
1184 : */
1185 51 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1186 : }
1187 :
1188 51 : goto fail;
1189 : }
1190 :
1191 15911 : if (!VALID_STAT_OF_DIR(smb_dirname->st)) {
1192 0 : status = NT_STATUS_OBJECT_PATH_NOT_FOUND;
1193 0 : goto fail;
1194 : }
1195 :
1196 : /*
1197 : * Only look at bad last component values
1198 : * once we know we have a valid directory. That
1199 : * way we won't confuse error messages from
1200 : * opening the directory path with error
1201 : * messages from a bad last component.
1202 : */
1203 :
1204 : /* Relative filename can't be empty */
1205 15911 : if (fname_rel[0] == '\0') {
1206 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1207 0 : goto fail;
1208 : }
1209 :
1210 : /* Relative filename can't be ".." */
1211 15911 : if (ISDOTDOT(fname_rel)) {
1212 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1213 0 : goto fail;
1214 : }
1215 : /* Relative name can only be dot if directory is empty. */
1216 15911 : if (ISDOT(fname_rel) && dirname[0] != '\0') {
1217 0 : status = NT_STATUS_OBJECT_NAME_INVALID;
1218 0 : goto fail;
1219 : }
1220 :
1221 15911 : TALLOC_FREE(dirname);
1222 :
1223 15911 : smb_fname_rel = synthetic_smb_fname(
1224 : mem_ctx,
1225 : fname_rel,
1226 : streamname,
1227 : NULL,
1228 : twrp,
1229 : posix ? SMB_FILENAME_POSIX_PATH : 0);
1230 15911 : if (smb_fname_rel == NULL) {
1231 0 : status = NT_STATUS_NO_MEMORY;
1232 0 : goto fail;
1233 : }
1234 :
1235 31822 : if ((conn->fs_capabilities & FILE_NAMED_STREAMS) &&
1236 15911 : is_named_stream(smb_fname_rel)) {
1237 : /*
1238 : * Find the base_fsp first without the stream.
1239 : */
1240 48 : saved_streamname = smb_fname_rel->stream_name;
1241 48 : smb_fname_rel->stream_name = NULL;
1242 : }
1243 :
1244 15911 : status = normalize_filename_case(
1245 : conn, smb_fname_rel->base_name, ucf_flags);
1246 15911 : if (!NT_STATUS_IS_OK(status)) {
1247 0 : DBG_ERR("normalize_filename_case %s failed: %s\n",
1248 : smb_fname_rel->base_name,
1249 : nt_errstr(status));
1250 0 : goto fail;
1251 : }
1252 :
1253 15911 : status = openat_pathref_fsp_case_insensitive(
1254 15911 : smb_dirname->fsp, smb_fname_rel, ucf_flags);
1255 :
1256 15911 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
1257 2726 : VALID_STAT(smb_fname_rel->st) &&
1258 900 : S_ISLNK(smb_fname_rel->st.st_ex_mode)) {
1259 :
1260 : /*
1261 : * If we're on an MSDFS share, see if this is
1262 : * an MSDFS link.
1263 : */
1264 1800 : if (lp_host_msdfs() &&
1265 1792 : lp_msdfs_root(SNUM(conn)) &&
1266 892 : is_msdfs_link(smb_dirname->fsp, smb_fname_rel))
1267 : {
1268 892 : status = NT_STATUS_PATH_NOT_COVERED;
1269 892 : goto fail;
1270 : }
1271 :
1272 : #if defined(WITH_SMB1SERVER)
1273 : /*
1274 : * In SMB1 posix mode, if this is a symlink,
1275 : * allow access to the name with a NULL smb_fname->fsp.
1276 : */
1277 8 : if (ucf_flags & UCF_LCOMP_LNK_OK) {
1278 0 : SMB_ASSERT(smb_fname_rel->fsp == NULL);
1279 0 : SMB_ASSERT(streamname == NULL);
1280 :
1281 0 : smb_fname = full_path_from_dirfsp_atname(
1282 : mem_ctx,
1283 0 : smb_dirname->fsp,
1284 : smb_fname_rel);
1285 0 : if (smb_fname == NULL) {
1286 0 : status = NT_STATUS_NO_MEMORY;
1287 0 : goto fail;
1288 : }
1289 0 : goto done;
1290 : }
1291 : #endif
1292 : }
1293 :
1294 15019 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
1295 1834 : !VALID_STAT(smb_fname_rel->st)) {
1296 :
1297 1826 : char *normalized = NULL;
1298 :
1299 : /*
1300 : * Creating a new file
1301 : */
1302 :
1303 1826 : status = filename_convert_normalize_new(
1304 : smb_fname_rel,
1305 : conn,
1306 : smb_fname_rel->base_name,
1307 : &normalized);
1308 1826 : if (!NT_STATUS_IS_OK(status)) {
1309 0 : DBG_DEBUG("filename_convert_normalize_new failed: "
1310 : "%s\n",
1311 : nt_errstr(status));
1312 0 : goto fail;
1313 : }
1314 1826 : if (normalized != NULL) {
1315 0 : smb_fname_rel->base_name = normalized;
1316 : }
1317 :
1318 1826 : smb_fname_rel->stream_name = saved_streamname;
1319 :
1320 1826 : smb_fname = full_path_from_dirfsp_atname(
1321 1826 : mem_ctx, smb_dirname->fsp, smb_fname_rel);
1322 1826 : if (smb_fname == NULL) {
1323 0 : status = NT_STATUS_NO_MEMORY;
1324 0 : goto fail;
1325 : }
1326 1826 : goto done;
1327 : }
1328 :
1329 13193 : if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_OPEN_RESTRICTION)) {
1330 : /* A vetoed file, pretend it's not there */
1331 0 : status = NT_STATUS_OBJECT_NAME_NOT_FOUND;
1332 : }
1333 13193 : if (!NT_STATUS_IS_OK(status)) {
1334 8 : goto fail;
1335 : }
1336 :
1337 13185 : if (saved_streamname == NULL) {
1338 : /* smb_fname must be allocated off mem_ctx. */
1339 13145 : smb_fname = cp_smb_filename(mem_ctx,
1340 13145 : smb_fname_rel->fsp->fsp_name);
1341 13145 : if (smb_fname == NULL) {
1342 0 : goto fail;
1343 : }
1344 13145 : status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
1345 13145 : if (!NT_STATUS_IS_OK(status)) {
1346 0 : goto fail;
1347 : }
1348 13145 : goto done;
1349 : }
1350 :
1351 40 : base_fsp = smb_fname_rel->fsp;
1352 40 : smb_fname_fsp_unlink(smb_fname_rel);
1353 40 : SET_STAT_INVALID(smb_fname_rel->st);
1354 :
1355 40 : smb_fname_rel->stream_name = saved_streamname;
1356 :
1357 40 : status = open_stream_pathref_fsp(&base_fsp, smb_fname_rel);
1358 :
1359 40 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND) &&
1360 10 : !conn->case_sensitive) {
1361 10 : char *found = NULL;
1362 :
1363 10 : status = get_real_stream_name(
1364 : smb_fname_rel,
1365 : base_fsp,
1366 10 : smb_fname_rel->stream_name,
1367 : &found);
1368 :
1369 10 : if (NT_STATUS_IS_OK(status)) {
1370 0 : smb_fname_rel->stream_name = found;
1371 0 : found = NULL;
1372 0 : status = open_stream_pathref_fsp(
1373 : &base_fsp, smb_fname_rel);
1374 : }
1375 : }
1376 :
1377 40 : if (NT_STATUS_IS_OK(status)) {
1378 : /* smb_fname must be allocated off mem_ctx. */
1379 30 : smb_fname = cp_smb_filename(mem_ctx,
1380 30 : smb_fname_rel->fsp->fsp_name);
1381 30 : if (smb_fname == NULL) {
1382 0 : goto fail;
1383 : }
1384 30 : status = move_smb_fname_fsp_link(smb_fname, smb_fname_rel);
1385 30 : if (!NT_STATUS_IS_OK(status)) {
1386 0 : goto fail;
1387 : }
1388 30 : goto done;
1389 : }
1390 :
1391 10 : if (NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
1392 : /*
1393 : * Creating a new stream
1394 : *
1395 : * We should save the already-open base fsp for
1396 : * create_file_unixpath() somehow.
1397 : */
1398 10 : smb_fname = full_path_from_dirfsp_atname(
1399 10 : mem_ctx, smb_dirname->fsp, smb_fname_rel);
1400 10 : if (smb_fname == NULL) {
1401 0 : status = NT_STATUS_NO_MEMORY;
1402 0 : goto fail;
1403 : }
1404 : /*
1405 : * When open_stream_pathref_fsp() returns
1406 : * NT_STATUS_OBJECT_NAME_NOT_FOUND, smb_fname_rel->fsp
1407 : * has been set to NULL, so we must free base_fsp separately
1408 : * to prevent fd-leaks when opening a stream that doesn't
1409 : * exist.
1410 : */
1411 10 : fd_close(base_fsp);
1412 10 : file_free(NULL, base_fsp);
1413 10 : base_fsp = NULL;
1414 10 : goto done;
1415 : }
1416 :
1417 0 : if (!NT_STATUS_IS_OK(status)) {
1418 0 : goto fail;
1419 : }
1420 :
1421 0 : done:
1422 15011 : *_dirfsp = smb_dirname->fsp;
1423 15011 : *_smb_fname = smb_fname;
1424 :
1425 15011 : smb_fname_fsp_unlink(smb_fname_rel);
1426 15011 : TALLOC_FREE(smb_fname_rel);
1427 15011 : return NT_STATUS_OK;
1428 :
1429 13845 : fail:
1430 : /*
1431 : * If open_stream_pathref_fsp() returns an error, smb_fname_rel->fsp
1432 : * has been set to NULL, so we must free base_fsp separately
1433 : * to prevent fd-leaks when opening a stream that doesn't
1434 : * exist.
1435 : */
1436 13845 : if (base_fsp != NULL) {
1437 0 : fd_close(base_fsp);
1438 0 : file_free(NULL, base_fsp);
1439 0 : base_fsp = NULL;
1440 : }
1441 13845 : TALLOC_FREE(dirname);
1442 13845 : TALLOC_FREE(smb_dirname);
1443 13845 : TALLOC_FREE(smb_fname_rel);
1444 13845 : return status;
1445 : }
1446 :
1447 25417 : NTSTATUS filename_convert_dirfsp(
1448 : TALLOC_CTX *mem_ctx,
1449 : connection_struct *conn,
1450 : const char *name_in,
1451 : uint32_t ucf_flags,
1452 : NTTIME twrp,
1453 : struct files_struct **_dirfsp,
1454 : struct smb_filename **_smb_fname)
1455 : {
1456 25417 : char *substitute = NULL;
1457 25417 : size_t unparsed = 0;
1458 : NTSTATUS status;
1459 25417 : char *target = NULL;
1460 25417 : char *safe_target = NULL;
1461 25417 : size_t symlink_redirects = 0;
1462 :
1463 28861 : next:
1464 28861 : if (symlink_redirects > 40) {
1465 4 : return NT_STATUS_OBJECT_PATH_NOT_FOUND;
1466 : }
1467 :
1468 28857 : status = filename_convert_dirfsp_nosymlink(
1469 : mem_ctx,
1470 : conn,
1471 : name_in,
1472 : ucf_flags,
1473 : twrp,
1474 : _dirfsp,
1475 : _smb_fname,
1476 : &substitute,
1477 : &unparsed);
1478 :
1479 28857 : if (!NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
1480 25413 : return status;
1481 : }
1482 :
1483 3444 : if (!lp_follow_symlinks(SNUM(conn))) {
1484 0 : return NT_STATUS_OBJECT_PATH_NOT_FOUND;
1485 : }
1486 :
1487 : /*
1488 : * Right now, SMB2 and SMB1 always traverse symlinks
1489 : * within the share. SMB1+POSIX traverses non-terminal
1490 : * symlinks within the share.
1491 : *
1492 : * When we add SMB2+POSIX we need to return
1493 : * a NT_STATUS_STOPPED_ON_SYMLINK error here, using the
1494 : * symlink target data read below if SMB2+POSIX has
1495 : * UCF_POSIX_PATHNAMES set to cause the client to
1496 : * resolve all symlinks locally.
1497 : */
1498 :
1499 3444 : target = symlink_target_path(mem_ctx, name_in, substitute, unparsed);
1500 3444 : if (target == NULL) {
1501 0 : return NT_STATUS_NO_MEMORY;
1502 : }
1503 :
1504 3444 : status = safe_symlink_target_path(mem_ctx,
1505 3444 : conn->connectpath,
1506 : NULL,
1507 : target,
1508 : unparsed,
1509 : &safe_target);
1510 3444 : TALLOC_FREE(target);
1511 3444 : if (!NT_STATUS_IS_OK(status)) {
1512 0 : return status;
1513 : }
1514 3444 : name_in = safe_target;
1515 :
1516 3444 : symlink_redirects += 1;
1517 :
1518 3444 : goto next;
1519 : }
1520 :
1521 : /*
1522 : * Build the full path from a dirfsp and dirfsp relative name
1523 : */
1524 171614 : struct smb_filename *full_path_from_dirfsp_atname(
1525 : TALLOC_CTX *mem_ctx,
1526 : const struct files_struct *dirfsp,
1527 : const struct smb_filename *atname)
1528 : {
1529 171614 : struct smb_filename *fname = NULL;
1530 171614 : char *path = NULL;
1531 :
1532 171614 : if (dirfsp == dirfsp->conn->cwd_fsp ||
1533 46418 : ISDOT(dirfsp->fsp_name->base_name) ||
1534 26649 : atname->base_name[0] == '/')
1535 : {
1536 144965 : path = talloc_strdup(mem_ctx, atname->base_name);
1537 : } else {
1538 26649 : path = talloc_asprintf(mem_ctx, "%s/%s",
1539 26649 : dirfsp->fsp_name->base_name,
1540 26649 : atname->base_name);
1541 : }
1542 171614 : if (path == NULL) {
1543 0 : return NULL;
1544 : }
1545 :
1546 171614 : fname = synthetic_smb_fname(mem_ctx,
1547 : path,
1548 171614 : atname->stream_name,
1549 : &atname->st,
1550 171614 : atname->twrp,
1551 171614 : atname->flags);
1552 171614 : TALLOC_FREE(path);
1553 171614 : if (fname == NULL) {
1554 0 : return NULL;
1555 : }
1556 :
1557 171614 : return fname;
1558 : }
|