s3: smbd: fix a crash in unix_convert()
[metze/samba/wip.git] / source3 / smbd / filename.c
index efa39f411914960eb50f0e6676a302f1289319e4..48814a5790258a3bedd304131656b9a2fe0dfe6a 100644 (file)
@@ -32,7 +32,6 @@
 
 static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
                                  connection_struct *conn,
-                                 const char *orig_path,
                                  struct smb_filename *smb_fname);
 
 /****************************************************************************
@@ -161,7 +160,7 @@ static NTSTATUS check_parent_exists(TALLOC_CTX *ctx,
        }
 
        /* Parent exists - set "start" to be the
-        * last compnent to shorten the tree walk. */
+        * last component to shorten the tree walk. */
 
        /*
         * Safe to use discard_const_p
@@ -323,7 +322,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
 
        if (conn->case_sensitive && !conn->case_preserve &&
                        !conn->short_case_preserve) {
-               if (!strnorm(smb_fname->base_name, lp_defaultcase(SNUM(conn)))) {
+               if (!strnorm(smb_fname->base_name, lp_default_case(SNUM(conn)))) {
                        DEBUG(0, ("strnorm %s failed\n", smb_fname->base_name));
                        status = NT_STATUS_INVALID_PARAMETER;
                        goto err;
@@ -371,6 +370,29 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                         */
                        *stream = '\0';
                        stream = tmp;
+
+                       if (smb_fname->base_name[0] == '\0') {
+                               /*
+                                * orig_name was just a stream name.
+                                * This is a stream on the root of
+                                * the share. Replace base_name with
+                                * a "."
+                                */
+                               smb_fname->base_name =
+                                       talloc_strdup(smb_fname, ".");
+                               if (smb_fname->base_name == NULL) {
+                                       status = NT_STATUS_NO_MEMORY;
+                                       goto err;
+                               }
+                               if (SMB_VFS_STAT(conn, smb_fname) != 0) {
+                                       status = map_nt_error_from_unix(errno);
+                                       goto err;
+                               }
+                               DEBUG(5, ("conversion finished %s -> %s\n",
+                                       orig_path,
+                                       smb_fname->base_name));
+                               goto done;
+                       }
                }
        }
 
@@ -450,13 +472,17 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
 
                if (errno == ENOENT) {
                        /* Optimization when creating a new file - only
-                          the last component doesn't exist. */
+                          the last component doesn't exist.
+                          NOTE : check_parent_exists() doesn't preserve errno.
+                       */
+                       int saved_errno = errno;
                        status = check_parent_exists(ctx,
                                                conn,
                                                posix_pathnames,
                                                smb_fname,
                                                &dirpath,
                                                &start);
+                       errno = saved_errno;
                        if (!NT_STATUS_IS_OK(status)) {
                                goto fail;
                        }
@@ -529,13 +555,16 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                 * Optimization for common case where the wildcard
                 * is in the last component and the client already
                 * sent the correct case.
+                * NOTE : check_parent_exists() doesn't preserve errno.
                 */
+               int saved_errno = errno;
                status = check_parent_exists(ctx,
                                        conn,
                                        posix_pathnames,
                                        smb_fname,
                                        &dirpath,
                                        &start);
+               errno = saved_errno;
                if (!NT_STATUS_IS_OK(status)) {
                        goto fail;
                }
@@ -711,12 +740,30 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
 
                                /*
                                 * ENOENT/EACCESS are the only valid errors
-                                * here. EACCESS needs handling here for
-                                * "dropboxes", i.e. directories where users
-                                * can only put stuff with permission -wx.
+                                * here.
                                 */
-                               if ((errno != 0) && (errno != ENOENT)
-                                   && (errno != EACCES)) {
+
+                               if (errno == EACCES) {
+                                       if ((ucf_flags & UCF_PREP_CREATEFILE) == 0) {
+                                               status = NT_STATUS_ACCESS_DENIED;
+                                               goto fail;
+                                       } else {
+                                               /*
+                                                * This is the dropbox
+                                                * behaviour. A dropbox is a
+                                                * directory with only -wx
+                                                * permissions, so
+                                                * get_real_filename fails
+                                                * with EACCESS, it needs to
+                                                * list the directory. We
+                                                * nevertheless want to allow
+                                                * users creating a file.
+                                                */
+                                               errno = 0;
+                                       }
+                               }
+
+                               if ((errno != 0) && (errno != ENOENT)) {
                                        /*
                                         * ENOTDIR and ELOOP both map to
                                         * NT_STATUS_OBJECT_PATH_NOT_FOUND
@@ -745,7 +792,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                                                   conn->params) &&
                                                 !conn->short_case_preserve)) {
                                        if (!strnorm(start,
-                                                       lp_defaultcase(SNUM(conn)))) {
+                                                       lp_default_case(SNUM(conn)))) {
                                                DEBUG(0, ("strnorm %s failed\n",
                                                        start));
                                                status = NT_STATUS_INVALID_PARAMETER;
@@ -956,7 +1003,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
                smb_fname->stream_name = stream;
 
                /* Check path now that the base_name has been converted. */
-               status = build_stream_path(ctx, conn, orig_path, smb_fname);
+               status = build_stream_path(ctx, conn, smb_fname);
                if (!NT_STATUS_IS_OK(status)) {
                        goto fail;
                }
@@ -966,7 +1013,7 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
        return NT_STATUS_OK;
  fail:
        DEBUG(10, ("dirpath = [%s] start = [%s]\n", dirpath, start));
-       if (*dirpath != '\0') {
+       if (dirpath && *dirpath != '\0') {
                smb_fname->base_name = talloc_asprintf(smb_fname, "%s/%s",
                                                       dirpath, start);
        } else {
@@ -987,10 +1034,10 @@ NTSTATUS unix_convert(TALLOC_CTX *ctx,
 }
 
 /****************************************************************************
- Ensure a path is not vetod.
+ Ensure a path is not vetoed.
 ****************************************************************************/
 
-NTSTATUS check_veto_path(connection_struct *conn, const char *name)
+static NTSTATUS check_veto_path(connection_struct *conn, const char *name)
 {
        if (IS_VETO_PATH(conn, name))  {
                /* Is it not dot or dot dot. */
@@ -1018,7 +1065,7 @@ NTSTATUS check_name(connection_struct *conn, const char *name)
                return status;
        }
 
-       if (!lp_widelinks(SNUM(conn)) || !lp_symlinks(SNUM(conn))) {
+       if (!lp_widelinks(SNUM(conn)) || !lp_follow_symlinks(SNUM(conn))) {
                status = check_reduced_name(conn,name);
                if (!NT_STATUS_IS_OK(status)) {
                        DEBUG(5,("check_name: name %s failed with %s\n",name,
@@ -1208,7 +1255,6 @@ int get_real_filename(connection_struct *conn, const char *path,
 
 static NTSTATUS build_stream_path(TALLOC_CTX *mem_ctx,
                                  connection_struct *conn,
-                                 const char *orig_path,
                                  struct smb_filename *smb_fname)
 {
        NTSTATUS status;