xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_directory.c (revision 66582b606a8194f7f3ba5b3a3a6dca5b0d346361)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * Copyright 2017 Nexenta Systems, Inc.  All rights reserved.
26  */
27 
28 #include <smbsrv/smb_kproto.h>
29 #include <smbsrv/smbinfo.h>
30 #include <smbsrv/smb_fsops.h>
31 
32 /*
33  * The create directory message is sent to create a new directory.  The
34  * appropriate Tid and additional pathname are passed.  The directory must
35  * not exist for it to be created.
36  *
37  * Client Request                     Description
38  * ================================== =================================
39  * UCHAR WordCount;                   Count of parameter words = 0
40  * USHORT ByteCount;                  Count of data bytes; min = 2
41  * UCHAR BufferFormat;                0x04
42  * STRING DirectoryName[];            Directory name
43  *
44  * Servers require clients to have at least create permission for the
45  * subtree containing the directory in order to create a new directory.
46  * The creator's access rights to the new directory are be determined by
47  * local policy on the server.
48  *
49  * Server Response                    Description
50  * ================================== =================================
51  * UCHAR WordCount;                   Count of parameter words = 0
52  * USHORT ByteCount;                  Count of data bytes = 0
53  */
54 smb_sdrc_t
55 smb_pre_create_directory(smb_request_t *sr)
56 {
57 	int rc;
58 
59 	rc = smbsr_decode_data(sr, "%S", sr,
60 	    &sr->arg.dirop.fqi.fq_path.pn_path);
61 
62 	DTRACE_SMB_START(op__CreateDirectory, smb_request_t *, sr);
63 
64 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
65 }
66 
67 void
68 smb_post_create_directory(smb_request_t *sr)
69 {
70 	DTRACE_SMB_DONE(op__CreateDirectory, smb_request_t *, sr);
71 }
72 
73 smb_sdrc_t
74 smb_com_create_directory(smb_request_t *sr)
75 {
76 	int rc = 0;
77 	smb_pathname_t *pn = &sr->arg.dirop.fqi.fq_path;
78 
79 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
80 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
81 		    ERRDOS, ERROR_ACCESS_DENIED);
82 		return (SDRC_ERROR);
83 	}
84 
85 	smb_pathname_init(sr, pn, pn->pn_path);
86 	if (!smb_pathname_validate(sr, pn) ||
87 	    !smb_validate_dirname(sr, pn)) {
88 		return (SDRC_ERROR);
89 	}
90 
91 	if ((rc = smb_common_create_directory(sr)) != 0) {
92 		smbsr_errno(sr, rc);
93 		return (SDRC_ERROR);
94 	}
95 
96 	rc = smbsr_encode_empty_result(sr);
97 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
98 }
99 
100 /*
101  * smb_common_create_directory
102  *
103  * Currently called from:
104  *		smb_com_create_directory
105  *		smb_com_trans2_create_directory
106  *
107  * Returns errno values.
108  */
109 int
110 smb_common_create_directory(smb_request_t *sr)
111 {
112 	int rc;
113 	smb_attr_t new_attr;
114 	smb_fqi_t *fqi;
115 	smb_node_t *tnode;
116 
117 	fqi = &sr->arg.dirop.fqi;
118 	tnode = sr->tid_tree->t_snode;
119 
120 	rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
121 	    tnode, tnode, &fqi->fq_dnode, fqi->fq_last_comp);
122 	if (rc != 0)
123 		return (rc);
124 
125 	if (smb_is_invalid_filename(fqi->fq_last_comp)) {
126 		smb_node_release(fqi->fq_dnode);
127 		return (EILSEQ); /* NT_STATUS_OBJECT_NAME_INVALID */
128 	}
129 
130 	/* lookup node - to ensure that it does NOT exist */
131 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
132 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
133 	if (rc == 0) {
134 		smb_node_release(fqi->fq_dnode);
135 		smb_node_release(fqi->fq_fnode);
136 		return (EEXIST);
137 	}
138 	if (rc != ENOENT) {
139 		smb_node_release(fqi->fq_dnode);
140 		return (rc);
141 	}
142 
143 	rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode,
144 	    FILE_ADD_SUBDIRECTORY);
145 	if (rc != NT_STATUS_SUCCESS) {
146 		smb_node_release(fqi->fq_dnode);
147 		return (EACCES);
148 	}
149 
150 	/*
151 	 * Explicitly set sa_dosattr, otherwise the file system may
152 	 * automatically apply FILE_ATTRIBUTE_ARCHIVE which, for
153 	 * compatibility with windows servers, should not be set.
154 	 */
155 	bzero(&new_attr, sizeof (new_attr));
156 	new_attr.sa_dosattr = FILE_ATTRIBUTE_DIRECTORY;
157 	new_attr.sa_vattr.va_type = VDIR;
158 	new_attr.sa_vattr.va_mode = 0777;
159 	new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_DOSATTR;
160 
161 	rc = smb_fsop_mkdir(sr, sr->user_cr, fqi->fq_dnode, fqi->fq_last_comp,
162 	    &new_attr, &fqi->fq_fnode);
163 	if (rc != 0) {
164 		smb_node_release(fqi->fq_dnode);
165 		return (rc);
166 	}
167 
168 	sr->arg.open.create_options = FILE_DIRECTORY_FILE;
169 
170 	smb_node_release(fqi->fq_dnode);
171 	smb_node_release(fqi->fq_fnode);
172 	return (0);
173 }
174 
175 /*
176  * The delete directory message is sent to delete an empty directory. The
177  * appropriate Tid and additional pathname are passed. The directory must
178  * be empty for it to be deleted.
179  *
180  * NT supports a hidden permission known as File Delete Child (FDC). If
181  * the user has FullControl access to a directory, the user is permitted
182  * to delete any object in the directory regardless of the permissions
183  * on the object.
184  *
185  * Client Request                     Description
186  * ================================== =================================
187  * UCHAR WordCount;                   Count of parameter words = 0
188  * USHORT ByteCount;                  Count of data bytes; min = 2
189  * UCHAR BufferFormat;                0x04
190  * STRING DirectoryName[];            Directory name
191  *
192  * The directory to be deleted cannot be the root of the share specified
193  * by Tid.
194  *
195  * Server Response                    Description
196  * ================================== =================================
197  * UCHAR WordCount;                   Count of parameter words = 0
198  * USHORT ByteCount;                  Count of data bytes = 0
199  */
200 smb_sdrc_t
201 smb_pre_delete_directory(smb_request_t *sr)
202 {
203 	int rc;
204 
205 	rc = smbsr_decode_data(sr, "%S", sr,
206 	    &sr->arg.dirop.fqi.fq_path.pn_path);
207 
208 	DTRACE_SMB_START(op__DeleteDirectory, smb_request_t *, sr);
209 
210 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
211 }
212 
213 void
214 smb_post_delete_directory(smb_request_t *sr)
215 {
216 	DTRACE_SMB_DONE(op__DeleteDirectory, smb_request_t *, sr);
217 }
218 
219 smb_sdrc_t
220 smb_com_delete_directory(smb_request_t *sr)
221 {
222 	int rc;
223 	uint32_t flags = 0;
224 	smb_fqi_t *fqi;
225 	smb_node_t *tnode;
226 
227 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
228 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
229 		    ERRDOS, ERROR_ACCESS_DENIED);
230 		return (SDRC_ERROR);
231 	}
232 
233 	fqi = &sr->arg.dirop.fqi;
234 	tnode = sr->tid_tree->t_snode;
235 
236 	smb_pathname_init(sr, &fqi->fq_path, fqi->fq_path.pn_path);
237 	if (!smb_pathname_validate(sr, &fqi->fq_path) ||
238 	    !smb_validate_dirname(sr, &fqi->fq_path)) {
239 		return (SDRC_ERROR);
240 	}
241 
242 	rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
243 	    tnode, tnode, &fqi->fq_dnode, fqi->fq_last_comp);
244 
245 	if (rc != 0) {
246 		smbsr_errno(sr, rc);
247 		return (SDRC_ERROR);
248 	}
249 
250 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
251 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
252 	if (rc != 0) {
253 		smbsr_errno(sr, rc);
254 		smb_node_release(fqi->fq_dnode);
255 		return (SDRC_ERROR);
256 	}
257 
258 	/*
259 	 * Delete should fail if this is the root of a share
260 	 * or a DFS link
261 	 */
262 	if ((fqi->fq_fnode == tnode) || smb_node_is_dfslink(fqi->fq_fnode)) {
263 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
264 		    ERRDOS, ERROR_ACCESS_DENIED);
265 		smb_node_release(fqi->fq_dnode);
266 		smb_node_release(fqi->fq_fnode);
267 		return (SDRC_ERROR);
268 	}
269 
270 	if (!smb_node_is_dir(fqi->fq_fnode)) {
271 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
272 		    ERRDOS, ERROR_PATH_NOT_FOUND);
273 		smb_node_release(fqi->fq_dnode);
274 		smb_node_release(fqi->fq_fnode);
275 		return (SDRC_ERROR);
276 	}
277 
278 	/*
279 	 * Using kcred because we just want the DOS attrs
280 	 * and don't want access errors for this.
281 	 */
282 	fqi->fq_fattr.sa_mask = SMB_AT_DOSATTR;
283 	rc = smb_node_getattr(sr, fqi->fq_fnode, zone_kcred(), NULL,
284 	    &fqi->fq_fattr);
285 	if (rc != 0) {
286 		smbsr_errno(sr, rc);
287 		smb_node_release(fqi->fq_dnode);
288 		smb_node_release(fqi->fq_fnode);
289 		return (SDRC_ERROR);
290 	}
291 
292 	if ((fqi->fq_fattr.sa_dosattr & FILE_ATTRIBUTE_READONLY) ||
293 	    (smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, DELETE)
294 	    != NT_STATUS_SUCCESS)) {
295 		smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
296 		    ERRDOS, ERROR_ACCESS_DENIED);
297 		smb_node_release(fqi->fq_dnode);
298 		smb_node_release(fqi->fq_fnode);
299 		return (SDRC_ERROR);
300 	}
301 
302 	if (SMB_TREE_SUPPORTS_CATIA(sr))
303 		flags |= SMB_CATIA;
304 
305 	rc = smb_fsop_rmdir(sr, sr->user_cr, fqi->fq_dnode,
306 	    fqi->fq_fnode->od_name, flags);
307 
308 	smb_node_release(fqi->fq_fnode);
309 	smb_node_release(fqi->fq_dnode);
310 
311 	if (rc != 0) {
312 		if (rc == EEXIST)
313 			smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY,
314 			    ERRDOS, ERROR_DIR_NOT_EMPTY);
315 		else
316 			smbsr_errno(sr, rc);
317 		return (SDRC_ERROR);
318 	}
319 
320 	rc = smbsr_encode_empty_result(sr);
321 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
322 }
323 
324 /*
325  * This SMB is used to verify that a path exists and is a directory.  No
326  * error is returned if the given path exists and the client has read
327  * access to it.  Client machines which maintain a concept of a "working
328  * directory" will find this useful to verify the validity of a "change
329  * working directory" command.  Note that the servers do NOT have a concept
330  * of working directory for a particular client.  The client must always
331  * supply full pathnames relative to the Tid in the SMB header.
332  *
333  * Client Request                     Description
334  * ================================== =================================
335  *
336  * UCHAR WordCount;                   Count of parameter words = 0
337  * USHORT ByteCount;                  Count of data bytes;    min = 2
338  * UCHAR BufferFormat;                0x04
339  * STRING DirectoryPath[];            Directory path
340  *
341  * Server Response                    Description
342  * ================================== =================================
343  *
344  * UCHAR WordCount;                   Count of parameter words = 0
345  * USHORT ByteCount;                  Count of data bytes = 0
346  *
347  * DOS clients, in particular, depend on ERRbadpath if the directory is
348  * not found.
349  */
350 smb_sdrc_t
351 smb_pre_check_directory(smb_request_t *sr)
352 {
353 	int rc;
354 
355 	rc = smbsr_decode_data(sr, "%S", sr,
356 	    &sr->arg.dirop.fqi.fq_path.pn_path);
357 
358 	DTRACE_SMB_START(op__CheckDirectory, smb_request_t *, sr);
359 
360 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
361 }
362 
363 void
364 smb_post_check_directory(smb_request_t *sr)
365 {
366 	DTRACE_SMB_DONE(op__CheckDirectory, smb_request_t *, sr);
367 }
368 
369 smb_sdrc_t
370 smb_com_check_directory(smb_request_t *sr)
371 {
372 	int rc;
373 	smb_fqi_t *fqi;
374 	smb_node_t *tnode;
375 	smb_node_t *node;
376 	char *path;
377 	smb_pathname_t *pn;
378 
379 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
380 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
381 		    ERROR_ACCESS_DENIED);
382 		return (SDRC_ERROR);
383 	}
384 
385 	fqi = &sr->arg.dirop.fqi;
386 	pn = &fqi->fq_path;
387 
388 	if (pn->pn_path[0] == '\0') {
389 		rc = smbsr_encode_empty_result(sr);
390 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
391 	}
392 
393 	smb_pathname_init(sr, pn, pn->pn_path);
394 	if (!smb_pathname_validate(sr, pn) ||
395 	    !smb_validate_dirname(sr, pn)) {
396 		return (SDRC_ERROR);
397 	}
398 
399 	path = pn->pn_path;
400 	tnode = sr->tid_tree->t_snode;
401 
402 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
403 	    &fqi->fq_dnode, fqi->fq_last_comp);
404 	if (rc != 0) {
405 		smbsr_errno(sr, rc);
406 		return (SDRC_ERROR);
407 	}
408 
409 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
410 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
411 	smb_node_release(fqi->fq_dnode);
412 	if (rc != 0) {
413 		smbsr_errno(sr, rc);
414 		return (SDRC_ERROR);
415 	}
416 
417 	node = fqi->fq_fnode;
418 	if (!smb_node_is_dir(node)) {
419 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
420 		    ERRDOS, ERROR_PATH_NOT_FOUND);
421 		smb_node_release(node);
422 		return (SDRC_ERROR);
423 	}
424 
425 	if ((sr->smb_flg2 & SMB_FLAGS2_DFS) && smb_node_is_dfslink(node)) {
426 		smbsr_error(sr, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath);
427 		smb_node_release(node);
428 		return (SDRC_ERROR);
429 	}
430 
431 	rc = smb_fsop_access(sr, sr->user_cr, node, FILE_TRAVERSE);
432 
433 	smb_node_release(node);
434 
435 	if (rc != 0) {
436 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
437 		    ERRDOS, ERROR_ACCESS_DENIED);
438 		return (SDRC_ERROR);
439 	}
440 
441 	rc = smbsr_encode_empty_result(sr);
442 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
443 }
444