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, 70 &sr->arg.dirop.fqi.fq_path.pn_path); 71 72 DTRACE_SMB_2(op__CreateDirectory__start, smb_request_t *, sr, 73 struct dirop *, &sr->arg.dirop); 74 75 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 76 } 77 78 void 79 smb_post_create_directory(smb_request_t *sr) 80 { 81 DTRACE_SMB_1(op__CreateDirectory__done, smb_request_t *, sr); 82 } 83 84 /* 85 * smb_com_create_directory 86 * 87 * It is possible to get a full pathname here and the client expects any 88 * or all of the components to be created if they don't already exist. 89 */ 90 smb_sdrc_t 91 smb_com_create_directory(smb_request_t *sr) 92 { 93 smb_dirpath_t *spp; 94 smb_attr_t *attr; 95 DWORD status; 96 int rc = 0; 97 98 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 99 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 100 ERRDOS, ERROR_ACCESS_DENIED); 101 return (SDRC_ERROR); 102 } 103 104 if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.fq_path.pn_path)) { 105 smbsr_error(sr, NT_STATUS_OBJECT_PATH_SYNTAX_BAD, 106 ERRDOS, ERROR_BAD_PATHNAME); 107 return (SDRC_ERROR); 108 } 109 110 status = smb_validate_dirname(sr->arg.dirop.fqi.fq_path.pn_path); 111 if (status != 0) { 112 smbsr_error(sr, status, ERRDOS, ERROR_INVALID_NAME); 113 return (SDRC_ERROR); 114 } 115 116 /* 117 * Try each component of the path. EEXIST on path 118 * components is okay except on the last one. 119 */ 120 spp = smb_dirpath_new(sr); 121 122 while (smb_dirpath_next(spp)) { 123 rc = smb_common_create_directory(sr); 124 125 switch (rc) { 126 case 0: 127 break; 128 case EEXIST: 129 attr = &sr->arg.dirop.fqi.fq_fattr; 130 131 if (attr->sa_vattr.va_type != VDIR) { 132 smbsr_error(sr, NT_STATUS_OBJECT_NAME_COLLISION, 133 ERRDOS, ERROR_PATH_NOT_FOUND); 134 return (SDRC_ERROR); 135 } 136 break; 137 case ENOENT: 138 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 139 ERRDOS, ERROR_FILE_NOT_FOUND); 140 return (SDRC_ERROR); 141 case ENOTDIR: 142 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 143 ERRDOS, ERROR_PATH_NOT_FOUND); 144 return (SDRC_ERROR); 145 default: 146 smbsr_errno(sr, rc); 147 return (SDRC_ERROR); 148 } 149 } 150 151 if (rc != 0) { 152 smbsr_errno(sr, rc); 153 return (SDRC_ERROR); 154 } 155 156 rc = smbsr_encode_empty_result(sr); 157 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 158 } 159 160 /* 161 * smb_validate_dirname 162 * 163 * Very basic directory name validation: checks for colons in a path. 164 * Need to skip the drive prefix since it contains a colon. 165 * 166 * Returns NT_STATUS_SUCCESS if the name is valid, 167 * otherwise NT_STATUS_NOT_A_DIRECTORY. 168 */ 169 uint32_t 170 smb_validate_dirname(char *path) 171 { 172 char *name; 173 174 if ((name = path) != 0) { 175 name += strspn(name, "\\"); 176 177 if (strchr(name, ':') != 0) 178 return (NT_STATUS_NOT_A_DIRECTORY); 179 } 180 181 return (NT_STATUS_SUCCESS); 182 } 183 184 /* 185 * smb_common_create_directory 186 * 187 * Currently called from: 188 * smb_com_create_directory 189 * smb_com_trans2_create_directory 190 * 191 * Returns errno values. 192 */ 193 int 194 smb_common_create_directory(smb_request_t *sr) 195 { 196 int rc; 197 smb_attr_t new_attr; 198 smb_node_t *dnode; 199 smb_node_t *node; 200 201 sr->arg.dirop.fqi.fq_sattr = 0; 202 203 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_NOT_EXIST); 204 if (rc) 205 return (rc); 206 207 /* 208 * Because of FQM_PATH_MUST_NOT_EXIST and the successful return 209 * value, only fqi.fq_dnode has a valid parameter (fqi.fq_fnode 210 * is NULL). 211 */ 212 dnode = sr->arg.dirop.fqi.fq_dnode; 213 214 rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_ADD_SUBDIRECTORY); 215 if (rc != NT_STATUS_SUCCESS) 216 return (EACCES); 217 218 /* 219 * Explicitly set sa_dosattr, otherwise the file system may 220 * automatically apply FILE_ATTRIBUTE_ARCHIVE which, for 221 * compatibility with windows servers, should not be set. 222 */ 223 bzero(&new_attr, sizeof (new_attr)); 224 new_attr.sa_dosattr = FILE_ATTRIBUTE_DIRECTORY; 225 new_attr.sa_vattr.va_type = VDIR; 226 new_attr.sa_vattr.va_mode = 0777; 227 new_attr.sa_mask = SMB_AT_TYPE | SMB_AT_MODE | SMB_AT_DOSATTR; 228 229 if ((rc = smb_fsop_mkdir(sr, sr->user_cr, dnode, 230 sr->arg.dirop.fqi.fq_last_comp, &new_attr, 231 &sr->arg.dirop.fqi.fq_fnode, 232 &sr->arg.dirop.fqi.fq_fattr)) != 0) { 233 smb_node_release(dnode); 234 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 235 return (rc); 236 } 237 238 node = sr->arg.dirop.fqi.fq_fnode; 239 node->flags |= NODE_FLAGS_CREATED; 240 241 sr->arg.open.create_options = FILE_DIRECTORY_FILE; 242 243 smb_node_release(node); 244 smb_node_release(dnode); 245 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 246 return (0); 247 } 248 249 static smb_dirpath_t * 250 smb_dirpath_new(smb_request_t *sr) 251 { 252 int pathLen; 253 char *xpath; 254 smb_dirpath_t *spp; 255 256 /* Malloc from the request storage area. This is freed automatically */ 257 /* so we don't need to worry about freeing it later */ 258 spp = smbsr_malloc(&sr->request_storage, sizeof (smb_dirpath_t)); 259 spp->sp_path = sr->arg.dirop.fqi.fq_path.pn_path; 260 pathLen = strlen(spp->sp_path); 261 spp->sp_curp = spp->sp_path; 262 xpath = smbsr_malloc(&sr->request_storage, pathLen + 1); 263 sr->arg.dirop.fqi.fq_path.pn_path = xpath; 264 spp->sp_sr = sr; 265 266 return (spp); 267 } 268 269 /* 270 * Perhaps somewhat dangerous since everything happens as a side effect. The 271 * returns 1 if there is a valid component updated to the fqi, 0 otherwise. 272 */ 273 static int 274 smb_dirpath_next(smb_dirpath_t *spp) 275 { 276 char *xp; 277 int xlen; 278 279 if (spp == 0) 280 return (0); 281 282 /* Move the index to the "next" "\" and copy the path to the fqi */ 283 /* path for the next component. */ 284 285 /* First look for the next component */ 286 while (*spp->sp_curp == '\\') 287 spp->sp_curp++; 288 289 /* Now get to the end of the component */ 290 xp = spp->sp_curp; /* Remember from where we started */ 291 while (*spp->sp_curp != '\0' && *spp->sp_curp != '\\') { 292 spp->sp_curp++; 293 } 294 295 /* If we made no progress, we are done */ 296 if (xp == spp->sp_curp) 297 return (0); 298 299 /* 300 * Now copy the original path up to but not including our current 301 * pointer 302 */ 303 304 /*LINTED E_PTRDIFF_OVERFLOW*/ 305 xlen = spp->sp_curp - spp->sp_path; 306 (void) strncpy(spp->sp_sr->arg.dirop.fqi.fq_path.pn_path, 307 spp->sp_path, xlen); 308 309 /* Now NULL terminate it */ 310 spp->sp_sr->arg.dirop.fqi.fq_path.pn_path[xlen] = '\0'; 311 return (1); 312 } 313 314 /* 315 * The delete directory message is sent to delete an empty directory. The 316 * appropriate Tid and additional pathname are passed. The directory must 317 * be empty for it to be deleted. 318 * 319 * NT supports a hidden permission known as File Delete Child (FDC). If 320 * the user has FullControl access to a directory, the user is permitted 321 * to delete any object in the directory regardless of the permissions 322 * on the object. 323 * 324 * Client Request Description 325 * ================================== ================================= 326 * UCHAR WordCount; Count of parameter words = 0 327 * USHORT ByteCount; Count of data bytes; min = 2 328 * UCHAR BufferFormat; 0x04 329 * STRING DirectoryName[]; Directory name 330 * 331 * The directory to be deleted cannot be the root of the share specified 332 * by Tid. 333 * 334 * Server Response Description 335 * ================================== ================================= 336 * UCHAR WordCount; Count of parameter words = 0 337 * USHORT ByteCount; Count of data bytes = 0 338 */ 339 smb_sdrc_t 340 smb_pre_delete_directory(smb_request_t *sr) 341 { 342 int rc; 343 344 rc = smbsr_decode_data(sr, "%S", sr, 345 &sr->arg.dirop.fqi.fq_path.pn_path); 346 347 DTRACE_SMB_2(op__DeleteDirectory__start, smb_request_t *, sr, 348 struct dirop *, &sr->arg.dirop); 349 350 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 351 } 352 353 void 354 smb_post_delete_directory(smb_request_t *sr) 355 { 356 DTRACE_SMB_1(op__DeleteDirectory__done, smb_request_t *, sr); 357 } 358 359 smb_sdrc_t 360 smb_com_delete_directory(smb_request_t *sr) 361 { 362 smb_node_t *dnode; 363 smb_attr_t *attr; 364 int rc; 365 uint32_t flags = 0; 366 367 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 368 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 369 ERRDOS, ERROR_ACCESS_DENIED); 370 return (SDRC_ERROR); 371 } 372 373 sr->arg.dirop.fqi.fq_sattr = 0; 374 375 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 376 if (rc) { 377 if (rc == ENOENT) 378 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 379 ERRDOS, ERROR_FILE_NOT_FOUND); 380 else 381 smbsr_errno(sr, rc); 382 return (SDRC_ERROR); 383 } 384 385 attr = &sr->arg.dirop.fqi.fq_fattr; 386 if (attr->sa_vattr.va_type != VDIR) { 387 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 388 ERRDOS, ERROR_PATH_NOT_FOUND); 389 return (SDRC_ERROR); 390 } 391 392 dnode = sr->arg.dirop.fqi.fq_fnode; 393 rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE); 394 395 if ((rc != NT_STATUS_SUCCESS) || 396 (dnode->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) { 397 smb_node_release(dnode); 398 smb_node_release(sr->arg.dirop.fqi.fq_dnode); 399 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 400 smbsr_error(sr, NT_STATUS_CANNOT_DELETE, 401 ERRDOS, ERROR_ACCESS_DENIED); 402 return (SDRC_ERROR); 403 } 404 405 smb_node_release(dnode); 406 407 dnode = sr->arg.dirop.fqi.fq_dnode; 408 409 if (SMB_TREE_SUPPORTS_CATIA(sr)) 410 flags |= SMB_CATIA; 411 412 rc = smb_fsop_rmdir(sr, sr->user_cr, dnode, 413 sr->arg.dirop.fqi.fq_od_name, flags); 414 if (rc != 0) { 415 smb_node_release(dnode); 416 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 417 if (rc == EEXIST) 418 smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY, 419 ERRDOS, ERROR_DIR_NOT_EMPTY); 420 else 421 smbsr_errno(sr, rc); 422 return (SDRC_ERROR); 423 } 424 425 smb_node_release(dnode); 426 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 427 428 rc = smbsr_encode_empty_result(sr); 429 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 430 } 431 432 /* 433 * This SMB is used to verify that a path exists and is a directory. No 434 * error is returned if the given path exists and the client has read 435 * access to it. Client machines which maintain a concept of a "working 436 * directory" will find this useful to verify the validity of a "change 437 * working directory" command. Note that the servers do NOT have a concept 438 * of working directory for a particular client. The client must always 439 * supply full pathnames relative to the Tid in the SMB header. 440 * 441 * Client Request Description 442 * ================================== ================================= 443 * 444 * UCHAR WordCount; Count of parameter words = 0 445 * USHORT ByteCount; Count of data bytes; min = 2 446 * UCHAR BufferFormat; 0x04 447 * STRING DirectoryPath[]; Directory path 448 * 449 * Server Response Description 450 * ================================== ================================= 451 * 452 * UCHAR WordCount; Count of parameter words = 0 453 * USHORT ByteCount; Count of data bytes = 0 454 * 455 * DOS clients, in particular, depend on ERRbadpath if the directory is 456 * not found. 457 */ 458 smb_sdrc_t 459 smb_pre_check_directory(smb_request_t *sr) 460 { 461 int rc; 462 463 rc = smbsr_decode_data(sr, "%S", sr, 464 &sr->arg.dirop.fqi.fq_path.pn_path); 465 466 DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr, 467 struct dirop *, &sr->arg.dirop); 468 469 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 470 } 471 472 void 473 smb_post_check_directory(smb_request_t *sr) 474 { 475 DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr); 476 } 477 478 smb_sdrc_t 479 smb_com_check_directory(smb_request_t *sr) 480 { 481 smb_node_t *dnode; 482 int rc; 483 484 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 485 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, 486 ERROR_ACCESS_DENIED); 487 return (SDRC_ERROR); 488 } 489 490 if (sr->arg.dirop.fqi.fq_path.pn_path[0] == '\0') { 491 rc = smbsr_encode_empty_result(sr); 492 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 493 } 494 495 if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.fq_path.pn_path)) { 496 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 497 ERRDOS, ERROR_PATH_NOT_FOUND); 498 return (SDRC_ERROR); 499 } 500 501 sr->arg.dirop.fqi.fq_sattr = 0; 502 503 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 504 if (rc) { 505 if (rc == ENOENT) 506 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 507 ERRDOS, ERROR_PATH_NOT_FOUND); 508 else 509 smbsr_errno(sr, rc); 510 return (SDRC_ERROR); 511 } 512 513 smb_node_release(sr->arg.dirop.fqi.fq_dnode); 514 515 dnode = sr->arg.dirop.fqi.fq_fnode; 516 517 if (sr->arg.dirop.fqi.fq_fattr.sa_vattr.va_type != VDIR) { 518 smb_node_release(dnode); 519 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 520 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 521 ERRDOS, ERROR_PATH_NOT_FOUND); 522 return (SDRC_ERROR); 523 } 524 525 rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE); 526 527 smb_node_release(dnode); 528 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 529 530 if (rc != 0) { 531 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 532 ERRDOS, ERROR_ACCESS_DENIED); 533 return (SDRC_ERROR); 534 } 535 536 rc = smbsr_encode_empty_result(sr); 537 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 538 } 539 540 static boolean_t 541 smb_dirpath_isvalid(const char *path) 542 { 543 struct { 544 char *name; 545 int len; 546 } *bad, bad_paths[] = { 547 { ".\0", 2 }, 548 { ".\\\0", 3 }, 549 { "..\0", 3 }, 550 { "..\\", 3 } 551 }; 552 553 char *cp; 554 char *p; 555 int i; 556 557 if (*path == '\0') 558 return (B_TRUE); 559 560 cp = smb_kstrdup(path, MAXPATHLEN); 561 p = strcanon(cp, "\\"); 562 p += strspn(p, "\\"); 563 564 for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) { 565 bad = &bad_paths[i]; 566 567 if (strncmp(p, bad->name, bad->len) == 0) { 568 kmem_free(cp, MAXPATHLEN); 569 return (B_FALSE); 570 } 571 } 572 573 kmem_free(cp, MAXPATHLEN); 574 return (B_TRUE); 575 } 576