diff options
author | Jann Horn <jann@thejh.net> | 2015-04-18 20:48:39 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-19 16:46:21 -0400 |
commit | 8b01fc86b9f425899f8a3a8fc1c47d73c2c20543 (patch) | |
tree | 99e05c7758aed78cfaf10556cbe7057efaa5c02c /fs | |
parent | 5224b9613b91d937c6948fe977023247afbcc04e (diff) |
fs: take i_mutex during prepare_binprm for set[ug]id executables
This prevents a race between chown() and execve(), where chowning a
setuid-user binary to root would momentarily make the binary setuid
root.
This patch was mostly written by Linus Torvalds.
Signed-off-by: Jann Horn <jann@thejh.net>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/exec.c | 76 |
1 files changed, 48 insertions, 28 deletions
@@ -1275,6 +1275,53 @@ static void check_unsafe_exec(struct linux_binprm *bprm) | |||
1275 | spin_unlock(&p->fs->lock); | 1275 | spin_unlock(&p->fs->lock); |
1276 | } | 1276 | } |
1277 | 1277 | ||
1278 | static void bprm_fill_uid(struct linux_binprm *bprm) | ||
1279 | { | ||
1280 | struct inode *inode; | ||
1281 | unsigned int mode; | ||
1282 | kuid_t uid; | ||
1283 | kgid_t gid; | ||
1284 | |||
1285 | /* clear any previous set[ug]id data from a previous binary */ | ||
1286 | bprm->cred->euid = current_euid(); | ||
1287 | bprm->cred->egid = current_egid(); | ||
1288 | |||
1289 | if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) | ||
1290 | return; | ||
1291 | |||
1292 | if (task_no_new_privs(current)) | ||
1293 | return; | ||
1294 | |||
1295 | inode = file_inode(bprm->file); | ||
1296 | mode = READ_ONCE(inode->i_mode); | ||
1297 | if (!(mode & (S_ISUID|S_ISGID))) | ||
1298 | return; | ||
1299 | |||
1300 | /* Be careful if suid/sgid is set */ | ||
1301 | mutex_lock(&inode->i_mutex); | ||
1302 | |||
1303 | /* reload atomically mode/uid/gid now that lock held */ | ||
1304 | mode = inode->i_mode; | ||
1305 | uid = inode->i_uid; | ||
1306 | gid = inode->i_gid; | ||
1307 | mutex_unlock(&inode->i_mutex); | ||
1308 | |||
1309 | /* We ignore suid/sgid if there are no mappings for them in the ns */ | ||
1310 | if (!kuid_has_mapping(bprm->cred->user_ns, uid) || | ||
1311 | !kgid_has_mapping(bprm->cred->user_ns, gid)) | ||
1312 | return; | ||
1313 | |||
1314 | if (mode & S_ISUID) { | ||
1315 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
1316 | bprm->cred->euid = uid; | ||
1317 | } | ||
1318 | |||
1319 | if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { | ||
1320 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
1321 | bprm->cred->egid = gid; | ||
1322 | } | ||
1323 | } | ||
1324 | |||
1278 | /* | 1325 | /* |
1279 | * Fill the binprm structure from the inode. | 1326 | * Fill the binprm structure from the inode. |
1280 | * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes | 1327 | * Check permissions, then read the first 128 (BINPRM_BUF_SIZE) bytes |
@@ -1283,36 +1330,9 @@ static void check_unsafe_exec(struct linux_binprm *bprm) | |||
1283 | */ | 1330 | */ |
1284 | int prepare_binprm(struct linux_binprm *bprm) | 1331 | int prepare_binprm(struct linux_binprm *bprm) |
1285 | { | 1332 | { |
1286 | struct inode *inode = file_inode(bprm->file); | ||
1287 | umode_t mode = inode->i_mode; | ||
1288 | int retval; | 1333 | int retval; |
1289 | 1334 | ||
1290 | 1335 | bprm_fill_uid(bprm); | |
1291 | /* clear any previous set[ug]id data from a previous binary */ | ||
1292 | bprm->cred->euid = current_euid(); | ||
1293 | bprm->cred->egid = current_egid(); | ||
1294 | |||
1295 | if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && | ||
1296 | !task_no_new_privs(current) && | ||
1297 | kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) && | ||
1298 | kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) { | ||
1299 | /* Set-uid? */ | ||
1300 | if (mode & S_ISUID) { | ||
1301 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
1302 | bprm->cred->euid = inode->i_uid; | ||
1303 | } | ||
1304 | |||
1305 | /* Set-gid? */ | ||
1306 | /* | ||
1307 | * If setgid is set but no group execute bit then this | ||
1308 | * is a candidate for mandatory locking, not a setgid | ||
1309 | * executable. | ||
1310 | */ | ||
1311 | if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { | ||
1312 | bprm->per_clear |= PER_CLEAR_ON_SETID; | ||
1313 | bprm->cred->egid = inode->i_gid; | ||
1314 | } | ||
1315 | } | ||
1316 | 1336 | ||
1317 | /* fill in binprm security blob */ | 1337 | /* fill in binprm security blob */ |
1318 | retval = security_bprm_set_creds(bprm); | 1338 | retval = security_bprm_set_creds(bprm); |