1 # Unix SMB/CIFS implementation.
2 # Copyright Volker Lendecke <vl@samba.org> 2022
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation; either version 3 of the License, or
7 # (at your option) any later version.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 from samba
.samba3
import libsmb_samba_internal
as libsmb
19 from samba
import reparse_symlink
20 from samba
import (ntstatus
,NTSTATUSError
)
21 from samba
.dcerpc
import security
as sec
22 import samba
.tests
.libsmb
24 class Smb2SymlinkTests(samba
.tests
.libsmb
.LibsmbTests
):
26 def connections(self
):
27 share
= samba
.tests
.env_get_var_value(
28 "SMB1_SHARE", allow_missing
=True)
30 share
= "nosymlinks_smb1allow"
38 except NTSTATUSError
as e
:
39 if e
.args
[0] != ntstatus
.NT_STATUS_CONNECTION_RESET
:
42 share
= samba
.tests
.env_get_var_value(
43 "SMB2_SHARE", allow_missing
=True)
53 def clean_file(self
, conn
, filename
):
56 except NTSTATUSError
as e
:
57 if e
.args
[0] != ntstatus
.NT_STATUS_OBJECT_NAME_NOT_FOUND
:
60 def create_symlink(self
, conn
, target
, symlink
):
61 self
.clean_file(conn
, symlink
)
62 if (conn
.protocol() < libsmb
.PROTOCOL_SMB2_02
and conn
.have_posix()):
63 conn
.smb1_symlink(target
, symlink
)
65 flags
= 0 if target
[0]=='/' else 1
68 DesiredAccess
=sec
.SEC_FILE_READ_ATTRIBUTE|
69 sec
.SEC_FILE_WRITE_ATTRIBUTE|
71 FileAttributes
=libsmb
.FILE_ATTRIBUTE_NORMAL
,
72 CreateDisposition
=libsmb
.FILE_OPEN_IF
,
73 CreateOptions
=libsmb
.FILE_OPEN_REPARSE_POINT
)
74 b
= reparse_symlink
.symlink_put(target
, target
, 0, 1)
75 conn
.fsctl(syml
, libsmb
.FSCTL_SET_REPARSE_POINT
, b
, 0)
78 def assert_symlink_exception(self
, e
, expect
):
79 self
.assertEqual(e
.args
[0], ntstatus
.NT_STATUS_STOPPED_ON_SYMLINK
)
80 for k
,v
in expect
.items():
82 # Ignore symlink trust flags for now
83 expected
= v
& ~libsmb
.SYMLINK_TRUST_MASK
84 got
= e
.args
[2].get(k
) & ~libsmb
.SYMLINK_TRUST_MASK
85 self
.assertEqual((k
,got
), (k
,expected
))
87 self
.assertEqual((k
,e
.args
[2].get(k
)), (k
,v
))
89 def test_symlinkerror_directory(self
):
90 """Test a symlink in a nonterminal path component"""
91 (smb1
,smb2
) = self
.connections()
96 self
.create_symlink(smb1
, target
, symlink
);
98 with self
.assertRaises(NTSTATUSError
) as e
:
99 fd
= smb2
.create_ex(f
'{symlink}\\{suffix}')
101 self
.assert_symlink_exception(
103 { 'unparsed_path_length' : len(suffix
)+1,
104 'substitute_name' : target
,
105 'print_name' : target
,
106 'flags' : 0x20000001 })
108 self
.clean_file(smb1
, symlink
)
110 def test_symlinkerror_file(self
):
111 """Test a simple symlink in a terminal path"""
112 (smb1
,smb2
) = self
.connections()
116 self
.create_symlink(smb1
, target
, symlink
);
118 with self
.assertRaises(NTSTATUSError
) as e
:
119 fd
= smb2
.create_ex(f
'{symlink}')
121 self
.assert_symlink_exception(
123 { 'unparsed_path_length' : 0,
124 'substitute_name' : target
,
125 'print_name' : target
,
126 'flags' : 0x20000001 })
128 self
.clean_file(smb1
, symlink
)
130 def test_symlinkerror_absolute_outside_share(self
):
132 Test symlinks to outside of the share
133 We return the contents 1:1
135 (smb1
,smb2
) = self
.connections()
138 for target
in ["/etc", "//foo/bar", "/"]:
140 self
.create_symlink(smb1
, target
, symlink
)
142 with self
.assertRaises(NTSTATUSError
) as e
:
143 fd
= smb2
.create_ex(f
'{symlink}')
145 self
.assert_symlink_exception(
147 { 'unparsed_path_length' : 0,
148 'substitute_name' : target
,
149 'print_name' : target
,
152 self
.clean_file(smb1
, symlink
)
154 def test_symlinkerror_absolute_inshare(self
):
155 """Test an absolute symlink inside the share"""
156 (smb1
,smb2
) = self
.connections()
159 localpath
=samba
.tests
.env_get_var_value("LOCAL_PATH")
160 shareroot
=f
'{localpath}/nosymlinks'
162 target
=f
'{shareroot}/{rel_dest}'
164 self
.create_symlink(smb1
, target
, symlink
)
166 with self
.assertRaises(NTSTATUSError
) as e
:
167 fd
= smb2
.create_ex(f
'{symlink}')
169 self
.assert_symlink_exception(
171 { 'unparsed_path_length' : 0,
172 'substitute_name' : rel_dest
,
173 'print_name' : rel_dest
,
176 self
.clean_file(smb1
, symlink
)
178 if __name__
== '__main__':