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