LSM(Linux Security Module) -1-

LSM(Linux Security Module) -1-

CONFIG_SECURITY 커널 옵션을 사용하여 리눅스 시큐리티 기능을 활용할 수 있다.

필요한 시큐리티 모듈을 로드하여 사용한다.

  • rpi2: 디폴트 설정으로 사용하지 않게 설정되어 있다.
  • arm64: 디폴트 설정으로 CONFIG_SECURITY 커널 옵션을 사용하며, 추가적으로 관련 모듈들을 로드하여 사용할 수 있다.

 

참고로 리눅스 개발자들은 LSM의 접근 기능 제어가 다른 기능으로의 남용이 될 수 있는 가능성이 있으므로 좋아하지 않는다.

  • windows OS등과 같이 각 접근 제어용 기능 함수들에 대한 hook을 사용하는데 원래 기능적 목적과 달리 악의적인 모듈(rootkit)을 설치할 수도 있다.

 

Security Module

 

시큐리티 모듈 등록

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 할 수 없고 아래 두 파라메터로 결정한다
  • 코드 라인 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
  • 코드 라인 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%
  • 코드 라인 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

 

댓글 남기기