xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_directory.c (revision 2dea4eed7ad1c66ae4770263aa2911815a8b86eb)
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 
26 #include <smbsrv/smb_kproto.h>
27 #include <smbsrv/smbinfo.h>
28 #include <smbsrv/smb_fsops.h>
29 
30 /*
31  * The create directory message is sent to create a new directory.  The
32  * appropriate Tid and additional pathname are passed.  The directory must
33  * not exist for it to be created.
34  *
35  * Client Request                     Description
36  * ================================== =================================
37  * UCHAR WordCount;                   Count of parameter words = 0
38  * USHORT ByteCount;                  Count of data bytes; min = 2
39  * UCHAR BufferFormat;                0x04
40  * STRING DirectoryName[];            Directory name
41  *
42  * Servers require clients to have at least create permission for the
43  * subtree containing the directory in order to create a new directory.
44  * The creator's access rights to the new directory are be determined by
45  * local policy on the server.
46  *
47  * Server Response                    Description
48  * ================================== =================================
49  * UCHAR WordCount;                   Count of parameter words = 0
50  * USHORT ByteCount;                  Count of data bytes = 0
51  */
52 smb_sdrc_t
53 smb_pre_create_directory(smb_request_t *sr)
54 {
55 	int rc;
56 
57 	rc = smbsr_decode_data(sr, "%S", sr,
58 	    &sr->arg.dirop.fqi.fq_path.pn_path);
59 
60 	DTRACE_SMB_2(op__CreateDirectory__start, smb_request_t *, sr,
61 	    struct dirop *, &sr->arg.dirop);
62 
63 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
64 }
65 
66 void
67 smb_post_create_directory(smb_request_t *sr)
68 {
69 	DTRACE_SMB_1(op__CreateDirectory__done, smb_request_t *, sr);
70 }
71 
72 smb_sdrc_t
73 smb_com_create_directory(smb_request_t *sr)
74 {
75 	int rc = 0;
76 	smb_pathname_t *pn = &sr->arg.dirop.fqi.fq_path;
77 
78 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
79 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
80 		    ERRDOS, ERROR_ACCESS_DENIED);
81 		return (SDRC_ERROR);
82 	}
83 
84 	smb_pathname_init(sr, pn, pn->pn_path);
85 	if (!smb_pathname_validate(sr, pn) ||
86 	    !smb_validate_dirname(sr, pn)) {
87 		return (SDRC_ERROR);
88 	}
89 
90 	if ((rc = smb_common_create_directory(sr)) != 0) {
91 		smbsr_errno(sr, rc);
92 		return (SDRC_ERROR);
93 	}
94 
95 	rc = smbsr_encode_empty_result(sr);
96 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
97 }
98 
99 /*
100  * smb_common_create_directory
101  *
102  * Currently called from:
103  *		smb_com_create_directory
104  *		smb_com_trans2_create_directory
105  *
106  * Returns errno values.
107  */
108 int
109 smb_common_create_directory(smb_request_t *sr)
110 {
111 	int rc;
112 	smb_attr_t new_attr;
113 	smb_fqi_t *fqi;
114 	smb_node_t *tnode;
115 
116 	fqi = &sr->arg.dirop.fqi;
117 	tnode = sr->tid_tree->t_snode;
118 
119 	rc = smb_pathname_reduce(sr, sr->user_cr, fqi->fq_path.pn_path,
120 	    tnode, tnode, &fqi->fq_dnode, fqi->fq_last_comp);
121 	if (rc != 0)
122 		return (rc);
123 
124 	if (smb_is_invalid_filename(fqi->fq_last_comp)) {
125 		smb_node_release(fqi->fq_dnode);
126 		return (EILSEQ); /* NT_STATUS_OBJECT_NAME_INVALID */
127 	}
128 
129 	/* lookup node - to ensure that it does NOT exist */
130 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
131 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
132 	if (rc == 0) {
133 		smb_node_release(fqi->fq_dnode);
134 		smb_node_release(fqi->fq_fnode);
135 		return (EEXIST);
136 	}
137 	if (rc != ENOENT) {
138 		smb_node_release(fqi->fq_dnode);
139 		return (rc);
140 	}
141 
142 	rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_dnode,
143 	    FILE_ADD_SUBDIRECTORY);
144 	if (rc != NT_STATUS_SUCCESS) {
145 		smb_node_release(fqi->fq_dnode);
146 		return (EACCES);
147 	}
148 
149 	/*
150 	 * Explicitly set sa_dosattr, otherwise the file system may
151 	 * automatically apply FILE_ATTRIBUTE_ARCHIVE which, for
152 	 * compatibility with windows servers, should not be set.
153 	 */
154 	bzero(&new_attr, sizeof (new_attr));
155 	new_attr.sa_dosattr = FILE_ATTRIBUTE_DIRECTORY;
156 	new_attr.sa_vattr.va_type = VDIR;
157 	new_attr.sa_vattr.va_mode = 0777;
158 	new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_DOSATTR;
159 
160 	rc = smb_fsop_mkdir(sr, sr->user_cr, fqi->fq_dnode, fqi->fq_last_comp,
161 	    &new_attr, &fqi->fq_fnode);
162 	if (rc != 0) {
163 		smb_node_release(fqi->fq_dnode);
164 		return (rc);
165 	}
166 
167 	sr->arg.open.create_options = FILE_DIRECTORY_FILE;
168 
169 	smb_node_release(fqi->fq_dnode);
170 	smb_node_release(fqi->fq_fnode);
171 	return (0);
172 }
173 
174 /*
175  * The delete directory message is sent to delete an empty directory. The
176  * appropriate Tid and additional pathname are passed. The directory must
177  * be empty for it to be deleted.
178  *
179  * NT supports a hidden permission known as File Delete Child (FDC). If
180  * the user has FullControl access to a directory, the user is permitted
181  * to delete any object in the directory regardless of the permissions
182  * on the object.
183  *
184  * Client Request                     Description
185  * ================================== =================================
186  * UCHAR WordCount;                   Count of parameter words = 0
187  * USHORT ByteCount;                  Count of data bytes; min = 2
188  * UCHAR BufferFormat;                0x04
189  * STRING DirectoryName[];            Directory name
190  *
191  * The directory to be deleted cannot be the root of the share specified
192  * by Tid.
193  *
194  * Server Response                    Description
195  * ================================== =================================
196  * UCHAR WordCount;                   Count of parameter words = 0
197  * USHORT ByteCount;                  Count of data bytes = 0
198  */
199 smb_sdrc_t
200 smb_pre_delete_directory(smb_request_t *sr)
201 {
202 	int rc;
203 
204 	rc = smbsr_decode_data(sr, "%S", sr,
205 	    &sr->arg.dirop.fqi.fq_path.pn_path);
206 
207 	DTRACE_SMB_2(op__DeleteDirectory__start, smb_request_t *, sr,
208 	    struct dirop *, &sr->arg.dirop);
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_1(op__DeleteDirectory__done, 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 	if (rc != 0) {
245 		smbsr_errno(sr, rc);
246 		return (SDRC_ERROR);
247 	}
248 
249 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
250 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
251 	if (rc != 0) {
252 		if (rc == ENOENT)
253 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
254 			    ERRDOS, ERROR_FILE_NOT_FOUND);
255 		else
256 			smbsr_errno(sr, rc);
257 		smb_node_release(fqi->fq_dnode);
258 		return (SDRC_ERROR);
259 	}
260 
261 	/* Do not allow delete of root of share */
262 	if (fqi->fq_fnode == tnode) {
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 	rc = smb_node_getattr(sr, fqi->fq_fnode, &fqi->fq_fattr);
271 	if (rc != 0) {
272 		smbsr_errno(sr, rc);
273 		smb_node_release(fqi->fq_dnode);
274 		smb_node_release(fqi->fq_fnode);
275 		return (SDRC_ERROR);
276 	}
277 
278 	if (fqi->fq_fattr.sa_vattr.va_type != VDIR) {
279 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
280 		    ERRDOS, ERROR_PATH_NOT_FOUND);
281 		smb_node_release(fqi->fq_dnode);
282 		smb_node_release(fqi->fq_fnode);
283 		return (SDRC_ERROR);
284 	}
285 
286 	if ((fqi->fq_fattr.sa_dosattr & FILE_ATTRIBUTE_READONLY) ||
287 	    (smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, DELETE)
288 	    != NT_STATUS_SUCCESS)) {
289 		smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
290 		    ERRDOS, ERROR_ACCESS_DENIED);
291 		smb_node_release(fqi->fq_dnode);
292 		smb_node_release(fqi->fq_fnode);
293 		return (SDRC_ERROR);
294 	}
295 
296 	if (SMB_TREE_SUPPORTS_CATIA(sr))
297 		flags |= SMB_CATIA;
298 
299 	rc = smb_fsop_rmdir(sr, sr->user_cr, fqi->fq_dnode,
300 	    fqi->fq_fnode->od_name, flags);
301 
302 	smb_node_release(fqi->fq_fnode);
303 	smb_node_release(fqi->fq_dnode);
304 
305 	if (rc != 0) {
306 		if (rc == EEXIST)
307 			smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY,
308 			    ERRDOS, ERROR_DIR_NOT_EMPTY);
309 		else
310 			smbsr_errno(sr, rc);
311 		return (SDRC_ERROR);
312 	}
313 
314 	rc = smbsr_encode_empty_result(sr);
315 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
316 }
317 
318 /*
319  * This SMB is used to verify that a path exists and is a directory.  No
320  * error is returned if the given path exists and the client has read
321  * access to it.  Client machines which maintain a concept of a "working
322  * directory" will find this useful to verify the validity of a "change
323  * working directory" command.  Note that the servers do NOT have a concept
324  * of working directory for a particular client.  The client must always
325  * supply full pathnames relative to the Tid in the SMB header.
326  *
327  * Client Request                     Description
328  * ================================== =================================
329  *
330  * UCHAR WordCount;                   Count of parameter words = 0
331  * USHORT ByteCount;                  Count of data bytes;    min = 2
332  * UCHAR BufferFormat;                0x04
333  * STRING DirectoryPath[];            Directory path
334  *
335  * Server Response                    Description
336  * ================================== =================================
337  *
338  * UCHAR WordCount;                   Count of parameter words = 0
339  * USHORT ByteCount;                  Count of data bytes = 0
340  *
341  * DOS clients, in particular, depend on ERRbadpath if the directory is
342  * not found.
343  */
344 smb_sdrc_t
345 smb_pre_check_directory(smb_request_t *sr)
346 {
347 	int rc;
348 
349 	rc = smbsr_decode_data(sr, "%S", sr,
350 	    &sr->arg.dirop.fqi.fq_path.pn_path);
351 
352 	DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr,
353 	    struct dirop *, &sr->arg.dirop);
354 
355 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
356 }
357 
358 void
359 smb_post_check_directory(smb_request_t *sr)
360 {
361 	DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr);
362 }
363 
364 smb_sdrc_t
365 smb_com_check_directory(smb_request_t *sr)
366 {
367 	int rc;
368 	smb_fqi_t *fqi;
369 	smb_node_t *tnode;
370 	char *path;
371 	smb_pathname_t *pn;
372 
373 	if (STYPE_ISIPC(sr->tid_tree->t_res_type)) {
374 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
375 		    ERROR_ACCESS_DENIED);
376 		return (SDRC_ERROR);
377 	}
378 
379 	fqi = &sr->arg.dirop.fqi;
380 	pn = &fqi->fq_path;
381 
382 	if (pn->pn_path[0] == '\0') {
383 		rc = smbsr_encode_empty_result(sr);
384 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
385 	}
386 
387 	smb_pathname_init(sr, pn, pn->pn_path);
388 	if (!smb_pathname_validate(sr, pn) ||
389 	    !smb_validate_dirname(sr, pn)) {
390 		return (SDRC_ERROR);
391 	}
392 
393 	path = pn->pn_path;
394 	tnode = sr->tid_tree->t_snode;
395 
396 	rc = smb_pathname_reduce(sr, sr->user_cr, path, tnode, tnode,
397 	    &fqi->fq_dnode, fqi->fq_last_comp);
398 	if (rc != 0) {
399 		smbsr_errno(sr, rc);
400 		return (SDRC_ERROR);
401 	}
402 
403 	rc = smb_fsop_lookup(sr, sr->user_cr, SMB_FOLLOW_LINKS,
404 	    tnode, fqi->fq_dnode, fqi->fq_last_comp, &fqi->fq_fnode);
405 	smb_node_release(fqi->fq_dnode);
406 	if (rc != 0) {
407 		if (rc == ENOENT)
408 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
409 			    ERRDOS, ERROR_PATH_NOT_FOUND);
410 		else
411 			smbsr_errno(sr, rc);
412 		return (SDRC_ERROR);
413 	}
414 
415 	rc = smb_node_getattr(sr, fqi->fq_fnode, &fqi->fq_fattr);
416 	if (rc != 0) {
417 		smbsr_errno(sr, rc);
418 		smb_node_release(fqi->fq_fnode);
419 		return (SDRC_ERROR);
420 	}
421 
422 	if (fqi->fq_fattr.sa_vattr.va_type != VDIR) {
423 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
424 		    ERRDOS, ERROR_PATH_NOT_FOUND);
425 		smb_node_release(fqi->fq_fnode);
426 		return (SDRC_ERROR);
427 	}
428 
429 	rc = smb_fsop_access(sr, sr->user_cr, fqi->fq_fnode, FILE_TRAVERSE);
430 
431 	smb_node_release(fqi->fq_fnode);
432 
433 	if (rc != 0) {
434 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
435 		    ERRDOS, ERROR_ACCESS_DENIED);
436 		return (SDRC_ERROR);
437 	}
438 
439 	rc = smbsr_encode_empty_result(sr);
440 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
441 }
442