xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb_directory.c (revision c211fc479225fa54805cf480633bf6689ca9a2db)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <smbsrv/nterror.h>
27 #include <smbsrv/ntstatus.h>
28 #include <smbsrv/smbinfo.h>
29 #include <smbsrv/smb_incl.h>
30 #include <smbsrv/smb_fsops.h>
31 
32 typedef struct smb_dirpath {
33 	char	*sp_path;	/* Original path */
34 	char	*sp_curp;	/* Current pointer into the original path */
35 	smb_request_t *sp_sr;	/* Current request pointer */
36 } smb_dirpath_t;
37 
38 static smb_dirpath_t *smb_dirpath_new(smb_request_t *);
39 static int smb_dirpath_next(smb_dirpath_t *);
40 static boolean_t smb_dirpath_isvalid(const char *);
41 
42 /*
43  * The create directory message is sent to create a new directory.  The
44  * appropriate Tid and additional pathname are passed.  The directory must
45  * not exist for it to be created.
46  *
47  * Client Request                     Description
48  * ================================== =================================
49  * UCHAR WordCount;                   Count of parameter words = 0
50  * USHORT ByteCount;                  Count of data bytes; min = 2
51  * UCHAR BufferFormat;                0x04
52  * STRING DirectoryName[];            Directory name
53  *
54  * Servers require clients to have at least create permission for the
55  * subtree containing the directory in order to create a new directory.
56  * The creator's access rights to the new directory are be determined by
57  * local policy on the server.
58  *
59  * Server Response                    Description
60  * ================================== =================================
61  * UCHAR WordCount;                   Count of parameter words = 0
62  * USHORT ByteCount;                  Count of data bytes = 0
63  */
64 smb_sdrc_t
65 smb_pre_create_directory(smb_request_t *sr)
66 {
67 	int rc;
68 
69 	rc = smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path);
70 
71 	DTRACE_SMB_2(op__CreateDirectory__start, smb_request_t *, sr,
72 	    struct dirop *, &sr->arg.dirop);
73 
74 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
75 }
76 
77 void
78 smb_post_create_directory(smb_request_t *sr)
79 {
80 	DTRACE_SMB_1(op__CreateDirectory__done, smb_request_t *, sr);
81 }
82 
83 /*
84  * smb_com_create_directory
85  *
86  * It is possible to get a full pathname here and the client expects any
87  * or all of the components to be created if they don't already exist.
88  */
89 smb_sdrc_t
90 smb_com_create_directory(smb_request_t *sr)
91 {
92 	smb_dirpath_t *spp;
93 	smb_attr_t *attr;
94 	DWORD status;
95 	int rc = 0;
96 
97 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
98 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
99 		    ERRDOS, ERROR_ACCESS_DENIED);
100 		return (SDRC_ERROR);
101 	}
102 
103 	if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.path)) {
104 		smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD,
105 		    ERRDOS, ERROR_BAD_PATHNAME);
106 		return (SDRC_ERROR);
107 	}
108 
109 	if ((status = smb_validate_dirname(sr->arg.dirop.fqi.path)) != 0) {
110 		smbsr_error(sr, status, ERRDOS, ERROR_INVALID_NAME);
111 		return (SDRC_ERROR);
112 	}
113 
114 	/*
115 	 * Try each component of the path.  EEXIST on path
116 	 * components is okay except on the last one.
117 	 */
118 	spp = smb_dirpath_new(sr);
119 
120 	while (smb_dirpath_next(spp)) {
121 		rc = smb_common_create_directory(sr);
122 
123 		switch (rc) {
124 		case 0:
125 			break;
126 		case EEXIST:
127 			attr = &sr->arg.dirop.fqi.last_attr;
128 
129 			if (attr->sa_vattr.va_type != VDIR) {
130 				smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION,
131 				    ERRDOS, ERROR_PATH_NOT_FOUND);
132 				return (SDRC_ERROR);
133 			}
134 			break;
135 		case ENOENT:
136 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
137 			    ERRDOS, ERROR_FILE_NOT_FOUND);
138 			return (SDRC_ERROR);
139 		case ENOTDIR:
140 			smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
141 			    ERRDOS, ERROR_PATH_NOT_FOUND);
142 			return (SDRC_ERROR);
143 		default:
144 			smbsr_errno(sr, rc);
145 			return (SDRC_ERROR);
146 		}
147 	}
148 
149 	if (rc != 0) {
150 		smbsr_errno(sr, rc);
151 		return (SDRC_ERROR);
152 	}
153 
154 	rc = smbsr_encode_empty_result(sr);
155 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
156 }
157 
158 /*
159  * smb_validate_dirname
160  *
161  * Very basic directory name validation: checks for colons in a path.
162  * Need to skip the drive prefix since it contains a colon.
163  *
164  * Returns 0 if the name is valid, otherwise NT_STATUS_NOT_A_DIRECTORY.
165  */
166 DWORD
167 smb_validate_dirname(char *path)
168 {
169 	char *name;
170 
171 	if ((name = path) != 0) {
172 		name += strspn(name, "\\");
173 
174 		if (strchr(name, ':') != 0)
175 			return (NT_STATUS_NOT_A_DIRECTORY);
176 	}
177 
178 	return (0);
179 }
180 
181 /*
182  * smb_common_create_directory
183  *
184  * Currently called from:
185  *		smb_com_create_directory
186  *		smb_com_trans2_create_directory
187  *
188  * Returns errno values.
189  */
190 int
191 smb_common_create_directory(smb_request_t *sr)
192 {
193 	int rc;
194 	smb_attr_t new_attr;
195 	smb_node_t *dnode;
196 	smb_node_t *node;
197 
198 	sr->arg.dirop.fqi.srch_attr = 0;
199 
200 	rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_NOT_EXIST);
201 	if (rc)
202 		return (rc);
203 
204 	/*
205 	 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return
206 	 * value, only fqi.dir_snode has a valid parameter (fqi.last_snode
207 	 * is NULL).
208 	 */
209 	dnode = sr->arg.dirop.fqi.dir_snode;
210 
211 	rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_ADD_SUBDIRECTORY);
212 	if (rc != NT_STATUS_SUCCESS)
213 		return (EACCES);
214 
215 	/*
216 	 * Explicitly set sa_dosattr, otherwise the file system may
217 	 * automatically apply FILE_ATTRIBUTE_ARCHIVE which, for
218 	 * compatibility with windows servers, should not be set.
219 	 */
220 	bzero(&new_attr, sizeof (new_attr));
221 	new_attr.sa_dosattr = FILE_ATTRIBUTE_DIRECTORY;
222 	new_attr.sa_vattr.va_type = VDIR;
223 	new_attr.sa_vattr.va_mode = 0777;
224 	new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_DOSATTR;
225 
226 	if ((rc = smb_fsop_mkdir(sr, sr->user_cr, dnode,
227 	    sr->arg.dirop.fqi.last_comp, &new_attr,
228 	    &sr->arg.dirop.fqi.last_snode,
229 	    &sr->arg.dirop.fqi.last_attr)) != 0) {
230 		smb_node_release(dnode);
231 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
232 		return (rc);
233 	}
234 
235 	node = sr->arg.dirop.fqi.last_snode;
236 	node->flags |= NODE_FLAGS_CREATED;
237 
238 	sr->arg.open.create_options = FILE_DIRECTORY_FILE;
239 
240 	smb_node_release(node);
241 	smb_node_release(dnode);
242 	SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
243 	return (0);
244 }
245 
246 static smb_dirpath_t *
247 smb_dirpath_new(smb_request_t *sr)
248 {
249 	int pathLen;
250 	char *xpath;
251 	smb_dirpath_t *spp;
252 
253 	/* Malloc from the request storage area. This is freed automatically */
254 	/* so we don't need to worry about freeing it later */
255 	spp = smbsr_malloc(&sr->request_storage, sizeof (smb_dirpath_t));
256 	spp->sp_path = sr->arg.dirop.fqi.path;
257 	pathLen = strlen(spp->sp_path);
258 	spp->sp_curp = spp->sp_path;
259 	xpath = smbsr_malloc(&sr->request_storage, pathLen + 1);
260 	sr->arg.dirop.fqi.path = xpath;
261 	spp->sp_sr = sr;
262 
263 	return (spp);
264 }
265 
266 /*
267  * Perhaps somewhat dangerous since everything happens as a side effect. The
268  * returns 1 if there is a valid component updated to the fqi, 0 otherwise.
269  */
270 static int
271 smb_dirpath_next(smb_dirpath_t *spp)
272 {
273 	char *xp;
274 	int xlen;
275 
276 	if (spp == 0)
277 		return (0);
278 
279 	/* Move the index to the "next" "\" and copy the path to the fqi */
280 	/* path for the next component. */
281 
282 	/* First look for the next component */
283 	while (*spp->sp_curp == '\\')
284 		spp->sp_curp++;
285 
286 	/* Now get to the end of the component */
287 	xp = spp->sp_curp; /* Remember from where we started */
288 	while (*spp->sp_curp != '\0' && *spp->sp_curp != '\\') {
289 		spp->sp_curp++;
290 	}
291 
292 	/* If we made no progress, we are done */
293 	if (xp == spp->sp_curp)
294 		return (0);
295 
296 	/*
297 	 * Now copy the original path up to but not including our current
298 	 * pointer
299 	 */
300 
301 	/*LINTED E_PTRDIFF_OVERFLOW*/
302 	xlen = spp->sp_curp - spp->sp_path;
303 	(void) strncpy(spp->sp_sr->arg.dirop.fqi.path, spp->sp_path, xlen);
304 
305 	/* Now NULL terminate it */
306 	spp->sp_sr->arg.dirop.fqi.path[xlen] = '\0';
307 	return (1);
308 }
309 
310 /*
311  * The delete directory message is sent to delete an empty directory. The
312  * appropriate Tid and additional pathname are passed. The directory must
313  * be empty for it to be deleted.
314  *
315  * NT supports a hidden permission known as File Delete Child (FDC). If
316  * the user has FullControl access to a directory, the user is permitted
317  * to delete any object in the directory regardless of the permissions
318  * on the object.
319  *
320  * Client Request                     Description
321  * ================================== =================================
322  * UCHAR WordCount;                   Count of parameter words = 0
323  * USHORT ByteCount;                  Count of data bytes; min = 2
324  * UCHAR BufferFormat;                0x04
325  * STRING DirectoryName[];            Directory name
326  *
327  * The directory to be deleted cannot be the root of the share specified
328  * by Tid.
329  *
330  * Server Response                    Description
331  * ================================== =================================
332  * UCHAR WordCount;                   Count of parameter words = 0
333  * USHORT ByteCount;                  Count of data bytes = 0
334  */
335 smb_sdrc_t
336 smb_pre_delete_directory(smb_request_t *sr)
337 {
338 	int rc;
339 
340 	rc = smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path);
341 
342 	DTRACE_SMB_2(op__DeleteDirectory__start, smb_request_t *, sr,
343 	    struct dirop *, &sr->arg.dirop);
344 
345 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
346 }
347 
348 void
349 smb_post_delete_directory(smb_request_t *sr)
350 {
351 	DTRACE_SMB_1(op__DeleteDirectory__done, smb_request_t *, sr);
352 }
353 
354 smb_sdrc_t
355 smb_com_delete_directory(smb_request_t *sr)
356 {
357 	smb_node_t *dnode;
358 	smb_attr_t *attr;
359 	int rc;
360 
361 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
362 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
363 		    ERRDOS, ERROR_ACCESS_DENIED);
364 		return (SDRC_ERROR);
365 	}
366 
367 	sr->arg.dirop.fqi.srch_attr = 0;
368 
369 	rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST);
370 	if (rc) {
371 		if (rc == ENOENT)
372 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
373 			    ERRDOS, ERROR_FILE_NOT_FOUND);
374 		else
375 			smbsr_errno(sr, rc);
376 		return (SDRC_ERROR);
377 	}
378 
379 	attr = &sr->arg.dirop.fqi.last_attr;
380 	if (attr->sa_vattr.va_type != VDIR) {
381 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
382 		    ERRDOS, ERROR_PATH_NOT_FOUND);
383 		return (SDRC_ERROR);
384 	}
385 
386 	dnode = sr->arg.dirop.fqi.last_snode;
387 	rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE);
388 
389 	if ((rc != NT_STATUS_SUCCESS) ||
390 	    (dnode->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
391 		smb_node_release(dnode);
392 		smb_node_release(sr->arg.dirop.fqi.dir_snode);
393 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
394 		smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
395 		    ERRDOS, ERROR_ACCESS_DENIED);
396 		return (SDRC_ERROR);
397 	}
398 
399 	smb_node_release(dnode);
400 
401 	dnode = sr->arg.dirop.fqi.dir_snode;
402 
403 	rc = smb_fsop_rmdir(sr, sr->user_cr, dnode,
404 	    sr->arg.dirop.fqi.last_comp_od, 1);
405 	if (rc != 0) {
406 		smb_node_release(dnode);
407 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
408 		if (rc == EEXIST)
409 			smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY,
410 			    ERRDOS, ERROR_DIR_NOT_EMPTY);
411 		else
412 			smbsr_errno(sr, rc);
413 		return (SDRC_ERROR);
414 	}
415 
416 	smb_node_release(dnode);
417 	SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
418 
419 	rc = smbsr_encode_empty_result(sr);
420 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
421 }
422 
423 /*
424  * This SMB is used to verify that a path exists and is a directory.  No
425  * error is returned if the given path exists and the client has read
426  * access to it.  Client machines which maintain a concept of a "working
427  * directory" will find this useful to verify the validity of a "change
428  * working directory" command.  Note that the servers do NOT have a concept
429  * of working directory for a particular client.  The client must always
430  * supply full pathnames relative to the Tid in the SMB header.
431  *
432  * Client Request                     Description
433  * ================================== =================================
434  *
435  * UCHAR WordCount;                   Count of parameter words = 0
436  * USHORT ByteCount;                  Count of data bytes;    min = 2
437  * UCHAR BufferFormat;                0x04
438  * STRING DirectoryPath[];            Directory path
439  *
440  * Server Response                    Description
441  * ================================== =================================
442  *
443  * UCHAR WordCount;                   Count of parameter words = 0
444  * USHORT ByteCount;                  Count of data bytes = 0
445  *
446  * DOS clients, in particular, depend on ERRbadpath if the directory is
447  * not found.
448  */
449 smb_sdrc_t
450 smb_pre_check_directory(smb_request_t *sr)
451 {
452 	int rc;
453 
454 	rc = smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path);
455 
456 	DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr,
457 	    struct dirop *, &sr->arg.dirop);
458 
459 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
460 }
461 
462 void
463 smb_post_check_directory(smb_request_t *sr)
464 {
465 	DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr);
466 }
467 
468 smb_sdrc_t
469 smb_com_check_directory(smb_request_t *sr)
470 {
471 	smb_node_t *dnode;
472 	int rc;
473 
474 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
475 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
476 		    ERROR_ACCESS_DENIED);
477 		return (SDRC_ERROR);
478 	}
479 
480 	if (sr->arg.dirop.fqi.path[0] == '\0') {
481 		rc = smbsr_encode_empty_result(sr);
482 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
483 	}
484 
485 	if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.path)) {
486 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
487 		    ERRDOS, ERROR_PATH_NOT_FOUND);
488 		return (SDRC_ERROR);
489 	}
490 
491 	sr->arg.dirop.fqi.srch_attr = 0;
492 
493 	rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST);
494 	if (rc) {
495 		if (rc == ENOENT)
496 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
497 			    ERRDOS, ERROR_PATH_NOT_FOUND);
498 		else
499 			smbsr_errno(sr, rc);
500 		return (SDRC_ERROR);
501 	}
502 
503 	smb_node_release(sr->arg.dirop.fqi.dir_snode);
504 
505 	dnode = sr->arg.dirop.fqi.last_snode;
506 
507 	if (sr->arg.dirop.fqi.last_attr.sa_vattr.va_type != VDIR) {
508 		smb_node_release(dnode);
509 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
510 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
511 		    ERRDOS, ERROR_PATH_NOT_FOUND);
512 		return (SDRC_ERROR);
513 	}
514 
515 	rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE);
516 
517 	smb_node_release(dnode);
518 	SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
519 
520 	if (rc != 0) {
521 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
522 		    ERRDOS, ERROR_ACCESS_DENIED);
523 		return (SDRC_ERROR);
524 	}
525 
526 	rc = smbsr_encode_empty_result(sr);
527 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
528 }
529 
530 static boolean_t
531 smb_dirpath_isvalid(const char *path)
532 {
533 	struct {
534 		char *name;
535 		int len;
536 	} *bad, bad_paths[] = {
537 		{ ".\0",   2 },
538 		{ ".\\\0", 3 },
539 		{ "..\0",  3 },
540 		{ "..\\",  3 }
541 	};
542 
543 	char *cp;
544 	char *p;
545 	int i;
546 
547 	if (*path == '\0')
548 		return (B_TRUE);
549 
550 	cp = smb_kstrdup(path, MAXPATHLEN);
551 	p = strcanon(cp, "\\");
552 	p += strspn(p, "\\");
553 
554 	for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) {
555 		bad = &bad_paths[i];
556 
557 		if (strncmp(p, bad->name, bad->len) == 0) {
558 			kmem_free(cp, MAXPATHLEN);
559 			return (B_FALSE);
560 		}
561 	}
562 
563 	kmem_free(cp, MAXPATHLEN);
564 	return (B_TRUE);
565 }
566