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 	uint32_t flags = 0;
361 
362 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
363 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
364 		    ERRDOS, ERROR_ACCESS_DENIED);
365 		return (SDRC_ERROR);
366 	}
367 
368 	sr->arg.dirop.fqi.srch_attr = 0;
369 
370 	rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST);
371 	if (rc) {
372 		if (rc == ENOENT)
373 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
374 			    ERRDOS, ERROR_FILE_NOT_FOUND);
375 		else
376 			smbsr_errno(sr, rc);
377 		return (SDRC_ERROR);
378 	}
379 
380 	attr = &sr->arg.dirop.fqi.last_attr;
381 	if (attr->sa_vattr.va_type != VDIR) {
382 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
383 		    ERRDOS, ERROR_PATH_NOT_FOUND);
384 		return (SDRC_ERROR);
385 	}
386 
387 	dnode = sr->arg.dirop.fqi.last_snode;
388 	rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE);
389 
390 	if ((rc != NT_STATUS_SUCCESS) ||
391 	    (dnode->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) {
392 		smb_node_release(dnode);
393 		smb_node_release(sr->arg.dirop.fqi.dir_snode);
394 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
395 		smbsr_error(sr, NT_STATUS_CANNOT_DELETE,
396 		    ERRDOS, ERROR_ACCESS_DENIED);
397 		return (SDRC_ERROR);
398 	}
399 
400 	smb_node_release(dnode);
401 
402 	dnode = sr->arg.dirop.fqi.dir_snode;
403 
404 	if (SMB_TREE_SUPPORTS_CATIA(sr))
405 		flags |= SMB_CATIA;
406 
407 	rc = smb_fsop_rmdir(sr, sr->user_cr, dnode,
408 	    sr->arg.dirop.fqi.last_comp_od, flags);
409 	if (rc != 0) {
410 		smb_node_release(dnode);
411 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
412 		if (rc == EEXIST)
413 			smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY,
414 			    ERRDOS, ERROR_DIR_NOT_EMPTY);
415 		else
416 			smbsr_errno(sr, rc);
417 		return (SDRC_ERROR);
418 	}
419 
420 	smb_node_release(dnode);
421 	SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
422 
423 	rc = smbsr_encode_empty_result(sr);
424 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
425 }
426 
427 /*
428  * This SMB is used to verify that a path exists and is a directory.  No
429  * error is returned if the given path exists and the client has read
430  * access to it.  Client machines which maintain a concept of a "working
431  * directory" will find this useful to verify the validity of a "change
432  * working directory" command.  Note that the servers do NOT have a concept
433  * of working directory for a particular client.  The client must always
434  * supply full pathnames relative to the Tid in the SMB header.
435  *
436  * Client Request                     Description
437  * ================================== =================================
438  *
439  * UCHAR WordCount;                   Count of parameter words = 0
440  * USHORT ByteCount;                  Count of data bytes;    min = 2
441  * UCHAR BufferFormat;                0x04
442  * STRING DirectoryPath[];            Directory path
443  *
444  * Server Response                    Description
445  * ================================== =================================
446  *
447  * UCHAR WordCount;                   Count of parameter words = 0
448  * USHORT ByteCount;                  Count of data bytes = 0
449  *
450  * DOS clients, in particular, depend on ERRbadpath if the directory is
451  * not found.
452  */
453 smb_sdrc_t
454 smb_pre_check_directory(smb_request_t *sr)
455 {
456 	int rc;
457 
458 	rc = smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path);
459 
460 	DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr,
461 	    struct dirop *, &sr->arg.dirop);
462 
463 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
464 }
465 
466 void
467 smb_post_check_directory(smb_request_t *sr)
468 {
469 	DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr);
470 }
471 
472 smb_sdrc_t
473 smb_com_check_directory(smb_request_t *sr)
474 {
475 	smb_node_t *dnode;
476 	int rc;
477 
478 	if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) {
479 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS,
480 		    ERROR_ACCESS_DENIED);
481 		return (SDRC_ERROR);
482 	}
483 
484 	if (sr->arg.dirop.fqi.path[0] == '\0') {
485 		rc = smbsr_encode_empty_result(sr);
486 		return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
487 	}
488 
489 	if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.path)) {
490 		smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID,
491 		    ERRDOS, ERROR_PATH_NOT_FOUND);
492 		return (SDRC_ERROR);
493 	}
494 
495 	sr->arg.dirop.fqi.srch_attr = 0;
496 
497 	rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST);
498 	if (rc) {
499 		if (rc == ENOENT)
500 			smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND,
501 			    ERRDOS, ERROR_PATH_NOT_FOUND);
502 		else
503 			smbsr_errno(sr, rc);
504 		return (SDRC_ERROR);
505 	}
506 
507 	smb_node_release(sr->arg.dirop.fqi.dir_snode);
508 
509 	dnode = sr->arg.dirop.fqi.last_snode;
510 
511 	if (sr->arg.dirop.fqi.last_attr.sa_vattr.va_type != VDIR) {
512 		smb_node_release(dnode);
513 		SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
514 		smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY,
515 		    ERRDOS, ERROR_PATH_NOT_FOUND);
516 		return (SDRC_ERROR);
517 	}
518 
519 	rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE);
520 
521 	smb_node_release(dnode);
522 	SMB_NULL_FQI_NODES(sr->arg.dirop.fqi);
523 
524 	if (rc != 0) {
525 		smbsr_error(sr, NT_STATUS_ACCESS_DENIED,
526 		    ERRDOS, ERROR_ACCESS_DENIED);
527 		return (SDRC_ERROR);
528 	}
529 
530 	rc = smbsr_encode_empty_result(sr);
531 	return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR);
532 }
533 
534 static boolean_t
535 smb_dirpath_isvalid(const char *path)
536 {
537 	struct {
538 		char *name;
539 		int len;
540 	} *bad, bad_paths[] = {
541 		{ ".\0",   2 },
542 		{ ".\\\0", 3 },
543 		{ "..\0",  3 },
544 		{ "..\\",  3 }
545 	};
546 
547 	char *cp;
548 	char *p;
549 	int i;
550 
551 	if (*path == '\0')
552 		return (B_TRUE);
553 
554 	cp = smb_kstrdup(path, MAXPATHLEN);
555 	p = strcanon(cp, "\\");
556 	p += strspn(p, "\\");
557 
558 	for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) {
559 		bad = &bad_paths[i];
560 
561 		if (strncmp(p, bad->name, bad->len) == 0) {
562 			kmem_free(cp, MAXPATHLEN);
563 			return (B_FALSE);
564 		}
565 	}
566 
567 	kmem_free(cp, MAXPATHLEN);
568 	return (B_TRUE);
569 }
570