MACF означає Систему обов'язкового контролю доступу, яка є системою безпеки, вбудованою в операційну систему для захисту вашого комп'ютера. Вона працює, встановлюючи суворі правила щодо того, хто або що може отримати доступ до певних частин системи, таких як файли, програми та системні ресурси. Застосовуючи ці правила автоматично, MACF забезпечує, що лише авторизовані користувачі та процеси можуть виконувати певні дії, зменшуючи ризик несанкціонованого доступу або шкідливих дій.
Зверніть увагу, що MACF насправді не приймає жодних рішень, оскільки просто перехоплює дії, залишаючи рішення модулям політики (розширенням ядра), які вона викликає, таким як AppleMobileFileIntegrity.kext, Quarantine.kext, Sandbox.kext, TMSafetyNet.kext та mcxalr.kext.
Flow
Процес виконує syscall/mach trap
Відповідна функція викликається всередині ядра
Функція викликає MACF
MACF перевіряє модулі політики, які запитували підключення цієї функції у своїй політиці
MACF викликає відповідні політики
Політики вказують, чи дозволяють або забороняють дію
Apple є єдиною компанією, яка може використовувати KPI MAC Framework.
Labels
MACF використовує мітки, які потім політики перевіряють, чи повинні вони надати доступ чи ні. Код оголошення структури міток можна знайти тут, яка потім використовується всередині struct ucredтут в частині cr_label. Мітка містить прапорці та кількість слотів, які можуть використовуватися політиками MACF для виділення вказівників. Наприклад, Sanbox вказуватиме на профіль контейнера.
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. Це поле вказує, які операції цікавлять політику. Зверніть увагу, що їх сотні, тому можливо обнулити всі з них, а потім вибрати лише ті, які цікавлять політику. З тут:
Практично всі хуки будуть викликані 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`** використовує це дуже часто.Перевіряючи **`__DATA.__const*`** kext, можна ідентифікувати структуру `mac_policy_ops`, яка використовується під час реєстрації політики. Це можливо знайти, оскільки її вказівник знаходиться на зсуві всередині `mpo_policy_conf`, а також через кількість NULL вказівників, які будуть у цій області.Крім того, також можливо отримати список kext, які налаштували політику, скидаючи з пам'яті структуру **`_mac_policy_list`**, яка оновлюється з кожною зареєстрованою політикою.## Ініціалізація MACFMACF ініціалізується дуже швидко. Він налаштовується в `bootstrap_thread` XNU: після `ipc_bootstrap` викликається `mac_policy_init()`, яка ініціалізує `mac_policy_list`, а через кілька моментів викликається `mac_policy_initmach()`. Серед іншого, ця функція отримає всі Apple kext з ключем `AppleSecurityExtension` у їх Info.plist, такі як `ALF.kext`, `AppleMobileFileIntegrity.kext`, `Quarantine.kext`, `Sandbox.kext` та `TMSafetyNet.kext` і завантажить їх.
## Виклики MACFЗвичайно, можна знайти виклики до MACF, визначені в коді, такі як: **`#if CONFIG_MAC`** умовні блоки. Більше того, всередині цих блоків можна знайти виклики до `mac_proc_check*`, які викликають MACF для **перевірки дозволів** на виконання певних дій. Крім того, формат викликів 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, який буде використовуватися для дозволу або заборони дії. Однак також можливо знайти `notify`, що дозволить kext реагувати на дану дію.Ви можете знайти приклад у [https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern\_mman.c#L621](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/bsd/kern/kern\_mman.c#L621):<pre class="language-c"><code class="lang-c">intmmap(proc_t p, struct mmap_args *uap, user_addr_t *retval){[...]#if CONFIG_MACF<strong> error = mac_file_check_mmap(vfs_context_ucred(ctx),</strong> fp->fp_glob, prot, flags, file_pos + pageoff,&maxprot);if (error) {(void)vnode_put(vp);goto bad;}#endif /* MAC */[...]</code></pre>Тоді можливо знайти код `mac_file_check_mmap` у [https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/security/mac\_file.c#L174](https://github.com/apple-oss-distributions/xnu/blob/94d3b452840153a99b38a3a9659680b2a006908e/security/mac\_file.c#L174)
/** 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, викликаючи їх функції та зберігаючи вихідні дані в змінній помилки, яка може бути перевизначена лише 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.
Деякий код ядра викликатиме priv_check_cred() з bsd/kern/kern_priv.c з KAuth обліковими даними процесу та одним з кодів привілеїв, який викликатиме mac_priv_check, щоб перевірити, чи будь-яка політика забороняє надання привілею, а потім викликатиме mac_priv_grant, щоб перевірити, чи будь-яка політика надає привілей.
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(), яка встановлює бітову маску системних викликів у процесі, викликається Sandbox для встановлення масок на пісочницях.
Відкриті системні виклики MACF
Можливо взаємодіяти з MACF через деякі системні виклики, визначені в security/mac.h:
/** 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*/