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は、スーパブロックに設定されます。
aclが取得出来たら、posix_acl_permission()で参照チェックとなります。
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はプロセスとしてファイルとの関係がないチェックでの安全の担保です。
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; }