1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-21 18:04:06 +03:00
samba-mirror/source3/script/tests/test_symlink_traversal_smb1_posix.sh

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

270 lines
9.6 KiB
Bash
Raw Normal View History

#!/bin/sh
if [ $# -lt 7 ]; then
cat <<EOF
Usage: test_symlink_traversal_smb1_posix.sh SERVER SERVER_IP USERNAME PASSWORD LOCAL_PATH PREFIX SMBCLIENT
EOF
exit 1
fi
SERVER="${1}"
SERVER_IP="${2}"
USERNAME="${3}"
PASSWORD="${4}"
LOCAL_PATH="${5}"
PREFIX="${6}"
SMBCLIENT="${7}"
SMBCLIENT="$VALGRIND ${SMBCLIENT}"
shift 6
incdir=$(dirname "$0")/../../../testprogs/blackbox
. "$incdir"/subunit.sh
failed=0
# Do not let deprecated option warnings muck this up
SAMBA_DEPRECATED_SUPPRESS=1
export SAMBA_DEPRECATED_SUPPRESS
# Define the test environment/filenames.
#
share_test_dir="$LOCAL_PATH"
#
# These files/directories will be created.
#
file_outside_share="${TMPDIR:-/tmp}/symlink_traverse_test_file.$$"
dir_outside_share="${TMPDIR:-/tmp}/symlink_traverse_test_dir.$$"
file_outside_share_noperms="${TMPDIR:-/tmp}/symlink_traverse_test_file_noperm.$$"
dir_outside_share_noperms="${TMPDIR:-/tmp}/symlink_traverse_test_dir_noperm.$$"
#
# These two objects do not exist.
#
file_outside_share_noexist="${TMPDIR:-/tmp}/symlink_traverse_test_noexist.$$"
dir_outside_share_noexist="${TMPDIR:-/tmp}/symlink_traverse_test_dir_noexist.$$"
#
# Cleanup function.
#
do_cleanup()
{
(
#subshell.
cd "$share_test_dir" || return
rm -f "file_exists"
rm -f "symlink_noexist"
rm -f "symlink_file_outside_share"
rm -f "symlink_file_outside_share_noexist"
rm -f "symlink_dir_outside_share"
rm -f "symlink_dir_outside_share_noexist"
rm -f "symlink_file_outside_share_noperms"
rm -f "symlink_dir_outside_share_noperms"
rm -rf "emptydir"
# Links inside share.
rm -f "symlink_file_inside_share_noperms"
rm -f "file_inside_share_noperms"
rm -f "symlink_dir_inside_share_noperms"
chmod 755 "dir_inside_share_noperms"
rm -rf "dir_inside_share_noperms"
)
rm -f "$file_outside_share"
rm -rf "$dir_outside_share"
rm -f "$file_outside_share_noperms"
rm -rf "$dir_outside_share_noperms"
}
#
# Ensure we start from a clean slate.
#
do_cleanup
#
# Create the test files/directories/symlinks.
#
# File/directory explicitly outside share.
touch "$file_outside_share"
mkdir "$dir_outside_share"
# File/directory explicitly outside share with permission denied.
touch "$file_outside_share_noperms"
chmod 0 "$file_outside_share_noperms"
mkdir "$dir_outside_share_noperms"
chmod 0 "$dir_outside_share_noperms"
#
# Create links to these objects inside the share definition.
(
#subshell.
cd "$share_test_dir" || return
touch "file_exists"
ln -s "noexist" "symlink_noexist"
ln -s "$file_outside_share" "symlink_file_outside_share"
ln -s "$file_outside_share_noexist" "symlink_file_outside_share_noexist"
ln -s "$dir_outside_share" "symlink_dir_outside_share"
ln -s "$dir_outside_share_noexist" "symlink_dir_outside_share_noexist"
ln -s "$file_outside_share_noperms" "symlink_file_outside_share_noperms"
ln -s "$dir_outside_share_noperms" "symlink_dir_outside_share_noperms"
#
# Create the identical symlink set underneath "emptydir"
mkdir "emptydir"
(
#subshell
cd "emptydir" || return
touch "file_exists"
ln -s "noexist" "symlink_noexist"
ln -s "$file_outside_share" "symlink_file_outside_share"
ln -s "$file_outside_share_noexist" "symlink_file_outside_share_noexist"
ln -s "$dir_outside_share" "symlink_dir_outside_share"
ln -s "$dir_outside_share_noexist" "symlink_dir_outside_share_noexist"
ln -s "$file_outside_share_noperms" "symlink_file_outside_share_noperms"
ln -s "$dir_outside_share_noperms" "symlink_dir_outside_share_noperms"
)
#
# Create symlinks to access denied file and directory
# objects within the share
touch "file_inside_share_noperms"
chmod 0 "file_inside_share_noperms"
ln -s "file_inside_share_noperms" "symlink_file_inside_share_noperms"
mkdir "dir_inside_share_noperms"
touch "dir_inside_share_noperms/noperm_file_exists"
chmod 0 "dir_inside_share_noperms"
ln -s "dir_inside_share_noperms" "symlink_dir_inside_share_noperms"
)
#
# smbclient function given command, path, expected error, and posix.
#
smbclient_expect_error()
{
filecmd="$1"
filename1="$2"
filename2="$3"
expected_error="$4"
tmpfile=$PREFIX/smbclient_interactive_prompt_commands
cat >"$tmpfile" <<EOF
posix
$filecmd $filename1 $filename2
quit
EOF
cmd='CLI_FORCE_INTERACTIVE=yes $SMBCLIENT -U$USERNAME%$PASSWORD //$SERVER/local_symlinks -I$SERVER_IP -mNT1 < $tmpfile 2>&1'
eval echo "$cmd"
out=$(eval "$cmd")
ret=$?
rm -f "$tmpfile"
if [ $ret != 0 ]; then
printf "%s\n" "$out"
printf "failed accessing local_symlinks with error %s\n" "$ret"
return 1
fi
if [ "$expected_error" = "NT_STATUS_OK" ]; then
printf "%s" "$out" | grep -v "NT_STATUS_"
else
printf "%s" "$out" | grep "$expected_error"
fi
ret=$?
if [ $ret != 0 ]; then
printf "%s\n" "$out"
printf "failed - should get %s doing posix \"%s %s %s\"\n" "$expected_error" "$filecmd" "$filename1" "$filename2"
return 1
fi
}
#
# SMB1+posix tests.
#
test_symlink_traversal_SMB1_posix_onename()
{
name="$1"
do_rename="$2"
#
# get commands.
#
# Remember in SMB1+POSIX, "*" is a perfectly valid pathname component,
# and symlinks can be seen, but not necessarily followed.
#
smbclient_expect_error "get" "$name" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "get" "$name/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
smbclient_expect_error "get" "$name/*" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
smbclient_expect_error "get" "$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbclient_expect_error "get" "emptydir/$name" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "get" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
smbclient_expect_error "get" "emptydir/$name/*" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
smbclient_expect_error "get" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
#
# ls commands.
#
smbclient_expect_error "ls" "$name" "" "NT_STATUS_OK" || return 1
smbd: Simplify openat_pathref_fsp_case_insensitive() This is more lines of code, but it's still a simplification. With this patch we don't call the full openat_pathref_fsp() anymore when looking up the last component in filename_convert_dirfsp(), instead we do the direct SMB_VFS_OPENAT(). We don't need the whole complexity of non_widelink_open() for this case, we do know that we have a real non-cwd dirfsp. The other big change that is not obvious just from looking at the patch: This removes the special case for looking up posix symlinks. Before this patch, filename_convert_dirfsp() returned a proper smb_filename but without an attached fsp when a smb1 posix client hits a symlink. This caused all sorts of special case code everywhere. For example smbd_do_qfilepathinfo() needs to cover both cases just for the smb1 posix symlink case. This special-case handling can go now. We can do the path lookup in the smb1-only qpathinfo code and call into the common code with a proper fsp. When hitting a symlink and with O_PATH available, we'll get the symlink opened with an O_PATH fd. Without O_PATH we obviously can't do that, there we get fd=-1 and an indication that we don't have the procfd fallback around. Why all this? I want to present FIFOs (and eventually symlinks) as reparse points as the very next step. Without this patch, there is no real unified way to get the file attributes from disk. Now we can use the proper logic of fdos_mode() everywhere and not rely on special cases for fsp==NULL. This patch also changes some error codes for smb1 posix extensions. I chose to just change the test instead of going after each and every change. As long as we do get an error, I'm willing to accept that we slightly change error path behaviour for this deprecated code. And, I tried to split this up into smaller patches but I failed. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
2023-10-18 11:50:20 +02:00
smbclient_expect_error "ls" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbclient_expect_error "ls" "emptydir/$name" "" "NT_STATUS_OK" || return 1
smbd: Simplify openat_pathref_fsp_case_insensitive() This is more lines of code, but it's still a simplification. With this patch we don't call the full openat_pathref_fsp() anymore when looking up the last component in filename_convert_dirfsp(), instead we do the direct SMB_VFS_OPENAT(). We don't need the whole complexity of non_widelink_open() for this case, we do know that we have a real non-cwd dirfsp. The other big change that is not obvious just from looking at the patch: This removes the special case for looking up posix symlinks. Before this patch, filename_convert_dirfsp() returned a proper smb_filename but without an attached fsp when a smb1 posix client hits a symlink. This caused all sorts of special case code everywhere. For example smbd_do_qfilepathinfo() needs to cover both cases just for the smb1 posix symlink case. This special-case handling can go now. We can do the path lookup in the smb1-only qpathinfo code and call into the common code with a proper fsp. When hitting a symlink and with O_PATH available, we'll get the symlink opened with an O_PATH fd. Without O_PATH we obviously can't do that, there we get fd=-1 and an indication that we don't have the procfd fallback around. Why all this? I want to present FIFOs (and eventually symlinks) as reparse points as the very next step. Without this patch, there is no real unified way to get the file attributes from disk. Now we can use the proper logic of fdos_mode() everywhere and not rely on special cases for fsp==NULL. This patch also changes some error codes for smb1 posix extensions. I chose to just change the test instead of going after each and every change. As long as we do get an error, I'm willing to accept that we slightly change error path behaviour for this deprecated code. And, I tried to split this up into smaller patches but I failed. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
2023-10-18 11:50:20 +02:00
smbclient_expect_error "ls" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "emptydir/$name/*" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
smbclient_expect_error "ls" "emptydir/$name/*/noexist" "" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
#
# SMB1+POSIX stat commands. All symlinks can be stat'ed.
#
smbclient_expect_error "stat" "$name" "" "NT_STATUS_OK" || return 1
smbclient_expect_error "stat" "emptydir/$name" "" "NT_STATUS_OK" || return 1
#
# del commands. Under SMB1+POSIX we can legitimately delete symlinks, so don't
# try and delete symlink targets, we need them for the later tests.
#
smbd: Simplify openat_pathref_fsp_case_insensitive() This is more lines of code, but it's still a simplification. With this patch we don't call the full openat_pathref_fsp() anymore when looking up the last component in filename_convert_dirfsp(), instead we do the direct SMB_VFS_OPENAT(). We don't need the whole complexity of non_widelink_open() for this case, we do know that we have a real non-cwd dirfsp. The other big change that is not obvious just from looking at the patch: This removes the special case for looking up posix symlinks. Before this patch, filename_convert_dirfsp() returned a proper smb_filename but without an attached fsp when a smb1 posix client hits a symlink. This caused all sorts of special case code everywhere. For example smbd_do_qfilepathinfo() needs to cover both cases just for the smb1 posix symlink case. This special-case handling can go now. We can do the path lookup in the smb1-only qpathinfo code and call into the common code with a proper fsp. When hitting a symlink and with O_PATH available, we'll get the symlink opened with an O_PATH fd. Without O_PATH we obviously can't do that, there we get fd=-1 and an indication that we don't have the procfd fallback around. Why all this? I want to present FIFOs (and eventually symlinks) as reparse points as the very next step. Without this patch, there is no real unified way to get the file attributes from disk. Now we can use the proper logic of fdos_mode() everywhere and not rely on special cases for fsp==NULL. This patch also changes some error codes for smb1 posix extensions. I chose to just change the test instead of going after each and every change. As long as we do get an error, I'm willing to accept that we slightly change error path behaviour for this deprecated code. And, I tried to split this up into smaller patches but I failed. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
2023-10-18 11:50:20 +02:00
smbclient_expect_error "del" "$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbd: Simplify openat_pathref_fsp_case_insensitive() This is more lines of code, but it's still a simplification. With this patch we don't call the full openat_pathref_fsp() anymore when looking up the last component in filename_convert_dirfsp(), instead we do the direct SMB_VFS_OPENAT(). We don't need the whole complexity of non_widelink_open() for this case, we do know that we have a real non-cwd dirfsp. The other big change that is not obvious just from looking at the patch: This removes the special case for looking up posix symlinks. Before this patch, filename_convert_dirfsp() returned a proper smb_filename but without an attached fsp when a smb1 posix client hits a symlink. This caused all sorts of special case code everywhere. For example smbd_do_qfilepathinfo() needs to cover both cases just for the smb1 posix symlink case. This special-case handling can go now. We can do the path lookup in the smb1-only qpathinfo code and call into the common code with a proper fsp. When hitting a symlink and with O_PATH available, we'll get the symlink opened with an O_PATH fd. Without O_PATH we obviously can't do that, there we get fd=-1 and an indication that we don't have the procfd fallback around. Why all this? I want to present FIFOs (and eventually symlinks) as reparse points as the very next step. Without this patch, there is no real unified way to get the file attributes from disk. Now we can use the proper logic of fdos_mode() everywhere and not rely on special cases for fsp==NULL. This patch also changes some error codes for smb1 posix extensions. I chose to just change the test instead of going after each and every change. As long as we do get an error, I'm willing to accept that we slightly change error path behaviour for this deprecated code. And, I tried to split this up into smaller patches but I failed. Signed-off-by: Volker Lendecke <vl@samba.org> Reviewed-by: Ralph Boehme <slow@samba.org>
2023-10-18 11:50:20 +02:00
smbclient_expect_error "del" "emptydir/$name/noexist" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
if [ "$do_rename" = "do rename" ]; then
#
# rename commands. Under SMB1+POSIX we can legitimately rename symlinks, so don't
# try and rename symlink targets, we need them for the later tests.
#
smbclient_expect_error "rename" "file_exists" "$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
# Now in subdirectory emptydir
smbclient_expect_error "rename" "file_exists" "emptydir/$name/noexist" "NT_STATUS_OBJECT_PATH_NOT_FOUND" || return 1
fi
return 0
}
#
# Check error code returns traversing through different
# kinds of symlinks over SMB1+posix.
#
test_symlink_traversal_SMB1_posix()
{
test_symlink_traversal_SMB1_posix_onename "symlink_noexist" "no rename" || return 1
test_symlink_traversal_SMB1_posix_onename "symlink_file_outside_share" "do rename" || return 1
test_symlink_traversal_SMB1_posix_onename "symlink_dir_outside_share" "do rename" || return 1
test_symlink_traversal_SMB1_posix_onename "symlink_dir_outside_share_noexist" "no rename" || return 1
test_symlink_traversal_SMB1_posix_onename "symlink_file_outside_share_noperms" "do rename" || return 1
test_symlink_traversal_SMB1_posix_onename "symlink_dir_outside_share_noperms" "do rename" || return 1
#
# Test paths within share with no permissions.
#
# Can't 'get' file with no perms.
smbclient_expect_error "get" "file_inside_share_noperms" "" "NT_STATUS_ACCESS_DENIED" || return 1
# In SMB1+POSIX you can't "get" a symlink at all.
smbclient_expect_error "get" "symlink_file_inside_share_noperms" "" "NT_STATUS_OBJECT_NAME_NOT_FOUND" || return 1
# But can list it and the symlink to it.
smbclient_expect_error "ls" "file_inside_share_noperms" "" "NT_STATUS_OK" || return 1
smbclient_expect_error "ls" "symlink_file_inside_share_noperms" "" "NT_STATUS_OK" || return 1
# Can't 'get' file inside a directory with no perms.
smbclient_expect_error "get" "dir_inside_share_noperms/noperm_file_exists" "" "NT_STATUS_ACCESS_DENIED" || return 1
# In SMB1+POSIX you can't traverse through a symlink that points to a noperm directory.
smbclient_expect_error "get" "symlink_dir_inside_share_noperms/noperm_file_exists" "" "NT_STATUS_ACCESS_DENIED" || return 1
# But can list the directory with no perms and the symlink to it.
smbclient_expect_error "ls" "dir_inside_share_noperms" "" "NT_STATUS_OK" || return 1
smbclient_expect_error "ls" "symlink_dir_inside_share_noperms" "" "NT_STATUS_OK" || return 1
}
testit "symlink_traversal_SMB1_posix" \
test_symlink_traversal_SMB1_posix ||
failed=$((failed + 1))
#
# Cleanup.
do_cleanup
testok "$0" "$failed"