ACL


通常ファイル参照チェックは、inode->i_modeで行われますが、aclオプションでマウントすると、inode.i_aclで管理するACLを介して参照チェックが行われます。

inode->i_modeでは、1ユーザ/1グループ単位で、複数グループに権限を与えるに、各プロセス毎に参照するinodeグループを割り当て、グループIDは複数割り当てる事ができます。ただしユーザIDは割り当てられません。ACLはユーザ/グループおよび各権限属性を、inodeに設定します。そもそも権限は、参照側でなく参照元に設定するのが理に適っています。このACL情報はinodeブロック下のinodeデータでなく、inodeブロック領域とは別に、新規セクタのブロックデバイスに書き込まれため、inodeキャッシュに影響し、それ故ACLはデフォルトの実装なっていません。

ファイル参照チェック時acl_permission_check()がコールされます。ACLの参照チェックとなるには、namespaceがinit_rootと同じで、プロセスのfsuidとinode->i_uidが違って、そしてACLによるマウントされている必要があります。ACLのmountは、スーパブロックに設定されます。
#define IS_POSIXACL(inode)      __IS_FLG(inode, MS_POSIXACL)
#define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))

static int acl_permission_check(struct inode *inode, int mask)
{
       unsigned int mode = inode->i_mode;

       if (current_user_ns() != inode_userns(inode))
               goto other_perms;

       if (likely(current_fsuid() == inode->i_uid))
               mode >>= 6;
       else {
               if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
                       int error = check_acl(inode, mask);
                       if (error != -EAGAIN)
                               return error;
               }

               if (in_group_p(inode->i_gid))
                       mode >>= 3;
       }

other_perms:
       if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
               return 0;
       return -EACCES;
}
MAY_NOT_BLOCKはACLをinodeのキャッシュから取得します。 inodeがキャッシュされていてもACLは別セクタに有するためキャッシュされているとは限りません。MAY_NOT_BLOCKでなく、キャッシュされていないならinodeコールバックのget_acl()から実デバイスから取得します。

aclが取得出来たら、posix_acl_permission()で参照チェックとなります。
static int check_acl(struct inode *inode, int mask)
{
#ifdef CONFIG_FS_POSIX_ACL
       struct posix_acl *acl;

       if (mask & MAY_NOT_BLOCK) {
               acl = get_cached_acl_rcu(inode, ACL_TYPE_ACCESS);
               if (!acl)
                       return -EAGAIN;

               if (acl == ACL_NOT_CACHED)
                       return -ECHILD;
               return posix_acl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
       }

       acl = get_cached_acl(inode, ACL_TYPE_ACCESS);

       if (acl == ACL_NOT_CACHED) {
               if (inode->i_op->get_acl) {
                       acl = inode->i_op->get_acl(inode, ACL_TYPE_ACCESS);
                       if (IS_ERR(acl))
                               return PTR_ERR(acl);
               } else {
                       set_cached_acl(inode, ACL_TYPE_ACCESS, NULL);
                       return -EAGAIN;
               }
       }

       if (acl) {
               int error = posix_acl_permission(inode, acl, mask);
               posix_acl_release(acl);
               return error;
       }
#endif

       return -EAGAIN;
}
posix_acl_permission()は、pa=a_entries[0]をスタートとし、pe=a_entries[a_count - 1]の終端までをforループ走査します。ACL属性(e_tag)は以下6つ有しています。
ACL_USER_OBJ  :プロセスfsuidがinode所有者の場合
ACL_USER      :プロセスfsuidがaclのe_idの場合
ACL_GROUP_OBJ :inodeグループIDをプロセスのグループIDに有している場合
ACL_GROUP     :aclのe_idをプロセスのグループIDに有している場合
ACL_OTHER     :その他(ただし、プロセスのグループIDは、inodeグループID/ACL IDを有さない。)
ACL_MASK      :上記e_tagを満たした時のmask属性

ACL_USER_OBJ/ACL_OTHERは、acl.e_permに参照条件を有しているかチェックし、他はACL_MASKを超えないacl.e_permに参照条件を有しているかチェックします。ACL_MASKがなければ、ACL_USER_OBJ/ACL_OTHERと同じでacl.e_permだけによる参照条件となります。

ACL_MASKは複数設定でき、チェックとなるa_entries[]の以降のa_entries[]のACL_MASKで処理されます。としてe_permを考慮して参照条件をチェックします。ACL_MASKはプロセスとしてファイルとの関係がないチェックでの安全の担保です。
struct posix_acl        inode.i_acl;
struct posix_acl        inode.i_default_acl;

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

struct posix_acl_entry {
       short                   e_tag;
       unsigned short          e_perm;
       unsigned int            e_id;
};

#define FOREACH_ACL_ENTRY(pa, acl, pe) \
       for(pa=(acl)->a_entries, pe=pa+(acl)->a_count; pa<pe; pa++)

int
posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
{
       const struct posix_acl_entry *pa, *pe, *mask_obj;
       int found = 0;

       want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;

       FOREACH_ACL_ENTRY(pa, acl, pe) {
               switch(pa->e_tag) {
                       case ACL_USER_OBJ:
                               if (inode->i_uid == current_fsuid())
                                       goto check_perm;
                               break;
                       case ACL_USER:
                               if (pa->e_id == current_fsuid())
                                       goto mask;
                               break;
                       case ACL_GROUP_OBJ:
                               if (in_group_p(inode->i_gid)) {
                                       found = 1;
                                       if ((pa->e_perm & want) == want)
                                               goto mask;
                               }
                               break;
                       case ACL_GROUP:
                               if (in_group_p(pa->e_id)) {
                                       found = 1;
                                       if ((pa->e_perm & want) == want)
                                               goto mask;
                               }
                               break;
                       case ACL_MASK:
                               break;
                       case ACL_OTHER:
                               if (found)
                                       return -EACCES;
                               else
                                       goto check_perm;
                       default:
                               return -EIO;
               }
       }
       return -EIO;

mask:
       for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
               if (mask_obj->e_tag == ACL_MASK) {
                       if ((pa->e_perm & mask_obj->e_perm & want) == want)
                               return 0;
                       return -EACCES;
               }
       }

check_perm:
       if ((pa->e_perm & want) == want)
               return 0;
       return -EACCES;
}

補足

ACL_MASKはチェックするa_entries[]直下のa_entries[]となるため、各a_entries[]毎のACL_MASKを設定する事が可能となっています。


最終更新 2015/07/23 16:14:19 - north
(2010/12/15 17:53:13 作成)


検索

アクセス数
3588951
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。