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 361 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 362 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 363 ERRDOS, ERROR_ACCESS_DENIED); 364 return (SDRC_ERROR); 365 } 366 367 sr->arg.dirop.fqi.srch_attr = 0; 368 369 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 370 if (rc) { 371 if (rc == ENOENT) 372 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 373 ERRDOS, ERROR_FILE_NOT_FOUND); 374 else 375 smbsr_errno(sr, rc); 376 return (SDRC_ERROR); 377 } 378 379 attr = &sr->arg.dirop.fqi.last_attr; 380 if (attr->sa_vattr.va_type != VDIR) { 381 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 382 ERRDOS, ERROR_PATH_NOT_FOUND); 383 return (SDRC_ERROR); 384 } 385 386 dnode = sr->arg.dirop.fqi.last_snode; 387 rc = smb_fsop_access(sr, sr->user_cr, dnode, DELETE); 388 389 if ((rc != NT_STATUS_SUCCESS) || 390 (dnode->attr.sa_dosattr & FILE_ATTRIBUTE_READONLY)) { 391 smb_node_release(dnode); 392 smb_node_release(sr->arg.dirop.fqi.dir_snode); 393 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 394 smbsr_error(sr, NT_STATUS_CANNOT_DELETE, 395 ERRDOS, ERROR_ACCESS_DENIED); 396 return (SDRC_ERROR); 397 } 398 399 smb_node_release(dnode); 400 401 dnode = sr->arg.dirop.fqi.dir_snode; 402 403 rc = smb_fsop_rmdir(sr, sr->user_cr, dnode, 404 sr->arg.dirop.fqi.last_comp_od, 1); 405 if (rc != 0) { 406 smb_node_release(dnode); 407 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 408 if (rc == EEXIST) 409 smbsr_error(sr, NT_STATUS_DIRECTORY_NOT_EMPTY, 410 ERRDOS, ERROR_DIR_NOT_EMPTY); 411 else 412 smbsr_errno(sr, rc); 413 return (SDRC_ERROR); 414 } 415 416 smb_node_release(dnode); 417 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 418 419 rc = smbsr_encode_empty_result(sr); 420 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 421 } 422 423 /* 424 * This SMB is used to verify that a path exists and is a directory. No 425 * error is returned if the given path exists and the client has read 426 * access to it. Client machines which maintain a concept of a "working 427 * directory" will find this useful to verify the validity of a "change 428 * working directory" command. Note that the servers do NOT have a concept 429 * of working directory for a particular client. The client must always 430 * supply full pathnames relative to the Tid in the SMB header. 431 * 432 * Client Request Description 433 * ================================== ================================= 434 * 435 * UCHAR WordCount; Count of parameter words = 0 436 * USHORT ByteCount; Count of data bytes; min = 2 437 * UCHAR BufferFormat; 0x04 438 * STRING DirectoryPath[]; Directory path 439 * 440 * Server Response Description 441 * ================================== ================================= 442 * 443 * UCHAR WordCount; Count of parameter words = 0 444 * USHORT ByteCount; Count of data bytes = 0 445 * 446 * DOS clients, in particular, depend on ERRbadpath if the directory is 447 * not found. 448 */ 449 smb_sdrc_t 450 smb_pre_check_directory(smb_request_t *sr) 451 { 452 int rc; 453 454 rc = smbsr_decode_data(sr, "%S", sr, &sr->arg.dirop.fqi.path); 455 456 DTRACE_SMB_2(op__CheckDirectory__start, smb_request_t *, sr, 457 struct dirop *, &sr->arg.dirop); 458 459 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 460 } 461 462 void 463 smb_post_check_directory(smb_request_t *sr) 464 { 465 DTRACE_SMB_1(op__CheckDirectory__done, smb_request_t *, sr); 466 } 467 468 smb_sdrc_t 469 smb_com_check_directory(smb_request_t *sr) 470 { 471 smb_node_t *dnode; 472 int rc; 473 474 if (!STYPE_ISDSK(sr->tid_tree->t_res_type)) { 475 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, ERRDOS, 476 ERROR_ACCESS_DENIED); 477 return (SDRC_ERROR); 478 } 479 480 if (sr->arg.dirop.fqi.path[0] == '\0') { 481 rc = smbsr_encode_empty_result(sr); 482 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 483 } 484 485 if (!smb_dirpath_isvalid(sr->arg.dirop.fqi.path)) { 486 smbsr_error(sr, NT_STATUS_OBJECT_NAME_INVALID, 487 ERRDOS, ERROR_PATH_NOT_FOUND); 488 return (SDRC_ERROR); 489 } 490 491 sr->arg.dirop.fqi.srch_attr = 0; 492 493 rc = smbd_fs_query(sr, &sr->arg.dirop.fqi, FQM_PATH_MUST_EXIST); 494 if (rc) { 495 if (rc == ENOENT) 496 smbsr_error(sr, NT_STATUS_OBJECT_NAME_NOT_FOUND, 497 ERRDOS, ERROR_PATH_NOT_FOUND); 498 else 499 smbsr_errno(sr, rc); 500 return (SDRC_ERROR); 501 } 502 503 smb_node_release(sr->arg.dirop.fqi.dir_snode); 504 505 dnode = sr->arg.dirop.fqi.last_snode; 506 507 if (sr->arg.dirop.fqi.last_attr.sa_vattr.va_type != VDIR) { 508 smb_node_release(dnode); 509 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 510 smbsr_error(sr, NT_STATUS_NOT_A_DIRECTORY, 511 ERRDOS, ERROR_PATH_NOT_FOUND); 512 return (SDRC_ERROR); 513 } 514 515 rc = smb_fsop_access(sr, sr->user_cr, dnode, FILE_TRAVERSE); 516 517 smb_node_release(dnode); 518 SMB_NULL_FQI_NODES(sr->arg.dirop.fqi); 519 520 if (rc != 0) { 521 smbsr_error(sr, NT_STATUS_ACCESS_DENIED, 522 ERRDOS, ERROR_ACCESS_DENIED); 523 return (SDRC_ERROR); 524 } 525 526 rc = smbsr_encode_empty_result(sr); 527 return ((rc == 0) ? SDRC_SUCCESS : SDRC_ERROR); 528 } 529 530 static boolean_t 531 smb_dirpath_isvalid(const char *path) 532 { 533 struct { 534 char *name; 535 int len; 536 } *bad, bad_paths[] = { 537 { ".\0", 2 }, 538 { ".\\\0", 3 }, 539 { "..\0", 3 }, 540 { "..\\", 3 } 541 }; 542 543 char *cp; 544 char *p; 545 int i; 546 547 if (*path == '\0') 548 return (B_TRUE); 549 550 cp = smb_kstrdup(path, MAXPATHLEN); 551 p = strcanon(cp, "\\"); 552 p += strspn(p, "\\"); 553 554 for (i = 0; i < sizeof (bad_paths) / sizeof (bad_paths[0]); ++i) { 555 bad = &bad_paths[i]; 556 557 if (strncmp(p, bad->name, bad->len) == 0) { 558 kmem_free(cp, MAXPATHLEN); 559 return (B_FALSE); 560 } 561 } 562 563 kmem_free(cp, MAXPATHLEN); 564 return (B_TRUE); 565 } 566