LSM(Linux Security Module) -1-
CONFIG_SECURITY 커널 옵션을 사용하여 리눅스 시큐리티 기능을 활용할 수 있다.
필요한 시큐리티 모듈을 로드하여 사용한다.
- rpi2: 디폴트 설정으로 사용하지 않게 설정되어 있다.
- arm64: 디폴트 설정으로 CONFIG_SECURITY 커널 옵션을 사용하며, 추가적으로 관련 모듈들을 로드하여 사용할 수 있다.
참고로 리눅스 개발자들은 LSM의 접근 기능 제어가 다른 기능으로의 남용이 될 수 있는 가능성이 있으므로 좋아하지 않는다.
- windows OS등과 같이 각 접근 제어용 기능 함수들에 대한 hook을 사용하는데 원래 기능적 목적과 달리 악의적인 모듈(rootkit)을 설치할 수도 있다.
Security Module
- AppArmor(Application Armor)
- SELinux(Security Enhanced Linux)
- SMACK(Simplified Mandatory Access Control Kernel)
- TOMOYO Linux – mandatory access control
- Yama
시큐리티 모듈 등록
register_security()
security/security.c
/**
* register_security - registers a security framework with the kernel
* @ops: a pointer to the struct security_options that is to be registered
*
* This function allows a security module to register itself with the
* kernel security subsystem. Some rudimentary checking is done on the @ops
* value passed to this function. You'll need to check first if your LSM
* is allowed to register its @ops by calling security_module_enable(@ops).
*
* If there is already a security module registered with the kernel,
* an error will be returned. Otherwise %0 is returned on success.
*/
int __init register_security(struct security_operations *ops)
{
if (verify(ops)) {
printk(KERN_DEBUG "%s could not verify "
"security_operations structure.\n", __func__);
return -EINVAL;
}
if (security_ops != &default_security_ops)
return -EAGAIN;
security_ops = ops;
return 0;
}
커널파라메터로 시큐리티모듈 이름 등록
choose_lsm()
security/security.c
/* Save user chosen LSM */
static int __init choose_lsm(char *str)
{
strncpy(chosen_lsm, str, SECURITY_NAME_MAX);
return 1;
}
__setup("security=", choose_lsm);
“security=” 커널 파라메터를 사용하여 리눅스 시큐리티 모듈의 이름을 설정한다.
security_module_enable()
security/security.
/**
* security_module_enable - Load given security module on boot ?
* @ops: a pointer to the struct security_operations that is to be checked.
*
* Each LSM must pass this method before registering its own operations
* to avoid security registration races. This method may also be used
* to check if your LSM is currently loaded during kernel initialization.
*
* Return true if:
* -The passed LSM is the one chosen by user at boot time,
* -or the passed LSM is configured as the default and the user did not
* choose an alternate LSM at boot time.
* Otherwise, return false.
*/
int __init security_module_enable(struct security_operations *ops)
{
return !strcmp(ops->name, chosen_lsm);
}
부트 타임에 주어진 “security=” 커널 파라메터로 설정한 시큐리티 모듈의 이름과 비교하여 모듈이 활성화(로드)되었는지 확인한다.
Common Capability – 가상 주소 매핑 허용 체크
security_mmap_addr()
include/linux/security.h
static inline int security_mmap_addr(unsigned long addr)
{
return cap_mmap_addr(addr);
}
가상 주소 요청 시 금지 여부를 반환한다. 0=성공, 그 외=금지
cap_mmap_addr()
security/commoncap.c
/*
* cap_mmap_addr - check if able to map given addr
* @addr: address attempting to be mapped
*
* If the process is attempting to map memory below dac_mmap_min_addr they need
* CAP_SYS_RAWIO. The other parameters to this function are unused by the
* capability security module. Returns 0 if this mapping should be allowed
* -EPERM if not.
*/
int cap_mmap_addr(unsigned long addr)
{
int ret = 0;
if (addr < dac_mmap_min_addr) {
ret = cap_capable(current_cred(), &init_user_ns, CAP_SYS_RAWIO,
SECURITY_CAP_AUDIT);
/* set PF_SUPERPRIV if it turns out we allow the low mmap */
if (ret == 0)
current->flags |= PF_SUPERPRIV;
}
return ret;
}
가상 주소 요청 시 dac_mmap_min_addr 이하의 경우 필요 보안 권한 이 설정되지 않은 경우 에러를 반환한다. 0=성공
- arm: dac_mmap_min_addr에 기본값으로 CONFIG_DEFAULT_MMAP_MIN_ADDR(arm=0x1000, x86=0x10000)이다.
- “/proc/sys/vm/mmap_min_addr” 에서 설정할 수 있다.
Common Capability – 새로운 가상 매핑의 할당 허용 제한
security_vm_enough_memory_mm()
include/linux/security.h
static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
return cap_vm_enough_memory(mm, pages);
}
새로운 요청 페이지 수 만큼의 가상 매핑의 할당을 허가할지 여부를 반환한다.
cap_vm_enough_memory()
security/commoncap.c
/**
* cap_vm_enough_memory - Determine whether a new virtual mapping is permitted
* @mm: The VM space in which the new mapping is to be made
* @pages: The size of the mapping
*
* Determine whether the allocation of a new virtual mapping by the current
* task is permitted, returning 0 if permission is granted, -ve if not.
*/
int cap_vm_enough_memory(struct mm_struct *mm, long pages)
{
int cap_sys_admin = 0;
if (cap_capable(current_cred(), &init_user_ns, CAP_SYS_ADMIN,
SECURITY_CAP_NOAUDIT) == 0)
cap_sys_admin = 1;
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
요청 메모리 디스크립터에 대해 새로운 요청 페이지 수 만큼의 가상 매핑의 할당을 허가할지 여부를 반환한다.
- 현재 태스크가 admin 자격이 있는 경우에 인수 cap_sys_admin=1로 __vm_enough_memory() 함수가 호출된다.
__vm_enough_memory()
mm/mmap.c
/*
* Check that a process has enough memory to allocate a new virtual
* mapping. 0 means there is enough memory for the allocation to
* succeed and -ENOMEM implies there is not.
*
* We currently support three overcommit policies, which are set via the
* vm.overcommit_memory sysctl. See Documentation/vm/overcommit-accounting
*
* Strict overcommit modes added 2002 Feb 26 by Alan Cox.
* Additional code 2002 Jul 20 by Robert Love.
*
* cap_sys_admin is 1 if the process has admin privileges, 0 otherwise.
*
* Note this is a helper function intended to be used by LSMs which
* wish to use this logic.
*/
int __vm_enough_memory(struct mm_struct *mm, long pages, int cap_sys_admin)
{
long free, allowed, reserve;
VM_WARN_ONCE(percpu_counter_read(&vm_committed_as) <
-(s64)vm_committed_as_batch * num_online_cpus(),
"memory commitment underflow");
vm_acct_memory(pages);
/*
* Sometimes we want to use more memory than we have
*/
if (sysctl_overcommit_memory == OVERCOMMIT_ALWAYS)
return 0;
if (sysctl_overcommit_memory == OVERCOMMIT_GUESS) {
free = global_page_state(NR_FREE_PAGES);
free += global_page_state(NR_FILE_PAGES);
/*
* shmem pages shouldn't be counted as free in this
* case, they can't be purged, only swapped out, and
* that won't affect the overall amount of available
* memory in the system.
*/
free -= global_page_state(NR_SHMEM);
free += get_nr_swap_pages();
/*
* Any slabs which are created with the
* SLAB_RECLAIM_ACCOUNT flag claim to have contents
* which are reclaimable, under pressure. The dentry
* cache and most inode caches should fall into this
*/
free += global_page_state(NR_SLAB_RECLAIMABLE);
/*
* Leave reserved pages. The pages are not for anonymous pages.
*/
if (free <= totalreserve_pages)
goto error;
else
free -= totalreserve_pages;
/*
* Reserve some for root
*/
if (!cap_sys_admin)
free -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
if (free > pages)
return 0;
goto error;
}
allowed = vm_commit_limit();
/*
* Reserve some for root
*/
if (!cap_sys_admin)
allowed -= sysctl_admin_reserve_kbytes >> (PAGE_SHIFT - 10);
/*
* Don't let a single process grow so big a user can't recover
*/
if (mm) {
reserve = sysctl_user_reserve_kbytes >> (PAGE_SHIFT - 10);
allowed -= min_t(long, mm->total_vm / 32, reserve);
}
if (percpu_counter_read_positive(&vm_committed_as) < allowed)
return 0;
error:
vm_unacct_memory(pages);
return -ENOMEM;
}
현재 태스크에서 새로운 요청 페이지 수 만큼의 가상 매핑의 할당을 허가할지 여부를 반환한다
- 코드 라인 25에서 per-cpu 전역 변수 vm_committed_as 카운터에 pages 수 만큼 추가한다.
- commit된 페이지 수를 accounting한다.
- 코드 라인 30~31에서 over commit이 항상 허용되는 경우 성공적으로 0을 반환한다.
- “/proc/sys/vm/overcommit_memory”
- 0: guess (free 페이지 수 + file 페이지 수 + 스왑 페이지 수 + slab 회수 가능 페이지 수 – 전체 리저브 페이지 수 – admin 리저브 페이지 수 만큼 가능)
- 1: 항상 over commit이 가능하다.
- 2: over commit 할 수 없고 아래 두 파라메터로 결정한다
- “/proc/sys/vm/overcommit_memory”
- 코드 라인 33에서 over commit 가능량을 sysctl 값에 의존한다.
- 코드 라인 34~53에서 free 페이지 + file 페이지 – 공유메모리 페이지 + 스왑 페이지 수 + slab 회수 페이지 수를 free에 대입한다.
- 코드 라인 58~59에서 free가 전체 reserve 페이지 수보다 작은 경우 error 레이블로 이동한다.
- 코드 라인 60~61에서 free에서 전체 reserve 페이지 수를 뺀다.
- 코드 라인 66~67 현재 태스크가 user 권한인 경우 free에서 admin 리저브 페이지 수 만큼을 뺀다.
- “/ proc/sys/vm/admin_reserve_kbytes”
- 디폴트로 8MB
- “/ proc/sys/vm/admin_reserve_kbytes”
- 코드 라인 69~72에서 free가 요청 페이지 수보다 큰 경우 성공리에 0을 반환하고 그렇지 않은 경우 error 레이블로 이동한다.
- 코드 라인 75에서 over commit 제한치 이상을 허용하지 않는 경우이다. 먼저 over commit 제한치를 알아온다.
- 코드 라인 79~80에서 현재 태스크가 user 권한인 경우 free에서 admin 리저브 페이지 수 만큼을 뺀다.
- 코드 라인 85~88에서 단일 프로세서가 너무 많은 할당을 할 수 없도록 허용치를 약간 제한한다.
- 메모리 디스크립터 mm이 지정된 경우 태스크의 전체 vm 페이지의 1/32와 유저 reserve 페이지 중 작은 값을 allowed에서 감소시킨다.
- “/ proc/sys/vm/user_reserve_kbytes”
- 디폴트로 128MB
- 코드 라인 90~91에서 vm commit된 페이지 수가 allowed보다 작은 경우 성공리에 0을 반환한다.
- 코드 라인 92~95에서 error 처리를 하는 레이블로 미리 commit 시킨 페이지 수를 감소시킨다.
vm_commit_limit()
mm/util.c
/*
* Committed memory limit enforced when OVERCOMMIT_NEVER policy is used
*/
unsigned long vm_commit_limit(void)
{
unsigned long allowed;
if (sysctl_overcommit_kbytes)
allowed = sysctl_overcommit_kbytes >> (PAGE_SHIFT - 10);
else
allowed = ((totalram_pages - hugetlb_total_pages())
* sysctl_overcommit_ratio / 100);
allowed += total_swap_pages;
return allowed;
}
over commit 제한치 이상을 허용하지 않는 정책을 사용하는 경우를 위해 over commit 제한치를 산출한다.
- 코드 라인 8~9에서 “/proc/sys/vm/overcommit_kbytes”가 0이 아닌 경우 지정된 값 x kbytes를 allowed에 대입한다.
- 코드 라인 10~12에서 그렇지 않은 경우 전체 메모리 페이지 – 전체 hugetlb 페이지 수를 over commit 백분율 비율로 allowed에 대입한다.
- “/proc/sys/vm/overcommit_ratio”
- 디폴트로 50%
- “/proc/sys/vm/overcommit_ratio”
- 코드 라인 13~15에서 계산된 허용 페이지 수에 전체 스왑 페이지 수를 더한다.
참고
- LSM(Linux Security Module) -1- | 문c – 현재글
- LSM -2- (AppArmor) | 문c – not yet
- LSM -3- (SELinux) | 문c – not yet
- LSM -4- (SMACK) | 문c – not yet
- LSM -5- (TOMOYO) | 문c – not yet
- LSM -6- (Yama) | 문c – not yet
- LIM(Linux Integrity Module) -1- | 문c
- Linux Security 기능 총정리 | jinb-park
- [Linux] POSIX capability | F/OSS
- capabilities – overview of Linux capabilities | man7.org
- User virtual maps (brk) | 문c