diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/namei.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/fs/namei.c b/fs/namei.c index afa087649ddb..3861d85f8488 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
@@ -650,6 +650,119 @@ static inline void put_link(struct nameidata *nd, struct path *link, void *cooki | |||
650 | path_put(link); | 650 | path_put(link); |
651 | } | 651 | } |
652 | 652 | ||
653 | int sysctl_protected_symlinks __read_mostly = 1; | ||
654 | int sysctl_protected_hardlinks __read_mostly = 1; | ||
655 | |||
656 | /** | ||
657 | * may_follow_link - Check symlink following for unsafe situations | ||
658 | * @link: The path of the symlink | ||
659 | * | ||
660 | * In the case of the sysctl_protected_symlinks sysctl being enabled, | ||
661 | * CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is | ||
662 | * in a sticky world-writable directory. This is to protect privileged | ||
663 | * processes from failing races against path names that may change out | ||
664 | * from under them by way of other users creating malicious symlinks. | ||
665 | * It will permit symlinks to be followed only when outside a sticky | ||
666 | * world-writable directory, or when the uid of the symlink and follower | ||
667 | * match, or when the directory owner matches the symlink's owner. | ||
668 | * | ||
669 | * Returns 0 if following the symlink is allowed, -ve on error. | ||
670 | */ | ||
671 | static inline int may_follow_link(struct path *link, struct nameidata *nd) | ||
672 | { | ||
673 | const struct inode *inode; | ||
674 | const struct inode *parent; | ||
675 | |||
676 | if (!sysctl_protected_symlinks) | ||
677 | return 0; | ||
678 | |||
679 | /* Allowed if owner and follower match. */ | ||
680 | inode = link->dentry->d_inode; | ||
681 | if (current_cred()->fsuid == inode->i_uid) | ||
682 | return 0; | ||
683 | |||
684 | /* Allowed if parent directory not sticky and world-writable. */ | ||
685 | parent = nd->path.dentry->d_inode; | ||
686 | if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH)) | ||
687 | return 0; | ||
688 | |||
689 | /* Allowed if parent directory and link owner match. */ | ||
690 | if (parent->i_uid == inode->i_uid) | ||
691 | return 0; | ||
692 | |||
693 | path_put_conditional(link, nd); | ||
694 | path_put(&nd->path); | ||
695 | return -EACCES; | ||
696 | } | ||
697 | |||
698 | /** | ||
699 | * safe_hardlink_source - Check for safe hardlink conditions | ||
700 | * @inode: the source inode to hardlink from | ||
701 | * | ||
702 | * Return false if at least one of the following conditions: | ||
703 | * - inode is not a regular file | ||
704 | * - inode is setuid | ||
705 | * - inode is setgid and group-exec | ||
706 | * - access failure for read and write | ||
707 | * | ||
708 | * Otherwise returns true. | ||
709 | */ | ||
710 | static bool safe_hardlink_source(struct inode *inode) | ||
711 | { | ||
712 | umode_t mode = inode->i_mode; | ||
713 | |||
714 | /* Special files should not get pinned to the filesystem. */ | ||
715 | if (!S_ISREG(mode)) | ||
716 | return false; | ||
717 | |||
718 | /* Setuid files should not get pinned to the filesystem. */ | ||
719 | if (mode & S_ISUID) | ||
720 | return false; | ||
721 | |||
722 | /* Executable setgid files should not get pinned to the filesystem. */ | ||
723 | if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) | ||
724 | return false; | ||
725 | |||
726 | /* Hardlinking to unreadable or unwritable sources is dangerous. */ | ||
727 | if (inode_permission(inode, MAY_READ | MAY_WRITE)) | ||
728 | return false; | ||
729 | |||
730 | return true; | ||
731 | } | ||
732 | |||
733 | /** | ||
734 | * may_linkat - Check permissions for creating a hardlink | ||
735 | * @link: the source to hardlink from | ||
736 | * | ||
737 | * Block hardlink when all of: | ||
738 | * - sysctl_protected_hardlinks enabled | ||
739 | * - fsuid does not match inode | ||
740 | * - hardlink source is unsafe (see safe_hardlink_source() above) | ||
741 | * - not CAP_FOWNER | ||
742 | * | ||
743 | * Returns 0 if successful, -ve on error. | ||
744 | */ | ||
745 | static int may_linkat(struct path *link) | ||
746 | { | ||
747 | const struct cred *cred; | ||
748 | struct inode *inode; | ||
749 | |||
750 | if (!sysctl_protected_hardlinks) | ||
751 | return 0; | ||
752 | |||
753 | cred = current_cred(); | ||
754 | inode = link->dentry->d_inode; | ||
755 | |||
756 | /* Source inode owner (or CAP_FOWNER) can hardlink all they like, | ||
757 | * otherwise, it must be a safe source. | ||
758 | */ | ||
759 | if (cred->fsuid == inode->i_uid || safe_hardlink_source(inode) || | ||
760 | capable(CAP_FOWNER)) | ||
761 | return 0; | ||
762 | |||
763 | return -EPERM; | ||
764 | } | ||
765 | |||
653 | static __always_inline int | 766 | static __always_inline int |
654 | follow_link(struct path *link, struct nameidata *nd, void **p) | 767 | follow_link(struct path *link, struct nameidata *nd, void **p) |
655 | { | 768 | { |
@@ -1818,6 +1931,9 @@ static int path_lookupat(int dfd, const char *name, | |||
1818 | while (err > 0) { | 1931 | while (err > 0) { |
1819 | void *cookie; | 1932 | void *cookie; |
1820 | struct path link = path; | 1933 | struct path link = path; |
1934 | err = may_follow_link(&link, nd); | ||
1935 | if (unlikely(err)) | ||
1936 | break; | ||
1821 | nd->flags |= LOOKUP_PARENT; | 1937 | nd->flags |= LOOKUP_PARENT; |
1822 | err = follow_link(&link, nd, &cookie); | 1938 | err = follow_link(&link, nd, &cookie); |
1823 | if (err) | 1939 | if (err) |
@@ -2778,6 +2894,9 @@ static struct file *path_openat(int dfd, const char *pathname, | |||
2778 | error = -ELOOP; | 2894 | error = -ELOOP; |
2779 | break; | 2895 | break; |
2780 | } | 2896 | } |
2897 | error = may_follow_link(&link, nd); | ||
2898 | if (unlikely(error)) | ||
2899 | break; | ||
2781 | nd->flags |= LOOKUP_PARENT; | 2900 | nd->flags |= LOOKUP_PARENT; |
2782 | nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); | 2901 | nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL); |
2783 | error = follow_link(&link, nd, &cookie); | 2902 | error = follow_link(&link, nd, &cookie); |
@@ -3421,6 +3540,9 @@ SYSCALL_DEFINE5(linkat, int, olddfd, const char __user *, oldname, | |||
3421 | error = -EXDEV; | 3540 | error = -EXDEV; |
3422 | if (old_path.mnt != new_path.mnt) | 3541 | if (old_path.mnt != new_path.mnt) |
3423 | goto out_dput; | 3542 | goto out_dput; |
3543 | error = may_linkat(&old_path); | ||
3544 | if (unlikely(error)) | ||
3545 | goto out_dput; | ||
3424 | error = security_path_link(old_path.dentry, &new_path, new_dentry); | 3546 | error = security_path_link(old_path.dentry, &new_path, new_dentry); |
3425 | if (error) | 3547 | if (error) |
3426 | goto out_dput; | 3548 | goto out_dput; |