In apparmor_path_rename(), when handling RENAME_EXCHANGE, the cond_exchange structure is supposed to carry the attributes of the *new* dentry (since it is used to authorize moving new_dentry to the old location). However, line 412 reads: vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(old_dentry)); This fetches the uid of old_dentry instead of new_dentry. As a result, the RENAME_EXCHANGE permission check uses the wrong file owner, which can allow a rename that should be denied (if old_dentry's owner has more privileges) or deny one that should be allowed. Note that cond_exchange.mode on the line above correctly uses new_dentry. Only the uid lookup is wrong. Fix by changing old_dentry to new_dentry in the i_uid_into_vfsuid call.
Unified · Split
| @@ -410,7 +410,7 @@ static int apparmor_path_rename(const struct path *old_dir, struct dentry *old_d · 409 unmodified lines | |||
| 410 | struct path_cond cond_exchange = { | 410 | struct path_cond cond_exchange = { |
| 411 | .mode = d_backing_inode(new_dentry)->i_mode, | 411 | .mode = d_backing_inode(new_dentry)->i_mode, |
| 412 | }; | 412 | }; |
| 413 | vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(old_dentry)); | 413 | vfsuid = i_uid_into_vfsuid(idmap, d_backing_inode(new_dentry)); |
| 414 | cond_exchange.uid = vfsuid_into_kuid(vfsuid); | 414 | cond_exchange.uid = vfsuid_into_kuid(vfsuid); |
| 415 | 415 | ||
| 416 | error = aa_path_perm(OP_RENAME_SRC, current_cred(), | 416 | error = aa_path_perm(OP_RENAME_SRC, current_cred(), |