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