無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

ファイルケイパビリティー


ファイルケイパビリティーは、ACLと同じで、inodeの拡張属性に有していて、そこからvfs_cap_data構造体に設定されます。シェルからコマンドを実行すると、コマンドのファイル名を引数に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権限の組み合わせで、正直いまひとつピンと来ません。スマートなコーディングなんでしょうが、もっと直感的に結びつくコーディングで実装してもらえば。と。また実際の検証してみて、具体的な意味合いが見えてきたら再度アップしたいと思っています。とりあえずあ、ファイルケイパビリティーとはこんな感じ。と言うことです。

補足

カーネル3.3では、ファイルケイパビリティーは2つのバージョンあります。なお今後、ACLのように、ユーザ指定でのファイルケイパビリティーの割り当てが可能になるのではと思います。


最終更新 2013/12/25 16:37:39 - north
(2013/12/25 16:32:34 作成)