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