Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selinux: Revalidate invalid inode security labels

When fetching an inode's security label, check if it is still valid, and
try reloading it if it is not. Reloading will fail when we are in RCU
context which doesn't allow sleeping, or when we can't find a dentry for
the inode. (Reloading happens via iop->getxattr which takes a dentry
parameter.) When reloading fails, continue using the old, invalid
label.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Acked-by: Stephen Smalley <sds@tycho.nsa.gov>
Signed-off-by: Paul Moore <pmoore@redhat.com>

authored by

Andreas Gruenbacher and committed by
Paul Moore
5d226df4 6f3be9f5

+68 -8
+68 -8
security/selinux/hooks.c
··· 242 242 return 0; 243 243 } 244 244 245 + static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); 246 + 247 + /* 248 + * Try reloading inode security labels that have been marked as invalid. The 249 + * @may_sleep parameter indicates when sleeping and thus reloading labels is 250 + * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is 251 + * invalid. The @opt_dentry parameter should be set to a dentry of the inode; 252 + * when no dentry is available, set it to NULL instead. 253 + */ 254 + static int __inode_security_revalidate(struct inode *inode, 255 + struct dentry *opt_dentry, 256 + bool may_sleep) 257 + { 258 + struct inode_security_struct *isec = inode->i_security; 259 + 260 + might_sleep_if(may_sleep); 261 + 262 + if (isec->initialized == LABEL_INVALID) { 263 + if (!may_sleep) 264 + return -ECHILD; 265 + 266 + /* 267 + * Try reloading the inode security label. This will fail if 268 + * @opt_dentry is NULL and no dentry for this inode can be 269 + * found; in that case, continue using the old label. 270 + */ 271 + inode_doinit_with_dentry(inode, opt_dentry); 272 + } 273 + return 0; 274 + } 275 + 276 + static void inode_security_revalidate(struct inode *inode) 277 + { 278 + __inode_security_revalidate(inode, NULL, true); 279 + } 280 + 281 + static struct inode_security_struct *inode_security_novalidate(struct inode *inode) 282 + { 283 + return inode->i_security; 284 + } 285 + 286 + static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) 287 + { 288 + int error; 289 + 290 + error = __inode_security_revalidate(inode, NULL, !rcu); 291 + if (error) 292 + return ERR_PTR(error); 293 + return inode->i_security; 294 + } 295 + 245 296 /* 246 297 * Get the security label of an inode. 247 298 */ 248 299 static struct inode_security_struct *inode_security(struct inode *inode) 249 300 { 301 + __inode_security_revalidate(inode, NULL, true); 250 302 return inode->i_security; 251 303 } 252 304 ··· 309 257 { 310 258 struct inode *inode = d_backing_inode(dentry); 311 259 260 + __inode_security_revalidate(inode, dentry, true); 312 261 return inode->i_security; 313 262 } 314 263 ··· 415 362 "uses mountpoint labeling", 416 363 "uses native labeling", 417 364 }; 418 - 419 - static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry); 420 365 421 366 static inline int inode_doinit(struct inode *inode) 422 367 { ··· 1706 1655 1707 1656 ad.type = LSM_AUDIT_DATA_DENTRY; 1708 1657 ad.u.dentry = dentry; 1658 + __inode_security_revalidate(inode, dentry, true); 1709 1659 return inode_has_perm(cred, inode, av, &ad); 1710 1660 } 1711 1661 ··· 1722 1670 1723 1671 ad.type = LSM_AUDIT_DATA_PATH; 1724 1672 ad.u.path = *path; 1673 + __inode_security_revalidate(inode, path->dentry, true); 1725 1674 return inode_has_perm(cred, inode, av, &ad); 1726 1675 } 1727 1676 ··· 2924 2871 ad.type = LSM_AUDIT_DATA_DENTRY; 2925 2872 ad.u.dentry = dentry; 2926 2873 sid = cred_sid(cred); 2927 - isec = inode_security(inode); 2874 + isec = inode_security_rcu(inode, rcu); 2875 + if (IS_ERR(isec)) 2876 + return PTR_ERR(isec); 2928 2877 2929 2878 return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad, 2930 2879 rcu ? MAY_NOT_BLOCK : 0); ··· 2978 2923 perms = file_mask_to_av(inode->i_mode, mask); 2979 2924 2980 2925 sid = cred_sid(cred); 2981 - isec = inode_security(inode); 2926 + isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK); 2927 + if (IS_ERR(isec)) 2928 + return PTR_ERR(isec); 2982 2929 2983 2930 rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd); 2984 2931 audited = avc_audit_required(perms, &avd, rc, ··· 3289 3232 /* No change since file_open check. */ 3290 3233 return 0; 3291 3234 3235 + inode_security_revalidate(inode); 3292 3236 return selinux_revalidate_file_permission(file, mask); 3293 3237 } 3294 3238 ··· 3595 3537 * new inode label or new policy. 3596 3538 * This check is not redundant - do not remove. 3597 3539 */ 3540 + inode_security_revalidate(file_inode(file)); 3598 3541 return file_path_has_perm(cred, file, open_file_to_av(file)); 3599 3542 } 3600 3543 ··· 4137 4078 int type, int protocol, int kern) 4138 4079 { 4139 4080 const struct task_security_struct *tsec = current_security(); 4140 - struct inode_security_struct *isec = inode_security(SOCK_INODE(sock)); 4081 + struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); 4141 4082 struct sk_security_struct *sksec; 4142 4083 int err = 0; 4143 4084 ··· 4337 4278 if (err) 4338 4279 return err; 4339 4280 4340 - newisec = inode_security(SOCK_INODE(newsock)); 4281 + newisec = inode_security_novalidate(SOCK_INODE(newsock)); 4341 4282 4342 - isec = inode_security(SOCK_INODE(sock)); 4283 + isec = inode_security_novalidate(SOCK_INODE(sock)); 4343 4284 newisec->sclass = isec->sclass; 4344 4285 newisec->sid = isec->sid; 4345 4286 newisec->initialized = LABEL_INITIALIZED; ··· 4677 4618 4678 4619 static void selinux_sock_graft(struct sock *sk, struct socket *parent) 4679 4620 { 4680 - struct inode_security_struct *isec = inode_security(SOCK_INODE(parent)); 4621 + struct inode_security_struct *isec = 4622 + inode_security_novalidate(SOCK_INODE(parent)); 4681 4623 struct sk_security_struct *sksec = sk->sk_security; 4682 4624 4683 4625 if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||