capgetシステムコール


cap_header.versionは_LINUX_CAPABILITY_VERSION_1/_LINUX_CAPABILITY_VERSION_2/_LINUX_CAPABILITY_VERSION_3のいずれかで、設定可能CAP数となります。cap_header.pidは取得するスレッドIDで、0ならカレントスレッドのCAPを取得します。
#include <sys/syscall.h>
#include <stdio.h>

#define _LINUX_CAPABILITY_VERSION_1  0x19980330
#define _LINUX_CAPABILITY_U32S_1     1

#define _LINUX_CAPABILITY_VERSION_2  0x20071026
#define _LINUX_CAPABILITY_U32S_2     2

#define _LINUX_CAPABILITY_VERSION_3  0x20080522
#define _LINUX_CAPABILITY_U32S_3     2

typedef unsigned int __u32;

struct cap_header {
       __u32 version;
       int pid;
};

struct cap_data {
       __u32 effective;
       __u32 permitted;
       __u32 inheritable;
};

void  main(int argc, char *argv[])
{
       struct cap_header hdrp;
       struct cap_data datap[2];
       int     i, ret;

       hdrp.pid = 0;
       hdrp.version = _LINUX_CAPABILITY_VERSION_2;
       ret = syscall(SYS_capget, &hdrp, &datap);
       if (!ret) {
                       printf("effective  :%08x:%08x\n", datap[1].effective, datap[0].effective);
                       printf("permitted  :%08x:%08x\n", datap[1].permitted, datap[0].permitted);
                       printf("inheritable:%08x:%08x\n", datap[1].inheritable, datap[0].inheritable);
       }
       else {
               printf("err:%d\n", ret);
       }
}
実行結果
[root@localhost test]# ./a.out
effective  :ffffffff:ffffffff
permitted  :ffffffff:ffffffff
inheritable:00000000:00000000

[kitamura@localhost test]$ ./a.out
effective  :00000000:00000000
permitted  :00000000:00000000
inheritable:00000000:00000000
Linux 3では35ケのCAPをサポートしています。実装できるCAP数は、_LINUX_CAPABILITY_VERSION_1なら32、_LINUX_CAPABILITY_VERSION_2/_LINUX_CAPABILITY_VERSION_3なら64です。

.cap_effectiveがスレッドの実際の権限属性です。.cap_permittedはCAP更新する時の許可属性です。.cap_inheritableはroot権限のプロセスがexecした時のプロセスへ継承する属性ですが、これはセキュアLinuxに依存し、そうでない場合、継承する属性は、.cap_bsetと.cap_inheritableの論理和となり、.cap_bsetはFULL故.cap_inheritableは実際の有効性はありません。

init_credは初期プロセスのCAPで、.cap_inheritableはEMPT、.cap_permitted/.cap_effective/.cap_bsetはFULLで、root権限のプロセスがexecしたプロセスへ継承されます。

#define CAP_WAKE_ALARM            35
#define CAP_LAST_CAP         CAP_WAKE_ALARM
#define cap_valid(x) ((x) >= 0 && (x) <= CAP_LAST_CAP)

# define CAP_EMPTY_SET    ((kernel_cap_t){{ 0, 0 }})
# define CAP_FULL_SET     ((kernel_cap_t){{ ~0, ~0 }})

struct cred init_cred = {
       .usage                  = ATOMIC_INIT(4),
#ifdef CONFIG_DEBUG_CREDENTIALS
       .subscribers            = ATOMIC_INIT(2),
       .magic                  = CRED_MAGIC,
#endif
       .securebits             = SECUREBITS_DEFAULT,
       .cap_inheritable        = CAP_EMPTY_SET,
       .cap_permitted          = CAP_FULL_SET,
       .cap_effective          = CAP_FULL_SET,
       .cap_bset               = CAP_FULL_SET,
       .user                   = INIT_USER,
       .user_ns                = &init_user_ns,
       .group_info             = &init_groups,
#ifdef CONFIG_KEYS
       .tgcred                 = &init_tgcred,
#endif
};

SYSCALL_DEFINE2(capget, cap_user_header_t, header, cap_user_data_t, dataptr)
{
       int ret = 0;
       pid_t pid;
       unsigned tocopy;
       kernel_cap_t pE, pI, pP;

       ret = cap_validate_magic(header, &tocopy);
       if ((dataptr == NULL) || (ret != 0))
               return ((dataptr == NULL) && (ret == -EINVAL)) ? 0 : ret;

       if (get_user(pid, &header->pid))
               return -EFAULT;

       if (pid < 0)
               return -EINVAL;

       ret = cap_get_target_pid(pid, &pE, &pI, &pP);
       if (!ret) {
               struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S];
               unsigned i;

               for (i = 0; i < tocopy; i++) {
                       kdata[i].effective = pE.cap[i];
                       kdata[i].permitted = pP.cap[i];
                       kdata[i].inheritable = pI.cap[i];
               }

               if (copy_to_user(dataptr, kdata, tocopy
                                * sizeof(struct __user_cap_data_struct))) {
                       return -EFAULT;
               }
       }

       return ret;
}

static int cap_validate_magic(cap_user_header_t header, unsigned *tocopy)
{
       __u32 version;

       if (get_user(version, &header->version))
               return -EFAULT;

       switch (version) {
       case _LINUX_CAPABILITY_VERSION_1:
               warn_legacy_capability_use();
               *tocopy = _LINUX_CAPABILITY_U32S_1;
               break;
       case _LINUX_CAPABILITY_VERSION_2:
               warn_deprecated_v2();
       case _LINUX_CAPABILITY_VERSION_3:
               *tocopy = _LINUX_CAPABILITY_U32S_3;
               break;
       default:
               if (put_user((u32)_KERNEL_CAPABILITY_VERSION, &header->version))
                       return -EFAULT;
               return -EINVAL;
       }

       return 0;
}

static inline int cap_get_target_pid(pid_t pid, kernel_cap_t *pEp,
                                    kernel_cap_t *pIp, kernel_cap_t *pPp)
{
       int ret;

       if (pid && (pid != task_pid_vnr(current))) {
               struct task_struct *target;

               rcu_read_lock();

               target = find_task_by_vpid(pid);
               if (!target)
                       ret = -ESRCH;
               else
                       ret = security_capget(target, pEp, pIp, pPp);

               rcu_read_unlock();
       } else
               ret = security_capget(current, pEp, pIp, pPp);

       return ret;
}

static inline int security_capget(struct task_struct *target,
                                  kernel_cap_t *effective,
                                  kernel_cap_t *inheritable,
                                  kernel_cap_t *permitted)
{
       return cap_capget(target, effective, inheritable, permitted);
} 

int cap_capget(struct task_struct *target, kernel_cap_t *effective,
              kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
       const struct cred *cred;

       rcu_read_lock();
       cred = __task_cred(target);
       *effective   = cred->cap_effective;
       *inheritable = cred->cap_inheritable;
       *permitted   = cred->cap_permitted;
       rcu_read_unlock();
       return 0;
}

補足

suコマンドでのuidを変更は、シェルからforkでプロセスを作成し、かかるuidを設定した後、execでシェルを起動することになり、この時、forkでのプロセスのCAPに応じたCAPが設定されるのですが、rootでない場合、無条件にすべてのCAPはクリアされます。

Linux2では、capsetシステムコールでcap_header.pidのスレッドのCAPを設定可能となっていましたが、Linux3では、root権限であってもカレントスレッドのみのCAPしか更新できないようになっており、運用面として思うに、root権限で動作させるコマンド等のプロセスで、プログラミングミスによる脆弱性を回避させる故かと思います。


最終更新 2015/09/14 14:28:12 - north
(2015/09/14 14:28:12 作成)


検索

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