xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_directory.c (revision 93bc28dbaee6387120d48b12b3dc1ba5f7418e6e)
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 		if (rc == ENOENT)
254 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
255 			    ERRDOS, ERROR_FILE_NOT_FOUND);
256 		else
257 			smbsr_errno(sr, rc);
258 		smb_node_release(fqi->fq_dnode);
259 		return (SDRC_ERROR);
260 	}
261 
262 	/*
263 	 * Delete should fail if this is the root of a share
264 	 * or a DFS link
265 	 */
266 	if ((fqi->fq_fnode == tnode) || smb_node_is_dfslink(fqi->fq_fnode)) {
267 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
268 		    ERRDOS, ERROR_ACCESS_DENIED);
269 		smb_node_release(fqi->fq_dnode);
270 		smb_node_release(fqi->fq_fnode);
271 		return (SDRC_ERROR);
272 	}
273 
274 	if (!smb_node_is_dir(fqi->fq_fnode)) {
275 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
276 		    ERRDOS, ERROR_PATH_NOT_FOUND);
277 		smb_node_release(fqi->fq_dnode);
278 		smb_node_release(fqi->fq_fnode);
279 		return (SDRC_ERROR);
280 	}
281 
282 	/*
283 	 * Using kcred because we just want the DOS attrs
284 	 * and don't want access errors for this.
285 	 */
286 	fqi->fq_fattr.sa_mask = SMB_AT_DOSATTR;
287 	rc = smb_node_getattr(sr, fqi->fq_fnode, zone_kcred(), NULL,
288 	    &fqi->fq_fattr);
289 	if (rc != 0) {
290 		smbsr_errno(sr, rc);
291 		smb_node_release(fqi->fq_dnode);
292 		smb_node_release(fqi->fq_fnode);
293 		return (SDRC_ERROR);
294 	}
295 
296 	if ((fqi->fq_fattr.sa_dosattr & FILE_ATTRIBUTE_READONLY) ||
297 	    (smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, DELETE)
298 	    != NT_STATUS_SUCCESS)) {
299 		smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
300 		    ERRDOS, ERROR_ACCESS_DENIED);
301 		smb_node_release(fqi->fq_dnode);
302 		smb_node_release(fqi->fq_fnode);
303 		return (SDRC_ERROR);
304 	}
305 
306 	if (SMB_TREE_SUPPORTS_CATIA(sr))
307 		flags |= SMB_CATIA;
308 
309 	rc = smb_fsop_rmdir(sr, sr->user_cr, fqi->fq_dnode,
310 	    fqi->fq_fnode->od_name, flags);
311 
312 	smb_node_release(fqi->fq_fnode);
313 	smb_node_release(fqi->fq_dnode);
314 
315 	if (rc != 0) {
316 		if (rc == EEXIST)
317 			smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY,
318 			    ERRDOS, ERROR_DIR_NOT_EMPTY);
319 		else
320 			smbsr_errno(sr, rc);
321 		return (SDRC_ERROR);
322 	}
323 
324 	rc = smbsr_encode_empty_result(sr);
325 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
326 }
327 
328 /*
329  * This SMB is used to verify that a path exists and is a directory.  No
330  * error is returned if the given path exists and the client has read
331  * access to it.  Client machines which maintain a concept of a "working
332  * directory" will find this useful to verify the validity of a "change
333  * working directory" command.  Note that the servers do NOT have a concept
334  * of working directory for a particular client.  The client must always
335  * supply full pathnames relative to the Tid in the SMB header.
336  *
337  * Client Request                     Description
338  * ================================== =================================
339  *
340  * UCHAR WordCount;                   Count of parameter words = 0
341  * USHORT ByteCount;                  Count of data bytes;    min = 2
342  * UCHAR BufferFormat;                0x04
343  * STRING DirectoryPath[];            Directory path
344  *
345  * Server Response                    Description
346  * ================================== =================================
347  *
348  * UCHAR WordCount;                   Count of parameter words = 0
349  * USHORT ByteCount;                  Count of data bytes = 0
350  *
351  * DOS clients, in particular, depend on ERRbadpath if the directory is
352  * not found.
353  */
354 smb_sdrc_t
355 smb_pre_check_directory(smb_request_t *sr)
356 {
357 	int rc;
358 
359 	rc = smbsr_decode_data(sr, "%S", sr,
360 	    &sr->arg.dirop.fqi.fq_path.pn_path);
361 
362 	DTRACE_SMB_START(op__CheckDirectory, smb_request_t *, sr);
363 
364 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
365 }
366 
367 void
368 smb_post_check_directory(smb_request_t *sr)
369 {
370 	DTRACE_SMB_DONE(op__CheckDirectory, smb_request_t *, sr);
371 }
372 
373 smb_sdrc_t
374 smb_com_check_directory(smb_request_t *sr)
375 {
376 	int rc;
377 	smb_fqi_t *fqi;
378 	smb_node_t *tnode;
379 	smb_node_t *node;
380 	char *path;
381 	smb_pathname_t *pn;
382 
383 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
384 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
385 		    ERROR_ACCESS_DENIED);
386 		return (SDRC_ERROR);
387 	}
388 
389 	fqi = &sr->arg.dirop.fqi;
390 	pn = &fqi->fq_path;
391 
392 	if (pn->pn_path[0] == '\0') {
393 		rc = smbsr_encode_empty_result(sr);
394 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
395 	}
396 
397 	smb_pathname_init(sr, pn, pn->pn_path);
398 	if (!smb_pathname_validate(sr, pn) ||
399 	    !smb_validate_dirname(sr, pn)) {
400 		return (SDRC_ERROR);
401 	}
402 
403 	path = pn->pn_path;
404 	tnode = sr->tid_tree->t_snode;
405 
406 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
407 	    &fqi->fq_dnode, fqi->fq_last_comp);
408 	if (rc != 0) {
409 		smbsr_errno(sr, rc);
410 		return (SDRC_ERROR);
411 	}
412 
413 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
414 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
415 	smb_node_release(fqi->fq_dnode);
416 	if (rc != 0) {
417 		if (rc == ENOENT)
418 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
419 			    ERRDOS, ERROR_PATH_NOT_FOUND);
420 		else
421 			smbsr_errno(sr, rc);
422 		return (SDRC_ERROR);
423 	}
424 
425 	node = fqi->fq_fnode;
426 	if (!smb_node_is_dir(node)) {
427 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
428 		    ERRDOS, ERROR_PATH_NOT_FOUND);
429 		smb_node_release(node);
430 		return (SDRC_ERROR);
431 	}
432 
433 	if ((sr->smb_flg2 & SMB_FLAGS2_DFS) && smb_node_is_dfslink(node)) {
434 		smbsr_error(sr, NT_STATUS_PATH_NOT_COVERED, ERRSRV, ERRbadpath);
435 		smb_node_release(node);
436 		return (SDRC_ERROR);
437 	}
438 
439 	rc = smb_fsop_access(sr, sr->user_cr, node, FILE_TRAVERSE);
440 
441 	smb_node_release(node);
442 
443 	if (rc != 0) {
444 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
445 		    ERRDOS, ERROR_ACCESS_DENIED);
446 		return (SDRC_ERROR);
447 	}
448 
449 	rc = smbsr_encode_empty_result(sr);
450 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
451 }
452