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