From: Volker Lendecke Date: Wed, 20 Nov 2024 11:56:33 +0000 (+0100) Subject: smbd: Return NT_STATUS_STOPPED_ON_SYMLINK X-Git-Tag: tdb-1.4.13~476 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=34be8ef596291686e35e90e00c4da99de6cd55f3;p=thirdparty%2Fsamba.git smbd: Return NT_STATUS_STOPPED_ON_SYMLINK Do this for "follow symlinks = now" and smb2 unix extensions Signed-off-by: Volker Lendecke Reviewed-by: Ralph Boehme --- diff --git a/selftest/knownfail.d/symlink b/selftest/knownfail.d/symlink deleted file mode 100644 index 64135666aec..00000000000 --- a/selftest/knownfail.d/symlink +++ /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 diff --git a/source3/script/tests/test_smbclient_s3.sh b/source3/script/tests/test_smbclient_s3.sh index cbff5026ee7..35a283a8546 100755 --- a/source3/script/tests/test_smbclient_s3.sh +++ b/source3/script/tests/test_smbclient_s3.sh @@ -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 diff --git a/source3/smbd/filename.c b/source3/smbd/filename.c index f37387100ea..554df2fd1f8 100644 --- a/source3/smbd/filename.c +++ b/source3/smbd/filename.c @@ -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; diff --git a/source3/smbd/open.c b/source3/smbd/open.c index 909fbac505d..6554467379f 100644 --- a/source3/smbd/open.c +++ b/source3/smbd/open.c @@ -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; diff --git a/source3/smbd/smb2_create.c b/source3/smbd/smb2_create.c index fe65ac2f590..01e06894364 100644 --- a/source3/smbd/smb2_create.c +++ b/source3/smbd/smb2_create.c @@ -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; }