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)) != 0) { 232 smb_node_release(dnode); 233 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 234 return (rc); 235 } 236 237 node = sr->arg.dirop.fqi.fq_fnode; 238 rc = smb_node_getattr(sr, node, &sr->arg.dirop.fqi.fq_fattr); 239 if (rc != 0) { 240 smb_node_release(dnode); 241 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 242 return (rc); 243 } 244 node->flags |= NODE_FLAGS_CREATED; 245 246 sr->arg.open.create_options = FILE_DIRECTORY_FILE; 247 248 smb_node_release(node); 249 smb_node_release(dnode); 250 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 251 return (0); 252 } 253 254 static smb_dirpath_t * 255 smb_dirpath_new(smb_request_t *sr) 256 { 257 int pathLen; 258 char *xpath; 259 smb_dirpath_t *spp; 260 261 /* Malloc from the request storage area. This is freed automatically */ 262 /* so we don't need to worry about freeing it later */ 263 spp = smbsr_malloc(&sr->request_storage, sizeof (smb_dirpath_t)); 264 spp->sp_path = sr->arg.dirop.fqi.fq_path.pn_path; 265 pathLen = strlen(spp->sp_path); 266 spp->sp_curp = spp->sp_path; 267 xpath = smbsr_malloc(&sr->request_storage, pathLen + 1); 268 sr->arg.dirop.fqi.fq_path.pn_path = xpath; 269 spp->sp_sr = sr; 270 271 return (spp); 272 } 273 274 /* 275 * Perhaps somewhat dangerous since everything happens as a side effect. The 276 * returns 1 if there is a valid component updated to the fqi, 0 otherwise. 277 */ 278 static int 279 smb_dirpath_next(smb_dirpath_t *spp) 280 { 281 char *xp; 282 int xlen; 283 284 if (spp == 0) 285 return (0); 286 287 /* Move the index to the "next" "\" and copy the path to the fqi */ 288 /* path for the next component. */ 289 290 /* First look for the next component */ 291 while (*spp->sp_curp == '\\') 292 spp->sp_curp++; 293 294 /* Now get to the end of the component */ 295 xp = spp->sp_curp; /* Remember from where we started */ 296 while (*spp->sp_curp != '\0' && *spp->sp_curp != '\\') { 297 spp->sp_curp++; 298 } 299 300 /* If we made no progress, we are done */ 301 if (xp == spp->sp_curp) 302 return (0); 303 304 /* 305 * Now copy the original path up to but not including our current 306 * pointer 307 */ 308 309 /*LINTED E_PTRDIFF_OVERFLOW*/ 310 xlen = spp->sp_curp - spp->sp_path; 311 (void) strncpy(spp->sp_sr->arg.dirop.fqi.fq_path.pn_path, 312 spp->sp_path, xlen); 313 314 /* Now NULL terminate it */ 315 spp->sp_sr->arg.dirop.fqi.fq_path.pn_path[xlen] = '\0'; 316 return (1); 317 } 318 319 /* 320 * The delete directory message is sent to delete an empty directory. The 321 * appropriate Tid and additional pathname are passed. The directory must 322 * be empty for it to be deleted. 323 * 324 * NT supports a hidden permission known as File Delete Child (FDC). If 325 * the user has FullControl access to a directory, the user is permitted 326 * to delete any object in the directory regardless of the permissions 327 * on the object. 328 * 329 * Client Request Description 330 * ================================== ================================= 331 * UCHAR WordCount; Count of parameter words = 0 332 * USHORT ByteCount; Count of data bytes; min = 2 333 * UCHAR BufferFormat; 0x04 334 * STRING DirectoryName[]; Directory name 335 * 336 * The directory to be deleted cannot be the root of the share specified 337 * by Tid. 338 * 339 * Server Response Description 340 * ================================== ================================= 341 * UCHAR WordCount; Count of parameter words = 0 342 * USHORT ByteCount; Count of data bytes = 0 343 */ 344 smb_sdrc_t 345 smb_pre_delete_directory(smb_request_t *sr) 346 { 347 int rc; 348 349 rc = smbsr_decode_data(sr, "%S", sr, 350 &sr->arg.dirop.fqi.fq_path.pn_path); 351 352 DTRACE_SMB_2(op__DeleteDirectory__start, smb_request_t *, sr, 353 struct dirop *, &sr->arg.dirop); 354 355 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 356 } 357 358 void 359 smb_post_delete_directory(smb_request_t *sr) 360 { 361 DTRACE_SMB_1(op__DeleteDirectory__done, smb_request_t *, sr); 362 } 363 364 smb_sdrc_t 365 smb_com_delete_directory(smb_request_t *sr) 366 { 367 smb_node_t *dnode; 368 smb_attr_t *attr; 369 int rc; 370 uint32_t flags = 0; 371 372 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 373 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 374 ERRDOS, ERROR_ACCESS_DENIED); 375 return (SDRC_ERROR); 376 } 377 378 sr->arg.dirop.fqi.fq_sattr = 0; 379 380 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 381 if (rc) { 382 if (rc == ENOENT) 383 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 384 ERRDOS, ERROR_FILE_NOT_FOUND); 385 else 386 smbsr_errno(sr, rc); 387 return (SDRC_ERROR); 388 } 389 390 attr = &sr->arg.dirop.fqi.fq_fattr; 391 if (attr->sa_vattr.va_type != VDIR) { 392 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 393 ERRDOS, ERROR_PATH_NOT_FOUND); 394 return (SDRC_ERROR); 395 } 396 397 dnode = sr->arg.dirop.fqi.fq_fnode; 398 rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE); 399 400 if ((rc != NT_STATUS_SUCCESS) || 401 attr->sa_dosattr & FILE_ATTRIBUTE_READONLY) { 402 smb_node_release(dnode); 403 smb_node_release(sr->arg.dirop.fqi.fq_dnode); 404 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 405 smbsr_error(sr, NT_STATUS_CANNOT_DELETE, 406 ERRDOS, ERROR_ACCESS_DENIED); 407 return (SDRC_ERROR); 408 } 409 410 smb_node_release(dnode); 411 412 dnode = sr->arg.dirop.fqi.fq_dnode; 413 414 if (SMB_TREE_SUPPORTS_CATIA(sr)) 415 flags |= SMB_CATIA; 416 417 rc = smb_fsop_rmdir(sr, sr->user_cr, dnode, 418 sr->arg.dirop.fqi.fq_od_name, flags); 419 if (rc != 0) { 420 smb_node_release(dnode); 421 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 422 if (rc == EEXIST) 423 smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY, 424 ERRDOS, ERROR_DIR_NOT_EMPTY); 425 else 426 smbsr_errno(sr, rc); 427 return (SDRC_ERROR); 428 } 429 430 smb_node_release(dnode); 431 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 432 433 rc = smbsr_encode_empty_result(sr); 434 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 435 } 436 437 /* 438 * This SMB is used to verify that a path exists and is a directory. No 439 * error is returned if the given path exists and the client has read 440 * access to it. Client machines which maintain a concept of a "working 441 * directory" will find this useful to verify the validity of a "change 442 * working directory" command. Note that the servers do NOT have a concept 443 * of working directory for a particular client. The client must always 444 * supply full pathnames relative to the Tid in the SMB header. 445 * 446 * Client Request Description 447 * ================================== ================================= 448 * 449 * UCHAR WordCount; Count of parameter words = 0 450 * USHORT ByteCount; Count of data bytes; min = 2 451 * UCHAR BufferFormat; 0x04 452 * STRING DirectoryPath[]; Directory path 453 * 454 * Server Response Description 455 * ================================== ================================= 456 * 457 * UCHAR WordCount; Count of parameter words = 0 458 * USHORT ByteCount; Count of data bytes = 0 459 * 460 * DOS clients, in particular, depend on ERRbadpath if the directory is 461 * not found. 462 */ 463 smb_sdrc_t 464 smb_pre_check_directory(smb_request_t *sr) 465 { 466 int rc; 467 468 rc = smbsr_decode_data(sr, "%S", sr, 469 &sr->arg.dirop.fqi.fq_path.pn_path); 470 471 DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr, 472 struct dirop *, &sr->arg.dirop); 473 474 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 475 } 476 477 void 478 smb_post_check_directory(smb_request_t *sr) 479 { 480 DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr); 481 } 482 483 smb_sdrc_t 484 smb_com_check_directory(smb_request_t *sr) 485 { 486 smb_node_t *dnode; 487 int rc; 488 489 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 490 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, 491 ERROR_ACCESS_DENIED); 492 return (SDRC_ERROR); 493 } 494 495 if (sr->arg.dirop.fqi.fq_path.pn_path[0] == '\0') { 496 rc = smbsr_encode_empty_result(sr); 497 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 498 } 499 500 if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.fq_path.pn_path)) { 501 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 502 ERRDOS, ERROR_PATH_NOT_FOUND); 503 return (SDRC_ERROR); 504 } 505 506 sr->arg.dirop.fqi.fq_sattr = 0; 507 508 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 509 if (rc) { 510 if (rc == ENOENT) 511 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 512 ERRDOS, ERROR_PATH_NOT_FOUND); 513 else 514 smbsr_errno(sr, rc); 515 return (SDRC_ERROR); 516 } 517 518 smb_node_release(sr->arg.dirop.fqi.fq_dnode); 519 520 dnode = sr->arg.dirop.fqi.fq_fnode; 521 522 if (sr->arg.dirop.fqi.fq_fattr.sa_vattr.va_type != VDIR) { 523 smb_node_release(dnode); 524 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 525 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 526 ERRDOS, ERROR_PATH_NOT_FOUND); 527 return (SDRC_ERROR); 528 } 529 530 rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE); 531 532 smb_node_release(dnode); 533 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 534 535 if (rc != 0) { 536 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 537 ERRDOS, ERROR_ACCESS_DENIED); 538 return (SDRC_ERROR); 539 } 540 541 rc = smbsr_encode_empty_result(sr); 542 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 543 } 544 545 static boolean_t 546 smb_dirpath_isvalid(const char *path) 547 { 548 struct { 549 char *name; 550 int len; 551 } *bad, bad_paths[] = { 552 { ".\0", 2 }, 553 { ".\\\0", 3 }, 554 { "..\0", 3 }, 555 { "..\\", 3 } 556 }; 557 558 char *cp; 559 char *p; 560 int i; 561 562 if (*path == '\0') 563 return (B_TRUE); 564 565 cp = smb_kstrdup(path, MAXPATHLEN); 566 p = strcanon(cp, "\\"); 567 p += strspn(p, "\\"); 568 569 for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) { 570 bad = &bad_paths[i]; 571 572 if (strncmp(p, bad->name, bad->len) == 0) { 573 kmem_free(cp, MAXPATHLEN); 574 return (B_FALSE); 575 } 576 } 577 578 kmem_free(cp, MAXPATHLEN); 579 return (B_TRUE); 580 } 581