תאימות למדיניות

בדף הזה מוסבר איך מערכת Android מטפלת בבעיות תאימות למדיניות בעדכונים של הפלטפורמה דרך האוויר (OTA), שבהם הגדרות SELinux חדשות של הפלטפורמה עשויות להיות שונות מהגדרות SELinux ישנות של הספק.

בעלות על אובייקטים ותיוג שלהם

צריך להגדיר בבירור את הבעלות על כל אובייקט כדי להפריד בין המדיניות של הפלטפורמה לבין המדיניות של הספק. לדוגמה, אם תוויות מדיניות הספק /dev/foo ותוויות מדיניות הפלטפורמה /dev/foo ב-OTA הבא, תהיה התנהגות לא מוגדרת כמו דחייה לא צפויה, או גרוע מכך, כשל באתחול. ב-SELinux, זה מתבטא בהתנגשות של תיוגים. לצומת המכשיר יכולה להיות רק תווית אחת, שמוגדרת לפי התווית האחרונה שהוחלה. כתוצאה מכך:

  • תהליכים שזקוקים לגישה לתווית שלא הוחלה בהצלחה מאבדים את הגישה למשאב.
  • תהליכים שמקבלים גישה לקובץ עלולים להיכשל כי נוצר צומת מכשיר שגוי.

התנגשויות בין תוויות של פלטפורמות וספקים יכולות להתרחש בכל אובייקט שיש לו תוויות SELinux, כולל מאפיינים, שירותים, תהליכים, קבצים ושקעים. כדי למנוע את הבעיות האלה, צריך להגדיר בבירור את הבעלות על האובייקטים האלה.

הוספת מרחב שמות לסוגים או למאפיינים

בנוסף להתנגשויות של תוויות, יכולות להיות גם התנגשויות בין שמות של סוגים ומאפיינים ב-SELinux. ‫SELinux לא מאפשר הצהרות מרובות של אותם סוגים ומאפיינים. קומפילציה של מדיניות עם הצהרות כפולות נכשלת. כדי למנוע התנגשויות בין סוגים לבין שמות מאפיינים, מומלץ מאוד שכל הצהרות הספקים יתחילו בקידומת vendor_. לדוגמה, ספקים צריכים להשתמש ב-type vendor_foo, domain; במקום ב-type foo, domain;.

הבעלות על הקובץ

קשה למנוע התנגשויות בקבצים כי המדיניות של הפלטפורמה ושל הספק מספקת בדרך כלל תוויות לכל מערכות הקבצים. בניגוד למתן שמות לסוגים, לא מעשי להשתמש במרחבי שמות לקבצים, כי הרבה מהם נוצרים על ידי ליבת המערכת. כדי למנוע התנגשויות כאלה, כדאי לפעול לפי ההנחיות למתן שמות למערכות קבצים שמפורטות בקטע הזה. ב-Android מגרסה 8.0, אלה המלצות ללא אכיפה טכנית. בעתיד, ההמלצות האלה ייאכפו על ידי חבילת הבדיקות של הספק (VTS).

מערכת (/system)

רק קובץ אימג' של המערכת צריכה לספק תוויות לרכיבים /system עד file_contexts, service_contexts וכו'. אם מוסיפים תוויות לרכיבים /system במדיניות הספק, יכול להיות שעדכון OTA רק של המסגרת לא יתאפשר.

ספק (/vendor)

מדיניות SELinux ב-AOSP כבר מסמנת חלקים במחיצה vendor שהפלטפורמה מקיימת איתה אינטראקציה, וכך מאפשרת לכתוב כללי SELinux לתהליכי פלטפורמה כדי שיוכלו לתקשר עם חלקים במחיצה vendor או לגשת אליהם. דוגמאות:

‫/vendor path תווית שסופקה על ידי הפלטפורמה תהליכי הפלטפורמה בהתאם לתווית
/vendor(/.*)? vendor_file כל לקוחות ה-HAL ב-framework‏, ueventd וכו'.
/vendor/framework(/.*)? vendor_framework_file dex2oat, appdomain וכו'.
/vendor/app(/.*)? vendor_app_file dex2oat, installd, idmap וכו'.
/vendor/overlay(/.*) vendor_overlay_file system_server, zygote, idmap וכו'.

לכן, כשמתייגים קבצים נוספים במחיצה vendor, צריך לפעול לפי כללים ספציפיים (שנאכפים באמצעות neverallows):

  • vendor_file חייבת להיות תווית ברירת המחדל לכל הקבצים במחיצה vendor. מדיניות הפלטפורמה מחייבת את זה כדי לגשת להטמעות של HAL passthrough.
  • לכל exec_types חדש שנוסף במחיצה vendor באמצעות מדיניות הספק צריך להיות מאפיין vendor_file_type. האכיפה מתבצעת באמצעות neverallows.
  • כדי למנוע התנגשויות עם עדכונים עתידיים של הפלטפורמה או המסגרת, אל תתנו תווית לקבצים אחרים מלבד exec_types במחיצה vendor.
  • כל התלויות בספרייה עבור HALs באותו תהליך שזוהו ב-AOSP צריכות להיות מסומנות בתווית same_process_hal_file.

Procfs ‏ (/proc)

אפשר להוסיף תוויות לקבצים ב-/proc רק באמצעות התווית genfscon. ב-Android 7.0, גם המדיניות של הפלטפורמה וגם המדיניות של הספק השתמשו ב-genfscon כדי לתייג קבצים ב-procfs.

המלצה: רק תוויות מדיניות של פלטפורמה /proc. אם לספקי תהליכים נדרשת גישה לקבצים ב-/proc שכרגע מסומנים בתווית ברירת המחדל (proc), מדיניות הספק לא צריכה לסמן אותם באופן מפורש, אלא להשתמש בסוג proc הגנרי כדי להוסיף כללים לדומיינים של ספקים. כך עדכוני הפלטפורמה יוכלו להתאים לממשקי ליבה עתידיים שנחשפים דרך procfs ולתייג אותם באופן מפורש לפי הצורך.

‫Debugfs (/sys/kernel/debug)

אפשר לתייג את Debugfs גם ב-file_contexts וגם ב-genfscon. ב-Android מגרסה 7.0 עד גרסה 10, התווית של הפלטפורמה וגם התווית של הספק debugfs.

ב-Android 11, אי אפשר לגשת אל debugfs או לטעון אותו במכשירי ייצור. יצרני המכשירים צריכים להסיר את debugfs.

Tracefs (/sys/kernel/debug/tracing)

אפשר לתייג את Tracefs גם ב-file_contexts וגם ב-genfscon. ב-Android 7.0, רק תוויות הפלטפורמה tracefs.

המלצה: רק הפלטפורמה יכולה להוסיף את התווית tracefs.

Sysfs (/sys)

אפשר להוסיף תוויות לקבצים ב-/sys באמצעות file_contexts וגם באמצעות genfscon. ב-Android 7.0, גם הפלטפורמה וגם הספק משתמשים ב-genfscon כדי לתייג קבצים ב-sysfs.

המלצה: יכול להיות שהפלטפורמה תסמן צמתים sysfs שאינם ספציפיים למכשיר. אחרת, רק ספקים יכולים להוסיף תוויות לקבצים.

tmpfs (/dev)

יכול להיות שקבצים ב-/dev יסומנו בתווית ב-file_contexts. ב-Android 7.0, קובצי התוויות של הפלטפורמה ושל הספק נמצאים כאן.

המלצה: הספק יכול לתייג רק קבצים ב-/dev/vendor (לדוגמה, /dev/vendor/foo,‏ /dev/vendor/socket/bar).

Rootfs ‏ (/)

יכול להיות שקבצים ב-/ יסומנו בתווית ב-file_contexts. ב-Android 7.0, קובצי התוויות של הפלטפורמה והספק נמצאים כאן.

המלצה: רק המערכת יכולה להוסיף תוויות לקבצים ב-/.

נתונים (/data)

הנתונים מתויגים באמצעות שילוב של file_contexts ושל seapp_contexts.

המלצה: לא לאפשר סיווג של ספקים מחוץ ל-/data/vendor. רק הפלטפורמה יכולה לתייג חלקים אחרים של /data.

גרסת תוויות Genfs

החל מרמת API של הספק‏ 202504, תוויות SELinux חדשות יותר שהוקצו באמצעות genfscon ב-system/sepolicy/compat/plat_sepolicy_genfs_ver.cil הן אופציונליות למחיצות vendor ישנות יותר. כך מחיצות ישנות יותר של vendor יכולות לשמור על ההטמעה הקיימת של SEPolicy. השליטה בזה מתבצעת באמצעות משתנה ה-Makefile‏ BOARD_GENFS_LABELS_VERSION, שמאוחסן ב-/vendor/etc/selinux/genfs_labels_version.txt.

לדוגמה:

  • ב-API של הספק ברמת API 202404, הצומת /sys/class/udc מסומן בתווית sysfs כברירת מחדל.
  • החל מרמת ספק API‏ 202504, ‏ /sys/class/udc מסומן כ-sysfs_udc.

עם זאת, יכול להיות שהמחיצות /sys/class/udc נמצאות בשימוש ב-vendor ברמת API‏ 202404, עם התווית sysfs שמוגדרת כברירת מחדל או עם תווית ספציפית לספק. הוספת התווית sysfs_udc ל-/sys/class/udc ללא תנאי עלולה לפגוע בתאימות למחיצות vendor האלה. אם מסמנים את התיבה BOARD_GENFS_LABELS_VERSION, הפלטפורמה ממשיכה להשתמש בתוויות ובהרשאות הקודמות למחיצות הישנות יותר של vendor.

הערך של BOARD_GENFS_LABELS_VERSION יכול להיות גדול או שווה לרמת ה-API של הספק. לדוגמה, vendor מחיצות שמשתמשות ברמת API‏ 202404 יכולות להגדיר את BOARD_GENFS_LABELS_VERSION ל-202504 כדי להשתמש בתוויות חדשות שהוצגו ב-202504. רשימת תוויות של תכונות גנרטיביות ספציפיות לגרסה 202504

כשמתייגים צמתים של genfscon, הפלטפורמה צריכה להתחשב במחיצות ישנות יותר של vendor ולהטמיע מנגנוני חזרה לצורך תאימות כשצריך. הפלטפורמה יכולה להשתמש בספריות שמתאימות רק לפלטפורמה כדי לשלוח שאילתה לגבי גרסת התוויות של genfs.

מדיניות ציבורית של פלטפורמה

מדיניות SELinux של הפלטפורמה מחולקת לפרטית ולציבורית. מדיניות הפלטפורמה הציבורית מורכבת מסוגים וממאפיינים שתמיד זמינים ברמת ספק ה-API, ומשמשת כ-API בין הפלטפורמה לספק. המדיניות הזו מוצגת לכותבי מדיניות של ספקים כדי לאפשר לספקים ליצור קובצי מדיניות של ספקים. כשמשלבים אותם עם המדיניות הפרטית של הפלטפורמה, מתקבלת מדיניות מתפקדת באופן מלא למכשיר. מדיניות הפלטפורמה לציבור מוגדרת ב-system/sepolicy/public.

לדוגמה, סוג vendor_init, שמייצג את תהליך האתחול בהקשר של ספק, מוגדר בקטע system/sepolicy/public/vendor_init.te:

type vendor_init, domain;

ספקים יכולים להשתמש בסוג vendor_init כדי לכתוב כללי מדיניות בהתאמה אישית:

# Allow vendor_init to set vendor_audio_prop in vendor's init scripts
set_prop(vendor_init, vendor_audio_prop)

מאפייני תאימות

מדיניות SELinux היא אינטראקציה בין סוגי מקור ויעד עבור מחלקות אובייקטים והרשאות ספציפיות. לכל אובייקט (לדוגמה, תהליכים, קבצים) שמושפע ממדיניות SELinux יכול להיות רק סוג אחד, אבל לסוג הזה יכולים להיות כמה מאפיינים.

המדיניות כתובה בעיקר במונחים של סוגים קיימים. בדוגמה הזו, גם vendor_init וגם debugfs הם סוגים:

allow vendor_init debugfs:dir { mounton };

השיטה הזו עובדת כי המדיניות נכתבה על סמך ידע בכל הסוגים. עם זאת, אם במדיניות הספק ובמדיניות הפלטפורמה נעשה שימוש בסוגים ספציפיים, והתווית של אובייקט ספציפי משתנה רק באחת מהמדיניות האלה, יכול להיות שהמדיניות השנייה תכיל מדיניות שהסתמכה בעבר על גישה שהושגה או אבדה. לדוגמה, נניח שתוויות המדיניות של הפלטפורמה מסמנות צמתים של sysfs כ-sysfs:

/sys(/.*)? u:object_r:sysfs:s0

מדיניות הספק מעניקה גישה אל /sys/usb, שמסומן כ-sysfs:

allow vendor_init sysfs:chr_file rw_file_perms;

אם המדיניות של הפלטפורמה משתנה כך שהתווית /sys/usb מוגדרת כ-sysfs_usb, המדיניות של הספק נשארת ללא שינוי, אבל ל-vendor_init אין יותר גישה ל-/sys/usb כי אין מדיניות לסוג החדש sysfs_usb:

/sys/usb u:object_r:sysfs_usb:s0

כדי לפתור את הבעיה, ב-Android מוצג קונספט של מאפיינים עם גרסאות. בזמן ההידור, מערכת ה-Build מתרגמת באופן אוטומטי את הסוגים הציבוריים של הפלטפורמה שמשמשים במדיניות הספק למאפיינים האלה עם הגרסה. התרגום הזה מופעל על ידי מיפוי קבצים שמשייכים מאפיין עם גרסה לסוג ציבורי אחד או יותר מהפלטפורמה.

לדוגמה, נניח שהמוצר /sys/usb מסומן בתווית sysfs במדיניות הפלטפורמה 202504, ומדיניות הספק 202504 מעניקה ל-vendor_init גישה ל-/sys/usb. במקרה הזה:

  • מדיניות הספק כותבת כלל allow vendor_init sysfs:chr_file rw_file_perms;, כי /sys/usb מסומן בתווית sysfs במדיניות הפלטפורמה 202504. כשמערכת build מהדרת את מדיניות הספק, היא מתרגמת אוטומטית את הכלל ל-allow vendor_init_202504 sysfs_202504:chr_file rw_file_perms;. המאפיינים vendor_init_202504 ו-sysfs_202504 תואמים לסוגים vendor_init ו-sysfs, שהם הסוגים שמוגדרים על ידי הפלטפורמה.
  • מערכת ה-build יוצרת קובץ מיפוי זהויות /system/etc/selinux/mapping/202504.cil. מכיוון שגם המחיצות system וגם vendor משתמשות באותה גרסה של 202504, קובץ המיפוי מכיל מיפויי זהויות מ-type_202504 ל-type. לדוגמה: vendor_init_202504 ממופה ל-vendor_init, ו- sysfs_202504 ממופה ל-sysfs:
    (typeattributeset sysfs_202504 (sysfs))
    (typeattributeset vendor_init_202504 (vendor_init))
    ...

כשהגרסה משתנה מ-202504 ל-202604, נוצר קובץ מיפוי חדש למחיצות 202504 vendor בתיקייה system/sepolicy/private/compat/202504/202504.cil, והוא מותקן בתיקייה /system/etc/selinux/mapping/202504.cil למחיצות 202604 או למחיצות חדשות יותר system. בתחילה, קובץ המיפוי הזה מכיל מיפויי זהויות, כפי שמתואר בהמשך. אם תווית חדשה sysfs_usb בשביל /sys/usb תתווסף למדיניות הפלטפורמה 202604, קובץ המיפוי יעודכן כדי למפות את sysfs_202504 ל-sysfs_usb:

(typeattributeset sysfs_202504 (sysfs sysfs_usb))
(typeattributeset vendor_init_202504 (vendor_init))
...

העדכון הזה מאפשר לכלל המדיניות של הספק שהומר allow vendor_init_202504 sysfs_202504:chr_file rw_file_perms; להעניק באופן אוטומטי גישה vendor_init לסוג החדש sysfs_usb.

כדי לשמור על תאימות למחיצות vendor ישנות יותר, בכל פעם שמוסיפים סוג חדש של נתונים שגלויים לכולם, צריך למפות את הסוג הזה לפחות לאחד מהמאפיינים עם הגרסאות בקובץ המיפוי system/sepolicy/private/compat/ver/ver.cil, או לציין אותו בקטע system/sepolicy/private/compat/ver/ver.ignore.cil כדי לציין שאין סוג תואם בגרסאות הקודמות של הספק.

השילוב של מדיניות הפלטפורמה, מדיניות הספק וקובץ המיפוי מאפשר למערכת להתעדכן בלי לעדכן את מדיניות הספק. בנוסף, ההמרה של המאפיינים לגרסה מתבצעת באופן אוטומטי, כך שמדיניות הספק לא צריכה לטפל בניהול הגרסאות, והיא ממשיכה להשתמש בסוגים הציבוריים כמו שהם.

מדיניות ציבורית של מערכת_ext ומדיניות ציבורית של מוצר

החל מ-Android 11, למחיצות system_ext ו-product מותר לייצא את הסוגים הציבוריים הייעודיים שלהן למחיצה vendor. בדומה למדיניות הציבורית של הפלטפורמה, מדיניות הספק משתמשת בסוגים ובכללים שמתורגמים אוטומטית למאפיינים עם גרסאות, למשל מ-type ל-type_ver, כאשר ver הוא רמת ה-API של הספק במחיצה vendor.

אם המחיצות system_ext ו-product מבוססות על אותה גרסת פלטפורמה ver, מערכת ה-build יוצרת קובצי מיפוי בסיסיים ל-system_ext/etc/selinux/mapping/ver.cil ול-product/etc/selinux/mapping/ver.cil, שמכילים מיפויי זהויות מ-type ל-type_ver. ספק המדיניות יכול לגשת אל type באמצעות מאפיין עם גרסה type_ver.

אם רק המחיצות system_ext ו-product מתעדכנות, למשל מ-ver ל-ver+1 (או לגרסה מאוחרת יותר), בזמן שהמחיצה vendor נשארת בגרסה ver, יכול להיות שספק המדיניות יאבד את הגישה לסוגים של המחיצות system_ext ו-product. כדי למנוע שבירה, במחיצות system_ext ו-product צריך לספק קובצי מיפוי מסוגים קונקרטיים למאפייני type_ver. כל שותף אחראי לתחזוקת קובצי המיפוי, אם הוא תומך בחלוקת מחיצות של ver vendor עם ver+1 (או גרסה מתקדמת יותר) system_ext ומחיצות product.

כדי להתקין קובצי מיפוי במחיצות system_ext ו-product, ספקי מכשירים או ספקים צריכים:

  1. מעתיקים את קובצי מיפוי הבסיס שנוצרו מהמחיצות ver system_ext ו-product אל עץ המקור שלהם.
  2. משנים את קובצי המיפוי לפי הצורך.
  3. מתקינים את קובצי המיפוי ב-ver+1 (או בגרסה מתקדמת יותר) system_ext ובמחיצות product.

לדוגמה, נניח שלמחיצה 202504 system_ext יש סוג ציבורי אחד בשם foo_type. אחר כך system_ext/etc/selinux/mapping/202504.cil במחיצה 202504 system_ext זה נראה כך:

(typeattributeset foo_type_202504 (foo_type))
(expandtypeattribute foo_type_202504 true)
(typeattribute foo_type_202504)

אם bar_type נוסף למחיצה 202604 system_ext, ואם צריך למפות את bar_type אל foo_type עבור המחיצה 202504 vendor, אפשר לעדכן את 202504.cil מ-(typeattributeset foo_type_202504 (foo_type)) ל-(typeattributeset foo_type_202504 (foo_type bar_type)) ואז להתקין אותו במחיצה 202604 system_ext. מחיצת 202504 vendor יכולה להמשיך לגשת אל foo_type וbar_type של 202604 system_ext.

שינויים בשיוך ב-Android מגרסה 9

במכשירים שמשדרגים ל-Android 9 אפשר להשתמש במאפיינים הבאים, אבל במכשירים שמופעלים עם Android 9 אסור להשתמש בהם.

מאפיינים של מפרים

‫Android 9 כולל את המאפיינים הבאים שקשורים לדומיין:

  • data_between_core_and_vendor_violators. מאפיין לכל הדומיינים שלא עומדים בדרישה לא לשתף קבצים לפי נתיב בין vendor ל-coredomains. תהליכים של פלטפורמות וספקים לא צריכים להשתמש בקבצים בדיסק כדי לתקשר (ABI לא יציב). המלצה:
    • קוד הספק צריך להשתמש ב-/data/vendor.
    • המערכת לא צריכה להשתמש ב-/data/vendor.
  • system_executes_vendor_violators. Attribute for all system domains (except init and shell domains) that violate the requirement of not executing vendor binaries. ההרצה של קבצים בינאריים של ספקים כוללת API לא יציב. הפלטפורמה לא אמורה להריץ קבצים בינאריים של ספקים באופן ישיר. המלצה:
    • יחסי תלות כאלה של פלטפורמות בקבצים בינאריים של ספקים חייבים להיות מאחורי HIDL HAL.

      או

    • coredomains שצריכים גישה לקבצים בינאריים של ספקים צריכים לעבור למחיצה vendor וכך להפסיק להיות coredomain.

מאפיינים לא מהימנים

לאפליקציות לא מהימנות שמארחות קוד שרירותי לא צריכה להיות גישה לשירותי HwBinder, למעט לאפליקציות שנחשבות בטוחות מספיק לגישה מאפליקציות כאלה (ראו שירותים בטוחים בהמשך). יש שתי סיבות עיקריות לכך:

  1. שרתי HwBinder לא מבצעים אימות של לקוחות כי HIDL לא חושף כרגע מידע על מזהה המשתמש של המתקשר. גם אם HIDL היה חושף נתונים כאלה, הרבה שירותי HwBinder פועלים ברמה נמוכה יותר מזו של אפליקציות (למשל, HAL) או שלא יכולים להסתמך על זהות האפליקציה לצורך הרשאה. לכן, כדי להיות בטוחים, ההנחה שמוגדרת כברירת מחדל היא שכל שירות HwBinder מתייחס לכל הלקוחות שלו כבעלי הרשאה שווה לבצע פעולות שהשירות מציע.
  2. שרתי HAL (קבוצת משנה של שירותי HwBinder) מכילים קוד עם שיעור גבוה יותר של בעיות אבטחה בהשוואה לרכיבי system/core, ויש להם גישה לשכבות התחתונות של הערימה (עד לרמת החומרה), ולכן הם מגדילים את הסיכוי לעקוף את מודל האבטחה של Android.

שירותים בטוחים

שירותים בטוחים כוללים:

  • same_process_hwservice. השירותים האלה (בהגדרה) פועלים בתהליך של הלקוח, ולכן יש להם את אותה גישה כמו לדומיין של הלקוח שבו התהליך פועל.
  • coredomain_hwservice. השירותים האלה לא יוצרים סיכונים שקשורים לסיבה מספר 2.
  • hal_configstore_ISurfaceFlingerConfigs. השירות הזה מיועד לשימוש בכל דומיין.
  • hal_graphics_allocator_hwservice. הפעולות האלה מוצעות גם על ידי שירות Binder‏, שאפליקציות מורשות יכולות לגשת אליו.surfaceflinger
  • hal_omx_hwservice. זוהי גרסת HwBinder של mediacodec שירות Binder, שאפליקציות מורשות לגשת אליו.
  • hal_codec2_hwservice. זו גרסה חדשה יותר של hal_omx_hwservice.

מאפיינים שאפשר להשתמש בהם

לכל האפליקציות hwservices שלא נחשבות בטוחות יש את המאפיין untrusted_app_visible_hwservice. לשרתי ה-HAL המתאימים יש את המאפיין untrusted_app_visible_halserver. במכשירים עם Android 9 לא יכול להיות שימוש במאפיין untrusted.

המלצה:

  • במקום זאת, אפליקציות לא מהימנות צריכות לתקשר עם שירות מערכת שמתקשר עם HIDL HAL של הספק. לדוגמה, אפליקציות יכולות לתקשר עם binderservicedomain, ואז mediaserver (שהוא binderservicedomain) בתורו מתקשר עם hal_graphics_allocator.

    או

  • לאפליקציות שזקוקות לגישה ישירה ל-vendor HALs צריך להיות דומיין sepolicy משלהן שמוגדר על ידי הספק.

בדיקות של מאפייני קובץ

‫Android 9 כולל בדיקות בזמן בנייה שמוודאות שלכל הקבצים במיקומים ספציפיים יש את המאפיינים המתאימים (לדוגמה, לכל הקבצים ב-sysfs יש את המאפיין הנדרש sysfs_type).

תיוג הקשרים של SELinux

כדי לתמוך בהבחנה בין מדיניות SELinux של פלטפורמה לבין מדיניות SELinux של ספק, המערכת יוצרת קובצי הקשר של SELinux באופן שונה כדי לשמור אותם בנפרד.

הקשרים של קבצים

ב-Android מגרסה 8.0 ומעלה, בוצעו השינויים הבאים בחשבון file_contexts:

  • כדי למנוע תקורה נוספת של קומפילציה במכשיר במהלך האתחול, file_contexts מפסיק להתקיים בפורמט הבינארי. במקום זאת, הם קריאים, קובץ טקסט של ביטוי רגולרי כמו {property, service}_contexts (כמו שהיה לפני גרסה 7.0).
  • הנתונים של file_contexts מחולקים לשני קבצים:
    • plat_file_contexts
      • פלטפורמת Android‏ file_context שאין לה תוויות ספציפיות למכשיר, למעט תיוג חלקים של מחיצת /vendor שחייבים לתייג במדויק כדי להבטיח תפקוד תקין של קובצי sepolicy.
      • הוא חייב להיות במחיצה system בכתובת /system/etc/selinux/plat_file_contexts במכשיר, להיטען על ידי init בתחילת ההפעלה יחד עם file_context הספק.
    • vendor_file_contexts
      • file_context ספציפי למכשיר, נוצר משילוב של file_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים של Boardconfig.mk המכשיר.
      • התוסף צריך להיות מותקן ב-/vendor/etc/selinux/vendor_file_contexts במחיצה vendor, וצריך לטעון אותו על ידי init בתחילת ההפעלה יחד עם הפלטפורמה file_context.

הקשרים של הנכס

ב-Android מגרסה 8.0, הקובץ property_contexts מחולק לשני קבצים:

  • plat_property_contexts
    • פלטפורמת Android‏ property_context שלא כוללת תוויות ספציפיות למכשיר.
    • הוא חייב להיות במחיצה system בכתובת /system/etc/selinux/plat_property_contexts ולהיטען על ידי init בהתחלה יחד עם property_contexts הספק.
  • vendor_property_contexts
    • property_context ספציפי למכשיר שנוצר משילוב של property_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים של Boardconfig.mk המכשיר.
    • הוא חייב להיות במחיצה vendor בכתובת /vendor/etc/selinux/vendor_property_contexts, ולהיטען על ידי init בתחילת התהליך יחד עם הפלטפורמה property_context

הקשרים של השירות

ב-Android 8.0, הקובץ service_contexts מחולק לקבצים הבאים:

  • plat_service_contexts
    • service_context ספציפי לפלטפורמת Android עבור servicemanager. ל-service_context אין תוויות ספציפיות למכשיר.
    • הקובץ חייב להיות במחיצה system בנתיב /system/etc/selinux/plat_service_contexts, והוא נטען על ידי servicemanager בתחילת התהליך יחד עם הספק service_contexts.
  • vendor_service_contexts
    • service_context ספציפי למכשיר, נוצר משילוב של service_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים של Boardconfig.mk המכשיר.
    • הקובץ חייב להיות במחיצה vendor בנתיב /vendor/etc/selinux/vendor_service_contexts, והוא נטען על ידי servicemanager בתחילת התהליך יחד עם הפלטפורמה service_contexts.
    • למרות שאפליקציית servicemanager מחפשת את הקובץ הזה בזמן האתחול, במכשיר TREBLE שעומד בדרישות התאימות, הקובץ vendor_service_contexts לא צריך להתקיים. הסיבה לכך היא שכל האינטראקציות בין vendor ל-system חייבות לעבור דרך hwservicemanager/hwbinder.
  • plat_hwservice_contexts
    • פלטפורמת Android‏ hwservice_context עבור hwservicemanager שלא הוגדרו לה תוויות ספציפיות למכשיר.
    • הקובץ חייב להיות במחיצה system בנתיב /system/etc/selinux/plat_hwservice_contexts, והוא נטען על ידי hwservicemanager בתחילת התהליך יחד עם vendor_hwservice_contexts.
  • vendor_hwservice_contexts
    • hwservice_context ספציפי למכשיר, נוצר משילוב של hwservice_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים של Boardconfig.mk המכשיר.
    • הוא חייב להיות במחיצה vendor בכתובת /vendor/etc/selinux/vendor_hwservice_contexts ולהיטען על ידי hwservicemanager בתחילת התהליך יחד עם plat_service_contexts.
  • vndservice_contexts
    • service_context ספציפי למכשיר עבור vndservicemanager שנוצר משילוב של vndservice_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS ב-Boardconfig.mk של המכשיר.
    • הקובץ הזה צריך להיות במחיצה vendor בכתובת /vendor/etc/selinux/vndservice_contexts, וצריך לטעון אותו באמצעות vndservicemanager בהתחלה.

הקשרים של Seapp

ב-Android מגרסה 8.0, הקובץ seapp_contexts מחולק לשני קבצים:

  • plat_seapp_contexts
    • פלטפורמת Android seapp_context ללא שינויים ספציפיים למכשיר.
    • צריך להיות ממוקם במחיצה system ב-/system/etc/selinux/plat_seapp_contexts.
  • vendor_seapp_contexts
    • תוסף ספציפי למכשיר לפלטפורמה seapp_context שנוצר על ידי שילוב של seapp_contexts שנמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים Boardconfig.mk של המכשיר.
    • חייבים להתגורר במחיצה vendor בכתובת /vendor/etc/selinux/vendor_seapp_contexts.

הרשאות MAC

ב-Android מגרסה 8.0, הקובץ mac_permissions.xml מחולק לשני קבצים:

  • פלטפורמה mac_permissions.xml
    • פלטפורמת Android mac_permissions.xml ללא שינויים ספציפיים למכשיר.
    • צריך להיות ממוקם במחיצה system ב-/system/etc/selinux/.
  • לא בפלטפורמה mac_permissions.xml
    • תוסף ספציפי למכשיר לפלטפורמה mac_permissions.xml נוצר מתוך mac_permissions.xml נמצא בספריות שאליהן מפנה BOARD_SEPOLICY_DIRS בקבצים של Boardconfig.mk המכשיר.
    • צריך להיות ממוקם במחיצה vendor ב-/vendor/etc/selinux/.

שינויים בזיכרון המשותף ב-Android 17

החל מ-Android 17, במכשירים שמופעלים עם המאפיינים הבאים צריך להפעיל את יכולת המדיניות memfd_class ולעדכן את המדיניות שקשורה לזיכרון המשותף כדי לתמוך באובייקטים של המחלקה memfd_file:

  • ספקים ויצרני ציוד מקורי יכולים לעדכן את מדיניות הספקים שלהם כדי לתמוך ב-memfd באמצעות API של ספקים ברמה 202604 ומעלה. הוא גם מאפשר למכשירים קיימים לשדרג לגרסאות גבוהות יותר של Android בלי לדרוש עדכון של מחיצת הספק שלהם.
  • android16-6.12 או גרסה מתקדמת יותר של הליבה, כי הליבות האלה תומכות בתכונה memfd_class, שנדרשת להטמעה של מדיניות מפורטת ל-memfd.

הפעלת היכולת memfd_class במדיניות

עד לאחרונה, SELinux תייג את memfd כקובץ עם אותו סוג כמו מערכת הקבצים הבסיסית שלו – tmpfs. כך אי אפשר להבחין בין memfd לבין קובץ אחר בהרכבה של tmpfs מנקודת מבט של מדיניות. עכשיו, SELinux מתייג memfd עם הקשר האבטחה של תהליך ההקצאה, ומתייחס לmemfds כאובייקטים מסוג memfd_file. הפונקציונליות הזו מוגנת על ידי יכולת המדיניות memfd_class כדי לשמור על תאימות לאחור עם סביבות ישנות יותר של מרחב המשתמש.

כדי להפעיל את היכולת של מדיניות memfd_class, יוצרים קובץ policy_capabilities בתיקייה BOARD_VENDOR_SEPOLICY_DIRS. הקובץ צריך להכיל את הרשומה הבאה:

# $BOARD_VENDOR_SEPOLICY_DIRS/*/policy_capabilities
policycap memfd_class;

לאחר מכן, בונים מחדש את התמונות ומשדרגים אותן למכשיר כדי לוודא שהיכולת מופעלת.

אימות שהיכולת של מדיניות memfd_class מופעלת

כדי לבדוק את הסטטוס של יכולת המדיניות memfd_class, משתמשים בפקודה הבאה:

adb shell 'cat /sys/fs/selinux/policy_capabilities/memfd_class'

אם התוצאה היא 1, היכולת של מדיניות memfd_class מופעלת. אחרת, התכונה לא מופעלת.

העברת מדיניות קיימת אל memfd

בתהליכים מסוימים נעשה שימוש בפקודת המאקרו tmpfs_domain() במדיניות כדי לגשת אל memfds ולמרחב השמות שלו, למשל:

# foo.te
tmpfs_domain(foo)

המשמעות היא:

# foo.te
type_transition foo tmpfs:file foo_tmpfs;
allow foo foo_tmpfs:file { read write getattr map };

ומאפשר לתהליך bar לגשת לmemfds של תהליך foo באופן הבא:

# bar.te
allow bar foo_tmpfs:file { read write getattr map };

אם האפשרות להשתמש במדיניות memfd_class מופעלת, אין יותר צורך במאקרו tmpfs_domain(), כי מדיניות הפלטפורמה עודכנה כך שכל תהליך יכול ליצור ולהשתמש ב-memfds משלו, כמו שרואים כאן:

# system/sepolicy/private/domain.te
allow domain self:memfd_file { create read write getattr map };

ולתהליך bar יש גישה ל-memfds שנוצר על ידי תהליך foo באופן הבא:

# bar.te
allow bar foo:memfd_file { read write getattr map };

המדיניות של הפלטפורמה עודכנה כדי להתייחס לשימושים קיימים ב-memfd. עם זאת, צריך לעדכן מדיניות ספציפית לספקים ולמכשירים שנעשה בה שימוש בתוויות tmpfs כדי להשתמש בתוויות memfd_file. אם המדיניות משותפת בין מערכות על שבב (SoC) או מכשירים שאין להם רמת API של ספק 202604 ומעלה, מומלץ לשמור את מדיניות tmpfs מדור קודם לצד מדיניות memfd_file החדשה לצורך תאימות.

זיהוי דחיות של AVC שקשורות ל-memfd

אפשר לאחזר דחיות שקשורות ל-Memfd באמצעות הפקודה הבאה:

adb shell logcat -d -b events | grep memfd

דחיות של avc עם tmpfs כיעד

בדוגמה הבאה מוצגת דחייה של avc שנתקלה בתהליך שניסה לכתוב ל-memfd שלא הייתה לו הרשאה לכתוב אליו:

audit(0.0:539): avc:  denied  { write } for  comm="binder:665_1" name="memfd:MessageQueue"
dev="tmpfs" ino=8324 scontext=u:r:mediacodec:s0 tcontext=u:object_r:tmpfs:s0 tclass=file
permissive=0

כשמפעילים את יכולת המדיניות memfd_class, הקשר של היעד של memfd הוא הקשר האבטחתי של תהליך ההקצאה, ולא tmpfs, והסיווג של היעד הוא memfd_file, ולא file. לכן, אם אתם רואים דחיות של avc שקשורות ל-memfd, כאשר memfd המדובר מסומן כקובץ tmpfs, יכולת המדיניות memfd_class לא מופעלת.

דחיות של avc עם memfd_file כסיווג היעד

בדוגמה הבאה מוצגת דחייה של avc שנתקלה בתהליך שמנסה לכתוב ל-memfd שלא הייתה לו הרשאה לכתוב אליו, והיכולת של מדיניות memfd_class הופעלה, וגם שורה נוספת ש-logd פולט אחרי הדחייה עם אותה חותמת זמן:

audit(0.0:86): avc: denied { read } for
path=2F6D656D66643A4D6564696142756666657247726F7570202864656C6574656429 ino=512 dev=""
scontext=u:r:mediaserver:s0 tcontext=u:object_r:mediaextractor:s0 tclass=memfd_file

auditd  : Decoded path for audit(0.0:86): /memfd:MediaBufferGroup (deleted)

חותמת הזמן התואמת מציינת שהבקשה Decoded path for … log קשורה לדחייה avc עם חותמת הזמן 0.0.86. היומן הזה מפענח את המחרוזת ההקסהדצימלית מתוך ערך הנתיב ב-avc denial, ומספק את השם של אזור הזיכרון memfd, שיכול לעזור להבין איזה מאגר משותף. ההקשר של המקור וההקשר של היעד עוזרים להבין אילו תהליכים צריכים לשתף זיכרון. מהדוגמה הקודמת ברור שתהליך mediaserver צריך לקבל גישה ל-memfds של mediaextractor. לכן, המדיניות המתאימה היא:

# mediaserver.te
allow mediaserver mediaextractor:memfd_file { getattr read write map };

עדכונים של תחום האבטחה ב-Android 17

ASharedMemory_create() API ב-Android 17 מטמיע לוגיקה מותנית כדי לבחור בין מנהל ההתקן ashmem מדור קודם לבין מסגרת memfd להקצאות של זיכרון משותף.

במכשירים שעומדים בדרישות memfd (רמת API של הספק 202604 ומעלה וליבת android16-6.12 ומעלה), ה-API מעריך את targetSdkVersion של האפליקציה שקוראת לו. אם גרסת ה-SDK לטירגוט היא 37 ומעלה, מוקצה memfd. כך המפתחים יכולים לפתור בעיות שהם נתקלים בהן במהלך השדרוג של גרסת ה-SDK שהם מטרגטים.

אם המכשיר לא עומד בדרישות המוקדמות של memfd's, ‏ ASharedMemory חוזר ל-ashmem. כך נשמרת התאימות למכשירים משודרגים עם מחיצות או ליבות ישנות יותר של ספקים.

כדי לאכוף את המעבר הזה, מדיניות SELinux של הפלטפורמה חוסמת אפליקציות שמיועדות לגרסה 37 של SDK ומעלה בתחומי האבטחה platform_app, priv_app ו-untrusted_app, כך שהן לא יכולות לפתוח את /dev/ashmem ולהפעיל פקודות ashmem ioctl ב-memfd. ההפרדה הזו מתבצעת על ידי פיצול של דומייני האפליקציות האלה על סמך גרסת ה-SDK של היעד. הגרסה הזו כוללת את דומייני האבטחה platform_app_36, priv_app_36 ו-untrusted_app_34, ששומרים על הרשאות פתיחה של ashmem ויכולים להפעיל פקודות ashmem ioctl ב-memfds, יחד עם דומיינים אחרים של אפליקציות.

בגרסת Android עתידית, קבוצת האפליקציות ששומרות על ההרשאות לפתוח את מכשיר ashmem ולהפעיל פקודות ashmem ioctl ב-memfds תצומצם רק ל-platform_app_36,‏ priv_app_36 ו-untrusted_app_34 ולדומיינים של אפליקציות לא מהימנות בגרסאות SDK ישנות יותר.

צריך לעדכן מדיניות SELinux מותאמת אישית של ספקים או יצרני ציוד מקורי (OEM) עבור אפליקציות שמוצמדות לגרסת ה-SDK שהן מיועדות לה, כדי שתתאים לשינויים בתחום, כפי שמפורט בקטעים הבאים.

עדכונים בדומיין SELinux של platform_app

הדומיין platform_app מחולק על סמך targetSdkVersion האפליקציה. לאפליקציות פלטפורמה שמיועדות לגרסה 37 ואילך של SDK מוקצה הדומיין platform_app, ולאפליקציות שמיועדות לגרסה 36 ומטה של SDK מוקצה הדומיין platform_app_36. בדומיין platform_app_36 עדיין אפשר לפתוח את /dev/ashmem לצורך תאימות לאחור. כדי לפשט את ניהול המדיניות בשני הדומיינים, משתמשים במאפיין platform_app_all.

נניח שאפליקציית פלטפורמה sample-plat-app צריכה לקרוא ולכתוב מתוך /dev/foo_device ואליה. מדיניות SELinux קיימת של ספק עשויה להיראות כך:

# This will only allow sample-plat-app to access the device if it
# is placed in the platform_app domain (i.e. target SDK version is 37 or higher).
allow platform_app foo_device:chr_file rw_file_perms;

עם זאת, אם sample-plat-app מוצמד לגרסת SDK יעד 36, הוא ממוקם בדומיין platform_app_36, ומדיניות SELinux מגרסה קודמת לא תחול, ותיעוד של דחיית AVC ייראה כך:

auditd  : type=1400 audit(0.0:11): avc:  denied  { read write } for  comm="sample-plat-app" path="/dev/foo_device" dev="tmpfs" ino=1609 scontext=u:r:platform_app_36:s0:c512,c768 tcontext=u:object_r:foo_device:s0 tclass=chr_file permissive=0

כדי לפתור את הבעיה, אפשר לעדכן את המדיניות באופן הבא, כי לאפליקציה צריכה להיות תמיד גישה לצומת המכשיר:

# This allows sample-plat-app to access the device independent of
# target SDK version.
allow platform_app_all foo_device:chr_file rw_file_perms;

יכולים להיות מצבים שבהם platform_app_all לא יעבוד. לדוגמה, אם משתמשים בפקודת המאקרו hal_client_domain() עם platform_app_all, המדיניות לא עוברת קומפילציה. הסיבה לכך היא ש-platform_app_all הוא מאפיין, ו-hal_client_domain() ינסה לצרף אליו מאפיין נוסף, וזה לא אפשרי:

# platform_app.te
hal_client_domain(platform_app, hal_foo)

בתרחישים האלה, חובה להשתמש ישירות בסוג platform_app_36, כך שהמדיניות תכלול את התוכן הבא:

# platform_app.te
hal_client_domain(platform_app, hal_foo)

# platform_app_36.te
hal_client_domain(platform_app_36, hal_foo)

עדכונים בדומיין SELinux של אפליקציות עם הרשאות מיוחדות

הדומיין priv_app מחולק על סמך targetSdkVersion האפליקציה. לאפליקציות עם הרשאות שמיועדות לגרסה 37 ואילך של SDK מוקצה הדומיין priv_app, ולאפליקציות שמיועדות לגרסה 36 ומטה של SDK מוקצה הדומיין priv_app_36. בדומיין priv_app_36 עדיין אפשר לפתוח את /dev/ashmem לצורך תאימות לאחור. כדי לפשט את ניהול המדיניות בשני הדומיינים, משתמשים במאפיין priv_app_all.

נניח שאפליקציית פלטפורמה sample-priv-app צריכה לקרוא ולכתוב מתוך /dev/foo_device ואליה. מדיניות SELinux קיימת של ספק עשויה להיראות כך:

# This will only allow sample-priv-app to access the device if it
# is placed in the priv_app domain (i.e. target SDK version is 37 or higher).
allow priv_app foo_device:chr_file rw_file_perms;

עם זאת, אם sample-priv-app מוצמד לגרסת SDK יעד 36, הוא ממוקם בדומיין priv_app_36, ומדיניות SELinux מגרסה קודמת לא תחול, ותיעוד של דחיית AVC ייראה כך:

auditd  : type=1400 audit(0.0:11): avc:  denied  { read write } for  comm="sample-priv-app" path="/dev/foo_device" dev="tmpfs" ino=1609 scontext=u:r:priv_app_36:s0:c512,c768 tcontext=u:object_r:foo_device:s0 tclass=chr_file permissive=0

כדי לפתור את הבעיה, אפשר לעדכן את המדיניות באופן הבא, כי לאפליקציה צריכה להיות תמיד גישה לצומת המכשיר:

# This allows sample-priv-app to access the device independent of
# target SDK version.
allow priv_app_all foo_device:chr_file rw_file_perms;

יכולים להיות מצבים שבהם priv_app_all לא יעבוד. לדוגמה, אם נעשה שימוש במאקרו hal_client_domain() עם priv_app_all, המדיניות לא תעבור קומפילציה. הסיבה לכך היא ש-priv_app_all הוא מאפיין, ו-hal_client_domain() ינסה לצרף אליו מאפיין נוסף, וזה לא אפשרי:

# priv_app.te
hal_client_domain(priv_app, hal_foo)

במקרים כאלה, חובה להשתמש ישירות בסוג priv_app_36, כך שקובצי המדיניות ייראו בערך כך:

# priv_app.te
hal_client_domain(priv_app, hal_foo)

# priv_app_36.te
hal_client_domain(priv_app_36, hal_foo)

עדכונים בדומיין SELinux של אפליקציית untrusted_app

הדומיין untrusted_app מחולק על סמך targetSdkVersion האפליקציה. לאפליקציות לא מהימנות שמיועדות לגרסה 37 ואילך של SDK מוקצה הדומיין untrusted_app, ולאפליקציות שמיועדות לגרסאות 34 עד 36 של SDK מוקצה הדומיין החדש untrusted_app_34. בדומיין untrusted_app_34 ובדומיינים untrusted_app_X, כאשר X היא גרסה ישנה יותר של SDK יעד, נשמרת האפשרות לפתוח את ‎/dev/ashmem לצורך תאימות לאחור.