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$
.\"
.Dd January 23, 2015
.Dd August 27, 2015
.Dt RIGHTS 4
.Os
.Sh NAME
@ -71,7 +71,7 @@ The
family of functions should be used to manage the structure.
.Sh RIGHTS
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
Permit
.Xr accept 2
@ -328,12 +328,28 @@ argument is non-NULL).
.Dv CAP_EVENT
is also required on file descriptors that will be monitored using
.Xr kevent 2 .
.It Dv CAP_LINKAT
.It Dv CAP_LINKAT_SOURCE
Permit
.Xr linkat 2
and
.Xr renameat 2
on the destination directory descriptor.
on the source directory descriptor.
This right includes the
.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
.Dv CAP_LOOKUP
right.
@ -474,10 +490,28 @@ is also required) and related system calls.
.It Dv CAP_RECV
An alias to
.Dv CAP_READ .
.It Dv CAP_RENAMEAT
.It Dv CAP_RENAMEAT_SOURCE
Permit
.Xr renameat 2 .
This right is required on the source directory descriptor.
.Xr renameat 2
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
.Dv CAP_LOOKUP
right.

View File

@ -56,13 +56,13 @@ __FBSDID("$FreeBSD$");
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_DIRECTORY, CAP_MKDIRAT) \
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FILE, CAP_CREATE) \
MAPPING(CLOUDABI_RIGHT_FILE_CREATE_FIFO, CAP_MKFIFOAT) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LOOKUP) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_SOURCE, CAP_LINKAT_SOURCE) \
MAPPING(CLOUDABI_RIGHT_FILE_LINK_TARGET, CAP_LINKAT_TARGET) \
MAPPING(CLOUDABI_RIGHT_FILE_OPEN, CAP_LOOKUP) \
MAPPING(CLOUDABI_RIGHT_FILE_READDIR, CAP_READ) \
MAPPING(CLOUDABI_RIGHT_FILE_READLINK, CAP_LOOKUP) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_LINKAT) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_SOURCE, CAP_RENAMEAT_SOURCE) \
MAPPING(CLOUDABI_RIGHT_FILE_RENAME_TARGET, CAP_RENAMEAT_TARGET) \
MAPPING(CLOUDABI_RIGHT_FILE_STAT_FGET, CAP_FSTAT) \
MAPPING(CLOUDABI_RIGHT_FILE_STAT_FPUT_SIZE, CAP_FTRUNCATE) \
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:
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)
return (error);
@ -1451,9 +1452,9 @@ kern_linkat(struct thread *td, int fd1, int fd2, char *path1, char *path2,
vrele(vp);
return (EPERM); /* POSIX */
}
NDINIT_ATRIGHTS(&nd, CREATE, LOCKPARENT | SAVENAME | AUDITVNODE2 |
NOCACHE, segflg, path2, fd2, cap_rights_init(&rights, CAP_LINKAT),
td);
NDINIT_ATRIGHTS(&nd, CREATE,
LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflg, path2, fd2,
cap_rights_init(&rights, CAP_LINKAT_TARGET), td);
if ((error = namei(&nd)) == 0) {
if (nd.ni_vp != NULL) {
NDFREE(&nd, NDF_ONLY_PNBUF);
@ -3461,10 +3462,11 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
#ifdef MAC
NDINIT_ATRIGHTS(&fromnd, DELETE, LOCKPARENT | LOCKLEAF | SAVESTART |
AUDITVNODE1, pathseg, old, oldfd,
cap_rights_init(&rights, CAP_RENAMEAT), td);
cap_rights_init(&rights, CAP_RENAMEAT_SOURCE), td);
#else
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
if ((error = namei(&fromnd)) != 0)
@ -3479,7 +3481,7 @@ kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new,
fvp = fromnd.ni_vp;
NDINIT_ATRIGHTS(&tond, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE |
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)
tond.ni_cnd.cn_flags |= WILLBEDIR;
if ((error = namei(&tond)) != 0) {

View File

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

View File

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