Decompose linkat()/renameat() rights to source and target.

To make it easier to understand how Capsicum interacts with linkat() and
renameat(), rename the rights to CAP_{LINK,RENAME}AT_{SOURCE,TARGET}.

This also addresses a shortcoming in Capsicum, where it isn't possible
to disable linking to files stored in a directory. Creating hardlinks
essentially makes it possible to access files with additional rights.

Reviewed by:	rwatson, wblock
Differential Revision:	https://reviews.freebsd.org/D3411
This commit is contained in:
Ed Schouten 2015-08-27 15:16:41 +00:00
parent 34d2e76a64
commit bc1ace0b96
5 changed files with 71 additions and 28 deletions

View File

@ -32,7 +32,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd January 23, 2015 .Dd August 27, 2015
.Dt RIGHTS 4 .Dt RIGHTS 4
.Os .Os
.Sh NAME .Sh NAME
@ -71,7 +71,7 @@ The
family of functions should be used to manage the structure. family of functions should be used to manage the structure.
.Sh RIGHTS .Sh RIGHTS
The following rights may be specified in a rights mask: The following rights may be specified in a rights mask:
.Bl -tag -width CAP_EXTATTR_DELETE .Bl -tag -width CAP_RENAMEAT_SOURCE
.It Dv CAP_ACCEPT .It Dv CAP_ACCEPT
Permit Permit
.Xr accept 2 .Xr accept 2
@ -328,12 +328,28 @@ argument is non-NULL).
.Dv CAP_EVENT .Dv CAP_EVENT
is also required on file descriptors that will be monitored using is also required on file descriptors that will be monitored using
.Xr kevent 2 . .Xr kevent 2 .
.It Dv CAP_LINKAT .It Dv CAP_LINKAT_SOURCE
Permit Permit
.Xr linkat 2 .Xr linkat 2
and on the source directory descriptor.
.Xr renameat 2 This right includes the
on the destination directory descriptor. .Dv CAP_LOOKUP
right.
.Pp
Warning:
.Dv CAP_LINKAT_SOURCE
makes it possible to link files in a directory for which file
descriptors exist that have additional rights.
For example,
a file stored in a directory that does not allow
.Dv CAP_READ
may be linked in another directory that does allow
.Dv CAP_READ ,
thereby granting read access to a file that is otherwise unreadable.
.It Dv CAP_LINKAT_TARGET
Permit
.Xr linkat 2
on the target directory descriptor.
This right includes the This right includes the
.Dv CAP_LOOKUP .Dv CAP_LOOKUP
right. right.
@ -474,10 +490,28 @@ is also required) and related system calls.
.It Dv CAP_RECV .It Dv CAP_RECV
An alias to An alias to
.Dv CAP_READ . .Dv CAP_READ .
.It Dv CAP_RENAMEAT .It Dv CAP_RENAMEAT_SOURCE
Permit Permit
.Xr renameat 2 . .Xr renameat 2
This right is required on the source directory descriptor. on the source directory descriptor.
This right includes the
.Dv CAP_LOOKUP
right.
.Pp
Warning:
.Dv CAP_RENAMEAT_SOURCE
makes it possible to move files to a directory for which file
descriptors exist that have additional rights.
For example,
a file stored in a directory that does not allow
.Dv CAP_READ
may be moved to another directory that does allow
.Dv CAP_READ ,
thereby granting read access to a file that is otherwise unreadable.
.It Dv CAP_RENAMEAT_TARGET
Permit
.Xr renameat 2
on the target directory descriptor.
This right includes the This right includes the
.Dv CAP_LOOKUP .Dv CAP_LOOKUP
right. right.

View File

@ -56,13 +56,13 @@ __FBSDID("$FreeBSD$");
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT) \
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE) \
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FIFO, CAP_MKFIFOAT) \ MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FIFO, CAP_MKFIFOAT) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LOOKUP) \ MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LINKAT_SOURCE) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT) \ MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT_TARGET) \
MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP) \ MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP) \
MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ) \ MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ) \
MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP) \ MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT) \ MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT_SOURCE) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_LINKAT) \ MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_RENAMEAT_TARGET) \
MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT) \
MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE) \
MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES, CAP_FUTIMES) \ MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_TIMES, CAP_FUTIMES) \

View File

@ -1441,7 +1441,8 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2,
again: again:
bwillwrite(); bwillwrite();
NDINIT_AT(&nd, LOOKUP, follow | AUDITVNODE1, segflg, path1, fd1, td); NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, segflg, path1, fd1,
cap_rights_init(&rights, CAP_LINKAT_SOURCE), td);
if ((error = namei(&nd)) != 0) if ((error = namei(&nd)) != 0)
return (error); return (error);
@ -1451,9 +1452,9 @@ again:
vrele(vp); vrele(vp);
return (EPERM); /* POSIX */ return (EPERM); /* POSIX */
} }
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2 | NDINIT_ATRIGHTS(&nd, CREATE,
NOCACHE, segflg, path2, fd2, cap_rights_init(&rights, CAP_LINKAT), LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, path2, fd2,
td); cap_rights_init(&rights, CAP_LINKAT_TARGET), td);
if ((error = namei(&nd)) == 0) { if ((error = namei(&nd)) == 0) {
if (nd.ni_vp != NULL) { if (nd.ni_vp != NULL) {
NDFREE(&nd, NDF_ONLY_PNBUF); NDFREE(&nd, NDF_ONLY_PNBUF);
@ -3461,10 +3462,11 @@ again:
#ifdef MAC #ifdef MAC
NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART | NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART |
AUDITVNODE1, pathseg, old, oldfd, AUDITVNODE1, pathseg, old, oldfd,
cap_rights_init(&rights, CAP_RENAMEAT), td); cap_rights_init(&rights, CAP_RENAMEAT_SOURCE), td);
#else #else
NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1, NDINIT_ATRIGHTS(&fromnd, DELETE, WANTPARENT | SAVESTART | AUDITVNODE1,
pathseg, old, oldfd, cap_rights_init(&rights, CAP_RENAMEAT), td); pathseg, old, oldfd,
cap_rights_init(&rights, CAP_RENAMEAT_SOURCE), td);
#endif #endif
if ((error = namei(&fromnd)) != 0) if ((error = namei(&fromnd)) != 0)
@ -3479,7 +3481,7 @@ again:
fvp = fromnd.ni_vp; fvp = fromnd.ni_vp;
NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE |
SAVESTART | AUDITVNODE2, pathseg, new, newfd, SAVESTART | AUDITVNODE2, pathseg, new, newfd,
cap_rights_init(&rights, CAP_LINKAT), td); cap_rights_init(&rights, CAP_RENAMEAT_TARGET), td);
if (fromnd.ni_vp->v_type == VDIR) if (fromnd.ni_vp->v_type == VDIR)
tond.ni_cnd.cn_flags |= WILLBEDIR; tond.ni_cnd.cn_flags |= WILLBEDIR;
if ((error = namei(&tond)) != 0) { if ((error = namei(&tond)) != 0) {

View File

@ -150,16 +150,16 @@
#define CAP_FUTIMES CAPRIGHT(0, 0x0000000000200000ULL) #define CAP_FUTIMES CAPRIGHT(0, 0x0000000000200000ULL)
/* Allows for futimens(2), futimes(2), futimesat(2) and utimensat(2). */ /* Allows for futimens(2), futimes(2), futimesat(2) and utimensat(2). */
#define CAP_FUTIMESAT (CAP_FUTIMES | CAP_LOOKUP) #define CAP_FUTIMESAT (CAP_FUTIMES | CAP_LOOKUP)
/* Allows for linkat(2) and renameat(2) (destination directory descriptor). */ /* Allows for linkat(2) (target directory descriptor). */
#define CAP_LINKAT (CAP_LOOKUP | 0x0000000000400000ULL) #define CAP_LINKAT_TARGET (CAP_LOOKUP | 0x0000000000400000ULL)
/* Allows for mkdirat(2). */ /* Allows for mkdirat(2). */
#define CAP_MKDIRAT (CAP_LOOKUP | 0x0000000000800000ULL) #define CAP_MKDIRAT (CAP_LOOKUP | 0x0000000000800000ULL)
/* Allows for mkfifoat(2). */ /* Allows for mkfifoat(2). */
#define CAP_MKFIFOAT (CAP_LOOKUP | 0x0000000001000000ULL) #define CAP_MKFIFOAT (CAP_LOOKUP | 0x0000000001000000ULL)
/* Allows for mknodat(2). */ /* Allows for mknodat(2). */
#define CAP_MKNODAT (CAP_LOOKUP | 0x0000000002000000ULL) #define CAP_MKNODAT (CAP_LOOKUP | 0x0000000002000000ULL)
/* Allows for renameat(2). */ /* Allows for renameat(2) (source directory descriptor). */
#define CAP_RENAMEAT (CAP_LOOKUP | 0x0000000004000000ULL) #define CAP_RENAMEAT_SOURCE (CAP_LOOKUP | 0x0000000004000000ULL)
/* Allows for symlinkat(2). */ /* Allows for symlinkat(2). */
#define CAP_SYMLINKAT (CAP_LOOKUP | 0x0000000008000000ULL) #define CAP_SYMLINKAT (CAP_LOOKUP | 0x0000000008000000ULL)
/* /*
@ -197,6 +197,11 @@
/* Allows for connectat(2) on a directory descriptor. */ /* Allows for connectat(2) on a directory descriptor. */
#define CAP_CONNECTAT (CAP_LOOKUP | 0x0000010000000000ULL) #define CAP_CONNECTAT (CAP_LOOKUP | 0x0000010000000000ULL)
/* Allows for linkat(2) (source directory descriptor). */
#define CAP_LINKAT_SOURCE (CAP_LOOKUP | 0x0000020000000000ULL)
/* Allows for renameat(2) (target directory descriptor). */
#define CAP_RENAMEAT_TARGET (CAP_LOOKUP | 0x0000040000000000ULL)
#define CAP_SOCK_CLIENT \ #define CAP_SOCK_CLIENT \
(CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \ (CAP_CONNECT | CAP_GETPEERNAME | CAP_GETSOCKNAME | CAP_GETSOCKOPT | \
CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN) CAP_PEELOFF | CAP_RECV | CAP_SEND | CAP_SETSOCKOPT | CAP_SHUTDOWN)
@ -206,10 +211,10 @@
CAP_SETSOCKOPT | CAP_SHUTDOWN) CAP_SETSOCKOPT | CAP_SHUTDOWN)
/* All used bits for index 0. */ /* All used bits for index 0. */
#define CAP_ALL0 CAPRIGHT(0, 0x000001FFFFFFFFFFULL) #define CAP_ALL0 CAPRIGHT(0, 0x000007FFFFFFFFFFULL)
/* Available bits for index 0. */ /* Available bits for index 0. */
#define CAP_UNUSED0_42 CAPRIGHT(0, 0x0000020000000000ULL) #define CAP_UNUSED0_44 CAPRIGHT(0, 0x0000080000000000ULL)
/* ... */ /* ... */
#define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL) #define CAP_UNUSED0_57 CAPRIGHT(0, 0x0100000000000000ULL)

View File

@ -158,11 +158,13 @@ static struct cap_desc {
{ CAP_FSTAT, "fs" }, { CAP_FSTAT, "fs" },
{ CAP_FSTATFS, "sf" }, { CAP_FSTATFS, "sf" },
{ CAP_FUTIMES, "fu" }, { CAP_FUTIMES, "fu" },
{ CAP_LINKAT, "li" }, { CAP_LINKAT_SOURCE, "ls" },
{ CAP_LINKAT_TARGET, "lt" },
{ CAP_MKDIRAT, "md" }, { CAP_MKDIRAT, "md" },
{ CAP_MKFIFOAT, "mf" }, { CAP_MKFIFOAT, "mf" },
{ CAP_MKNODAT, "mn" }, { CAP_MKNODAT, "mn" },
{ CAP_RENAMEAT, "rn" }, { CAP_RENAMEAT_SOURCE, "rs" },
{ CAP_RENAMEAT_TARGET, "rt" },
{ CAP_SYMLINKAT, "sl" }, { CAP_SYMLINKAT, "sl" },
{ CAP_UNLINKAT, "un" }, { CAP_UNLINKAT, "un" },