Fix renameat(2) for CAPABILITIES kernels.

When renameat(2) is used with:
- absolute path for to;
- tofd not set to AT_FDCWD;
- the target exists
kern_renameat() requires CAP_UNLINK capability on tofd, but
corresponding namei ni_filecap is not initialized at all because the
lookup is absolute.  As result, the check was done against empty filecap
and syscall fails erronously.

Fix it by creating a return flags namei member and reporting if the
lookup was absolute, then do not touch to.ni_filecaps at all.

PR:	222258
Reviewed by:	jilles, ngie
Sponsored by:	The FreeBSD Foundation
MFC after:	1 week
X-MFC-note:	KBI breakage
Differential revision:	https://reviews.freebsd.org/D19096
This commit is contained in:
Konstantin Belousov 2019-02-08 04:18:17 +00:00
parent 6f26dd50c3
commit 7cdb0b9d82
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=343891
3 changed files with 13 additions and 2 deletions

View File

@ -410,6 +410,7 @@ namei(struct nameidata *ndp)
dp = NULL; dp = NULL;
cnp->cn_nameptr = cnp->cn_pnbuf; cnp->cn_nameptr = cnp->cn_pnbuf;
if (cnp->cn_pnbuf[0] == '/') { if (cnp->cn_pnbuf[0] == '/') {
ndp->ni_resflags |= NIRES_ABS;
error = namei_handle_root(ndp, &dp); error = namei_handle_root(ndp, &dp);
} else { } else {
if (ndp->ni_startdir != NULL) { if (ndp->ni_startdir != NULL) {
@ -1302,6 +1303,7 @@ NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, enum uio_seg segflg,
ndp->ni_dirp = namep; ndp->ni_dirp = namep;
ndp->ni_dirfd = dirfd; ndp->ni_dirfd = dirfd;
ndp->ni_startdir = startdir; ndp->ni_startdir = startdir;
ndp->ni_resflags = 0;
filecaps_init(&ndp->ni_filecaps); filecaps_init(&ndp->ni_filecaps);
ndp->ni_cnd.cn_thread = td; ndp->ni_cnd.cn_thread = td;
if (rightsp != NULL) if (rightsp != NULL)

View File

@ -3544,10 +3544,10 @@ kern_renameat(struct thread *td, int oldfd, const char *old, int newfd,
goto out; goto out;
} }
#ifdef CAPABILITIES #ifdef CAPABILITIES
if (newfd != AT_FDCWD) { if (newfd != AT_FDCWD && (tond.ni_resflags & NIRES_ABS) == 0) {
/* /*
* If the target already exists we require CAP_UNLINKAT * If the target already exists we require CAP_UNLINKAT
* from 'newfd'. * from 'newfd', when newfd was used for the lookup.
*/ */
error = cap_check(&tond.ni_filecaps.fc_rights, error = cap_check(&tond.ni_filecaps.fc_rights,
&cap_unlinkat_rights); &cap_unlinkat_rights);

View File

@ -87,6 +87,10 @@ struct nameidata {
*/ */
struct vnode *ni_vp; /* vnode of result */ struct vnode *ni_vp; /* vnode of result */
struct vnode *ni_dvp; /* vnode of intermediate directory */ struct vnode *ni_dvp; /* vnode of intermediate directory */
/*
* Results: flags returned from namei
*/
u_int ni_resflags;
/* /*
* Shared between namei and lookup/commit routines. * Shared between namei and lookup/commit routines.
*/ */
@ -159,6 +163,11 @@ struct nameidata {
#define NOCAPCHECK 0x20000000 /* do not perform capability checks */ #define NOCAPCHECK 0x20000000 /* do not perform capability checks */
#define PARAMASK 0x3ffffe00 /* mask of parameter descriptors */ #define PARAMASK 0x3ffffe00 /* mask of parameter descriptors */
/*
* Namei results flags
*/
#define NIRES_ABS 0x00000001 /* Path was absolute */
/* /*
* Flags in ni_lcf, valid for the duration of the namei call. * Flags in ni_lcf, valid for the duration of the namei call.
*/ */