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