1 /*
2    Unix SMB/CIFS implementation.
3    Test samba3 hide unreadable/unwriteable
4    Copyright (C) Volker Lendecke 2006
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program; if not, write to the Free Software
18    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "includes.h"
22 #include "torture/torture.h"
23 #include "libcli/raw/libcliraw.h"
24 #include "system/time.h"
25 #include "system/filesys.h"
26 #include "libcli/libcli.h"
27 #include "torture/util.h"
28 
init_unixinfo_nochange(union smb_setfileinfo * info)29 static void init_unixinfo_nochange(union smb_setfileinfo *info)
30 {
31 	ZERO_STRUCTP(info);
32 	info->unix_basic.level = RAW_SFILEINFO_UNIX_BASIC;
33 	info->unix_basic.in.mode = SMB_MODE_NO_CHANGE;
34 
35 	info->unix_basic.in.end_of_file = SMB_SIZE_NO_CHANGE_HI;
36 	info->unix_basic.in.end_of_file <<= 32;
37 	info->unix_basic.in.end_of_file |= SMB_SIZE_NO_CHANGE_LO;
38 
39 	info->unix_basic.in.num_bytes = SMB_SIZE_NO_CHANGE_HI;
40 	info->unix_basic.in.num_bytes <<= 32;
41 	info->unix_basic.in.num_bytes |= SMB_SIZE_NO_CHANGE_LO;
42 
43 	info->unix_basic.in.status_change_time = SMB_TIME_NO_CHANGE_HI;
44 	info->unix_basic.in.status_change_time <<= 32;
45 	info->unix_basic.in.status_change_time = SMB_TIME_NO_CHANGE_LO;
46 
47 	info->unix_basic.in.access_time = SMB_TIME_NO_CHANGE_HI;
48 	info->unix_basic.in.access_time <<= 32;
49 	info->unix_basic.in.access_time |= SMB_TIME_NO_CHANGE_LO;
50 
51 	info->unix_basic.in.change_time = SMB_TIME_NO_CHANGE_HI;
52 	info->unix_basic.in.change_time <<= 32;
53 	info->unix_basic.in.change_time |= SMB_TIME_NO_CHANGE_LO;
54 
55 	info->unix_basic.in.uid = SMB_UID_NO_CHANGE;
56 	info->unix_basic.in.gid = SMB_GID_NO_CHANGE;
57 }
58 
59 struct list_state {
60 	const char *fname;
61 	BOOL visible;
62 };
63 
set_visible(struct clilist_file_info * i,const char * mask,void * priv)64 static void set_visible(struct clilist_file_info *i, const char *mask,
65 			void *priv)
66 {
67 	struct list_state *state = priv;
68 
69 	if (strcasecmp_m(state->fname, i->name) == 0)
70 		state->visible = True;
71 }
72 
is_visible(struct smbcli_tree * tree,const char * fname)73 static BOOL is_visible(struct smbcli_tree *tree, const char *fname)
74 {
75 	struct list_state state;
76 
77 	state.visible = False;
78 	state.fname = fname;
79 
80 	if (smbcli_list(tree, "*.*", 0, set_visible, &state) < 0) {
81 		return False;
82 	}
83 	return state.visible;
84 }
85 
is_readable(struct smbcli_tree * tree,const char * fname)86 static BOOL is_readable(struct smbcli_tree *tree, const char *fname)
87 {
88 	int fnum;
89 	fnum = smbcli_open(tree, fname, O_RDONLY, DENY_NONE);
90 	if (fnum < 0) {
91 		return False;
92 	}
93 	smbcli_close(tree, fnum);
94 	return True;
95 }
96 
is_writeable(TALLOC_CTX * mem_ctx,struct smbcli_tree * tree,const char * fname)97 static BOOL is_writeable(TALLOC_CTX *mem_ctx, struct smbcli_tree *tree,
98 			 const char *fname)
99 {
100 	int fnum;
101 	fnum = smbcli_open(tree, fname, O_WRONLY, DENY_NONE);
102 	if (fnum < 0) {
103 		return False;
104 	}
105 	smbcli_close(tree, fnum);
106 	return True;
107 }
108 
109 /*
110  * This is not an exact method because there's a ton of reasons why a getatr
111  * might fail. But for our purposes it's sufficient.
112  */
113 
smbcli_file_exists(struct smbcli_tree * tree,const char * fname)114 static BOOL smbcli_file_exists(struct smbcli_tree *tree, const char *fname)
115 {
116 	return NT_STATUS_IS_OK(smbcli_getatr(tree, fname, NULL, NULL, NULL));
117 }
118 
smbcli_chmod(struct smbcli_tree * tree,const char * fname,uint64_t permissions)119 static NTSTATUS smbcli_chmod(struct smbcli_tree *tree, const char *fname,
120 			     uint64_t permissions)
121 {
122 	union smb_setfileinfo sfinfo;
123 	init_unixinfo_nochange(&sfinfo);
124 	sfinfo.unix_basic.in.file.path = fname;
125 	sfinfo.unix_basic.in.permissions = permissions;
126 	return smb_raw_setpathinfo(tree, &sfinfo);
127 }
128 
torture_samba3_hide(struct torture_context * torture)129 BOOL torture_samba3_hide(struct torture_context *torture)
130 {
131 	struct smbcli_state *cli;
132 	const char *fname = "test.txt";
133 	int fnum;
134 	NTSTATUS status;
135 	struct smbcli_tree *hideunread;
136 	struct smbcli_tree *hideunwrite;
137 
138 	if (!torture_open_connection_share(
139 		    torture, &cli, torture_setting_string(torture, "host", NULL),
140 		    torture_setting_string(torture, "share", NULL), NULL)) {
141 		d_printf("torture_open_connection_share failed\n");
142 		return False;
143 	}
144 
145 	status = torture_second_tcon(torture, cli->session, "hideunread",
146 				     &hideunread);
147 	if (!NT_STATUS_IS_OK(status)) {
148 		d_printf("second_tcon(hideunread) failed: %s\n",
149 			 nt_errstr(status));
150 		return False;
151 	}
152 
153 	status = torture_second_tcon(torture, cli->session, "hideunwrite",
154 				     &hideunwrite);
155 	if (!NT_STATUS_IS_OK(status)) {
156 		d_printf("second_tcon(hideunwrite) failed: %s\n",
157 			 nt_errstr(status));
158 		return False;
159 	}
160 
161 	status = smbcli_unlink(cli->tree, fname);
162 	if (NT_STATUS_EQUAL(status, NT_STATUS_CANNOT_DELETE)) {
163 		smbcli_setatr(cli->tree, fname, 0, -1);
164 		smbcli_unlink(cli->tree, fname);
165 	}
166 
167 	fnum = smbcli_open(cli->tree, fname, O_RDWR|O_CREAT, DENY_NONE);
168 	if (fnum == -1) {
169 		d_printf("Failed to create %s - %s\n", fname,
170 			 smbcli_errstr(cli->tree));
171 		return False;
172 	}
173 
174 	smbcli_close(cli->tree, fnum);
175 
176 	if (!smbcli_file_exists(cli->tree, fname)) {
177 		d_printf("%s does not exist\n", fname);
178 		return False;
179 	}
180 
181 	/* R/W file should be visible everywhere */
182 
183 	status = smbcli_chmod(cli->tree, fname, UNIX_R_USR|UNIX_W_USR);
184 	if (!NT_STATUS_IS_OK(status)) {
185 		d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
186 		return False;
187 	}
188 	if (!is_writeable(torture, cli->tree, fname)) {
189 		d_printf("File not writable\n");
190 		return False;
191 	}
192 	if (!is_readable(cli->tree, fname)) {
193 		d_printf("File not readable\n");
194 		return False;
195 	}
196 	if (!is_visible(cli->tree, fname)) {
197 		d_printf("r/w file not visible via normal share\n");
198 		return False;
199 	}
200 	if (!is_visible(hideunread, fname)) {
201 		d_printf("r/w file not visible via hide unreadable\n");
202 		return False;
203 	}
204 	if (!is_visible(hideunwrite, fname)) {
205 		d_printf("r/w file not visible via hide unwriteable\n");
206 		return False;
207 	}
208 
209 	/* R/O file should not be visible via hide unwriteable files */
210 
211 	status = smbcli_chmod(cli->tree, fname, UNIX_R_USR);
212 
213 	if (!NT_STATUS_IS_OK(status)) {
214 		d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
215 		return False;
216 	}
217 	if (is_writeable(torture, cli->tree, fname)) {
218 		d_printf("r/o is writable\n");
219 		return False;
220 	}
221 	if (!is_readable(cli->tree, fname)) {
222 		d_printf("r/o not readable\n");
223 		return False;
224 	}
225 	if (!is_visible(cli->tree, fname)) {
226 		d_printf("r/o file not visible via normal share\n");
227 		return False;
228 	}
229 	if (!is_visible(hideunread, fname)) {
230 		d_printf("r/o file not visible via hide unreadable\n");
231 		return False;
232 	}
233 	if (is_visible(hideunwrite, fname)) {
234 		d_printf("r/o file visible via hide unwriteable\n");
235 		return False;
236 	}
237 
238 	/* inaccessible file should be only visible on normal share */
239 
240 	status = smbcli_chmod(cli->tree, fname, 0);
241 	if (!NT_STATUS_IS_OK(status)) {
242 		d_printf("smbcli_chmod failed: %s\n", nt_errstr(status));
243 		return False;
244 	}
245 	if (is_writeable(torture, cli->tree, fname)) {
246 		d_printf("inaccessible file is writable\n");
247 		return False;
248 	}
249 	if (is_readable(cli->tree, fname)) {
250 		d_printf("inaccessible file is readable\n");
251 		return False;
252 	}
253 	if (!is_visible(cli->tree, fname)) {
254 		d_printf("inaccessible file not visible via normal share\n");
255 		return False;
256 	}
257 	if (is_visible(hideunread, fname)) {
258 		d_printf("inaccessible file visible via hide unreadable\n");
259 		return False;
260 	}
261 	if (is_visible(hideunwrite, fname)) {
262 		d_printf("inaccessible file visible via hide unwriteable\n");
263 		return False;
264 	}
265 
266 	return True;
267 }
268