--- source3/smbd/open.c.orig 2022-01-08 05:19:35.191220169 +0100 +++ source3/smbd/open.c 2022-01-09 16:47:03.324457754 +0100 @@ -3857,6 +3857,9 @@ static NTSTATUS mkdir_internal(connectio bool posix_open = false; bool need_re_stat = false; uint32_t access_mask = SEC_DIR_ADD_SUBDIR; + bool ok; + struct smb_filename *oldwd_fname = NULL; + struct smb_filename *smb_fname_rel = NULL; if (!CAN_WRITE(conn) || (access_mask & ~(conn->share_access))) { DEBUG(5,("mkdir_internal: failing share access " @@ -3864,8 +3867,16 @@ static NTSTATUS mkdir_internal(connectio return NT_STATUS_ACCESS_DENIED; } + ok = parent_smb_fname(talloc_tos(), + smb_dname, + &parent_dir, + &smb_fname_rel); + if (!ok) { + return NT_STATUS_NO_MEMORY; + } + if (!parent_dirname(talloc_tos(), smb_dname->base_name, &parent_dir, - NULL)) { + &smb_fname_rel)) { return NT_STATUS_NO_MEMORY; } @@ -3888,10 +3899,36 @@ static NTSTATUS mkdir_internal(connectio return status; } - if (SMB_VFS_MKDIR(conn, smb_dname, mode) != 0) { - return map_nt_error_from_unix(errno); + oldwd_fname = vfs_GetWd(talloc_tos(), conn); + if (oldwd_fname == NULL) { + return NT_STATUS_NO_MEMORY; } + /* Pin parent directory in place. */ + if (vfs_ChDir(conn, parent_dir_fname) == -1) { + status = map_nt_error_from_unix(errno); + TALLOC_FREE(oldwd_fname); + return status; + } + + /* Ensure the relative path is below the share. */ + status = check_reduced_name(conn, parent_dir_fname, smb_fname_rel); + if (!NT_STATUS_IS_OK(status)) { + goto need_chdir_err; + } + + if (SMB_VFS_MKDIR(conn, smb_fname_rel, mode) != 0) { + status = map_nt_error_from_unix(errno); + goto need_chdir_err; + } + + /* Return to share $cwd. */ + ret = vfs_ChDir(conn, oldwd_fname); + if (ret == -1) { + smb_panic("unable to get back to old directory\n"); + } + TALLOC_FREE(oldwd_fname); + /* Ensure we're checking for a symlink here.... */ /* We don't want to get caught by a symlink racer. */ @@ -3957,6 +3994,15 @@ static NTSTATUS mkdir_internal(connectio smb_dname->base_name); return NT_STATUS_OK; + + need_chdir_err: + + ret = vfs_ChDir(conn, oldwd_fname); + if (ret == -1) { + smb_panic("unable to get back to old directory\n"); + } + TALLOC_FREE(oldwd_fname); + return status; } /**************************************************************************** --- source3/lib/filename_util.c.orig 2022-01-09 16:42:51.666584750 +0100 +++ source3/lib/filename_util.c 2022-01-09 16:43:10.617822133 +0100 @@ -239,6 +239,69 @@ struct smb_filename *cp_smb_filename(TAL return out; } +/** + * Return allocated parent directory and basename of path + * + * Note: if requesting name, it is returned as talloc child of the + * parent. Freeing the parent is thus sufficient to free both. + */ +bool parent_smb_fname(TALLOC_CTX *mem_ctx, + const struct smb_filename *path, + struct smb_filename **_parent, + struct smb_filename **_name) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct smb_filename *parent = NULL; + struct smb_filename *name = NULL; + char *p = NULL; + + parent = cp_smb_filename(frame, path); + if (parent == NULL) { + TALLOC_FREE(frame); + return false; + } + TALLOC_FREE(parent->stream_name); + SET_STAT_INVALID(parent->st); + + p = strrchr_m(parent->base_name, '/'); /* Find final '/', if any */ + if (p == NULL) { + TALLOC_FREE(parent->base_name); + parent->base_name = talloc_strdup(parent, "."); + if (parent->base_name == NULL) { + TALLOC_FREE(frame); + return false; + } + p = path->base_name; + } else { + *p = '\0'; + p++; + } + + if (_name == NULL) { + *_parent = talloc_move(mem_ctx, &parent); + TALLOC_FREE(frame); + return true; + } + + name = cp_smb_filename(frame, path); + if (name == NULL) { + TALLOC_FREE(frame); + return false; + } + TALLOC_FREE(name->base_name); + + name->base_name = talloc_strdup(name, p); + if (name == NULL) { + TALLOC_FREE(frame); + return false; + } + + *_parent = talloc_move(mem_ctx, &parent); + *_name = talloc_move(*_parent, &name); + TALLOC_FREE(frame); + return true; +} + /**************************************************************************** Simple check to determine if the filename is a stream. ***************************************************************************/