ケーパビリティ
Rev.1を表示中。最新版はこちら。
システムタイマを変更するプロセスは、root権限が必要です。しかしこのプロセスにroot権限を与えてしまうと、このプロセスは他の権限も有することになってしまいよくありません。ケーパビリティは、root/通常ユーザ毎に管理する権限を、プロセス毎に権限を与えるというものです。rootプロセスのタスク構造体task_structに、kernel_cap_tの以下のメンバーを有しています。
cap_effective:ケーパビリティの実態
cap_inheritable:ケーパビリティの子への継承
cap_permitted:ケーパビリティ設定の許可
cap_bset:?
typedef struct kernel_cap_struct { __u32 cap[_KERNEL_CAPABILITY_U32S]; } kernel_cap_t;kernel_cap_tの__u32 capは_KERNEL_CAPABILITY_U32Sで64ビット構成となっていますが、ケーパビリティバージョンが1(_LINUX_CAPABILITY_VERSION_1)の時は、32ビットとして、バージョンが2( _LINUX_CAPABILITY_U32S_2)の時は、64ビット構成と使われることになります。
define _LINUX_CAPABILITY_VERSION_1 0x19980330 #define _LINUX_CAPABILITY_U32S_1 1 #define _LINUX_CAPABILITY_VERSION_2 0x20071026 /* deprecated - use v3 */ #define _LINUX_CAPABILITY_U32S_2 2 #define _LINUX_CAPABILITY_VERSION_3 0x20080522 #define _LINUX_CAPABILITY_U32S_3 2これらのメンバーに、ビットレベルで以下のケーパビリティが、設定されることになります。
#define CAP_CHOWN 0 #define CAP_DAC_OVERRIDE 1 #define CAP_DAC_READ_SEARCH 2 : : #define CAP_SETFCAP 31 #define CAP_MAC_OVERRIDE 32 #define CAP_MAC_ADMIN 33 #define CAP_LAST_CAP CAP_MAC_ADMINケーパビリティを設定するシステムコールは、cap_user_header_t/cap_user_data_tを引数とするcapsetです。cap_user_header_tにはケーパビリティバージョンおよび設定するプロセスIDを、__user_cap_data_structに設定するケーパビリティを設定します。
cap_validate_magic関数は、headerのバージョン情報から、ケーパビリティのサイズをtocopyに返し、ます。そのサイズでもってap_user_data_t dataをプロセスの上記capメンバーに設定するだけです。なおこの時、security_capset_check関数で、ケーパビリティ設定が可能かのチェックが行われます。チェックというのは、cap_permittedが設定されていないのに、cap_effectiveを設定しようとしているとかです。
typedef struct __user_cap_header_struct { __u32 version; int pid; } __user *cap_user_header_t; typedef struct __user_cap_data_struct { __u32 effective; __u32 permitted; __u32 inheritable; } __user *cap_user_data_t; SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data) { struct __user_cap_data_struct kdata[_KERNEL_CAPABILITY_U32S]; unsigned i, tocopy; kernel_cap_t inheritable, permitted, effective; int ret; pid_t pid; ret = cap_validate_magic(header, &tocopy); if (ret != 0) return ret; if (get_user(pid, &header->pid)) return -EFAULT; if (copy_from_user(&kdata, data, tocopy * sizeof(struct __user_cap_data_struct))) { return -EFAULT; } for (i = 0; i < tocopy; i++) { effective.cap[i] = kdata[i].effective; permitted.cap[i] = kdata[i].permitted; inheritable.cap[i] = kdata[i].inheritable; } while (i < _KERNEL_CAPABILITY_U32S) { effective.cap[i] = 0; permitted.cap[i] = 0; inheritable.cap[i] = 0; i++; } if (pid && (pid != task_pid_vnr(current))) ret = do_sys_capset_other_tasks(pid, &effective, &inheritable, &permitted); else { spin_lock(&task_capability_lock); ret = security_capset_check(current, &effective, &inheritable, &permitted); if (!ret) security_capset_set(current, &effective, &inheritable, &permitted); spin_unlock(&task_capability_lock); } return ret; }権限の必要とされる処理を呼び出す毎に、capable関数をコールして、その操作が可能かのチェックが行われます。本処理は最終的にcap_raisedマクロをコールする事で行っています。処理はいたって簡単で、要は該当する(引数のcap)のビットが、tsk->cap_effective.capに設定されているかどうかをチェックするだけです。
int capable(int cap) { if (has_capability(current, cap)) { current->flags |= PF_SUPERPRIV; return 1; } return 0; } #define has_capability(t, cap) (security_capable((t), (cap)) == 0) static inline int security_capable(struct task_struct *tsk, int cap) { return cap_capable(tsk, cap); } int cap_capable (struct task_struct *tsk, int cap) { if (cap_raised(tsk->cap_effective, cap)) return 0; return -EPERM; } #define cap_raised(c, flag) ((c).cap[CAP_TO_INDEX(flag)] & CAP_TO_MASK(flag)) #define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */ #define CAP_TO_MASK(x) (1 << ((x) & 31)) /* mask for indexed __u32 */cap_raisedマクロで、その展開にCAP_TO_INDEX/CAP_TO_MASKとビット処理を施していますが、これはcapが32ビット/64ビットを考慮してのことです。32ビットより大きなケーパビリティは配列インデックス1で、そうでない場合はインデックス0という具合です。