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 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "@(#)smb_delete.c 1.10 08/08/07 SMI" 27 28 #include <smbsrv/smb_incl.h> 29 #include <smbsrv/smb_fsops.h> 30 #include <smbsrv/smbinfo.h> 31 #include <sys/nbmlock.h> 32 33 static uint32_t smb_delete_check(smb_request_t *, smb_node_t *); 34 static boolean_t smb_delete_check_path(smb_request_t *, boolean_t *); 35 36 /* 37 * smb_com_delete 38 * 39 * The delete file message is sent to delete a data file. The appropriate 40 * Tid and additional pathname are passed. Read only files may not be 41 * deleted, the read-only attribute must be reset prior to file deletion. 42 * 43 * NT supports a hidden permission known as File Delete Child (FDC). If 44 * the user has FullControl access to a directory, the user is permitted 45 * to delete any object in the directory regardless of the permissions 46 * on the object. 47 * 48 * Client Request Description 49 * ================================== ================================= 50 * UCHAR WordCount; Count of parameter words = 1 51 * USHORT SearchAttributes; 52 * USHORT ByteCount; Count of data bytes; min = 2 53 * UCHAR BufferFormat; 0x04 54 * STRING FileName[]; File name 55 * 56 * Multiple files may be deleted in response to a single request as 57 * SMB_COM_DELETE supports wildcards 58 * 59 * SearchAttributes indicates the attributes that the target file(s) must 60 * have. If the attribute is zero then only normal files are deleted. If 61 * the system file or hidden attributes are specified then the delete is 62 * inclusive -both the specified type(s) of files and normal files are 63 * deleted. Attributes are described in the "Attribute Encoding" section 64 * of this document. 65 * 66 * If bit0 of the Flags2 field of the SMB header is set, a pattern is 67 * passed in, and the file has a long name, then the passed pattern much 68 * match the long file name for the delete to succeed. If bit0 is clear, a 69 * pattern is passed in, and the file has a long name, then the passed 70 * pattern must match the file's short name for the deletion to succeed. 71 * 72 * Server Response Description 73 * ================================== ================================= 74 * UCHAR WordCount; Count of parameter words = 0 75 * USHORT ByteCount; Count of data bytes = 0 76 * 77 * 4.2.10.1 Errors 78 * 79 * ERRDOS/ERRbadpath 80 * ERRDOS/ERRbadfile 81 * ERRDOS/ERRnoaccess 82 * ERRDOS/ERRbadshare # returned by NT for files that are already open 83 * ERRHRD/ERRnowrite 84 * ERRSRV/ERRaccess 85 * ERRSRV/ERRinvdevice 86 * ERRSRV/ERRinvid 87 * ERRSRV/ERRbaduid 88 */ 89 smb_sdrc_t 90 smb_pre_delete(smb_request_t *sr) 91 { 92 struct smb_fqi *fqi = &sr->arg.dirop.fqi; 93 int rc; 94 95 if ((rc = smbsr_decode_vwv(sr, "w", &fqi->srch_attr)) == 0) 96 rc = smbsr_decode_data(sr, "%S", sr, &fqi->path); 97 98 DTRACE_SMB_2(op__Delete__start, smb_request_t *, sr, 99 struct smb_fqi *, fqi); 100 101 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 102 } 103 104 void 105 smb_post_delete(smb_request_t *sr) 106 { 107 DTRACE_SMB_1(op__Delete__done, smb_request_t *, sr); 108 } 109 110 /* 111 * smb_com_delete 112 * 113 * readonly 114 * If a readonly entry is matched the search aborts with status 115 * NT_STATUS_CANNOT_DELETE. Entries found prior to the readonly 116 * entry will have been deleted. 117 * 118 * directories: 119 * smb_com_delete does not delete directories: 120 * A non-wildcard delete that finds a directory should result in 121 * NT_STATUS_FILE_IS_A_DIRECTORY. 122 * A wildcard delete that finds a directory will either: 123 * - abort with status NT_STATUS_FILE_IS_A_DIRECTORY, if 124 * FILE_ATTRIBUTE_DIRECTORY is specified in the search attributes, or 125 * - skip that entry, if FILE_ATTRIBUTE_DIRECTORY is NOT specified 126 * in the search attributes 127 * Entries found prior to the directory entry will have been deleted. 128 * 129 * search attribute not matched 130 * If an entry is found but it is either hidden or system and those 131 * attributes are not specified in the search attributes: 132 * - if deleting a single file, status NT_STATUS_NO_SUCH_FILE 133 * - if wildcard delete, skip the entry and continue 134 * 135 * path not found 136 * If smb_rdir_open cannot find the specified path, the error code 137 * is set to NT_STATUS_OBJECT_PATH_NOT_FOUND. If there are wildcards 138 * in the last_component, NT_STATUS_OBJECT_NAME_NOT_FOUND should be set 139 * instead. 140 * 141 * smb_delete_check_path() - checks dot, bad path syntax, wildcards in path 142 */ 143 144 smb_sdrc_t 145 smb_com_delete(smb_request_t *sr) 146 { 147 struct smb_fqi *fqi = &sr->arg.dirop.fqi; 148 int rc; 149 int deleted = 0; 150 struct smb_node *node = NULL; 151 smb_odir_context_t *pc; 152 unsigned short sattr; 153 boolean_t wildcards; 154 155 if (smb_delete_check_path(sr, &wildcards) != B_TRUE) 156 return (SDRC_ERROR); 157 158 /* 159 * specify all search attributes so that delete-specific 160 * search attribute handling can be performed 161 */ 162 sattr = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN | 163 FILE_ATTRIBUTE_SYSTEM; 164 165 if (smb_rdir_open(sr, fqi->path, sattr) != 0) { 166 /* 167 * If there are wildcards in the last_component, 168 * NT_STATUS_OBJECT_NAME_NOT_FOUND 169 * should be used in place of NT_STATUS_OBJECT_PATH_NOT_FOUND 170 */ 171 if ((wildcards == B_TRUE) && 172 (sr->smb_error.status == NT_STATUS_OBJECT_PATH_NOT_FOUND)) { 173 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 174 ERRDOS, ERROR_FILE_NOT_FOUND); 175 } 176 177 return (SDRC_ERROR); 178 } 179 180 pc = kmem_zalloc(sizeof (*pc), KM_SLEEP); 181 182 /* 183 * This while loop is meant to deal with wildcards. 184 * It is not expected that wildcards will exist for 185 * streams. For the streams case, it is expected 186 * that the below loop will be executed only once. 187 */ 188 189 while ((rc = smb_rdir_next(sr, &node, pc)) == 0) { 190 /* check directory */ 191 if (pc->dc_dattr & FILE_ATTRIBUTE_DIRECTORY) { 192 smb_node_release(node); 193 if (wildcards == B_FALSE) { 194 smbsr_error(sr, NT_STATUS_FILE_IS_A_DIRECTORY, 195 ERRDOS, ERROR_ACCESS_DENIED); 196 goto delete_error; 197 } else { 198 if (SMB_SEARCH_DIRECTORY(fqi->srch_attr) != 0) 199 break; 200 else 201 continue; 202 } 203 } 204 205 /* check readonly */ 206 if (SMB_PATHFILE_IS_READONLY(sr, node)) { 207 smb_node_release(node); 208 smbsr_error(sr, NT_STATUS_CANNOT_DELETE, 209 ERRDOS, ERROR_ACCESS_DENIED); 210 goto delete_error; 211 } 212 213 /* check search attributes */ 214 if (((pc->dc_dattr & FILE_ATTRIBUTE_HIDDEN) && 215 !(SMB_SEARCH_HIDDEN(fqi->srch_attr))) || 216 ((pc->dc_dattr & FILE_ATTRIBUTE_SYSTEM) && 217 !(SMB_SEARCH_SYSTEM(fqi->srch_attr)))) { 218 smb_node_release(node); 219 if (wildcards == B_FALSE) { 220 smbsr_error(sr, NT_STATUS_NO_SUCH_FILE, 221 ERRDOS, ERROR_FILE_NOT_FOUND); 222 goto delete_error; 223 } else { 224 continue; 225 } 226 } 227 228 /* 229 * NT does not always close a file immediately, which 230 * can cause the share and access checking to fail 231 * (the node refcnt is greater than one), and the file 232 * doesn't get deleted. Breaking the oplock before 233 * share and access checking gives the client a chance 234 * to close the file. 235 */ 236 237 smb_oplock_break(node); 238 239 smb_node_start_crit(node, RW_READER); 240 241 if (smb_delete_check(sr, node) != NT_STATUS_SUCCESS) { 242 smb_node_end_crit(node); 243 smb_node_release(node); 244 goto delete_error; 245 } 246 247 /* 248 * Use node->od_name so as to skip mangle checks and 249 * stream processing (which have already been done in 250 * smb_rdir_next()). 251 * Use node->dir_snode to obtain the correct parent node 252 * (especially for streams). 253 */ 254 rc = smb_fsop_remove(sr, sr->user_cr, node->dir_snode, 255 node->od_name, 1); 256 257 smb_node_end_crit(node); 258 smb_node_release(node); 259 node = NULL; 260 261 if (rc != 0) { 262 if (rc != ENOENT) { 263 smbsr_errno(sr, rc); 264 goto delete_error; 265 } 266 } else { 267 deleted++; 268 } 269 } 270 271 if ((rc != 0) && (rc != ENOENT)) { 272 smbsr_errno(sr, rc); 273 goto delete_error; 274 } 275 276 if (deleted == 0) { 277 if (wildcards == B_FALSE) 278 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 279 ERRDOS, ERROR_FILE_NOT_FOUND); 280 else 281 smbsr_error(sr, NT_STATUS_NO_SUCH_FILE, 282 ERRDOS, ERROR_FILE_NOT_FOUND); 283 goto delete_error; 284 } 285 286 smb_rdir_close(sr); 287 kmem_free(pc, sizeof (*pc)); 288 289 rc = smbsr_encode_empty_result(sr); 290 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 291 292 delete_error: 293 smb_rdir_close(sr); 294 kmem_free(pc, sizeof (*pc)); 295 return (SDRC_ERROR); 296 } 297 298 /* 299 * smb_delete_check_path 300 * 301 * Perform initial validation on the pathname and last_component. 302 * 303 * dot: 304 * A filename of '.' should result in NT_STATUS_OBJECT_NAME_INVALID 305 * Any wildcard filename that resolves to '.' should result in 306 * NT_STATUS_OBJECT_NAME_INVALID if the search attributes include 307 * FILE_ATTRIBUTE_DIRECTORY, otherwise handled as directory (see above). 308 * 309 * bad path syntax: 310 * On unix .. at the root of a file system links to the root. Thus 311 * an attempt to lookup "/../../.." will be the same as looking up "/" 312 * CIFs clients expect the above to result in 313 * NT_STATUS_OBJECT_PATH_SYNTAX_BAD. It is currently not possible 314 * (and questionable if it's desirable) to deal with all cases 315 * but paths beginning with \\.. are handled. See bad_paths[]. 316 * Cases like "\\dir\\..\\.." will still result in "\\" which is 317 * contrary to windows behavior. 318 * 319 * wildcards in path: 320 * Wildcards in the path (excluding the last_component) should result 321 * in NT_STATUS_OBJECT_NAME_INVALID. 322 * 323 * Returns: 324 * B_TRUE: path is valid. Sets *wildcard to TRUE if wildcard delete 325 * i.e. if wildcards in last component 326 * B_FALSE: path is invalid. Sets error information in sr. 327 */ 328 static boolean_t 329 smb_delete_check_path(smb_request_t *sr, boolean_t *wildcard) 330 { 331 struct smb_fqi *fqi = &sr->arg.dirop.fqi; 332 char *p, *last_component; 333 int i, wildcards; 334 335 struct { 336 char *name; 337 int len; 338 } *bad, bad_paths[] = { 339 {"\\..\0", 4}, 340 {"\\..\\", 4}, 341 {"..\0", 3}, 342 {"..\\", 3} 343 }; 344 345 wildcards = smb_convert_unicode_wildcards(fqi->path); 346 347 /* find last component, strip trailing '\\' */ 348 p = fqi->path + strlen(fqi->path) - 1; 349 while (*p == '\\') { 350 *p = '\0'; 351 --p; 352 } 353 if ((p = strrchr(fqi->path, '\\')) == NULL) { 354 last_component = fqi->path; 355 } else { 356 last_component = ++p; 357 358 /* 359 * Any wildcards in path (excluding last_component) should 360 * result in NT_STATUS_OBJECT_NAME_INVALID 361 */ 362 if (smb_convert_unicode_wildcards(last_component) 363 != wildcards) { 364 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 365 ERRDOS, ERROR_INVALID_NAME); 366 return (B_FALSE); 367 } 368 } 369 370 /* 371 * path above the mount point => NT_STATUS_OBJECT_PATH_SYNTAX_BAD 372 * This test doesn't cover all cases: e.g. \dir\..\.. 373 */ 374 for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) { 375 bad = &bad_paths[i]; 376 if (strncmp(fqi->path, bad->name, bad->len) == 0) { 377 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 378 ERRDOS, ERROR_BAD_PATHNAME); 379 return (B_FALSE); 380 } 381 } 382 383 /* 384 * Any file pattern that resolves to '.' is considered invalid. 385 * In the wildcard case, only an error if FILE_ATTRIBUTE_DIRECTORY 386 * is specified in search attributes, otherwise skipped (below) 387 */ 388 if ((strcmp(last_component, ".") == 0) || 389 (SMB_SEARCH_DIRECTORY(fqi->srch_attr) && 390 (smb_match(last_component, ".")))) { 391 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 392 ERRDOS, ERROR_INVALID_NAME); 393 return (B_FALSE); 394 } 395 396 *wildcard = (wildcards != 0); 397 return (B_TRUE); 398 } 399 400 /* 401 * For consistency with Windows 2000, the range check should be done 402 * after checking for sharing violations. Attempting to delete a 403 * locked file will result in sharing violation, which is the same 404 * thing that will happen if you try to delete a non-locked open file. 405 * 406 * Note that windows 2000 rejects lock requests on open files that 407 * have been opened with metadata open modes. The error is 408 * STATUS_ACCESS_DENIED. 409 */ 410 static uint32_t 411 smb_delete_check(smb_request_t *sr, smb_node_t *node) 412 { 413 uint32_t status; 414 415 status = smb_node_delete_check(node); 416 417 if (status == NT_STATUS_SHARING_VIOLATION) { 418 smbsr_error(sr, NT_STATUS_SHARING_VIOLATION, 419 ERRDOS, ERROR_SHARING_VIOLATION); 420 return (status); 421 } 422 423 status = smb_range_check(sr, node, 0, UINT64_MAX, B_TRUE); 424 425 if (status != NT_STATUS_SUCCESS) { 426 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 427 ERRDOS, ERROR_ACCESS_DENIED); 428 } 429 430 return (status); 431 } 432