计算机系统安全机制的主要目的是访问控制, 它包括三个任务:

  • 授权:确定哪些主体有权访问哪些客体
  • 确定访问权限
  • 实施访问权限

自主访问控制是基于客体所属用户/组身份,以及需知原则来约束对客体的访问的一种手段。这种控制是自主的意义在于:具有特定访问权限的一个主体能够将该权限(直接或间接地)传递给另一个主体。“自主”体现在:

  • 一个用户可以自主地说明他所拥有的资源允许系统中哪些用户以何种权限进行共享。从这种意义上讲,是“自主”的
  • “自主”也指对其他具有授予某种访问权力的用户能够自主地(可能是间接地)将访问权或访问权的某个子集授予另外的用户。

在UNIX/Linux系统中,实现了一种十分简单、常用而又有效的自主存取控制模式,即在每个文件上附加一段有关存取控制信息的二进制位,比如:Linux系统某个目录的访问模式为:r w _ r _ x r_ _ 或 654,这些二进制位(称为9bit位)反映了不同类别用户的存取权

  • Owner(前三位) 此客体的拥有者对它的访问权限;
  • Group(中间三位) owner同组用户对此客体的访问权限;
  • Other(最后三位) 其他用户对此客体的访问权限;

然而,9bit方式使得客体的拥有者不能精确控制某个用户对客体的访问权,比如:不能指定与owner同组的用户A对该客体具有读、写权限,而同组用户B对该客体却没有任何权限。

在这种情况下,可以通过ACL(访问控制列表)来完成访问控制。一个ACL是对应于一个客体的三元组集合,每个三元组称为 ACL的一项,比如: <type, id, perm> 其中,type表示id为用户ID还是用户组ID, perm表示允许id代表的用户对该文件的访问权限。用户可以对一个客体对应的ACL进行授权等操作。比如:linux 2.6以上的setfacl, getfacl, chacl等操作。

本文旨在寻找linux内核中关于9bit与ACL模式的数据结构,以加深对DAC的理解。

从inode开始

9bit模式与ACL均需要作用在文件或目录上,内核中inode节点与文件具有一对一的关系,因此,我们可以从inode结构体出发查找acl以及9bit的数据结构。使用find命令来查找inode结构体:

lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ find . -type f -name *.h | xargs grep "struct inode {"
./include/linux/fs.h:struct inode {
lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ 

可以看到inode结构体位于include/linux/fs.h中,与acl有关的结构体为struct posix_acl,与9bit相关的数据结构为umode_t i_mode。

struct inode {
    struct hlist_node   i_hash;
    struct list_head    i_list;     /* backing dev IO list */
    struct list_head    i_sb_list;
    struct list_head    i_dentry;
    unsigned long       i_ino;
    atomic_t        i_count;
    unsigned int        i_nlink;
    uid_t           i_uid;
    gid_t           i_gid;
    dev_t           i_rdev;
    u64         i_version;
    loff_t          i_size;
#ifdef __NEED_I_SIZE_ORDERED
    seqcount_t      i_size_seqcount;
#endif
    struct timespec     i_atime;
    struct timespec     i_mtime;
    struct timespec     i_ctime;
    blkcnt_t        i_blocks;
    unsigned int        i_blkbits;
    unsigned short          i_bytes;
    umode_t         i_mode;
    spinlock_t      i_lock; /* i_blocks, i_bytes, maybe i_size */
    struct mutex        i_mutex;
    struct rw_semaphore i_alloc_sem;
    const struct inode_operations   *i_op;
    const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
    struct super_block  *i_sb;
    struct file_lock    *i_flock;
    struct address_space    *i_mapping;
    struct address_space    i_data;
#ifdef CONFIG_QUOTA
    struct dquot        *i_dquot[MAXQUOTAS];
#endif
    struct list_head    i_devices;
    union {
        struct pipe_inode_info  *i_pipe;
        struct block_device *i_bdev;
        struct cdev     *i_cdev;
    };

    __u32           i_generation;

#ifdef CONFIG_FSNOTIFY
    __u32           i_fsnotify_mask; /* all events this inode cares about */
    struct hlist_head   i_fsnotify_mark_entries; /* fsnotify mark entries */
#endif

#ifdef CONFIG_INOTIFY
    struct list_head    inotify_watches; /* watches on this inode */
    struct mutex        inotify_mutex;  /* protects the watches list */
#endif

    unsigned long       i_state;
    unsigned long       dirtied_when;   /* jiffies of first dirtying */

    unsigned int        i_flags;

    atomic_t        i_writecount;
#ifdef CONFIG_SECURITY
    void            *i_security;
#endif
#ifdef CONFIG_FS_POSIX_ACL
    struct posix_acl    *i_acl;
    struct posix_acl    *i_default_acl;
#endif
    void            *i_private; /* fs or device private pointer */
};

9bit与i_mode

i_mode为文件的访问控制权限,然而,我们在inode结构体定义中没有找到i_mode每个bit的含义。在搜索i_mode时,我们在fs.h中找到了一个宏S_IXUGO:

static inline bool execute_ok(struct inode *inode)
{
    return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
}

可以猜测该函数的意思在于判断i_mode是否可执行,也即S_IXUGO与9bit有关。我们使用find命令来定义S_IXUGO宏:

lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ find . -type f -name *.h | xargs grep "#define S_IXUGO"
./include/linux/stat.h:#define S_IXUGO      (S_IXUSR|S_IXGRP|S_IXOTH)
lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ 

查看include/linux/stat.h:

#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)

显然,S_IRWXUGO即为对UserGroupOther的9bit全为1,S_IRUGO为允许UGO可读,S_IWUGO为允许UGO可写。
此外,i_mode为unshort,前7位的定义为:

#define S_IFMT  00170000
#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
#define S_ISUID  0004000
#define S_ISGID  0002000
#define S_ISVTX  0001000

#define S_ISLNK(m)  (((m) & S_IFMT) == S_IFLNK)
#define S_ISREG(m)  (((m) & S_IFMT) == S_IFREG)
#define S_ISDIR(m)  (((m) & S_IFMT) == S_IFDIR)
#define S_ISCHR(m)  (((m) & S_IFMT) == S_IFCHR)
#define S_ISBLK(m)  (((m) & S_IFMT) == S_IFBLK)
#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)

acl与struct posix_acl

使用find命令查找posix_acl的定义:

lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ find . -type f -name *.h | xargs grep "struct posix_acl {"
./include/linux/posix_acl.h:struct posix_acl {
lwyeluodeMacBook-Pro:linux-2.6.32.71 lwyeluo$ 

查看include/linux/posix_acl.h:

struct posix_acl_entry {
    short           e_tag;
    unsigned short      e_perm;
    union {
        kuid_t      e_uid;
        kgid_t      e_gid;
    };
};

struct posix_acl {
    union {
        atomic_t        a_refcount;
        struct rcu_head     a_rcu;
    };
    unsigned int        a_count;
    struct posix_acl_entry  a_entries[0];
};

显然,每个posix_aci_entry为一条acl记录,每条记录由三部分组成: e_tag、e_id、e_perm。每一个部分的定义:

/* e_tag entry in struct posix_acl_entry */
#define ACL_USER_OBJ        (0x01)
#define ACL_USER        (0x02)
#define ACL_GROUP_OBJ       (0x04)
#define ACL_GROUP       (0x08)
#define ACL_MASK        (0x10)
#define ACL_OTHER       (0x20)

/* permissions in the e_perm field */
#define ACL_READ        (0x04)
#define ACL_WRITE       (0x02)
#define ACL_EXECUTE     (0x01)
//#define ACL_ADD       (0x08)
//#define ACL_DELETE        (0x10)

e_tag表示ACL记录的标志,e_perm说明的是具体的访问权限,主要有rwx三种。如user:tux:rwx中user就是一个e_tag。e_id是ACL实体限制的用户或组id,如user:tux:rwx中的tux。
同时posix_acl.h声明了与acl相关的操作,具体实现在posix_acl.c中:

/*
 * Allocate a new ACL with the specified number of entries.
 */
struct posix_acl *
posix_acl_alloc(int count, gfp_t flags)
/*
 * Clone an ACL.
 */
struct posix_acl *
posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
/*
 * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
 */
int
posix_acl_valid(const struct posix_acl *acl)
/*
 * Returns 0 if the acl can be exactly represented in the traditional
 * file mode permission bits, or else 1. Returns -E... on error.
 */
int
posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p)
/*
 * Create an ACL representing the file mode permission bits of an inode.
 */
struct posix_acl *
posix_acl_from_mode(mode_t mode, gfp_t flags)
/*
 * Return 0 if current is granted want access to the inode
 * by the acl. Returns -E... otherwise.
 */
int
posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
/*
 * Modify acl when creating a new inode. The caller must ensure the acl is
 * only referenced once.
 *
 * mode_p initially must contain the mode parameter to the open() / creat()
 * system calls. All permissions that are not granted by the acl are removed.
 * The permissions in the acl are changed to reflect the mode_p parameter.
 */
int
posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p)
/*
 * Modify the ACL for the chmod syscall.
 */
int
posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode)

9bit转换为acl

值得一提的是,在posix_acl.c中,我们看到了一个从i_mode转为acl的函数posix_acl_from_mode,函数根据9bit创建了三个acl记录,定义如下:

struct posix_acl *
posix_acl_from_mode(mode_t mode, gfp_t flags)
{
    struct posix_acl *acl = posix_acl_alloc(3, flags);
    if (!acl)
        return ERR_PTR(-ENOMEM);

    acl->a_entries[0].e_tag  = ACL_USER_OBJ;
    acl->a_entries[0].e_id   = ACL_UNDEFINED_ID;
    acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;

    acl->a_entries[1].e_tag  = ACL_GROUP_OBJ;
    acl->a_entries[1].e_id   = ACL_UNDEFINED_ID;
    acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;

    acl->a_entries[2].e_tag  = ACL_OTHER;
    acl->a_entries[2].e_id   = ACL_UNDEFINED_ID;
    acl->a_entries[2].e_perm = (mode & S_IRWXO);
    return acl;
}

acl与ext4文件系统

查看fs/ext4/acl.h文件

typedef struct {
    __le16      e_tag;
    __le16      e_perm;
    __le32      e_id;
} ext4_acl_entry;

typedef struct {
    __le16      e_tag;
    __le16      e_perm;
} ext4_acl_entry_short;

typedef struct {
    __le32      a_version;
} ext4_acl_header;

上图中定义了在ext4文件系统中与acl相关的结构体。
在fs/ext4/acl.c中定义了相关函数:

  • 从磁盘中取出acl
static struct posix_acl *
ext4_acl_from_disk(const void *value, size_t size)
  • 将acl存入磁盘
static void *
ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
  • 从inode中获取acl
static struct posix_acl *
ext4_get_acl(struct inode *inode, int type)
  • 为inode设置acl
static int
ext4_set_acl(handle_t *handle, struct inode *inode, int type,
         struct posix_acl *acl)
  • 检查当前进程是否有访问inode的mask权限,内部调用了posix_acl_permission函数
int
ext4_check_acl(struct inode *inode, int mask)
  • 为一个新的inode初始化acl
int
ext4_init_acl(handle_t *handle, struct inode *inode, struct inode *dir)
  • 为某个inode修改acl
int
ext4_acl_chmod(struct inode *inode)
Logo

更多推荐