1.文件权限
static inline int do_inode_permission(struct vfsmount *mnt, struct inode *inode, int mask)
{
if (unlikely(!(inode->i_opflags & IOP_FASTPERM))) {
if (likely(mnt && inode->i_op->permission2))
return inode->i_op->permission2(mnt, inode, mask);
if (likely(inode->i_op->permission))
return inode->i_op->permission(inode, mask);
/* This gets set once for the inode lifetime */
spin_lock(&inode->i_lock);
inode->i_opflags |= IOP_FASTPERM;
spin_unlock(&inode->i_lock);
}
return generic_permission(inode, mask);
}
int generic_permission(struct inode *inode, int mask)
{
int ret;
/*
* Do the basic permission checks.
*/
ret = acl_permission_check(inode, mask); //基本权限检查
if (ret != -EACCES)
return ret;
if (S_ISDIR(inode->i_mode)) {
/* DACs are overridable for directories */
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE)) //CAP_DAC_OVERRIDE是CAP_DAC_READ_SEARCH的超集,不管读写都可以
return 0;
if (!(mask & MAY_WRITE))
if (capable_wrt_inode_uidgid(inode,
CAP_DAC_READ_SEARCH)) //不是写
return 0;
return -EACCES;
}
/*
* Read/write DACs are always overridable.
* Executable DACs are overridable when there is
* at least one exec bit set.
*/
if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))//如果是执行,则有些不同,至少要有一个执行权限
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
return 0;
/*
* Searching includes executable on directories, else just read.
*/
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (mask == MAY_READ)
if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
}
static int acl_permission_check(struct inode *inode, int mask)
{
unsigned int mode = inode->i_mode;
if (likely(uid_eq(current_fsuid(), inode->i_uid))) //同一个用户, 使用的是进程的fsuid
mode >>= 6;
else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG)) { //注意看acl是如何工作的
int error = check_acl(inode, mask);
if (error != -EAGAIN)
return error;
}
if (in_group_p(inode->i_gid)) //同一组
mode >>= 3;
//否则是other,不用移位
}
/*
* If the DACs are ok we don't need any capability check.
*/
if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
return 0;
return -EACCES;
}
2.能力的检查
bool ns_capable(struct user_namespace *ns, int cap)
{
return ns_capable_common(ns, cap, true);
}
static bool ns_capable_common(struct user_namespace *ns, int cap, bool audit)
{
int capable;
if (unlikely(!cap_valid(cap))) {
pr_crit("capable() called with invalid cap=%u\n", cap);
BUG();
}
capable = audit ? security_capable(current_cred(), ns, cap) :
security_capable_noaudit(current_cred(), ns, cap);
if (capable == 0) {
current->flags |= PF_SUPERPRIV;
return true;
}
return false;
}
struct security_hook_list capability_hooks[] = {
LSM_HOOK_INIT(capable, cap_capable),
.....}
*int cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
int cap, int audit)
{
int ret = __cap_capable(cred, targ_ns, cap, audit);
}
int __cap_capable(const struct cred *cred, struct user_namespace *targ_ns,
int cap, int audit)
{
struct user_namespace *ns = targ_ns;
/* See if cred has the capability in the target user namespace
* by examining the target user namespace and all of the target
* user namespace's parents.
*/
for (;;) {
/* Do we have the necessary capabilities? */
if (ns == cred->user_ns)
return cap_raised(cred->cap_effective, cap) ? 0 : -EPERM;
/* Have we tried all of the parent namespaces? */
if (ns == &init_user_ns)
return -EPERM;
/*
* The owner of the user namespace in the parent of the
* user namespace has all caps.
*/
if ((ns->parent == cred->user_ns) && uid_eq(ns->owner, cred->euid))
return 0;
/*
* If you have a capability in a parent user ns, then you have
* it over all children user namespaces as well.
*/
ns = ns->parent;
}
/* We never get here */
}
如果是root,则默认具有所有能力,即CapInh,CapPrm,CapEff,CapBnd所有bit都是1,包括CAP_DAC_OVERRIDE,所有root可以操作任何文件,通过setuid()和setgid()可以切换到非root用户,此时CapInh,CapPrm,CapEff都变为0,CapBnd维持不变,即默认非root用户失去所有的能力。
通过设置程序文件的能力bit,可以给普通用户授予某项能力
setcap cap_net_raw=eip /bin/ping
此时任何用户都可以ping,这是比suid更安全的一种做法,只授予需要的权限,而不像suid授予所有的权限(everything or nothing),文件能力存储在文件系统的xattr中,并且挂载时不能指定nosuid选项,否则文件能力失效。
如果文件设置了能力,程序能力具体的规则如下:
我们使用P代表执行exec
前的capabilities,P’代表执行exec
后的capabilities,F代表exec
执行的文件的capabilities。那么:
P’(Permitted) = (P(Inheritable) & F(Inheritable)) | (F(Permitted) & cap_bset)
P’(Effective) = F(Effective) ? P’(Permitted) : 0
P’(Inheritable) = P(Inheritable)
其中的cap_bset是capability bounding set。通过与文件的Permitted集合计算交集,可进一步限制某些capabilities的获取,从而降低了风险
对于文件没有设置能力,则按如下规则:
P’(Permitted) =P(Inheritable)
P’(Effective)=P(Effective)
P’(Inheritable) = P(Inheritable)
如果某个程序文件具有cap_setuid,cap_setgid的能力,则非root用户可以通过setuid()和setgid()可以切换到root用户,此时:
CapInh,CapPrm,CapEff=CapBnd