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