]> git.ipfire.org Git - thirdparty/samba.git/commitdiff
smbd: Return NT_STATUS_STOPPED_ON_SYMLINK
authorVolker Lendecke <vl@samba.org>
Wed, 20 Nov 2024 11:56:33 +0000 (12:56 +0100)
committerRalph Boehme <slow@samba.org>
Fri, 22 Nov 2024 09:50:37 +0000 (09:50 +0000)
Do this for "follow symlinks = now" and smb2 unix extensions

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Ralph Boehme <slow@samba.org>
selftest/knownfail.d/symlink [deleted file]
source3/script/tests/test_smbclient_s3.sh
source3/smbd/filename.c
source3/smbd/open.c
source3/smbd/smb2_create.c

diff --git a/selftest/knownfail.d/symlink b/selftest/knownfail.d/symlink
deleted file mode 100644 (file)
index 6413566..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_directory
-^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_file
-^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_absolute_outside_share
-^samba.tests.smb2symlink.samba.tests.smb2symlink.Smb2SymlinkTests.test_symlinkerror_absolute_inshare
index cbff5026ee7b6df4376c3091b9ffd42ba724e0e9..35a283a8546516a2651dfe4f3db1a031dcd05e72 100755 (executable)
@@ -1343,11 +1343,17 @@ EOF
                return 1
        fi
 
-       echo "$out" | grep 'NT_STATUS_OBJECT_NAME_NOT_FOUND'
+       if [ "$PROTOCOL" = "SMB3" ]; then
+           expected_error="NT_STATUS_STOPPED_ON_SYMLINK"
+       else
+           expected_error="NT_STATUS_OBJECT_NAME_NOT_FOUND"
+       fi
+
+       echo "$out" | grep "$expected_error"
        ret=$?
        if [ $ret -ne 0 ]; then
                echo "$out"
-               echo "failed - should get NT_STATUS_OBJECT_NAME_NOT_FOUND getting \\nosymlinks\\source"
+               echo "failed - should get ${expected_error} getting \\nosymlinks\\source"
                return 1
        fi
 
index f37387100ea9e11d511d30cac69db983a0440315..554df2fd1f8021abf520614e3d70179dd3a6983d 100644 (file)
@@ -41,11 +41,7 @@ uint32_t ucf_flags_from_smb_request(struct smb_request *req)
        }
 
        if (req->posix_pathnames) {
-               ucf_flags |= UCF_POSIX_PATHNAMES;
-
-               if (!conn_using_smb2(req->sconn)) {
-                       ucf_flags |= UCF_LCOMP_LNK_OK;
-               }
+               ucf_flags |= (UCF_POSIX_PATHNAMES|UCF_LCOMP_LNK_OK);
        }
        if (req->flags2 & FLAGS2_DFS_PATHNAMES) {
                ucf_flags |= UCF_DFS_PATHNAME;
index 909fbac505ddcaaca653d40b840f3496ee6b78d9..6554467379f3b9021ad7f601d71e9ce2fac6cd3b 100644 (file)
@@ -1216,17 +1216,6 @@ static NTSTATUS open_file(
                        return NT_STATUS_OBJECT_NAME_NOT_FOUND;
                }
 
-               if (S_ISLNK(smb_fname->st.st_ex_mode) &&
-                   !posix_open)
-               {
-                       /*
-                        * Don't allow stat opens on symlinks directly unless
-                        * it's a POSIX open. Match the return code from
-                        * openat_pathref_fsp().
-                        */
-                       return NT_STATUS_OBJECT_NAME_NOT_FOUND;
-               }
-
                if (!fsp->fsp_flags.is_pathref) {
                        /*
                         * There is only one legit case where end up here:
@@ -1236,11 +1225,6 @@ static NTSTATUS open_file(
                         * pathref fsp at this point. The subsequent checks
                         * assert this.
                         */
-                       if (!(smb_fname->flags & SMB_FILENAME_POSIX_PATH)) {
-                               DBG_ERR("[%s] is not a POSIX pathname\n",
-                                       smb_fname_str_dbg(smb_fname));
-                               return NT_STATUS_INTERNAL_ERROR;
-                       }
                        if (!S_ISLNK(smb_fname->st.st_ex_mode)) {
                                DBG_ERR("[%s] is not a symlink\n",
                                        smb_fname_str_dbg(smb_fname));
@@ -1290,13 +1274,19 @@ static NTSTATUS open_file(
        fsp->file_id = vfs_file_id_from_sbuf(conn, &smb_fname->st);
        fsp->vuid = req ? req->vuid : UID_FIELD_INVALID;
        fsp->file_pid = req ? req->smbpid : 0;
-       fsp->fsp_flags.can_lock = true;
-       fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) != 0);
-       fsp->fsp_flags.can_write =
-               CAN_WRITE(conn) &&
-               ((access_mask & (FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0);
-       if (fsp->fsp_name->twrp != 0) {
+       if (file_existed && S_ISLNK(smb_fname->st.st_ex_mode)) {
+               fsp->fsp_flags.can_lock = false;
+               fsp->fsp_flags.can_read = false;
                fsp->fsp_flags.can_write = false;
+       } else {
+               fsp->fsp_flags.can_lock = true;
+               fsp->fsp_flags.can_read = ((access_mask & FILE_READ_DATA) !=
+                                          0);
+               fsp->fsp_flags.can_write = CAN_WRITE(conn) &&
+                                          ((access_mask &
+                                            (FILE_WRITE_DATA |
+                                             FILE_APPEND_DATA)) != 0) &&
+                                          (fsp->fsp_name->twrp == 0);
        }
        fsp->print_file = NULL;
        fsp->fsp_flags.modified = false;
index fe65ac2f590807bbed7db271842fb6340547da21..01e06894364a55f02bf9a27acf7c759c066f4eb6 100644 (file)
@@ -479,7 +479,9 @@ static void smbd_smb2_request_create_done(struct tevent_req *tsubreq)
                if (smbd_smb2_is_compound(smb2req)) {
                        smb2req->compound_create_err = status;
                }
-               error = smbd_smb2_create_error(smb2req, status, NULL);
+               error = smbd_smb2_create_error(smb2req,
+                                              status,
+                                              symlink_reparse);
                if (!NT_STATUS_IS_OK(error)) {
                        smbd_server_connection_terminate(smb2req->xconn,
                                                         nt_errstr(error));
@@ -732,6 +734,9 @@ struct smbd_smb2_create_state {
        uint64_t out_file_id_persistent;
        uint64_t out_file_id_volatile;
        struct smb2_create_blobs *out_context_blobs;
+
+       /* symlink error data */
+       struct reparse_data_buffer *symlink_err;
 };
 
 static void smbd_smb2_create_purge_replay_cache(struct tevent_req *req,
@@ -1187,14 +1192,42 @@ static struct tevent_req *smbd_smb2_create_send(TALLOC_CTX *mem_ctx,
                                              state->in_create_disposition,
                                              state->in_create_options);
 
-       status = filename_convert_dirfsp(
-               req,
-               smb1req->conn,
-               state->fname,
-               ucf_flags,
-               state->twrp_time,
-               &dirfsp,
-               &smb_fname);
+       if (lp_follow_symlinks(SNUM(smb1req->conn)) &&
+           (state->posx == NULL)) {
+               status = filename_convert_dirfsp(mem_ctx,
+                                                smb1req->conn,
+                                                state->fname,
+                                                ucf_flags,
+                                                state->twrp_time,
+                                                &dirfsp,
+                                                &smb_fname);
+       } else {
+               struct smb_filename *smb_fname_rel = NULL;
+
+               status = filename_convert_dirfsp_nosymlink(
+                       mem_ctx,
+                       smb1req->conn,
+                       smb1req->conn->cwd_fsp,
+                       state->fname,
+                       ucf_flags,
+                       state->twrp_time,
+                       &dirfsp,
+                       &smb_fname,
+                       &smb_fname_rel,
+                       &state->symlink_err);
+               TALLOC_FREE(smb_fname_rel);
+
+               if ((state->symlink_err != NULL) &&
+                   !(state->in_create_options & FILE_OPEN_REPARSE_POINT))
+               {
+                       if (dirfsp != NULL) {
+                               close_file_free(NULL, &dirfsp, ERROR_CLOSE);
+                       }
+                       TALLOC_FREE(smb_fname);
+                       TALLOC_FREE(smb_fname_rel);
+                       status = NT_STATUS_STOPPED_ON_SYMLINK;
+               }
+       }
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, state->ev);
        }
@@ -1636,6 +1669,56 @@ static void smbd_smb2_create_after_exec(struct tevent_req *req)
 
        state->out_file_attributes = fdos_mode(state->result);
 
+       if ((state->out_file_attributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
+           (!(state->in_create_options & FILE_OPEN_REPARSE_POINT)))
+       {
+
+               uint32_t tag;
+               uint8_t *data = NULL;
+               uint32_t len = 0;
+
+               status = fsctl_get_reparse_point(
+                       state->result, talloc_tos(), &tag, &data, 65536, &len);
+               if (!NT_STATUS_IS_OK(status)) {
+                       goto fail;
+               }
+
+               if (tag != IO_REPARSE_TAG_SYMLINK) {
+                       status = NT_STATUS_IO_REPARSE_TAG_NOT_HANDLED;
+                       goto fail;
+               }
+
+               state->symlink_err = talloc_zero(state,
+                                                struct reparse_data_buffer);
+               if (state->symlink_err == NULL) {
+                       TALLOC_FREE(data);
+                       status = NT_STATUS_NO_MEMORY;
+                       goto fail;
+               }
+
+               status = reparse_data_buffer_parse(state->symlink_err,
+                                                  state->symlink_err,
+                                                  data,
+                                                  len);
+               TALLOC_FREE(data);
+               if (!NT_STATUS_IS_OK(status)) {
+                       DBG_DEBUG("reparse_data_buffer_parse failed: %s\n",
+                                 nt_errstr(status));
+                       goto fail;
+               }
+
+               /*
+                * Checked above, just to make sure
+                */
+               SMB_ASSERT(state->symlink_err->tag == IO_REPARSE_TAG_SYMLINK);
+
+               DBG_DEBUG("Redirecting to %s\n",
+                         state->symlink_err->parsed.lnk.substitute_name);
+
+               status = NT_STATUS_STOPPED_ON_SYMLINK;
+               goto fail;
+       }
+
        if (state->mxac != NULL) {
                NTTIME last_write_time;
 
@@ -1919,11 +2002,57 @@ static NTSTATUS smbd_smb2_create_recv(struct tevent_req *req,
                        struct smb2_create_blobs *out_context_blobs,
                        struct reparse_data_buffer **symlink_reparse)
 {
-       NTSTATUS status;
+       NTSTATUS status = NT_STATUS_OK;
        struct smbd_smb2_create_state *state = tevent_req_data(req,
                                               struct smbd_smb2_create_state);
+       bool error;
+
+       error = tevent_req_is_nterror(req, &status);
+
+       if (NT_STATUS_EQUAL(status, NT_STATUS_STOPPED_ON_SYMLINK)) {
+               struct symlink_reparse_struct *lnk = &state->symlink_err
+                                                             ->parsed.lnk;
+               size_t fname_len = strlen(state->fname);
+
+               /*
+                * filename_convert_dirfsp_nosymlink() calculates
+                * unparsed_path_length. Just assert it did not mess up big
+                * time.
+                */
+               SMB_ASSERT(lnk->unparsed_path_length <= fname_len);
+
+               if (lnk->unparsed_path_length != 0) {
+                       bool ok;
+                       char *unparsed_unix = NULL;
+                       char *utf_16 = NULL;
+                       size_t utf_16_len;
+
+                       unparsed_unix = state->fname + fname_len -
+                                       lnk->unparsed_path_length;
+
+                       ok = convert_string_talloc(talloc_tos(),
+                                                  CH_UNIX,
+                                                  CH_UTF16,
+                                                  unparsed_unix,
+                                                  lnk->unparsed_path_length,
+                                                  &utf_16,
+                                                  &utf_16_len);
+                       if (!ok) {
+                               tevent_req_received(req);
+                               return NT_STATUS_INTERNAL_ERROR;
+                       }
+                       TALLOC_FREE(utf_16);
+
+                       if (utf_16_len > UINT16_MAX) {
+                               tevent_req_received(req);
+                               return NT_STATUS_BUFFER_OVERFLOW;
+                       }
+                       lnk->unparsed_path_length = utf_16_len;
+               }
+               *symlink_reparse = talloc_move(mem_ctx, &state->symlink_err);
+       }
 
-       if (tevent_req_is_nterror(req, &status)) {
+       if (error) {
                tevent_req_received(req);
                return status;
        }