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