במאמר הזה נסביר איך Android מטפלת בבעיות תאימות המדיניות עם עדכוני OTA בפלטפורמה, שבהם הגדרות SELinux החדשות של הפלטפורמה עשויות להיות שונות מהגדרות SELinux הישנות של הספק.
בתכנון מדיניות SELinux שמבוססת על טרבל, יש הבחנה בינארית בין מדיניות הפלטפורמה לבין מדיניות הספק. התוכנית הופכת מורכבת יותר אם מחיצות של ספקים יוצרות יחסי תלות, כמו platform
< vendor
< oem
.
ב-Android מגרסה 8.0 ואילך, המדיניות הגלובלית של SELinux מחולקת לרכיבים פרטיים וציבוריים. הרכיבים הציבוריים מורכבים מהמדיניות ומהתשתית המשויכת, ומובטח שהם יהיו זמינים לגרסה של הפלטפורמה. המדיניות הזו תהיה חשופה לכותבי מדיניות של ספקים כדי לאפשר לספקים ליצור קובץ מדיניות של ספק, ששילוב שלו עם המדיניות שסופקה על ידי הפלטפורמה יוצר מדיניות פונקציונלית במלואה למכשיר.
- לצורך ניהול גרסאות, המדיניות הציבורית של הפלטפורמה שתיוצאת תתואר כמאפיינים.
- כדי להקל על כתיבת המדיניות, הסוגים שיוצאו יומרו למאפיינים עם גרסאות כחלק מתהליך היצירה של המדיניות. אפשר גם להשתמש בסוגי תוכן ציבוריים ישירות בהחלטות לגבי הוספת תוויות שמתקבלות מקובצי הקשר של הספקים.
מערכת Android שומרת על מיפוי בין סוגי נתונים ספציפיים שיוצאו במדיניות הפלטפורמה לבין המאפיינים המתאימים עם גרסה לכל גרסה של הפלטפורמה. כך אפשר לוודא שכשהאובייקטים מסומנים בתווית של סוג, הם לא מפירים את ההתנהגות שמובטחת על ידי המדיניות הציבורית של הפלטפורמה בגרסה קודמת. כדי לשמור על המיפוי הזה, אנחנו מעדכנים את קובץ המיפוי לכל גרסת פלטפורמה. הקובץ הזה מכיל את פרטי החברות במאפיינים של כל סוג שיוצאו במדיניות הציבורית.
בעלות על אובייקטים וסימון שלהם
כשמתאימים אישית את המדיניות ב-Android מגרסה 8.0 ואילך, צריך להגדיר בבירור את הבעלות על כל אובייקט כדי להפריד בין המדיניות של הפלטפורמה לבין המדיניות של הספק. לדוגמה, אם הספק מתייג את /dev/foo
ואז הפלטפורמה מתייגת את /dev/foo
בהודעת OTA הבאה, תהיה התנהגות לא מוגדרת. ב-SELinux, המצב הזה מתבטא כהתנגשות בתוויות. לצומת המכשיר יכולה להיות רק תווית אחת, שמפנה לתווית האחרונה שהוחלו עליו. כתוצאה מכך:
- תהליכים שצריכה להיות להם גישה לתווית שלא הוחלתה בהצלחה יאבדו את הגישה למשאב.
- תהליכים שמקבלים גישה לקובץ עלולים להישבר כי נוצר צומת המכשיר הלא נכון.
בנוסף, יש סיכוי למקרים של התנגשויות בשמות של מאפייני המערכת, שעלולות לגרום להתנהגות לא מוגדרת במערכת (וגם לתיוג של SELinux). התנגשויות בין תוויות של פלטפורמות לבין תוויות של ספקים יכולות להתרחש בכל אובייקט שיש לו תווית SELinux, כולל נכסים, שירותים, תהליכים, קבצים ושקעים. כדי למנוע את הבעיות האלה, צריך להגדיר בבירור את הבעלות על העצמים האלה.
בנוסף להתנגשויות בין תוויות, יכולות להיות גם התנגשויות בין שמות של סוגים או מאפיינים של SELinux. התנגשות בשמות של סוגים או מאפיינים תגרום תמיד לשגיאה במהלך הידור המדיניות.
שמות של סוגי מאפיינים
מערכת SELinux לא מאפשרת להצהיר על אותו סוג או מאפיין כמה פעמים. לא ניתן יהיה לבצע הידור של מדיניות עם הצהרות כפולות. כדי למנוע התנגשויות בין שמות של סוגים לשמות של מאפיינים, צריך להגדיר את כל ההצהרות של הספקים במרחב שמות שמתחיל ב-vendor_
.
type foo, domain; → type vendor_foo, domain;
בעלות על תוויות של מאפייני מערכת ותהליכים
כדי למנוע התנגשויות בתוויות, מומלץ להשתמש במרחבי שמות של נכסים. כדי לזהות בקלות מאפייני פלטפורמה ולהימנע מהתנגשויות בשמות כשמשנים את השם של מאפייני פלטפורמה מיוצאים או מוסיפים אותם, חשוב לוודא שלכל מאפייני הספקים יש תחילית משלהם:
סוג הנכס | קידומות קבילות |
---|---|
מאפייני בקרה | ctl.vendor. ctl.start$vendor. ctl.stop$vendor. init.svc.vendor.
|
read-writable | vendor. |
קריאה בלבד | ro.vendor. ro.boot. ro.hardware.
|
קבוע | persist.vendor. |
ספקים יכולים להמשיך להשתמש ב-ro.boot.*
(שנלקח מ-cmdline של הליבה) וב-ro.hardware.*
(מאפיין ברור שקשור לחומרה).
כל שירותי הספק בקובצי init rc צריכים לכלול את הערך vendor.
לשירותים בקובצי init rc של מחיצות שאינן מחיצות מערכת. כללים דומים חלים על תוויות SELinux של נכסי הספק (vendor_
לנכסי הספק).
הבעלות על הקובץ
קשה למנוע התנגשויות בקבצים כי בדרך כלל המדיניות של הפלטפורמה ושל הספק מספקת תוויות לכל מערכות הקבצים. בניגוד למתן שמות לסוגים, מתן שמות למרחבים של קבצים לא מעשי כי רבים מהם נוצרים על ידי הליבה. כדי למנוע התנגשויות כאלה, יש לפעול לפי ההנחיות לשמות של מערכות קבצים שמפורטות בקטע הזה. ב-Android מגרסה 8.0, אלה המלצות ללא אכיפה טכנית. בעתיד, ההמלצות האלה ייאכפו על ידי חבילת הבדיקות של הספק (VTS).
מערכת (/system)
רק קובץ האימג' של המערכת צריך לספק תוויות לרכיבי /system
דרך file_contexts
, service_contexts
וכו'. אם תוויות לרכיבי /system
מתווספות למדיניות /vendor
, יכול להיות שלא תהיה אפשרות לבצע עדכון OTA של המסגרת בלבד.
ספק (/vendor)
מדיניות SELinux של AOSP כבר מסמנת חלקים מהמחיצה vendor
שבהם הפלטפורמה מקיימת אינטראקציה, וכך מאפשרת לכתוב כללי SELinux לתהליכי הפלטפורמה כדי שיוכלו לתקשר עם חלקים מהמחיצה vendor
ו/או לגשת אליהם. לדוגמה:
נתיב /vendor |
תווית שסופקה על ידי הפלטפורמה | תהליכי הפלטפורמה בהתאם לתווית |
---|---|---|
/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 וכו'
|
לכן, צריך לפעול לפי כללים ספציפיים (המאוכפים באמצעות neverallows
) כשמתייגים קבצים נוספים במחיצה vendor
:
vendor_file
חייבת להיות תווית ברירת המחדל לכל הקבצים במחיצהvendor
. המדיניות של הפלטפורמה דורשת זאת כדי לגשת להטמעות HAL של העברה דרך.- כל
exec_types
חדש שנוסף למחיצהvendor
דרך SEPolicy של הספק חייב לכלול את המאפייןvendor_file_type
. האכיפה מתבצעת באמצעות neverallows. - כדי למנוע התנגשויות עם עדכונים עתידיים של פלטפורמה או מסגרת, מומלץ לא לתייג קבצים אחרים מלבד
exec_types
במחיצהvendor
. - כל יחסי התלות בספריות של ממשקי HAL לאותו תהליך שזוהו ב-AOSP חייבים להיות מסומנים בתווית
same_process_hal_file.
Procfs (/proc)
אפשר לתייג קבצים ב-/proc
רק באמצעות התווית genfscon
. ב-Android 7.0, גם המדיניות של הפלטפורמה וגם המדיניות של הספק השתמשו ב-genfscon
כדי לתייג קבצים ב-procfs
.
המלצה: רק תוויות של מדיניות הפלטפורמה /proc
.
אם לתהליכים של vendor
דרושה גישה לקבצים ב-/proc
שמסומנים כרגע בתווית ברירת המחדל (proc
), מדיניות הספק לא צריכה לסמן אותם באופן מפורש, אלא להשתמש במקום זאת בסוג הגנרי proc
כדי להוסיף כללים לדומיינים של ספקים. כך עדכוני הפלטפורמה יוכלו להתאים לממשקי ליבה עתידיים שייחשפו דרך procfs
ולתייג אותם באופן מפורש לפי הצורך.
Debugfs (/sys/kernel/debug)
אפשר לתייג את Debugfs
גם ב-file_contexts
וגם ב-genfscon
. ב-Android מגרסה 7.0 עד Android מגרסה 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
הן אופציונליות למחיצות ישנות יותר של ספקים. כך אפשר לשמור את ההטמעה הקיימת של SEPolicy במחיצות ישנות של ספקים. ההגדרה הזו נשלטת על ידי המשתנה BOARD_GENFS_LABELS_VERSION
ב-Makefile, שמאוחסן ב-/vendor/etc/selinux/genfs_labels_version.txt
.
דוגמה:
-
ברמת ה-API של הספק 202404, הצומת
/sys/class/udc
מסומן כברירת מחדל בתוויתsysfs
. -
החל מרמת ה-API של הספק 202504, השדה
/sys/class/udc
מסומן בתוויתsysfs_udc
.
עם זאת, יכול להיות ש-/sys/class/udc
נמצא בשימוש במחיצות של ספקים שמשתמשים ברמת API 202404, עם תווית sysfs
שמוגדרת כברירת מחדל או עם תווית ספציפית לספק. תיוג בלתי מותנה של /sys/class/udc
כ-sysfs_udc
עלול לפגוע בתאימות למחיצות של הספקים האלה. כשמסמנים את BOARD_GENFS_LABELS_VERSION
, הפלטפורמה ממשיכה להשתמש בתוויות ובהרשאות הקודמות של מחיצות הספק הישנות.
הערך של BOARD_GENFS_LABELS_VERSION
יכול להיות גדול מ-API של הספק או שווה לו. לדוגמה, מחיצות של ספקים שמשתמשות ברמת API 202404 יכולות להגדיר את BOARD_GENFS_LABELS_VERSION
לערך 202504 כדי לאמץ תוויות חדשות שהוצגו ב-202504.
רשימת תוויות genfs ספציפיות ל-202504
כשמתבצעת תיוג של צמתים מסוג genfscon
, הפלטפורמה צריכה להביא בחשבון מחיצות ישנות יותר של ספקים ולהטמיע מנגנוני חלופיים לתאימות במקרה הצורך. הפלטפורמה יכולה להשתמש בספריות ספציפיות לפלטפורמה כדי לשלוח שאילתה לגרסה של תוויות genfs.
-
במודעות מותאמות, משתמשים ב-
libgenfslabelsversion
. קובץ הכותרת שלlibgenfslabelsversion
מופיע בקובץgenfslabelsversion.h
. -
ב-Java, משתמשים ב-
android.os.SELinux.getGenfsLabelsVersion()
.
מאפייני תאימות
מדיניות SELinux היא אינטראקציה בין סוגי מקור לסוגי יעד של הרשאות ושל כיתות אובייקטים ספציפיות. לכל אובייקט (תהליכים, קבצים וכו') מושפע מדיניות SELinux יכול להיות רק סוג אחד, אבל לסוג הזה יכולים להיות כמה מאפיינים.
המדיניות נכתבת בעיקר במונחים של סוגים קיימים:
allow source_type target_type:target_class permission(s);
הסיבה לכך היא שהמדיניות נכתבה עם ידע בכל הסוגים. עם זאת, אם במדיניות הספק ובמדיניות הפלטפורמה נעשה שימוש בסוגי תוויות ספציפיים, והתווית של אובייקט ספציפי משתנה רק באחת מהמדיניות האלה, יכול להיות שהמדיניות השנייה מכילה מדיניות שקיבלה או איבדה גישה שהתבססה עליה בעבר. לדוגמה:
File_contexts: /sys/A u:object_r:sysfs:s0 Platform: allow p_domain sysfs:class perm; Vendor: allow v_domain sysfs:class perm;
אפשר לשנות ל:
File_contexts: /sys/A u:object_r:sysfs_A:s0
מדיניות הספק תישאר ללא שינוי, אבל ה-v_domain
יפסיד את הגישה בגלל שאין מדיניות לסוג החדש של sysfs_A
.
הגדרת מדיניות במונחים של מאפיינים מאפשרת לנו לתת לאובייקט הבסיסי סוג שיש לו מאפיין שתואם למדיניות גם לפלטפורמה וגם לקוד של הספק. אפשר לעשות זאת לכל הסוגים כדי ליצור בפועל מדיניות מאפיינים שבה אף פעם לא נעשה שימוש בסוגי נתונים ספציפיים. בפועל, הדרישה הזו רלוונטית רק לחלקים במדיניות שיש בהם חפיפה בין הפלטפורמה לבין הספק. החלקים האלה מוגדרים ומסופקים כמדיניות הפלטפורמה הציבורית שנוצרת כחלק ממדיניות הספק.
הגדרת מדיניות ציבורית כמאפיינים עם גרסאות עומדת בשני יעדי תאימות למדיניות:
- מוודאים שקוד הספק ממשיך לפעול אחרי עדכון הפלטפורמה. כדי לעשות זאת, מוסיפים מאפיינים לסוגי אובייקטים ספציפיים שתואמים לאלה שקוד הספק מסתמך עליהם, תוך שמירה על הגישה.
- יכולת להוציא משימוש מדיניות. כדי לעשות זאת, צריך להגדיר בבירור את קבוצות המדיניות כמאפיינים שאפשר להסיר ברגע שהגרסה שאליה הם תואמים לא נתמכת יותר. אפשר להמשיך בפיתוח בפלטפורמה, בידיעה שהמדיניות הישנה עדיין נמצאת במדיניות הספק ותוסרה באופן אוטומטי כשהיא תשודרג (אם היא תשודרג).
יכולת כתיבה של מדיניות
כדי לעמוד ביעד של פיתוח מדיניות בלי צורך בידע לגבי שינויים ספציפיים בגרסאות, Android 8.0 כולל מיפוי בין סוגי המדיניות שגלויים לכולם בפלטפורמה לבין המאפיינים שלהם. הסוג foo
ממופה למאפיין foo_vN
, כאשר N
הוא הגרסה שאליה מכוון הטירגוט. הערך של vN
תואם למשתנה ה-build PLATFORM_SEPOLICY_VERSION
, והוא בפורמט MM.NN
, כאשר MM
תואם למספר ה-SDK של הפלטפורמה ו-NN
הוא גרסת sepolicy ספציפית לפלטפורמה.
למאפיינים במדיניות הציבורית אין גרסאות, אלא הם קיימים כ-API שעליו אפשר לבנות את מדיניות הפלטפורמה והספק כדי לשמור על יציבות הממשק בין שני המחיצות. גם כותבי המדיניות של הפלטפורמה וגם כותבי המדיניות של הספקים יכולים להמשיך לכתוב מדיניות כפי שהיא כתובה היום.
מדיניות הפלטפורמה הציבורית שיוצאת כ-allow source_foo target_bar:class
perm;
נכללת כחלק ממדיניות הספק. במהלך הקמפלקציה (שכוללת את הגרסה המתאימה), היא עוברת טרנספורמציה למדיניות שתעבור לחלק של הספק במכשיר (מוצגת בשפה המתורגמת Common Intermediate Language (CIL)):
(allow source_foo_vN target_bar_vN (class (perm)))
מאחר שמדיניות הספקים אף פעם לא מקדימה את הפלטפורמה, אין צורך להתייחס לגרסאות קודמות. עם זאת, מדיניות הפלטפורמה תצטרך לדעת עד כמה מדיניות הספק היא קודמת, לכלול מאפיינים לסוגים שלה ולהגדיר מדיניות בהתאם למאפיינים עם גרסאות.
הבדלים בין גרסאות של מדיניות
יצירת מאפיינים באופן אוטומטי על ידי הוספת _vN
בסוף כל סוג לא תעשה כלום בלי מיפוי של מאפיינים לסוגי גרסאות שונות. מערכת Android שומרת על מיפוי בין גרסאות של מאפיינים ומיפוי של סוגים למאפיינים האלה. זה נעשה בקובצי המיפוי שצוינו למעלה באמצעות הצהרות, כמו (CIL):
(typeattributeset foo_vN (foo))
שדרוגי פלטפורמה
בקטע הבא מפורטים תרחישים לשדרוג פלטפורמות.
סוגים זהים
התרחיש הזה מתרחש כשלא משנים את התוויות של אובייקט בגרסאות של המדיניות.
זהו אותו ערך עבור סוגי המקור והיעד, וניתן לראות אותו באמצעות /dev/binder
, שמסומן בתווית binder_device
בכל הגרסאות. הוא מיוצג במדיניות שעברה טרנספורמציה בתור:
binder_device_v1 … binder_device_vN
כשמשדרגים מ-v1
ל-v2
, מדיניות הפלטפורמה חייבת לכלול:
type binder_device; -> (type binder_device) (in CIL)
בקובץ המיפוי של v1 (CIL):
(typeattributeset binder_device_v1 (binder_device))
בקובץ המיפוי של v2 (CIL):
(typeattributeset binder_device_v2 (binder_device))
במדיניות הספק בגרסה 1 (CIL):
(typeattribute binder_device_v1) (allow binder_device_v1 …)
במדיניות הספק בגרסה 2 (CIL):
(typeattribute binder_device_v2) (allow binder_device_v2 …)
סוגים חדשים
התרחיש הזה מתרחש כשנוסף לסוג חדש בפלטפורמה, דבר שיכול לקרות כשמוסיפים תכונות חדשות או במהלך הקשחת המדיניות.
- תכונה חדשה כשהסוג מתייג אובייקט שלא היה קיים בעבר (למשל תהליך שירות חדש), קוד הספק לא היה באינטראקציה ישירה איתו בעבר, ולכן אין מדיניות תואמת. למאפיין החדש התואם לסוג אין מאפיין בגרסה הקודמת, ולכן לא תהיה צורך ברשומה בקובץ המיפוי שמטרגט את הגרסה הזו.
- הקשחת מדיניות כשהסוג מייצג הקשחת מדיניות, מאפיין הסוג החדש חייב לקשר בחזרה לרשת של מאפיינים שתואמים למאפיין הקודם (בדומה לדוגמה הקודמת שבה השינויים ב-
/sys/A
מ-sysfs
ל-sysfs_A
). קוד הספק מסתמך על כלל שמאפשר גישה ל-sysfs
, וצריך לכלול את הכלל הזה כמאפיין של הסוג החדש.
כשמשדרגים מ-v1
ל-v2
, מדיניות הפלטפורמה חייבת לכלול:
type sysfs_A; -> (type sysfs_A) (in CIL) type sysfs; (type sysfs) (in CIL)
בקובץ המיפוי של v1 (CIL):
(typeattributeset sysfs_v1 (sysfs sysfs_A))
בקובץ המיפוי של v2 (CIL):
(typeattributeset sysfs_v2 (sysfs)) (typeattributeset sysfs_A_v2 (sysfs_A))
במדיניות הספק בגרסה 1 (CIL):
(typeattribute sysfs_v1) (allow … sysfs_v1 …)
במדיניות הספק בגרסה 2 (CIL):
(typeattribute sysfs_A_v2) (allow … sysfs_A_v2 …) (typeattribute sysfs_v2) (allow … sysfs_v2 …)
סוגי מודעות שהוסרו
התרחיש הזה (הנדיר) מתרחש כאשר מסירים סוג. זה יכול לקרות אם:
- נשארת, אבל מקבלת תווית אחרת.
- המערכת מסירה אותו.
במהלך הוספת החרגות למדיניות, סוג מסוים מוסר והאובייקט שמסומן בסוג הזה מקבל תווית אחרת שכבר קיימת. זהו מיזוג של מיפויי המאפיינים: קוד הספק עדיין צריך להיות מסוגל לגשת לאובייקט הבסיסי באמצעות המאפיין שהיה לו בעבר, אבל שאר המערכת צריכה להיות מסוגלת לגשת אליו עכשיו באמצעות המאפיין החדש שלו.
אם המאפיין שאליו הוא הועבר הוא חדש, תהליך התיוג מחדש זהה לתהליך במקרה של סוג חדש, מלבד העובדה שכאשר משתמשים בתווית קיימת, הוספת הסוג החדש של המאפיין הישן תגרום לכך שאובייקטים אחרים שסומנו גם הם בסוג הזה יהיו נגישים. זהו בעצם מה שהפלטפורמה עושה, והיא נחשבת כפשרה מקובלת לשמירה על תאימות.
(typeattribute sysfs_v1) (allow … sysfs_v1 …)
גרסה לדוגמה 1: קיבוץ סוגי נתונים (הסרת sysfs_A)
כשמשדרגים מ-v1
ל-v2
, מדיניות הפלטפורמה חייבת לכלול:
type sysfs; (type sysfs) (in CIL)
בקובץ המיפוי של v1 (CIL):
(typeattributeset sysfs_v1 (sysfs)) (type sysfs_A) # in case vendors used the sysfs_A label on objects (typeattributeset sysfs_A_v1 (sysfs sysfs_A))
בקובץ המיפוי של v2 (CIL):
(typeattributeset sysfs_v2 (sysfs))
במדיניות הספק בגרסה 1 (CIL):
(typeattribute sysfs_A_v1) (allow … sysfs_A_v1 …) (typeattribute sysfs_v1) (allow … sysfs_v1 …)
במדיניות הספק בגרסה 2 (CIL):
(typeattribute sysfs_v2) (allow … sysfs_v2 …)
דוגמה לגרסה 2: הסרה מלאה (סוג foo)
כשמשדרגים מ-v1
ל-v2
, מדיניות הפלטפורמה חייבת לכלול:
# nothing - we got rid of the type
בקובץ המיפוי של v1 (CIL):
(type foo) #needed in case vendors used the foo label on objects (typeattributeset foo_v1 (foo))
בקובץ המיפוי של v2 (CIL):
# nothing - get rid of it
במדיניות הספק בגרסה 1 (CIL):
(typeattribute foo_v1) (allow foo …) (typeattribute sysfs_v1) (allow sysfs_v1 …)
במדיניות הספק בגרסה 2 (CIL):
(typeattribute sysfs_v2) (allow sysfs_v2 …)
כיתה/הרשאות חדשות
התרחיש הזה מתרחש כששדרוג פלטפורמה כולל רכיבי מדיניות חדשים שלא קיימים בגרסאות קודמות. לדוגמה, כש-Android הוסיפה את מנהל האובייקטים servicemanager
שיצר את ההרשאות להוספה, לחיפוש וליצירת רשימה, דמונים של ספקים שרצו להירשם עם ההרשאות הנדרשות של servicemanager
לא היו זמינות. ב-Android 8.0, רק מדיניות הפלטפורמה יכולה להוסיף כיתות והרשאות חדשות.
כדי לאפשר לכל הדומיינים שיכולים להיות שנוצרו או הורחבו לפי מדיניות הספק להשתמש בכיתה החדשה ללא הפרעה, מדיניות הפלטפורמה צריכה לכלול כלל דומה לזה:
allow {domain -coredomain} *:new_class perm;
יכול להיות שצריך להגדיר מדיניות שמאפשרת גישה לכל סוגי הממשקים (מדיניות ציבורית), כדי לוודא שתמונת הספק תקבל גישה. אם הדבר יוביל למדיניות אבטחה לא מקובלת (כפי שעשוי לקרות עם השינויים ב-servicemanager), יכול להיות שנאלץ לאלץ שדרוג של הספק.
הכיתה או ההרשאות הוסרו
התרחיש הזה מתרחש כשמוסרים מנהל אובייקטים (למשל מנהל האובייקטים ZygoteConnection
), והוא לא אמור לגרום לבעיות. אפשר להגדיר את הכיתה וההרשאות של מנהל האובייקטים במדיניות עד שגרסת הספק לא תשתמש בהם יותר. כדי לעשות זאת, מוסיפים את ההגדרות לקובץ המיפוי המתאים.
התאמה אישית של הספק לסוגים חדשים או לסוגים שסומנו מחדש
סוגי ספקים חדשים הם הליבה של פיתוח מדיניות הספקים, כי הם נחוצים כדי לתאר תהליכים, קבצים בינאריים, מכשירים, תת-מערכות ונתונים מאוחסנים חדשים. לכן, חובה לאפשר יצירה של סוגים שהוגדרו על ידי הספק.
מכיוון שמדיניות הספק היא תמיד המדיניות הישנה ביותר במכשיר, אין צורך להמיר באופן אוטומטי את כל סוגי הספקים למאפיינים במדיניות. הפלטפורמה לא מסתמכת על שום דבר שמסומן במדיניות של הספק כי אין לה ידע בנושא. עם זאת, הפלטפורמה תספק את המאפיינים והסוגים הציבוריים שבהם היא משתמשת כדי לקיים אינטראקציה עם אובייקטים שמסומנים בסוגי המאפיינים האלה (כמו domain
, sysfs_type
וכו'). כדי שהפלטפורמה תמשיך לקיים אינטראקציה תקינה עם העצמים האלה, צריך להחיל את המאפיינים והסוגים בצורה מתאימה, ויכול להיות שיהיה צורך להוסיף כללים ספציפיים לדומיינים שניתן להתאים אישית (כמו init
).
שינויים במאפיינים ל-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
. מאפיין לכל הדומיינים של המערכת (למעטinit
ו-shell domains
) שמפירים את הדרישה לא להריץ קובצי בינאריים של ספקים. ביצוע של קובצי בינאריים של ספקים עם ממשק API לא יציב. הפלטפורמה לא צריכה להריץ קובצי בינארי של ספקים ישירות. המלצה:- יחסי התלות האלה של הפלטפורמה בקבצים הבינאריים של הספקים חייבים להיות מאחורי ממשקי HAL של HIDL.
או
coredomains
שצריכים גישה לקובצי הבינארי של הספק צריכים לעבור למחיצה של הספק, וכך להפסיק להיותcoredomain
.
- יחסי התלות האלה של הפלטפורמה בקבצים הבינאריים של הספקים חייבים להיות מאחורי ממשקי HAL של HIDL.
מאפיינים לא מהימנים
לאפליקציות לא מהימנות שמארחות קוד שרירותי לא אמורה להיות גישה לשירותי HwBinder, מלבד אלה שנחשבים בטוחים מספיק לגישה מאפליקציות כאלה (ראו שירותים בטוחים בהמשך). שתי הסיבות העיקריות לכך הן:
- שרתי HwBinder לא מבצעים אימות לקוח כי HIDL לא חושף כרגע את פרטי ה-UID של מבצע הקריאה החוזרת. גם אם HIDL חושף נתונים כאלה, הרבה שירותי HwBinder פועלים ברמה נמוכה יותר מזו של האפליקציות (למשל, HAL) או שאסור להם להסתמך על זהות האפליקציה לצורך הרשאה. לכן, כדי להימנע מבעיות, ההנחה שמוגדרת כברירת מחדל היא שכל שירות HwBinder מתייחס לכל הלקוחות שלו ככאלה שיש להם הרשאה זהה לבצע את הפעולות שהשירות מציע.
- שרתי HAL (קבוצת משנה של שירותי HwBinder) מכילים קוד עם שיעור גבוה יותר של בעיות אבטחה בהשוואה לרכיבי
system/core
, ויש להם גישה לשכבות התחתונות של הסטאק (עד לחומרה), כך שיש יותר הזדמנויות לעקוף את מודל האבטחה של Android.
שירותים בטוחים
שירותים בטוחים כוללים:
same_process_hwservice
. השירותים האלה (בהגדרה) פועלים בתהליך של הלקוח, ולכן יש להם את אותה גישה שיש לדומיין הלקוח שבו פועל התהליך.coredomain_hwservice
. השירותים האלה לא גורמים לסיכונים שקשורים לסיבה מס' 2.hal_configstore_ISurfaceFlingerConfigs
. השירות הזה תוכנן במיוחד לשימוש בכל דומיין.hal_graphics_allocator_hwservice
. הפעולות האלה מוצעות גם על ידי שירות ה-Bindersurfaceflinger
, לאפליקציות שיש להן גישה אליו.hal_omx_hwservice
. זוהי גרסה של HwBinder לשירותmediacodec
Binder, שאפליקציות מורשות לגשת אליו.hal_codec2_hwservice
. זוהי גרסה חדשה יותר שלhal_omx_hwservice
.
מאפיינים שאפשר להשתמש בהם
לכל hwservices
שלא נחשבים בטוחים יש את המאפיין untrusted_app_visible_hwservice
. למאפיין untrusted_app_visible_halserver
יש את הערך 'HAL' בשרתים התואמים של HAL. אסור להשתמש באף אחד מהמאפיינים untrusted
במכשירים שמריצים Android מגרסה 9 ואילך.
המלצה:
- במקום זאת, אפליקציות לא מהימנות צריכות לדבר עם שירות מערכת שמדבר עם HIDL HAL של הספק. לדוגמה, אפליקציות יכולות לדבר עם
binderservicedomain
, ואזmediaserver
(שנחשב ל-binderservicedomain
) מדבר עםhal_graphics_allocator
.או
- לאפליקציות שזקוקות לגישה ישירה ל-HALs של
vendor
צריך להיות דומיין משלהם של מדיניות אבטחה (sepolicy) שהוגדר על ידי הספק.
בדיקות של מאפייני קבצים
Android 9 כולל בדיקות בזמן ה-build שמבטיחות שלכל הקבצים במיקומים ספציפיים יש את המאפיינים המתאימים (לדוגמה, כל הקבצים ב-sysfs
כוללים את המאפיין הנדרש sysfs_type
).
מדיניות ציבורית של פלטפורמה
המדיניות הציבורית של הפלטפורמה היא הליבה של התאימות למודל הארכיטקטורה של Android 8.0, בלי פשוט לשמור על האיחוד של כללי המדיניות של הפלטפורמה מגרסה 1 וגרסה 2. הספקים נחשפים לקבוצת משנה של מדיניות הפלטפורמה שמכילה סוגים ומאפיינים שאפשר להשתמש בהם וכללים לגבי הסוגים והמאפיינים האלה, שהופכים לאחר מכן לחלק ממדיניות הספק (כלומר, vendor_sepolicy.cil
).
הסוגים והכללים מתורגמים באופן אוטומטי במדיניות שנוצרה על ידי הספק ל-attribute_vN
, כך שכל הסוגים שסופקו על ידי הפלטפורמה הם מאפיינים עם גרסאות (אבל למאפיינים אין גרסאות). הפלטפורמה אחראית למיפוי של הסוגים הספציפיים שהיא מספקת למאפיינים המתאימים, כדי לוודא שמדיניות הספק ממשיכה לפעול ושכל הכללים שסופקו לגרסה מסוימת נכללים. השילוב של המדיניות הציבורית של הפלטפורמה ומדיניות הספק עומד ביעד של מודל הארכיטקטורה של Android 8.0, שמאפשר גרסאות build עצמאיות של הפלטפורמה ושל הספק.
מיפוי לרשתות של מאפיינים
כשמשתמשים במאפיינים כדי למפות לגרסאות של מדיניות, סוג ממופה למאפיין או למספר מאפיינים, כדי להבטיח שניתן יהיה לגשת לאובייקטים שמסומנים בסוג דרך מאפיינים שתואמים לסוגי הקודמים שלהם.
כדי לשמור על המטרה של הסתרת פרטי הגרסה מכותב המדיניות, צריך ליצור באופן אוטומטי את המאפיינים עם הגרסאות ולהקצות אותם לסוגים המתאימים. במקרה הנפוץ של סוגים סטטיים, זה פשוט:
type_foo
ממופה ל-type_foo_v1
.
כשמשנים את תווית האובייקט, למשל sysfs
→ sysfs_A
או
mediaserver
→ audioserver
, יצירה של המיפוי הזה היא לא פשוטה (והיא מתוארת בדוגמאות שלמעלה). מנהלי המדיניות בפלטפורמה צריכים לקבוע איך ליצור את המיפוי בנקודות המעבר של אובייקטים. לשם כך, הם צריכים להבין את הקשר בין אובייקטים לבין התוויות שהוקצו להם, ולהחליט מתי המיפוי מתבצע. כדי לשמור על תאימות לאחור, צריך לנהל את המורכבות הזו בצד הפלטפורמה, שהיא המחיצה היחידה שאפשר לשדרג.
גרסאות משופרות
כדי לפשט את התהליך, פלטפורמת Android משחררת גרסה של sepolicy כשמחלקים את ההסתעפות החדשה של הגרסה. כפי שמתואר למעלה, מספר הגרסה נכלל ב-PLATFORM_SEPOLICY_VERSION
והוא בפורמט MM.nn
, כאשר MM
תואם לערך ה-SDK ו-nn
הוא ערך פרטי שנשמר ב- /platform/system/sepolicy.
. לדוגמה, 19.0
עבור Kitkat, 21.0
עבור Lollipop, 22.0
עבור Lollipop-MR1, 23.0
עבור Marshmallow, 24.0
עבור Nougat, 25.0
עבור Nougat-MR1, 26.0
עבור Oreo, 27.0
עבור Oreo-MR1 ו-28.0
עבור Android 9.
העלאות לא תמיד הן מספרים שלמים. לדוגמה, אם שדרוג של MR לגרסה מחייב שינוי לא תואם ב-system/sepolicy/public
אבל לא שדרוג של API, גרסת ה-sepolicy יכולה להיות: vN.1
. הגרסה שנמצאת בהסתעפות הפיתוח היא 10000.0
, שאסור להשתמש בה במכשירים שיוצאים לשוק.
מערכת Android עשויה להוציא משימוש את הגרסה הישנה ביותר במהלך השדרוג. כדי לקבל משוב לגבי מועד ההוצאה משימוש של גרסה, מערכת Android עשויה לאסוף את מספר המכשירים עם מדיניות הספק שפועלת בהם גרסת Android הזו ועדיין מקבלים עדכונים גדולים לפלטפורמה. אם המספר נמוך מסף מסוים, הגרסה הזו תוסר משימוש.
ההשפעה של מספר מאפיינים על הביצועים
כפי שמתואר במאמר https://github.com/SELinuxProject/cil/issues/9, מספר גדול של מאפיינים שמוקצים לסוג מסוים גורם לבעיות בביצועים במקרה של החמצה של מדיניות במטמון.
הבעיה אומתה כבעיה ב-Android, ולכן בוצעו שינויים ב-Android 8.0 כדי להסיר מאפיינים שנוספו למדיניות על ידי המהדר של המדיניות, וגם כדי להסיר מאפיינים שלא בשימוש. השינויים האלה פתרו נסיגות בביצועים.
המדיניות הציבורית של system_ext והמדיניות הציבורית של המוצר
החל מ-Android 11, אפשר לייצא את הסוגים הציבוריים הייעודיים של המחיצות system_ext
ו-product
למחיצה של הספק. בדומה למדיניות הציבורית של הפלטפורמה, הספק משתמש בסוגי כללים וכללים שמתורגמים באופן אוטומטי למאפיינים עם גרסאות, לדוגמה, מ-type
ל-type_N
, כאשר N
היא הגרסה של הפלטפורמה שבה הוקם המחיצה של הספק.
כשמחיצות system_ext
ו-product
מבוססות על אותה גרסת פלטפורמה N
, מערכת ה-build יוצרת קובצי מיפוי בסיס ל-system_ext/etc/selinux/mapping/N.cil
ול-product/etc/selinux/mapping/N.cil
, שמכילים מיפויים של זהויות מ-type
ל-type_N
. הספק יכול לגשת ל-type
באמצעות המאפיין המנוהל בגרסאות type_N
.
אם רק המחיצות system_ext
ו-product
מתעדכנות, למשל מ-N
ל-N+1
(או גרסה מתקדמת יותר), בעוד שהספק נשאר ב-N
, יכול להיות שהספק יאבד את הגישה לסוגי המחיצות system_ext
ו-product
. כדי למנוע שגיאות, צריך לספק קבצי מיפוי מהפלחים system_ext
ו-product
מהטיפוסים הספציפיים למאפייני type_N
. כל שותף אחראי על ניהול קובצי המיפוי, אם הוא תומך בספק N
עם מחיצות system_ext
ו-product
בגרסה N+1
ואילך.
כדי לעשות זאת, השותפים נדרשים:
- מעתיקים את קובצי המיפוי הבסיסיים שנוצרו מהמחיצות
N
,system_ext
ו-product
אל עץ המקור שלהן. - משנים את קובצי המיפוי לפי הצורך.
-
מתקינים את קובצי המיפוי במחיצות
system_ext
ו-product
שלN+1
(או גרסה מתקדמת יותר).
לדוגמה, נניח של-N
system_ext
יש טיפוס ציבורי אחד בשם foo_type
. לאחר מכן, system_ext/etc/selinux/mapping/N.cil
במחיצה N
system_ext
ייראה כך:
(typeattributeset foo_type_N (foo_type)) (expandtypeattribute foo_type_N true) (typeattribute foo_type_N)
אם bar_type
מתווסף ל-N+1
system_ext, ואם צריך למפות את bar_type
ל-foo_type
עבור הספק N
, אפשר לעדכן את N.cil
מ-
(typeattributeset foo_type_N (foo_type))
עד
(typeattributeset foo_type_N (foo_type bar_type))
ולאחר מכן מותקנים במחיצה N+1
system_ext.
הספק של N
יכול להמשיך לגשת ל-foo_type
ול-bar_type
של N+1
ב-system_ext.
תוויות של הקשרים ב-SELinux
כדי לתמוך בהבחנה בין מדיניות האבטחה של הפלטפורמה לבין מדיניות האבטחה של הספק, המערכת יוצרת את קובצי ההקשר של SELinux באופן שונה כדי לשמור על הפרדה ביניהם.
הקשרים של קבצים
ב-Android 8.0 הוכנסו השינויים הבאים ב-file_contexts
:
- כדי למנוע תקורה נוספת של הידור במכשיר במהלך האתחול,
file_contexts
מפסיק להתקיים בפורמט הבינארי. במקום זאת, הם קובצי טקסט של ביטויים רגולריים שאפשר לקרוא אותם, כמו{property, service}_contexts
(כפי שהיו לפני גרסה 7.0). - הקבצים
file_contexts
מחולקים לשני קבצים:plat_file_contexts
- פלטפורמת Android
file_context
ללא תוויות ספציפיות למכשיר, מלבד תוויות של חלקים במחיצה/vendor
שצריך לתייג במדויק כדי להבטיח את הפעולה התקינה של קובצי המדיניות. - צריך להיות במחיצה
system
ב-/system/etc/selinux/plat_file_contexts
במכשיר, ו-init
צריך לטעון אותו בהתחלה יחד עם הספקfile_context
.
- פלטפורמת Android
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
.
- פלטפורמת Android
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
.
- פלטפורמת Android
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.
- פלטפורמת Android
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/.
- פלטפורמת Android
- ללא פלטפורמה
mac_permissions.xml
- תוסף ספציפי למכשיר לפלטפורמה
mac_permissions.xml
שנוצר מ-mac_permissions.xml
שנמצא בתיקיות שמפניות אליוBOARD_SEPOLICY_DIRS
בקבציםBoardconfig.mk
של המכשיר. - חייב להיות במחיצה
vendor
בכתובת/vendor/etc/selinux/.
- תוסף ספציפי למכשיר לפלטפורמה