Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_syscalls.c
Show First 20 Lines • Show All 123 Lines • ▼ Show 20 Lines | if ((at_flags & AT_RESOLVE_BENEATH) != 0) | ||||
res |= RBENEATH; | res |= RBENEATH; | ||||
if ((at_flags & AT_SYMLINK_FOLLOW) != 0) | if ((at_flags & AT_SYMLINK_FOLLOW) != 0) | ||||
res |= FOLLOW; | res |= FOLLOW; | ||||
/* NOFOLLOW is pseudo flag */ | /* NOFOLLOW is pseudo flag */ | ||||
if ((mask & AT_SYMLINK_NOFOLLOW) != 0) { | if ((mask & AT_SYMLINK_NOFOLLOW) != 0) { | ||||
res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : | res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : | ||||
FOLLOW; | FOLLOW; | ||||
} | } | ||||
if ((mask & AT_EMPTY_PATH) != 0 && (at_flags & AT_EMPTY_PATH) != 0) | |||||
res |= EMPTYPATH; | |||||
return (res); | return (res); | ||||
} | } | ||||
int | int | ||||
kern_sync(struct thread *td) | kern_sync(struct thread *td) | ||||
{ | { | ||||
struct mount *mp, *nmp; | struct mount *mp, *nmp; | ||||
int save; | int save; | ||||
▲ Show 20 Lines • Show All 1,351 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_linkat(struct thread *td, struct linkat_args *uap) | sys_linkat(struct thread *td, struct linkat_args *uap) | ||||
{ | { | ||||
int flag; | int flag; | ||||
flag = uap->flag; | flag = uap->flag; | ||||
if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, | return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, | ||||
UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | | UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | | ||||
AT_RESOLVE_BENEATH))); | AT_RESOLVE_BENEATH | AT_EMPTY_PATH))); | ||||
} | } | ||||
int hardlink_check_uid = 0; | int hardlink_check_uid = 0; | ||||
SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, | SYSCTL_INT(_security_bsd, OID_AUTO, hardlink_check_uid, CTLFLAG_RW, | ||||
&hardlink_check_uid, 0, | &hardlink_check_uid, 0, | ||||
"Unprivileged processes cannot create hard links to files owned by other " | "Unprivileged processes cannot create hard links to files owned by other " | ||||
"users"); | "users"); | ||||
static int hardlink_check_gid = 0; | static int hardlink_check_gid = 0; | ||||
Show All 39 Lines | kern_linkat(struct thread *td, int fd1, int fd2, const char *path1, | ||||
do { | do { | ||||
bwillwrite(); | bwillwrite(); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflag, | NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflag, | ||||
path1, fd1, &cap_linkat_source_rights, td); | path1, fd1, &cap_linkat_source_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
error = kern_linkat_vp(td, nd.ni_vp, fd2, path2, segflag); | error = kern_linkat_vp(td, nd.ni_vp, fd2, path2, segflag); | ||||
markj: It looks like the committed version is still doing the priv_check() on the second path. | |||||
} while (error == EAGAIN || error == ERELOOKUP); | } while (error == EAGAIN || error == ERELOOKUP); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, | kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, | ||||
enum uio_seg segflag) | enum uio_seg segflag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
struct mount *mp; | struct mount *mp; | ||||
int error; | int error; | ||||
if (vp->v_type == VDIR) { | if (vp->v_type == VDIR) { | ||||
vrele(vp); | vrele(vp); | ||||
return (EPERM); /* POSIX */ | return (EPERM); /* POSIX */ | ||||
} | } | ||||
NDINIT_ATRIGHTS(&nd, CREATE, | NDINIT_ATRIGHTS(&nd, CREATE, | ||||
LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, | LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, | ||||
&cap_linkat_target_rights, td); | &cap_linkat_target_rights, td); | ||||
if ((error = namei(&nd)) == 0) { | if ((error = namei(&nd)) == 0) { | ||||
if ((nd.ni_resflags & NIRES_EMPTYPATH) != 0) { | |||||
error = priv_check(td, PRIV_VFS_FHOPEN); | |||||
Done Inline ActionsWhy is the check done after resolution? markj: Why is the check done after resolution? | |||||
Done Inline ActionsBecause I do not know if namei() would really use EMPTYPATH in advance, and I do not want to duplicate the logic. kib: Because I do not know if namei() would really use EMPTYPATH in advance, and I do not want to… | |||||
Done Inline ActionsActually I am confused by this. Shouldn't we be handling NIRES_EMPTY path for the first path, not the second? markj: Actually I am confused by this. Shouldn't we be handling NIRES_EMPTY path for the first path… | |||||
if (error != 0) { | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | |||||
if (nd.ni_vp != NULL) { | if (nd.ni_vp != NULL) { | ||||
if (nd.ni_dvp == nd.ni_vp) | |||||
vrele(nd.ni_dvp); | |||||
else | |||||
vput(nd.ni_dvp); | |||||
vrele(nd.ni_vp); | |||||
} else { | |||||
vput(nd.ni_dvp); | |||||
} | |||||
vrele(vp); | |||||
return (error); | |||||
} | |||||
} | |||||
if (nd.ni_vp != NULL) { | |||||
NDFREE(&nd, NDF_ONLY_PNBUF); | NDFREE(&nd, NDF_ONLY_PNBUF); | ||||
if (nd.ni_dvp == nd.ni_vp) | if (nd.ni_dvp == nd.ni_vp) | ||||
vrele(nd.ni_dvp); | vrele(nd.ni_dvp); | ||||
else | else | ||||
vput(nd.ni_dvp); | vput(nd.ni_dvp); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
vrele(vp); | vrele(vp); | ||||
return (EEXIST); | return (EEXIST); | ||||
▲ Show 20 Lines • Show All 792 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
kern_statat(struct thread *td, int flag, int fd, const char *path, | kern_statat(struct thread *td, int flag, int fd, const char *path, | ||||
enum uio_seg pathseg, struct stat *sbp, | enum uio_seg pathseg, struct stat *sbp, | ||||
void (*hook)(struct vnode *vp, struct stat *sbp)) | void (*hook)(struct vnode *vp, struct stat *sbp)) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH | | NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH | | ||||
AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, | AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) | LOCKSHARED | LOCKLEAF | | ||||
pathseg, path, fd, &cap_fstat_rights, td); | AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
error = VOP_STAT(nd.ni_vp, sbp, td->td_ucred, NOCRED, td); | error = VOP_STAT(nd.ni_vp, sbp, td->td_ucred, NOCRED, td); | ||||
if (error == 0) { | if (error == 0) { | ||||
if (__predict_false(hook != NULL)) | if (__predict_false(hook != NULL)) | ||||
hook(nd.ni_vp, sbp); | hook(nd.ni_vp, sbp); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 301 Lines • ▼ Show 20 Lines | struct chflagsat_args { | ||||
u_long flags; | u_long flags; | ||||
int atflag; | int atflag; | ||||
} | } | ||||
#endif | #endif | ||||
int | int | ||||
sys_chflagsat(struct thread *td, struct chflagsat_args *uap) | sys_chflagsat(struct thread *td, struct chflagsat_args *uap) | ||||
{ | { | ||||
if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, | return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, | ||||
uap->flags, uap->atflag)); | uap->flags, uap->atflag)); | ||||
} | } | ||||
/* | /* | ||||
* Same as chflags() but doesn't follow symlinks. | * Same as chflags() but doesn't follow symlinks. | ||||
Show All 16 Lines | |||||
kern_chflagsat(struct thread *td, int fd, const char *path, | kern_chflagsat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, u_long flags, int atflag) | enum uio_seg pathseg, u_long flags, int atflag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
AUDIT_ARG_FFLAGS(flags); | AUDIT_ARG_FFLAGS(flags); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW | | NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW | | ||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, | AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, | ||||
&cap_fchflags_rights, td); | fd, &cap_fchflags_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE_NOTHING(&nd); | NDFREE_NOTHING(&nd); | ||||
error = setfflags(td, nd.ni_vp, flags); | error = setfflags(td, nd.ni_vp, flags); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | struct fchmodat_args { | ||||
mode_t mode; | mode_t mode; | ||||
int flag; | int flag; | ||||
} | } | ||||
#endif | #endif | ||||
int | int | ||||
sys_fchmodat(struct thread *td, struct fchmodat_args *uap) | sys_fchmodat(struct thread *td, struct fchmodat_args *uap) | ||||
{ | { | ||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, | return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, | ||||
uap->mode, uap->flag)); | uap->mode, uap->flag)); | ||||
} | } | ||||
/* | /* | ||||
* Change mode of a file given path name (don't follow links.) | * Change mode of a file given path name (don't follow links.) | ||||
Show All 16 Lines | |||||
kern_fchmodat(struct thread *td, int fd, const char *path, | kern_fchmodat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, mode_t mode, int flag) | enum uio_seg pathseg, mode_t mode, int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
AUDIT_ARG_MODE(mode); | AUDIT_ARG_MODE(mode); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | ||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, | AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, | ||||
&cap_fchmod_rights, td); | fd, &cap_fchmod_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE_NOTHING(&nd); | NDFREE_NOTHING(&nd); | ||||
error = setfmode(td, td->td_ucred, nd.ni_vp, mode); | error = setfmode(td, td->td_ucred, nd.ni_vp, mode); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 77 Lines • ▼ Show 20 Lines | struct fchownat_args { | ||||
gid_t gid; | gid_t gid; | ||||
int flag; | int flag; | ||||
}; | }; | ||||
#endif | #endif | ||||
int | int | ||||
sys_fchownat(struct thread *td, struct fchownat_args *uap) | sys_fchownat(struct thread *td, struct fchownat_args *uap) | ||||
{ | { | ||||
if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, | return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, | ||||
uap->gid, uap->flag)); | uap->gid, uap->flag)); | ||||
} | } | ||||
int | int | ||||
kern_fchownat(struct thread *td, int fd, const char *path, | kern_fchownat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, int uid, int gid, int flag) | enum uio_seg pathseg, int uid, int gid, int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
int error; | int error; | ||||
AUDIT_ARG_OWNER(uid, gid); | AUDIT_ARG_OWNER(uid, gid); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | ||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, | AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, | ||||
&cap_fchown_rights, td); | fd, &cap_fchown_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
NDFREE_NOTHING(&nd); | NDFREE_NOTHING(&nd); | ||||
error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid); | error = setfown(td, td->td_ucred, nd.ni_vp, uid, gid); | ||||
vrele(nd.ni_vp); | vrele(nd.ni_vp); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 334 Lines • ▼ Show 20 Lines | |||||
kern_utimensat(struct thread *td, int fd, const char *path, | kern_utimensat(struct thread *td, int fd, const char *path, | ||||
enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg, | enum uio_seg pathseg, struct timespec *tptr, enum uio_seg tptrseg, | ||||
int flag) | int flag) | ||||
{ | { | ||||
struct nameidata nd; | struct nameidata nd; | ||||
struct timespec ts[2]; | struct timespec ts[2]; | ||||
int error, flags; | int error, flags; | ||||
if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | | ||||
AT_EMPTY_PATH)) != 0) | |||||
return (EINVAL); | return (EINVAL); | ||||
if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) | if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) | ||||
return (error); | return (error); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | | ||||
AT_RESOLVE_BENEATH) | AUDITVNODE1, | AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, | ||||
pathseg, path, fd, &cap_futimes_rights, td); | pathseg, path, fd, &cap_futimes_rights, td); | ||||
if ((error = namei(&nd)) != 0) | if ((error = namei(&nd)) != 0) | ||||
return (error); | return (error); | ||||
/* | /* | ||||
* We are allowed to call namei() regardless of 2xUTIME_OMIT. | * We are allowed to call namei() regardless of 2xUTIME_OMIT. | ||||
* POSIX states: | * POSIX states: | ||||
* "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected." | * "If both tv_nsec fields are UTIME_OMIT... EACCESS may be detected." | ||||
* "Search permission is denied by a component of the path prefix." | * "Search permission is denied by a component of the path prefix." | ||||
▲ Show 20 Lines • Show All 1,574 Lines • Show Last 20 Lines |
It looks like the committed version is still doing the priv_check() on the second path.