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