天天看點

linux核心權限控制之vfs_permission分析

在linux檔案系統的各種操作裡,很重要的一個東西就是權限控制,我們來一起追溯核心源代碼,弄清楚核心的權限之謎。

在VFS(linux虛拟檔案系統)裡,權限控制都是通過vfs_permission來實作的,我們從vfs_permission來看,vfs_permission函數定義在fs/namei.c,定義如下

int vfs_permission(struct nameidata *nd, int mask)
{
	return permission(nd->dentry->d_inode, mask, nd);
}
           

vfs_permission函數直接調用了permission函數,我們進入permission函數看看。permission函數定義在fs/namei.c,定義如下

int permission(struct inode *inode, int mask, struct nameidata *nd)
{
	/*得到檔案的權限*/
	umode_t mode = inode->i_mode;
	int retval, submask;


	if (mask & MAY_WRITE) {


		/*隻讀檔案系統不可以寫入*/
		if (IS_RDONLY(inode) &&
		    (S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
			return -EROFS;


		/*不可修改檔案不能寫*/
		if (IS_IMMUTABLE(inode))
			return -EACCES;
	}




	/*對于普通檔案的MAY_EXEC需要可執行權限,如果檔案系統不可以執行,就傳回錯誤*/
	if ((mask & MAY_EXEC) && S_ISREG(mode) && (!(mode & S_IXUGO) ||
			(nd && nd->mnt && (nd->mnt->mnt_flags & MNT_NOEXEC))))
		return -EACCES;


	/* 一般的權限程式不了解MAY_APPEND*/
	submask = mask & ~MAY_APPEND;
	/*如果inode的函數操作結構體有permission函數,就調用這個函數*/
	if (inode->i_op && inode->i_op->permission)
		retval = inode->i_op->permission(inode, submask, nd);
	else
		/*否則就調用通用的權限檢查函數*/
		retval = generic_permission(inode, submask, NULL);
	if (retval)
		return retval;
	/*安全操作,最後決定是否有權限,調用核心自己注冊的security_ops結構體的函數,新的核心特性*/
	return security_inode_permission(inode, mask, nd);
}
           

接下來進入generic_permission函數看一看通用的權限檢查函數的實作。generic_permission函數定義在fs/namei.c,定義如下

/**
 * generic_permission  -  在POSIX系列作業系統檢察權限
 * @inode:	檢察權限需要的inode
 * @mask:	權限檢查(%MAY_READ, %MAY_WRITE, %MAY_EXEC)
 * @check_acl:	回調函數,我們傳入的是NULL,不必考慮
 */
int generic_permission(struct inode *inode, int mask,
		int (*check_acl)(struct inode *inode, int mask))
{
	umode_t			mode = inode->i_mode;
	/*所有者的權限*/
	if (current->fsuid == inode->i_uid)
		mode >>= 6;
	else {
		if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
			int error = check_acl(inode, mask);
			if (error == -EACCES)
				goto check_capabilities;
			else if (error != -EAGAIN)
				return error;
		}
		/*如果對于組ID檢查成功,就右移三位*/
		if (in_group_p(inode->i_gid))
			mode >>= 3;
	}
	/*自主要制位檢查*/
	if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
		return 0;


 check_capabilities:
	/*DAC權限檢查*/
	if (!(mask & MAY_EXEC) ||
	    (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
		if (capable(CAP_DAC_OVERRIDE))
			return 0;


	/*
	 * Searching includes executable on directories, else just read.
	 */
	if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
		if (capable(CAP_DAC_READ_SEARCH))
			return 0;


	return -EACCES;
}