天天看點

Linux作業系統的權限代碼分析【轉】

現在關于核心的書很少涉及到Linux核心的安全,核心安全大概包括了密碼學實作(crypto)和通路控制(security)兩個部分。安全系 統作為Linux核心的一個重要的子系統,已經為我們提供了很多的相關接口,這裡我們就對安全通路控制做一個簡要的分析和介紹。 

       通路控制的原理注定要和虛拟檔案系統和程序管理有着非常緊密的聯系,因為作為使用者主體的表現形式就是程序,而作為資源客體對象的表現形式就是檔案,而通路 控制就是如何實作正确的使用者可以通路正确的資源。Linux能夠提供給我們許多可信的方式來處理這樣的問題。

初始化工作

這個初始化工作在init/main.c中的start_kernel()中security_init()定義了,其具體的實作是在security/security.c中:

int __init security_init(void)

{

        printk(KERN_INFO "Security Framework v" SECURITY_FRAMEWORK_VERSION

               " initialized/n");

        if (verify(&dummy_security_ops)) {

               printk(KERN_ERR "%s could not verify "

                      "dummy_security_ops structure./n", __FUNCTION__);

               return -EIO;

        }

        security_ops = &dummy_security_ops;

        do_security_initcalls();

        return 0;

}

這個函數首先用verify來驗證所指定的通路控制政策(dummy_security_ops)是否為空,如果為空就按“保持預設”的方式進行分 配,這裡的“保持沉默”就是對于任何的通路控制采取不管不問的方式處理了。然後就是把dummy_security_ops指定給系統全局安全政策 security_ops。

通路控制政策的相關接口

關于這些接口就是定義在了include/linux/security.h中的security_operations,包括如下一些操作:當父 程序trace子程序時進行的權限檢查,對權能的擷取、設定檢查、設定、有效性檢查,對程序做審計的檢查,當某個操作使用一般系統接口表時需要的權限檢 查,當使用核心消息環或改變登入終端時需要的權限檢查,當改變系統時間需要的檢查,當配置設定一個新的虛拟記憶體頁需要的權限檢查,當執行二進制程式時需要的各 種權限配置設定和檢查,對檔案系統操作時需要的各種通路控制操作,對inode索引節點操作時需要的各種通路控制操作,對檔案操作時的各種通路控制操作,對進 程操作的需要的各種通路控制操作,對程序間通信信号燈的權限控制,對消息隊列的控制,對程序間通信的共享記憶體區域的控制,對網絡消息處理需要的各種控制, 注冊與撤銷通路控制政策,對網絡連接配接的控制,對套接字的各種控制,對IPSEC中xfrm使用者自定義政策的配置設定,密鑰管理的控制等等,幾乎囊括了系統各種 行為的控制。

權限管理

        虛拟檔案系統為各種類型的檔案系統提供統一的操作接口,同時這樣的做法也可以簡化檔案權限的管理。那麼Linux時如何巧妙地實作這種想法呢?Linux 采用的是基于列的ACL自主通路控制,即在每個檔案裡存儲對本檔案的通路權限資訊,這裡我們采用索引節點inode(定義在 include/linux/fs.h)作為切入點進行分析。在inode結構體中有i_uid和i_gid元素,還有一個i_mode元素。這個 i_mode是16位的無符号整數表示,由9位權限方式位、3位“粘滞”标志位和4位檔案類型标志位,它們的具體的定義在 include/linux/stat.h中:

#define S_IFMT   00170000     /* 用于抽取i_mode域中類型部分的屏蔽位 */

#define S_IFSOCK 0140000     /* 套接字類型碼 */

#define S_IFLNK    0120000    /* 符号連接配接類型碼 */

#define S_IFREG   0100000    /* 普通檔案類型碼 */

#define S_IFBLK   0060000    /* 塊特别檔案類型碼 */

#define S_IFDIR   0040000     /* 目錄檔案類型碼 */

#define S_IFCHR   0020000    /* 字元特别檔案類型碼 */

#define S_IFIFO   0010000     /* 管道或FIFO類型碼 */

#define S_ISUID   0004000    /* 使用者粘滞位 */

#define S_ISGID   0002000    /* 使用者組粘滞位 */

#define S_ISVTX   0001000   /* 粘滞位 */

#define S_IRWXU 00700     /* 使用者讀寫執行 */

#define S_IRUSR 00400      /* 使用者讀 */

#define S_IWUSR 00200     /* 使用者寫 */

#define S_IXUSR 00100     /* 使用者執行 */

#define S_IRWXG 00070    /* 使用者組讀寫執行 */

#define S_IRGRP 00040    /* 使用者組讀 */

#define S_IWGRP 00020    /* 使用者組寫 */

#define S_IXGRP 00010    /* 使用者組執行 */

#define S_IRWXO 00007   /* 其他使用者讀寫執行 */

#define S_IROTH 00004    /* 其他使用者讀 */

#define S_IWOTH 00002   /* 其他使用者寫 */

#define S_IXOTH 00001   /* 其他使用者執行 */

#define S_IRWXUGO     (S_IRWXU|S_IRWXG|S_IRWXO)   /* 全部使用者讀寫執行 */

#define S_IALLUGO      (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO )/* 全部使用者全部權限 */

#define S_IRUGO          (S_IRUSR|S_IRGRP|S_IROTH)    /* 全部使用者讀 */

#define S_IWUGO         (S_IWUSR|S_IWGRP|S_IWOTH)   /* 全部使用者寫 */

#define S_IXUGO          (S_IXUSR|S_IXGRP|S_IXOTH)    /* 全部使用者執行 */

        同時,每個程序的task_struct中也有對應的uid,euid,suid,fsuid, gid,egid,sgid,fsgid等元素,當使用者登入系統就建立了一個shell程序,它從/etc/passwd中取得對應使用者的uid和gid 來唯一标志這個使用者,以後所有的程序就代代相傳。當核心在執行使用者程序通路檔案的請求時就要對比程序的uid、gid與檔案的通路模式位,由此決定該程序 是否有對檔案的操作權限。uid為零的使用者為超級使用者,可以對任何資源進行管理,當然這也導緻了系統安全的不完備性。

        判定一個程序是否有對某個檔案有某種通路的主要工作是由fs/namei.c中的permission函數決定的,具體的實作方式如下,其中的mask參數是所要求的通路方式位的标志位:

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;

        //是否滿足可執行

        if ((mask & MAY_EXEC) && S_ISREG(mode) && (!(mode & S_IXUGO) ||

                      (nd && nd->mnt && (nd->mnt->mnt_flags & MNT_NOEXEC))))

               return -EACCES;

        submask = mask & ~MAY_APPEND;

        //傳回适應的權限位

        if (inode->i_op && inode->i_op->permission)

               //交給了具體檔案系統實作,比如說ext3檔案系統

               retval = inode->i_op->permission(inode, submask, nd);

        else

               //如果目前程序的fsuid與檔案uid相同要比對檔案屬主的權限,否則比對使用者組

               retval = generic_permission(inode, submask, NULL);

        if (retval)

               return retval;

        //傳回适應的通路控制政策的權限位,比如說selinux

        return security_inode_permission(inode, mask, nd);

【新浪微網誌】 張昺華--sky

【twitter】 @sky2030_

【facebook】 張昺華 zhangbinghua

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.

繼續閱讀