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