Skip to content

Commit e586edc

Browse files
committed
virtiofs: drop remapped security.capability xattr as needed
On Linux, the 'security.capability' xattr holds a set of capabilities that can change when an executable is run, giving a limited form of privilege escalation to those programs that the writer of the file deemed worthy. Any write causes the 'security.capability' xattr to be dropped, stopping anyone from gaining privilege by modifying a blessed file. Fuse relies on the daemon to do this dropping, and in turn the daemon relies on the host kernel to drop the xattr for it. However, with the addition of -o xattrmap, the xattr that the guest stores its capabilities in is now not the same as the one that the host kernel automatically clears. Where the mapping changes 'security.capability', explicitly clear the remapped name to preserve the same behaviour. This bug is assigned CVE-2021-20263. Signed-off-by: Dr. David Alan Gilbert <dgilbert@redhat.com> Reviewed-by: Vivek Goyal <vgoyal@redhat.com>
1 parent c40ae5a commit e586edc

File tree

2 files changed

+80
-1
lines changed

2 files changed

+80
-1
lines changed

docs/tools/virtiofsd.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ The 'map' type adds a number of separate rules to add **prepend** as a prefix
228228
to the matched **key** (or all attributes if **key** is empty).
229229
There may be at most one 'map' rule and it must be the last rule in the set.
230230

231+
Note: When the 'security.capability' xattr is remapped, the daemon has to do
232+
extra work to remove it during many operations, which the host kernel normally
233+
does itself.
234+
231235
xattr-mapping Examples
232236
----------------------
233237

tools/virtiofsd/passthrough_ll.c

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ struct lo_data {
148148
int posix_lock;
149149
int xattr;
150150
char *xattrmap;
151+
char *xattr_security_capability;
151152
char *source;
152153
char *modcaps;
153154
double timeout;
@@ -217,6 +218,8 @@ static __thread bool cap_loaded = 0;
217218

218219
static struct lo_inode *lo_find(struct lo_data *lo, struct stat *st,
219220
uint64_t mnt_id);
221+
static int xattr_map_client(const struct lo_data *lo, const char *client_name,
222+
char **out_name);
220223

221224
static int is_dot_or_dotdot(const char *name)
222225
{
@@ -356,6 +359,37 @@ static int gain_effective_cap(const char *cap_name)
356359
return ret;
357360
}
358361

362+
/*
363+
* The host kernel normally drops security.capability xattr's on
364+
* any write, however if we're remapping xattr names we need to drop
365+
* whatever the clients security.capability is actually stored as.
366+
*/
367+
static int drop_security_capability(const struct lo_data *lo, int fd)
368+
{
369+
if (!lo->xattr_security_capability) {
370+
/* We didn't remap the name, let the host kernel do it */
371+
return 0;
372+
}
373+
if (!fremovexattr(fd, lo->xattr_security_capability)) {
374+
/* All good */
375+
return 0;
376+
}
377+
378+
switch (errno) {
379+
case ENODATA:
380+
/* Attribute didn't exist, that's fine */
381+
return 0;
382+
383+
case ENOTSUP:
384+
/* FS didn't support attribute anyway, also fine */
385+
return 0;
386+
387+
default:
388+
/* Hmm other error */
389+
return errno;
390+
}
391+
}
392+
359393
static void lo_map_init(struct lo_map *map)
360394
{
361395
map->elems = NULL;
@@ -737,6 +771,11 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
737771
uid_t uid = (valid & FUSE_SET_ATTR_UID) ? attr->st_uid : (uid_t)-1;
738772
gid_t gid = (valid & FUSE_SET_ATTR_GID) ? attr->st_gid : (gid_t)-1;
739773

774+
saverr = drop_security_capability(lo, ifd);
775+
if (saverr) {
776+
goto out_err;
777+
}
778+
740779
res = fchownat(ifd, "", uid, gid, AT_EMPTY_PATH | AT_SYMLINK_NOFOLLOW);
741780
if (res == -1) {
742781
saverr = errno;
@@ -759,6 +798,14 @@ static void lo_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
759798
}
760799
}
761800

801+
saverr = drop_security_capability(lo, truncfd);
802+
if (saverr) {
803+
if (!fi) {
804+
close(truncfd);
805+
}
806+
goto out_err;
807+
}
808+
762809
if (kill_suidgid) {
763810
res = drop_effective_cap("FSETID", &cap_fsetid_dropped);
764811
if (res != 0) {
@@ -1784,6 +1831,13 @@ static int lo_do_open(struct lo_data *lo, struct lo_inode *inode,
17841831
if (fd < 0) {
17851832
return -fd;
17861833
}
1834+
if (fi->flags & (O_TRUNC)) {
1835+
int err = drop_security_capability(lo, fd);
1836+
if (err) {
1837+
close(fd);
1838+
return err;
1839+
}
1840+
}
17871841
}
17881842

17891843
pthread_mutex_lock(&lo->mutex);
@@ -2191,6 +2245,12 @@ static void lo_write_buf(fuse_req_t req, fuse_ino_t ino,
21912245
"lo_write_buf(ino=%" PRIu64 ", size=%zd, off=%lu kill_priv=%d)\n",
21922246
ino, out_buf.buf[0].size, (unsigned long)off, fi->kill_priv);
21932247

2248+
res = drop_security_capability(lo_data(req), out_buf.buf[0].fd);
2249+
if (res) {
2250+
fuse_reply_err(req, res);
2251+
return;
2252+
}
2253+
21942254
/*
21952255
* If kill_priv is set, drop CAP_FSETID which should lead to kernel
21962256
* clearing setuid/setgid on file. Note, for WRITE, we need to do
@@ -2432,6 +2492,7 @@ static void parse_xattrmap(struct lo_data *lo)
24322492
{
24332493
const char *map = lo->xattrmap;
24342494
const char *tmp;
2495+
int ret;
24352496

24362497
lo->xattr_map_nentries = 0;
24372498
while (*map) {
@@ -2462,7 +2523,7 @@ static void parse_xattrmap(struct lo_data *lo)
24622523
* the last entry.
24632524
*/
24642525
parse_xattrmap_map(lo, map, sep);
2465-
return;
2526+
break;
24662527
} else {
24672528
fuse_log(FUSE_LOG_ERR,
24682529
"%s: Unexpected type;"
@@ -2531,6 +2592,19 @@ static void parse_xattrmap(struct lo_data *lo)
25312592
fuse_log(FUSE_LOG_ERR, "Empty xattr map\n");
25322593
exit(1);
25332594
}
2595+
2596+
ret = xattr_map_client(lo, "security.capability",
2597+
&lo->xattr_security_capability);
2598+
if (ret) {
2599+
fuse_log(FUSE_LOG_ERR, "Failed to map security.capability: %s\n",
2600+
strerror(ret));
2601+
exit(1);
2602+
}
2603+
if (!strcmp(lo->xattr_security_capability, "security.capability")) {
2604+
/* 1-1 mapping, don't need to do anything */
2605+
free(lo->xattr_security_capability);
2606+
lo->xattr_security_capability = NULL;
2607+
}
25342608
}
25352609

25362610
/*
@@ -3588,6 +3662,7 @@ static void fuse_lo_data_cleanup(struct lo_data *lo)
35883662

35893663
free(lo->xattrmap);
35903664
free_xattrmap(lo);
3665+
free(lo->xattr_security_capability);
35913666
free(lo->source);
35923667
}
35933668

0 commit comments

Comments
 (0)