MACF는 Mandatory Access Control Framework의 약자로, 컴퓨터를 보호하기 위해 운영 체제에 내장된 보안 시스템입니다. 이는 파일, 애플리케이션 및 시스템 리소스와 같은 시스템의 특정 부분에 접근할 수 있는 사람이나 사물에 대한 엄격한 규칙을 설정하여 작동합니다. 이러한 규칙을 자동으로 시행함으로써, MACF는 권한이 있는 사용자와 프로세스만 특정 작업을 수행할 수 있도록 보장하여 무단 접근이나 악의적인 활동의 위험을 줄입니다.
MACF는 실제로 결정을 내리지 않으며, 단지 작업을 가로채기 때문에, AppleMobileFileIntegrity.kext, Quarantine.kext, Sandbox.kext, TMSafetyNet.kext 및 mcxalr.kext와 같은 정책 모듈(커널 확장)에 결정을 맡깁니다.
Flow
프로세스가 syscall/mach trap을 수행합니다.
관련 함수가 커널 내에서 호출됩니다.
함수가 MACF를 호출합니다.
MACF는 해당 함수에 후킹을 요청한 정책 모듈을 확인합니다.
MACF는 관련 정책을 호출합니다.
정책은 작업을 허용할지 거부할지를 나타냅니다.
Apple만이 MAC Framework KPI를 사용할 수 있습니다.
Labels
MACF는 접근을 허용할지 여부를 확인하는 정책에서 사용할 수 있는 레이블을 사용합니다. 레이블 구조체 선언의 코드는 여기에서 찾을 수 있으며, 이는 struct ucred 내에서 여기cr_label 부분에서 사용됩니다. 레이블은 플래그와 MACF 정책이 포인터를 할당하는 데 사용할 수 있는 슬롯의 수를 포함합니다. 예를 들어, Sandbox는 컨테이너 프로필을 가리킵니다.
MACF Policies
MACF 정책은 특정 커널 작업에 적용될 규칙과 조건을 정의합니다.
커널 확장은 mac_policy_conf 구조체를 구성한 다음 mac_policy_register를 호출하여 등록할 수 있습니다. 여기에서:
#definempc_tstruct mac_policy_conf */**@brief Mac policy configurationThis structure specifies the configuration information for aMAC policy module. A policy module developer must supplya short unique policy name, a more descriptive full name, a list of labelnamespaces and count, a pointer to the registered enty point operations,any load time flags, and optionally, a pointer to a label slot identifier.The Framework will update the runtime flags (mpc_runtime_flags) toindicate that the module has been registered.If the label slot identifier (mpc_field_off) is NULL, the Frameworkwill not provide label storage for the policy. Otherwise, theFramework will store the label location (slot) in this field.The mpc_list field is used by the Framework and should not bemodified by policies.*//* XXX - reorder these for better aligment on 64bit platforms */struct mac_policy_conf {constchar*mpc_name; /** policy name */constchar*mpc_fullname; /** full name */constchar**mpc_labelnames; /** managed label namespaces */unsignedint mpc_labelname_count; /** number of managed label namespaces */struct mac_policy_ops *mpc_ops; /** operation vector */int mpc_loadtime_flags; /** load time flags */int*mpc_field_off; /** label slot */int mpc_runtime_flags; /** run time flags */mpc_t mpc_list; /** List reference */void*mpc_data; /** module data */};
커널 확장을 통해 이러한 정책을 구성하는 것은 mac_policy_register 호출을 확인함으로써 쉽게 식별할 수 있습니다. 또한, 확장의 디스어셈블을 확인하면 사용된 mac_policy_conf 구조체를 찾을 수 있습니다.
MACF 정책은 동적으로 등록 및 등록 해제할 수 있습니다.
mac_policy_conf의 주요 필드 중 하나는 **mpc_ops**입니다. 이 필드는 정책이 관심 있는 작업을 지정합니다. 수백 개가 있으므로 모든 작업을 0으로 설정한 다음 정책이 관심 있는 작업만 선택할 수 있습니다. 여기에서:
거의 모든 훅은 이러한 작업이 가로채어질 때 MACF에 의해 호출됩니다. 그러나 mpo_policy_* 훅은 예외입니다. mpo_hook_policy_init()은 등록 시 호출되는 콜백이며(즉, mac_policy_register() 이후) mpo_hook_policy_initbsd()는 BSD 서브시스템이 제대로 초기화된 후 늦은 등록 중에 호출됩니다.
게다가, mpo_policy_syscall 훅은 모든 kext에 의해 등록될 수 있으며, 이를 통해 개인 ioctl 스타일 호출 인터페이스를 노출할 수 있습니다. 그러면 사용자 클라이언트는 정책 이름과 정수 코드, 선택적 인수를 매개변수로 지정하여 mac_syscall (#381)을 호출할 수 있습니다.
예를 들어, **Sandbox.kext**는 이를 많이 사용합니다.
kext의 **__DATA.__const***를 확인하면 정책 등록 시 사용되는 mac_policy_ops 구조체를 식별할 수 있습니다. 이는 mpo_policy_conf 내부의 오프셋에 포인터가 있기 때문에 찾을 수 있으며, 해당 영역에 있는 NULL 포인터의 수로도 찾을 수 있습니다.
또한, 메모리에서 구조체 **_mac_policy_list**를 덤프하여 정책을 구성한 kext의 목록을 얻는 것도 가능합니다. 이 구조체는 등록된 각 정책으로 업데이트됩니다.
MACF 초기화
MACF는 매우 빨리 초기화됩니다. XNU의 bootstrap_thread에서 설정됩니다: ipc_bootstrap 후 mac_policy_init() 호출이 이루어지며, 이는 mac_policy_list를 초기화하고 잠시 후 mac_policy_initmach()가 호출됩니다. 이 함수는 ALF.kext, AppleMobileFileIntegrity.kext, Quarantine.kext, Sandbox.kext, TMSafetyNet.kext와 같은 Info.plist에 AppleSecurityExtension 키가 있는 모든 Apple kext를 가져와서 로드합니다.
MACF 호출
코드에서 #if CONFIG_MAC 조건부 블록과 같이 MACF에 대한 호출을 찾는 것은 일반적입니다. 또한, 이러한 블록 내에서 특정 작업을 수행하기 위한 권한을 확인하기 위해 MACF를 호출하는 mac_proc_check* 호출을 찾을 수 있습니다. MACF 호출의 형식은 **mac_<object>_<opType>_opName**입니다.
객체는 다음 중 하나입니다: bpfdesc, cred, file, proc, vnode, mount, devfs, ifnet, inpcb, mbuf, ipq, pipe, sysv[msg/msq/shm/sem], posix[shm/sem], socket, kext.
opType은 일반적으로 작업을 허용하거나 거부하는 데 사용되는 check입니다. 그러나 kext가 주어진 작업에 반응할 수 있도록 하는 notify를 찾는 것도 가능합니다.
/** MAC_CHECK performs the designated check by walking the policy* module list and checking with each as to how it feels about the* request. Note that it returns its value via 'error' in the scope* of the caller.*/#defineMAC_CHECK(check, args...) do { \error =0; \MAC_POLICY_ITERATE({ \if (mpc->mpc_ops->mpo_ ## check !=NULL) { \DTRACE_MACF3(mac__call__ ## check,void*, mpc,int, error,int, MAC_ITERATE_CHECK); \int __step_err =mpc->mpc_ops->mpo_ ## check (args); \DTRACE_MACF2(mac__rslt__ ## check,void*, mpc,int, __step_err); \error =mac_error_select(__step_err, error); \} \}); \} while (0)
어떤 것이 모든 등록된 mac 정책을 호출하고 그 함수들을 실행하여 출력 결과를 error 변수에 저장할 것입니다. 이 변수는 성공 코드에 의해 mac_error_select로만 재정의될 수 있으므로, 어떤 체크가 실패하면 전체 체크가 실패하고 행동이 허용되지 않습니다.
그러나 모든 MACF 호출이 행동을 거부하는 데만 사용되는 것은 아니라는 점을 기억하세요. 예를 들어, mac_priv_grant는 매크로 MAC_GRANT를 호출하며, 이는 어떤 정책이 0으로 응답하면 요청된 권한을 부여합니다:
/** MAC_GRANT performs the designated check by walking the policy* module list and checking with each as to how it feels about the* request. Unlike MAC_CHECK, it grants if any policies return '0',* and otherwise returns EPERM. Note that it returns its value via* 'error' in the scope of the caller.*/#defineMAC_GRANT(check, args...) do { \error = EPERM; \MAC_POLICY_ITERATE({ \if (mpc->mpc_ops->mpo_ ## check !=NULL) { \DTRACE_MACF3(mac__call__ ## check,void*, mpc,int, error,int, MAC_ITERATE_GRANT); \int __step_res =mpc->mpc_ops->mpo_ ## check (args); \if (__step_res ==0) { \error =0; \} \DTRACE_MACF2(mac__rslt__ ## check,void*, mpc,int, __step_res); \} \}); \} while (0)
priv_check & priv_grant
이 호출은 bsd/sys/priv.h에서 정의된 (수십 개의) 권한을 확인하고 제공하기 위한 것입니다.
일부 커널 코드는 프로세스의 KAuth 자격 증명과 함께 priv_check_cred()를 호출하여 mac_priv_check를 호출하여 어떤 정책이 권한 부여를 거부하는지 확인한 다음, mac_priv_grant를 호출하여 어떤 정책이 privilege를 부여하는지 확인합니다.
proc_check_syscall_unix
이 후크는 모든 시스템 호출을 가로챌 수 있게 해줍니다. bsd/dev/[i386|arm]/systemcalls.c에서 선언된 함수 unix_syscall를 확인할 수 있으며, 이 코드가 포함되어 있습니다:
어떤 것이 호출 프로세스의 비트마스크에서 현재 시스템 호출이 mac_proc_check_syscall_unix를 호출해야 하는지를 확인합니다. 이는 시스템 호출이 매우 자주 호출되기 때문에 매번 mac_proc_check_syscall_unix를 호출하는 것을 피하는 것이 흥미롭기 때문입니다.
proc_set_syscall_filter_mask() 함수는 프로세스의 비트마스크 시스템 호출을 설정하며, 이는 샌드박스가 샌드박스화된 프로세스에 마스크를 설정하기 위해 호출됩니다.
노출된 MACF 시스템 호출
security/mac.h에서 정의된 일부 시스템 호출을 통해 MACF와 상호작용하는 것이 가능합니다:
/** Extended non-POSIX.1e interfaces that offer additional services* available from the userland and kernel MAC frameworks.*/#ifdef__APPLE_API_PRIVATE__BEGIN_DECLSint__mac_execve(char*fname,char**argv,char**envv,mac_t _label);int__mac_get_fd(int _fd,mac_t _label);int__mac_get_file(constchar*_path,mac_t _label);int__mac_get_link(constchar*_path,mac_t _label);int__mac_get_pid(pid_t _pid,mac_t _label);int__mac_get_proc(mac_t _label);int__mac_set_fd(int _fildes,constmac_t _label);int__mac_set_file(constchar*_path,mac_t _label);int__mac_set_link(constchar*_path,mac_t _label);int__mac_mount(constchar*type,constchar*path,int flags,void*data,struct mac *label);int__mac_get_mount(constchar*path,struct mac *label);int__mac_set_proc(constmac_t _label);int__mac_syscall(constchar*_policyname,int _call,void*_arg);__END_DECLS#endif /*__APPLE_API_PRIVATE*/