ファイルケイパビリティー
Rev.2を表示中。最新版はこちら。
ファイルケイパビリティーは、ACLと同じで、inodeの拡張属性に設定されます。シェルからコマンドを実行すると、コマンドのファイル名を引数にdo_execve_common()をコールします。そこから prepare_bprm_creds(struct linux_binprm *bprm)とコールされ、コマンドのファイルのinodeの拡張attrから、ファイルケイパビリティーを取得し、親プロセスから引き継がれるケイパビリティーとの組み合わせで、プロセスのcredに設定されます。struct vfs_cap_data { __le32 magic_etc; /* Little endian */ struct { __le32 permitted; /* Little endian */ __le32 inheritable; /* Little endian */ } data[VFS_CAP_U32]; };get_file_caps()はファイルケイパビリティーを取得し、bprm->credのpermitted/inheritableにセットします。effectiveは以下の通りで、ファイルケイパビリティーpermittedを、プロセスのeffectiveとせよ。との事です。すなわち、VFS_CAP_FLAGS_EFFECTIVEが設定されていないと、ファイルケイパビリティーはプロセスに反映はされますが有効となりません。(ただし、rootユーザおよび一般ユーザでroot権限を有している場合は除きます。)
#define VFS_CAP_FLAGS_EFFECTIVE 0x000001 if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE) *effective = true;has_capは拡張attrにファイルケイパビリティーを有している場合、trueとなります。
#define VFS_CAP_REVISION_MASK 0xFF000000 if (caps->magic_etc & VFS_CAP_REVISION_MASK) *has_cap = true;プロセスにSECURE_NOROOTが設定されていなければ、if (!issecure(SECURE_NOROOT))の処理です。SECURE_NOROOTはプロセスのcred->securebitsに設定されていて、デフォルトは設定されていません。従って通常処理はif文の内部へとなります。
ファイルケイパビリティーを有して、一般ユーザがroot実効権限を有している時、goto skipで、ファイルケイパビリティーが反映されます。(親プロセスのケイパビリティーは無視と言う事)
rootユーザか、ファイルケイパビリティーが無く一般ユーザでroot権限を有している場合、親プロセスのケイパビリティーがcap_permittedに反映されます。cap_effectiveでないのがミソです。cap_effectiveに反映されるのは、effectiveがtrueかそうでないかにかかっています。
で、root実効権限を有しているなら、effective = trueとし、ファイルケイパビリティーのcaps->magic_etcに関係なく、cap_permittedはcap_effectiveに反映されることになります。
int cap_bprm_set_creds(struct linux_binprm *bprm) { const struct cred *old = current_cred(); struct cred *new = bprm->cred; bool effective, has_cap = false; int ret; effective = false; ret = get_file_caps(bprm, &effective, &has_cap); if (ret < 0) return ret; if (!issecure(SECURE_NOROOT)) { if (has_cap && new->uid != 0 && new->euid == 0) { warn_setuid_and_fcaps_mixed(bprm->filename); goto skip; } if (new->euid == 0 || new->uid == 0) { /* pP' = (cap_bset & ~0) | (pI & ~0) */ new->cap_permitted = cap_combine(old->cap_bset, old->cap_inheritable); } if (new->euid == 0) effective = true; } skip: if (!cap_issubset(new->cap_permitted, old->cap_permitted)) bprm->per_clear |= PER_CLEAR_ON_SETID; if ((new->euid != old->uid || new->egid != old->gid || !cap_issubset(new->cap_permitted, old->cap_permitted)) && bprm->unsafe & ~LSM_UNSAFE_PTRACE_CAP) { /* downgrade; they get no more than they had, and maybe less */ if (!capable(CAP_SETUID)) { new->euid = new->uid; new->egid = new->gid; } new->cap_permitted = cap_intersect(new->cap_permitted, old->cap_permitted); } new->suid = new->fsuid = new->euid; new->sgid = new->fsgid = new->egid; if (effective) new->cap_effective = new->cap_permitted; else cap_clear(new->cap_effective); bprm->cap_effective = effective; if (!cap_isclear(new->cap_effective)) { if (!cap_issubset(CAP_FULL_SET, new->cap_effective) || new->euid != 0 || new->uid != 0 || issecure(SECURE_NOROOT)) { ret = audit_log_bprm_fcaps(bprm, new, old); if (ret < 0) return ret; } } new->securebits &= ~issecure_mask(SECURE_KEEP_CAPS); return 0; }SECURE_NOROOTが設定されていなくて、ファイルケイパビリティーが設定しているケースでは、こんな感じでしょうか?
一般ユーザで一般実効権限 :ファイルケイパビリティーが反映。 有効無効はファイルケイパビリティーのcaps->magic_etc依存 一般ユーザでroot実効権限 :ファイルケイパビリティーが反映。 有効無効はファイルケイパビリティーのcaps->magic_etc依存 下記メッセージを表示 printk(KERN_INFO "warning: `%s' has both setuid-root and" " effective capabilities. Therefore not raising all" " capabilities.\n", fname); rootユーザでroot実効権限 :親プロセスのケイパビリティーが反映。 ケイパビリティーは有効 rootユーザで一般実効権限 :親プロセスのケイパビリティーが反映。 有効無効はファイルケイパビリティーのcaps->magic_etc依存
とりあえず、まとめてみましたが、rootユーザ/root権限の組み合わせで、正直いまひとつピンと来ません。スマートなコーディングなんでしょうが、もっと直感的に結びつくコーディングで実装してもらえば。と。また実際の検証してみて、具体的な意味合いが見えてきたら再度アップしたいと思っています。とりあえずあ、ファイルケイパビリティーとはこんな感じ。と言うことです。