diff options
Diffstat (limited to 'lib/ssh/test/ssh_sftpd_SUITE.erl')
-rw-r--r-- | lib/ssh/test/ssh_sftpd_SUITE.erl | 277 |
1 files changed, 218 insertions, 59 deletions
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 45439ce0fa..763649a12f 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2015. All Rights Reserved. +%% Copyright Ericsson AB 2006-2017. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -28,6 +28,7 @@ -include_lib("kernel/include/file.hrl"). -include("ssh_xfer.hrl"). -include("ssh.hrl"). +-include("ssh_test_lib.hrl"). -define(USER, "Alladin"). -define(PASSWD, "Sesame"). @@ -45,7 +46,7 @@ %%-------------------------------------------------------------------- suite() -> - [{timetrap,{minutes,3}}]. + [{timetrap,{seconds,40}}]. all() -> [open_close_file, @@ -64,7 +65,12 @@ all() -> ver3_open_flags, relpath, sshd_read_file, - ver6_basic]. + ver6_basic, + access_outside_root, + root_with_cwd, + relative_path, + open_file_dir_v5, + open_file_dir_v6]. groups() -> []. @@ -72,19 +78,22 @@ groups() -> %%-------------------------------------------------------------------- init_per_suite(Config) -> - DataDir = ?config(data_dir, Config), - PrivDir = ?config(priv_dir, Config), - ssh_test_lib:setup_dsa(DataDir, PrivDir), - %% to make sure we don't use public-key-auth - %% this should be tested by other test suites - UserDir = filename:join(?config(priv_dir, Config), nopubkey), - file:make_dir(UserDir), - Config. + ?CHECK_CRYPTO( + begin + DataDir = proplists:get_value(data_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), + ssh_test_lib:setup_dsa(DataDir, PrivDir), + %% to make sure we don't use public-key-auth + %% this should be tested by other test suites + UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), + file:make_dir(UserDir), + Config + end). end_per_suite(Config) -> - SysDir = ?config(priv_dir, Config), + SysDir = proplists:get_value(priv_dir, Config), ssh_test_lib:clean_dsa(SysDir), - UserDir = filename:join(?config(priv_dir, Config), nopubkey), + UserDir = filename:join(proplists:get_value(priv_dir, Config), nopubkey), file:del_dir(UserDir), ssh:stop(). @@ -101,11 +110,10 @@ end_per_group(_GroupName, Config) -> init_per_testcase(TestCase, Config) -> ssh:start(), prep(Config), - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), ClientUserDir = filename:join(PrivDir, nopubkey), - SystemDir = filename:join(?config(priv_dir, Config), system), + SystemDir = filename:join(proplists:get_value(priv_dir, Config), system), - Port = ssh_test_lib:inet_port(node()), Options = [{system_dir, SystemDir}, {user_dir, PrivDir}, {user_passwords,[{?USER, ?PASSWD}]}, @@ -113,18 +121,44 @@ init_per_testcase(TestCase, Config) -> {ok, Sftpd} = case TestCase of ver6_basic -> SubSystems = [ssh_sftpd:subsystem_spec([{sftpd_vsn, 6}])], - ssh:daemon(Port, [{subsystems, SubSystems}|Options]); + ssh:daemon(0, [{subsystems, SubSystems}|Options]); + access_outside_root -> + %% Build RootDir/access_outside_root/a/b and set Root and CWD + BaseDir = filename:join(PrivDir, access_outside_root), + RootDir = filename:join(BaseDir, a), + CWD = filename:join(RootDir, b), + %% Make the directory chain: + ok = filelib:ensure_dir(filename:join(CWD, tmp)), + SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir}, + {cwd, CWD}])], + ssh:daemon(0, [{subsystems, SubSystems}|Options]); + root_with_cwd -> + RootDir = filename:join(PrivDir, root_with_cwd), + CWD = filename:join(RootDir, home), + SubSystems = [ssh_sftpd:subsystem_spec([{root, RootDir}, {cwd, CWD}])], + ssh:daemon(0, [{subsystems, SubSystems}|Options]); + relative_path -> + SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])], + ssh:daemon(0, [{subsystems, SubSystems}|Options]); + open_file_dir_v5 -> + SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}])], + ssh:daemon(0, [{subsystems, SubSystems}|Options]); + open_file_dir_v6 -> + SubSystems = [ssh_sftpd:subsystem_spec([{cwd, PrivDir}, + {sftpd_vsn, 6}])], + ssh:daemon(0, [{subsystems, SubSystems}|Options]); _ -> SubSystems = [ssh_sftpd:subsystem_spec([])], - ssh:daemon(Port, [{subsystems, SubSystems}|Options]) + ssh:daemon(0, [{subsystems, SubSystems}|Options]) end, + + Port = ssh_test_lib:daemon_port(Sftpd), Cm = ssh_test_lib:connect(Port, [{user_dir, ClientUserDir}, {user, ?USER}, {password, ?PASSWD}, {user_interaction, false}, - {silently_accept_hosts, true}, - {pwdfun, fun(_,_) -> true end}]), + {silently_accept_hosts, true}]), {ok, Channel} = ssh_connection:session_channel(Cm, ?XFER_WINDOW_SIZE, ?XFER_PACKET_SIZE, ?TIMEOUT), @@ -153,8 +187,8 @@ init_per_testcase(TestCase, Config) -> [{sftp, {Cm, Channel}}, {sftpd, Sftpd }| Config]. end_per_testcase(_TestCase, Config) -> - ssh_sftpd:stop(?config(sftpd, Config)), - {Cm, Channel} = ?config(sftp, Config), + catch ssh:stop_daemon(proplists:get_value(sftpd, Config)), + {Cm, Channel} = proplists:get_value(sftp, Config), ssh_connection:close(Cm, Channel), ssh:close(Cm), ssh:stop(). @@ -165,9 +199,9 @@ end_per_testcase(_TestCase, Config) -> open_close_file() -> [{doc, "Test SSH_FXP_OPEN and SSH_FXP_CLOSE commands"}]. open_close_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = @@ -195,9 +229,9 @@ open_close_file(Config) when is_list(Config) -> ver3_open_flags() -> [{doc, "Test open flags"}]. ver3_open_flags(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "not_exist.txt"), - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = @@ -229,8 +263,8 @@ ver3_open_flags(Config) when is_list(Config) -> open_close_dir() -> [{doc,"Test SSH_FXP_OPENDIR and SSH_FXP_CLOSE commands"}]. open_close_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - {Cm, Channel} = ?config(sftp, Config), + PrivDir = proplists:get_value(priv_dir, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, @@ -256,11 +290,11 @@ open_close_dir(Config) when is_list(Config) -> read_file() -> [{doc, "Test SSH_FXP_READ command"}]. read_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, @@ -279,8 +313,8 @@ read_file(Config) when is_list(Config) -> read_dir() -> [{doc,"Test SSH_FXP_READDIR command"}]. read_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - {Cm, Channel} = ?config(sftp, Config), + PrivDir = proplists:get_value(priv_dir, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_dir(PrivDir, Cm, Channel, ReqId), @@ -290,11 +324,11 @@ read_dir(Config) when is_list(Config) -> write_file() -> [{doc, "Test SSH_FXP_WRITE command"}]. write_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, @@ -314,10 +348,10 @@ write_file(Config) when is_list(Config) -> remove_file() -> [{doc, "Test SSH_FXP_REMOVE command"}]. remove_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = @@ -335,11 +369,11 @@ remove_file(Config) when is_list(Config) -> rename_file() -> [{doc, "Test SSH_FXP_RENAME command"}]. rename_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), NewFileName = filename:join(PrivDir, "test1.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = @@ -372,8 +406,8 @@ rename_file(Config) when is_list(Config) -> mk_rm_dir() -> [{doc, "Test SSH_FXP_MKDIR and SSH_FXP_RMDIR command"}]. mk_rm_dir(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), - {Cm, Channel} = ?config(sftp, Config), + PrivDir = proplists:get_value(priv_dir, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), DirName = filename:join(PrivDir, "test"), ReqId = 0, {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), @@ -400,8 +434,8 @@ real_path(Config) when is_list(Config) -> {skip, "Not a relevant test on windows"}; _ -> ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), - PrivDir = ?config(priv_dir, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), + PrivDir = proplists:get_value(priv_dir, Config), TestDir = filename:join(PrivDir, "ssh_test"), ok = file:make_dir(TestDir), @@ -426,8 +460,8 @@ links(Config) when is_list(Config) -> {skip, "Links are not fully supported by windows"}; _ -> ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), - PrivDir = ?config(priv_dir, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), LinkFileName = filename:join(PrivDir, "link_test.txt"), @@ -450,10 +484,10 @@ links(Config) when is_list(Config) -> retrieve_attributes() -> [{"Test SSH_FXP_STAT, SSH_FXP_LSTAT AND SSH_FXP_FSTAT commands"}]. retrieve_attributes(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, FileInfo} = file:read_file_info(FileName), @@ -519,10 +553,10 @@ set_attributes(Config) when is_list(Config) -> {win32, _} -> {skip, "Known error bug in erts file:read_file_info"}; _ -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, FileInfo} = file:read_file_info(FileName), @@ -573,11 +607,11 @@ set_attributes(Config) when is_list(Config) -> ver3_rename() -> [{doc, "Test that ver3 rename message is handled OTP 6352"}]. ver3_rename(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), NewFileName = filename:join(PrivDir, "test1.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(?SSH_FX_OK), _/binary>>, _} = @@ -588,7 +622,7 @@ relpath() -> [{doc, "Check that realpath works ok seq10670"}]. relpath(Config) when is_list(Config) -> ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), case os:type() of {win32, _} -> @@ -610,11 +644,11 @@ relpath(Config) when is_list(Config) -> sshd_read_file() -> [{doc,"Test SSH_FXP_READ command, using sshd-server"}]. sshd_read_file(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), FileName = filename:join(PrivDir, "test.txt"), ReqId = 0, - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), Handle/binary>>, _} = open_file(FileName, Cm, Channel, ReqId, @@ -632,20 +666,147 @@ sshd_read_file(Config) when is_list(Config) -> ver6_basic() -> [{doc, "Test SFTP Version 6"}]. ver6_basic(Config) when is_list(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), %FileName = filename:join(PrivDir, "test.txt"), - {Cm, Channel} = ?config(sftp, Config), + {Cm, Channel} = proplists:get_value(sftp, Config), ReqId = 0, {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), % Ver 6 we have 5 ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = open_file(PrivDir, Cm, Channel, ReqId, ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, ?SSH_FXF_OPEN_EXISTING). + +%%-------------------------------------------------------------------- +access_outside_root() -> + [{doc, "Try access files outside the tree below RootDir"}]. +access_outside_root(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + BaseDir = filename:join(PrivDir, access_outside_root), + %% A file outside the tree below RootDir which is BaseDir/a + %% Make the file BaseDir/bad : + BadFilePath = filename:join([BaseDir, bad]), + ok = file:write_file(BadFilePath, <<>>), + {Cm, Channel} = proplists:get_value(sftp, Config), + %% Try to access a file parallell to the RootDir: + try_access("/../bad", Cm, Channel, 0), + %% Try to access the same file via the CWD which is /b relative to the RootDir: + try_access("../../bad", Cm, Channel, 1). + + +try_access(Path, Cm, Channel, ReqId) -> + Return = + open_file(Path, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + ct:log("Try open ~p -> ~p",[Path,Return]), + case Return of + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), _Handle0/binary>>, _} -> + ct:fail("Could open a file outside the root tree!"); + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), ?UINT32(Code), Rest/binary>>, <<>>} -> + case Code of + ?SSH_FX_FILE_IS_A_DIRECTORY -> + ct:log("Got the expected SSH_FX_FILE_IS_A_DIRECTORY status",[]), + ok; + ?SSH_FX_FAILURE -> + ct:log("Got the expected SSH_FX_FAILURE status",[]), + ok; + _ -> + case Rest of + <<?UINT32(Len), Txt:Len/binary, _/binary>> -> + ct:fail("Got unexpected SSH_FX_code: ~p (~p)",[Code,Txt]); + _ -> + ct:fail("Got unexpected SSH_FX_code: ~p",[Code]) + end + end; + _ -> + ct:fail("Completly unexpected return: ~p", [Return]) + end. + +%%-------------------------------------------------------------------- +root_with_cwd() -> + [{doc, "Check if files are found, if the CWD and Root are specified"}]. +root_with_cwd(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + RootDir = filename:join(PrivDir, root_with_cwd), + CWD = filename:join(RootDir, home), + FileName = "root_with_cwd.txt", + FilePath = filename:join(CWD, FileName), + ok = filelib:ensure_dir(FilePath), + ok = file:write_file(FilePath ++ "0", <<>>), + ok = file:write_file(FilePath ++ "1", <<>>), + ok = file:write_file(FilePath ++ "2", <<>>), + {Cm, Channel} = proplists:get_value(sftp, Config), + ReqId0 = 0, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId0), _Handle0/binary>>, _} = + open_file(FileName ++ "0", Cm, Channel, ReqId0, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + ReqId1 = 1, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId1), _Handle1/binary>>, _} = + open_file("./" ++ FileName ++ "1", Cm, Channel, ReqId1, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING), + ReqId2 = 2, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId2), _Handle2/binary>>, _} = + open_file("/home/" ++ FileName ++ "2", Cm, Channel, ReqId2, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). + +%%-------------------------------------------------------------------- +relative_path() -> + [{doc, "Test paths relative to CWD when opening a file handle."}]. +relative_path(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + FileName = "test_relative_path.txt", + FilePath = filename:join(PrivDir, FileName), + ok = filelib:ensure_dir(FilePath), + ok = file:write_file(FilePath, <<>>), + {Cm, Channel} = proplists:get_value(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_HANDLE, ?UINT32(ReqId), _Handle/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). + +%%-------------------------------------------------------------------- +open_file_dir_v5() -> + [{doc, "Test if open_file fails when opening existing directory."}]. +open_file_dir_v5(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + FileName = "open_file_dir_v5", + FilePath = filename:join(PrivDir, FileName), + ok = filelib:ensure_dir(FilePath), + ok = file:make_dir(FilePath), + {Cm, Channel} = proplists:get_value(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_FAILURE), _/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). + +%%-------------------------------------------------------------------- +open_file_dir_v6() -> + [{doc, "Test if open_file fails when opening existing directory."}]. +open_file_dir_v6(Config) when is_list(Config) -> + PrivDir = proplists:get_value(priv_dir, Config), + FileName = "open_file_dir_v6", + FilePath = filename:join(PrivDir, FileName), + ok = filelib:ensure_dir(FilePath), + ok = file:make_dir(FilePath), + {Cm, Channel} = proplists:get_value(sftp, Config), + ReqId = 0, + {ok, <<?SSH_FXP_STATUS, ?UINT32(ReqId), + ?UINT32(?SSH_FX_FILE_IS_A_DIRECTORY), _/binary>>, _} = + open_file(FileName, Cm, Channel, ReqId, + ?ACE4_READ_DATA bor ?ACE4_READ_ATTRIBUTES, + ?SSH_FXF_OPEN_EXISTING). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- prep(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), TestFile = filename:join(PrivDir, "test.txt"), TestFile1 = filename:join(PrivDir, "test1.txt"), @@ -653,7 +814,7 @@ prep(Config) -> file:delete(TestFile1), %% Initial config - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), FileName = filename:join(DataDir, "test.txt"), file:copy(FileName, TestFile), Mode = 8#00400 bor 8#00200 bor 8#00040, % read & write owner, read group @@ -683,9 +844,7 @@ reply(Cm, Channel, RBuf) -> 30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE]) end. - open_file(File, Cm, Channel, ReqId, Access, Flags) -> - Data = list_to_binary([?uint32(ReqId), ?binary(list_to_binary(File)), ?uint32(Access), |