LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 397 674 58.9 %
Date: 2024-06-13 04:01:37 Functions: 16 24 66.7 %

          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             : }

Generated by: LCOV version 1.13