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(-) |