Add O_RESOLVE_BENEATH and AT_RESOLVE_BENEATH to mimic Linux' RESOLVE_BENEATH.
It is like O_BENEATH, but disables to walk out of the subtree rooted in the starting directory. O_BENEATH does not care if path walks out if it returned. Requested by: Dan Gohman <dev@sunfishcode.online> PR: 248335 Reviewed by: markj Tested by: pho Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D25886
This commit is contained in:
parent
6a9c72d901
commit
1317da4349
@ -428,6 +428,16 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp)
|
||||
if (error == 0)
|
||||
ndp->ni_lcf |= NI_LCF_LATCH;
|
||||
}
|
||||
if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) {
|
||||
if (cnp->cn_pnbuf[0] == '/' ||
|
||||
(ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) {
|
||||
error = EINVAL;
|
||||
} else if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0) {
|
||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE |
|
||||
NI_LCF_CAP_DOTDOT;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we are auditing the kernel pathname, save the user pathname.
|
||||
*/
|
||||
|
@ -124,6 +124,8 @@ at2cnpflags(u_int at_flags, u_int mask)
|
||||
at_flags &= mask;
|
||||
if ((at_flags & AT_BENEATH) != 0)
|
||||
res |= BENEATH;
|
||||
if ((at_flags & AT_RESOLVE_BENEATH) != 0)
|
||||
res |= RBENEATH;
|
||||
if ((at_flags & AT_SYMLINK_FOLLOW) != 0)
|
||||
res |= FOLLOW;
|
||||
/* NOFOLLOW is pseudo flag */
|
||||
@ -1503,11 +1505,13 @@ sys_linkat(struct thread *td, struct linkat_args *uap)
|
||||
int flag;
|
||||
|
||||
flag = uap->flag;
|
||||
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2,
|
||||
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | AT_BENEATH)));
|
||||
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)));
|
||||
}
|
||||
|
||||
int hardlink_check_uid = 0;
|
||||
@ -1872,7 +1876,7 @@ kern_funlinkat(struct thread *td, int dfd, const char *path, int fd,
|
||||
restart:
|
||||
bwillwrite();
|
||||
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
|
||||
at2cnpflags(flag, AT_BENEATH),
|
||||
at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
|
||||
pathseg, path, dfd, &cap_unlinkat_rights, td);
|
||||
if ((error = namei(&nd)) != 0) {
|
||||
if (error == EINVAL)
|
||||
@ -2075,7 +2079,7 @@ kern_accessat(struct thread *td, int fd, const char *path,
|
||||
struct nameidata nd;
|
||||
int error;
|
||||
|
||||
if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0)
|
||||
if ((flag & ~(AT_EACCESS | AT_BENEATH | AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0)
|
||||
return (EINVAL);
|
||||
@ -2096,7 +2100,7 @@ kern_accessat(struct thread *td, int fd, const char *path,
|
||||
usecred = cred;
|
||||
AUDIT_ARG_VALUE(amode);
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF |
|
||||
AUDITVNODE1 | at2cnpflags(flag, AT_BENEATH),
|
||||
AUDITVNODE1 | at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
|
||||
pathseg, path, fd, &cap_fstat_rights, td);
|
||||
if ((error = namei(&nd)) != 0)
|
||||
goto out;
|
||||
@ -2387,11 +2391,12 @@ kern_statat(struct thread *td, int flag, int fd, const char *path,
|
||||
struct nameidata nd;
|
||||
int error;
|
||||
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_BENEATH |
|
||||
AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF |
|
||||
AT_RESOLVE_BENEATH | AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF |
|
||||
AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td);
|
||||
|
||||
if ((error = namei(&nd)) != 0)
|
||||
@ -2710,7 +2715,8 @@ int
|
||||
sys_chflagsat(struct thread *td, struct chflagsat_args *uap)
|
||||
{
|
||||
|
||||
if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE,
|
||||
@ -2743,7 +2749,7 @@ kern_chflagsat(struct thread *td, int fd, const char *path,
|
||||
|
||||
AUDIT_ARG_FFLAGS(flags);
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW |
|
||||
AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
&cap_fchflags_rights, td);
|
||||
if ((error = namei(&nd)) != 0)
|
||||
return (error);
|
||||
@ -2838,7 +2844,8 @@ int
|
||||
sys_fchmodat(struct thread *td, struct fchmodat_args *uap)
|
||||
{
|
||||
|
||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE,
|
||||
@ -2871,7 +2878,7 @@ kern_fchmodat(struct thread *td, int fd, const char *path,
|
||||
|
||||
AUDIT_ARG_MODE(mode);
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||
AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
&cap_fchmod_rights, td);
|
||||
if ((error = namei(&nd)) != 0)
|
||||
return (error);
|
||||
@ -2966,7 +2973,8 @@ int
|
||||
sys_fchownat(struct thread *td, struct fchownat_args *uap)
|
||||
{
|
||||
|
||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid,
|
||||
@ -2982,7 +2990,7 @@ kern_fchownat(struct thread *td, int fd, const char *path,
|
||||
|
||||
AUDIT_ARG_OWNER(uid, gid);
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||
AT_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd,
|
||||
&cap_fchown_rights, td);
|
||||
|
||||
if ((error = namei(&nd)) != 0)
|
||||
@ -3334,13 +3342,14 @@ kern_utimensat(struct thread *td, int fd, const char *path,
|
||||
struct timespec ts[2];
|
||||
int error, flags;
|
||||
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
|
||||
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0)
|
||||
return (error);
|
||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW |
|
||||
AT_BENEATH) | AUDITVNODE1,
|
||||
AT_BENEATH | AT_RESOLVE_BENEATH) | AUDITVNODE1,
|
||||
pathseg, path, fd, &cap_futimes_rights, td);
|
||||
if ((error = namei(&nd)) != 0)
|
||||
return (error);
|
||||
@ -3835,7 +3844,7 @@ kern_frmdirat(struct thread *td, int dfd, const char *path, int fd,
|
||||
restart:
|
||||
bwillwrite();
|
||||
NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 |
|
||||
at2cnpflags(flag, AT_BENEATH),
|
||||
at2cnpflags(flag, AT_BENEATH | AT_RESOLVE_BENEATH),
|
||||
pathseg, path, dfd, &cap_unlinkat_rights, td);
|
||||
if ((error = namei(&nd)) != 0)
|
||||
goto fdout;
|
||||
@ -4320,7 +4329,8 @@ int
|
||||
sys_getfhat(struct thread *td, struct getfhat_args *uap)
|
||||
{
|
||||
|
||||
if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0)
|
||||
if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH |
|
||||
AT_RESOLVE_BENEATH)) != 0)
|
||||
return (EINVAL);
|
||||
return (kern_getfhat(td, uap->flags, uap->fd, uap->path, UIO_USERSPACE,
|
||||
uap->fhp));
|
||||
@ -4339,7 +4349,7 @@ kern_getfhat(struct thread *td, int flags, int fd, const char *path,
|
||||
if (error != 0)
|
||||
return (error);
|
||||
NDINIT_AT(&nd, LOOKUP, at2cnpflags(flags, AT_SYMLINK_NOFOLLOW |
|
||||
AT_BENEATH) | LOCKLEAF | AUDITVNODE1,
|
||||
AT_BENEATH | AT_RESOLVE_BENEATH) | LOCKLEAF | AUDITVNODE1,
|
||||
pathseg, path, fd, td);
|
||||
error = namei(&nd);
|
||||
if (error != 0)
|
||||
|
@ -200,6 +200,8 @@ open2nameif(int fmode, u_int vn_open_flags)
|
||||
res = ISOPEN | LOCKLEAF;
|
||||
if ((fmode & O_BENEATH) != 0)
|
||||
res |= BENEATH;
|
||||
if ((fmode & O_RESOLVE_BENEATH) != 0)
|
||||
res |= RBENEATH;
|
||||
if ((vn_open_flags & VN_OPEN_NOAUDIT) == 0)
|
||||
res |= AUDITVNODE1;
|
||||
if ((vn_open_flags & VN_OPEN_NOCAPCHECK) != 0)
|
||||
|
@ -136,6 +136,9 @@ typedef __pid_t pid_t;
|
||||
#if __BSD_VISIBLE
|
||||
#define O_VERIFY 0x00200000 /* open only after verification */
|
||||
#define O_BENEATH 0x00400000 /* Fail if not under cwd */
|
||||
#define O_RESOLVE_BENEATH 0x00800000 /* As O_BENEATH, but do not allow
|
||||
resolve to walk out of cwd even to
|
||||
return back */
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -215,6 +218,9 @@ typedef __pid_t pid_t;
|
||||
#define AT_SYMLINK_FOLLOW 0x0400 /* Follow symbolic link */
|
||||
#define AT_REMOVEDIR 0x0800 /* Remove directory instead of file */
|
||||
#define AT_BENEATH 0x1000 /* Fail if not under dirfd */
|
||||
#define AT_RESOLVE_BENEATH 0x2000 /* As AT_BENEATH, but do not allow
|
||||
resolve to walk out of dirfd even
|
||||
to return back */
|
||||
#endif
|
||||
|
||||
/*
|
||||
|
@ -133,7 +133,8 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status,
|
||||
#define BENEATH 0x0080 /* No escape from the start dir */
|
||||
#define LOCKSHARED 0x0100 /* Shared lock leaf */
|
||||
#define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */
|
||||
#define MODMASK 0x01fc /* mask of operational modifiers */
|
||||
#define RBENEATH 0x100000000ULL /* No escape, even tmp, from start dir */
|
||||
#define MODMASK 0xf000001fcULL /* mask of operational modifiers */
|
||||
/*
|
||||
* Namei parameter descriptors.
|
||||
*
|
||||
|
Loading…
x
Reference in New Issue
Block a user