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