LCOV - code coverage report
Current view: top level - source3/smbd - filename.c (source / functions) Hit Total Coverage
Test: coverage report for recycleplus df22b230 Lines: 416 655 63.5 %
Date: 2024-02-14 10:14:15 Functions: 17 23 73.9 %

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

Generated by: LCOV version 1.14