|
Lines 369-374
static int link_errno_convert(int err)
Link Here
|
| 369 |
return err; |
369 |
return err; |
| 370 |
} |
370 |
} |
| 371 |
|
371 |
|
|
|
372 |
static int non_widelink_open(struct connection_struct *conn, |
| 373 |
const char *conn_rootdir, |
| 374 |
files_struct *fsp, |
| 375 |
struct smb_filename *smb_fname, |
| 376 |
int flags, |
| 377 |
mode_t mode, |
| 378 |
unsigned int link_depth); |
| 379 |
|
| 380 |
/**************************************************************************** |
| 381 |
Follow a symlink in userspace. |
| 382 |
****************************************************************************/ |
| 383 |
|
| 384 |
static int process_symlink_open(struct connection_struct *conn, |
| 385 |
const char *conn_rootdir, |
| 386 |
files_struct *fsp, |
| 387 |
struct smb_filename *smb_fname, |
| 388 |
int flags, |
| 389 |
mode_t mode, |
| 390 |
unsigned int link_depth) |
| 391 |
{ |
| 392 |
int fd = -1; |
| 393 |
char *link_target = NULL; |
| 394 |
int link_len = -1; |
| 395 |
char *oldwd = NULL; |
| 396 |
size_t rootdir_len = 0; |
| 397 |
char *resolved_name = NULL; |
| 398 |
bool matched = false; |
| 399 |
int saved_errno = 0; |
| 400 |
|
| 401 |
/* |
| 402 |
* Ensure we don't get stuck in a symlink loop. |
| 403 |
*/ |
| 404 |
link_depth++; |
| 405 |
if (link_depth >= 20) { |
| 406 |
errno = ELOOP; |
| 407 |
goto out; |
| 408 |
} |
| 409 |
|
| 410 |
/* Allocate space for the link target. */ |
| 411 |
link_target = talloc_array(talloc_tos(), char, PATH_MAX); |
| 412 |
if (link_target == NULL) { |
| 413 |
errno = ENOMEM; |
| 414 |
goto out; |
| 415 |
} |
| 416 |
|
| 417 |
/* Read the link target. */ |
| 418 |
link_len = SMB_VFS_READLINK(conn, |
| 419 |
smb_fname->base_name, |
| 420 |
link_target, |
| 421 |
PATH_MAX - 1); |
| 422 |
if (link_len == -1) { |
| 423 |
goto out; |
| 424 |
} |
| 425 |
|
| 426 |
/* Ensure it's at least null terminated. */ |
| 427 |
link_target[link_len] = '\0'; |
| 428 |
|
| 429 |
/* Convert to an absolute path. */ |
| 430 |
resolved_name = SMB_VFS_REALPATH(conn, link_target); |
| 431 |
if (resolved_name == NULL) { |
| 432 |
goto out; |
| 433 |
} |
| 434 |
|
| 435 |
/* |
| 436 |
* We know conn_rootdir starts with '/' and |
| 437 |
* does not end in '/'. FIXME ! Should we |
| 438 |
* smb_assert this ? |
| 439 |
*/ |
| 440 |
rootdir_len = strlen(conn_rootdir); |
| 441 |
|
| 442 |
matched = (strncmp(conn_rootdir, resolved_name, rootdir_len) == 0); |
| 443 |
if (!matched) { |
| 444 |
errno = EACCES; |
| 445 |
goto out; |
| 446 |
} |
| 447 |
|
| 448 |
/* |
| 449 |
* Turn into a path relative to the share root. |
| 450 |
*/ |
| 451 |
if (resolved_name[rootdir_len] == '\0') { |
| 452 |
/* Link to the root of the share. */ |
| 453 |
smb_fname->base_name = talloc_strdup(talloc_tos(), "."); |
| 454 |
if (smb_fname->base_name == NULL) { |
| 455 |
errno = ENOMEM; |
| 456 |
goto out; |
| 457 |
} |
| 458 |
} else if (resolved_name[rootdir_len] == '/') { |
| 459 |
smb_fname->base_name = &resolved_name[rootdir_len+1]; |
| 460 |
} else { |
| 461 |
errno = EACCES; |
| 462 |
goto out; |
| 463 |
} |
| 464 |
|
| 465 |
oldwd = vfs_GetWd(talloc_tos(), conn); |
| 466 |
if (oldwd == NULL) { |
| 467 |
goto out; |
| 468 |
} |
| 469 |
|
| 470 |
/* Ensure we operate from the root of the share. */ |
| 471 |
if (vfs_ChDir(conn, conn_rootdir) == -1) { |
| 472 |
goto out; |
| 473 |
} |
| 474 |
|
| 475 |
/* And do it all again.. */ |
| 476 |
fd = non_widelink_open(conn, |
| 477 |
conn_rootdir, |
| 478 |
fsp, |
| 479 |
smb_fname, |
| 480 |
flags, |
| 481 |
mode, |
| 482 |
link_depth); |
| 483 |
if (fd == -1) { |
| 484 |
saved_errno = errno; |
| 485 |
} |
| 486 |
|
| 487 |
out: |
| 488 |
|
| 489 |
SAFE_FREE(resolved_name); |
| 490 |
TALLOC_FREE(link_target); |
| 491 |
if (oldwd != NULL) { |
| 492 |
int ret = vfs_ChDir(conn, oldwd); |
| 493 |
if (ret == -1) { |
| 494 |
smb_panic("unable to get back to old directory\n"); |
| 495 |
} |
| 496 |
TALLOC_FREE(oldwd); |
| 497 |
} |
| 498 |
if (saved_errno != 0) { |
| 499 |
errno = saved_errno; |
| 500 |
} |
| 501 |
return fd; |
| 502 |
} |
| 503 |
|
| 504 |
/**************************************************************************** |
| 505 |
Non-widelink open. |
| 506 |
****************************************************************************/ |
| 507 |
|
| 508 |
static int non_widelink_open(struct connection_struct *conn, |
| 509 |
const char *conn_rootdir, |
| 510 |
files_struct *fsp, |
| 511 |
struct smb_filename *smb_fname, |
| 512 |
int flags, |
| 513 |
mode_t mode, |
| 514 |
unsigned int link_depth) |
| 515 |
{ |
| 516 |
NTSTATUS status; |
| 517 |
int fd = -1; |
| 518 |
struct smb_filename *smb_fname_rel = NULL; |
| 519 |
int saved_errno = 0; |
| 520 |
char *oldwd = NULL; |
| 521 |
char *parent_dir = NULL; |
| 522 |
const char *final_component = NULL; |
| 523 |
|
| 524 |
if (!parent_dirname(talloc_tos(), |
| 525 |
smb_fname->base_name, |
| 526 |
&parent_dir, |
| 527 |
&final_component)) { |
| 528 |
goto out; |
| 529 |
} |
| 530 |
|
| 531 |
oldwd = vfs_GetWd(talloc_tos(), conn); |
| 532 |
if (oldwd == NULL) { |
| 533 |
goto out; |
| 534 |
} |
| 535 |
|
| 536 |
/* Pin parent directory in place. */ |
| 537 |
if (vfs_ChDir(conn, parent_dir) == -1) { |
| 538 |
goto out; |
| 539 |
} |
| 540 |
|
| 541 |
/* Ensure the relative path is below the share. */ |
| 542 |
status = check_reduced_name(conn, final_component); |
| 543 |
if (!NT_STATUS_IS_OK(status)) { |
| 544 |
saved_errno = map_errno_from_nt_status(status); |
| 545 |
goto out; |
| 546 |
} |
| 547 |
|
| 548 |
smb_fname_rel = synthetic_smb_fname(talloc_tos(), |
| 549 |
final_component, |
| 550 |
smb_fname->stream_name, |
| 551 |
&smb_fname->st); |
| 552 |
|
| 553 |
flags |= O_NOFOLLOW; |
| 554 |
|
| 555 |
{ |
| 556 |
struct smb_filename *tmp_name = fsp->fsp_name; |
| 557 |
fsp->fsp_name = smb_fname_rel; |
| 558 |
fd = SMB_VFS_OPEN(conn, smb_fname_rel, fsp, flags, mode); |
| 559 |
fsp->fsp_name = tmp_name; |
| 560 |
} |
| 561 |
|
| 562 |
if (fd == -1) { |
| 563 |
saved_errno = link_errno_convert(errno); |
| 564 |
if (saved_errno == ELOOP) { |
| 565 |
if (fsp->posix_flags & FSP_POSIX_FLAGS_OPEN) { |
| 566 |
/* Never follow symlinks on posix open. */ |
| 567 |
goto out; |
| 568 |
} |
| 569 |
if (!lp_follow_symlinks(SNUM(conn))) { |
| 570 |
/* Explicitly no symlinks. */ |
| 571 |
goto out; |
| 572 |
} |
| 573 |
/* |
| 574 |
* We have a symlink. Follow in userspace |
| 575 |
* to ensure it's under the share definition. |
| 576 |
*/ |
| 577 |
fd = process_symlink_open(conn, |
| 578 |
conn_rootdir, |
| 579 |
fsp, |
| 580 |
smb_fname_rel, |
| 581 |
flags, |
| 582 |
mode, |
| 583 |
link_depth); |
| 584 |
if (fd == -1) { |
| 585 |
saved_errno = |
| 586 |
link_errno_convert(errno); |
| 587 |
} |
| 588 |
} |
| 589 |
} |
| 590 |
|
| 591 |
out: |
| 592 |
|
| 593 |
TALLOC_FREE(parent_dir); |
| 594 |
TALLOC_FREE(smb_fname_rel); |
| 595 |
|
| 596 |
if (oldwd != NULL) { |
| 597 |
int ret = vfs_ChDir(conn, oldwd); |
| 598 |
if (ret == -1) { |
| 599 |
smb_panic("unable to get back to old directory\n"); |
| 600 |
} |
| 601 |
TALLOC_FREE(oldwd); |
| 602 |
} |
| 603 |
if (saved_errno != 0) { |
| 604 |
errno = saved_errno; |
| 605 |
} |
| 606 |
return fd; |
| 607 |
} |
| 608 |
|
| 372 |
/**************************************************************************** |
609 |
/**************************************************************************** |
| 373 |
fd support routines - attempt to do a dos_open. |
610 |
fd support routines - attempt to do a dos_open. |
| 374 |
****************************************************************************/ |
611 |
****************************************************************************/ |
| 375 |
- |
|
|
| 376 |
-- |
| 377 |
source3/smbd/open.c | 23 ++++++++++++++++++++++- |
612 |
source3/smbd/open.c | 23 ++++++++++++++++++++++- |
| 378 |
1 file changed, 22 insertions(+), 1 deletion(-) |
613 |
1 file changed, 22 insertions(+), 1 deletion(-) |