אתחול הספק

לתהליך ה-init יש הרשאות כמעט בלתי מוגבלות, והוא משתמש בסקריפטים של קלט גם מהמחיצות של המערכת וגם מהמחיצות של הספק כדי לאתחל את המערכת במהלך תהליך האתחול. הגישה הזו יוצרת חור גדול בחלוקה של Treble למערכת ולספק, כי סקריפטים של ספקים עשויים להורות ל-init לגשת לקבצים, למאפיינים וכו' שלא נכללים בממשק הבינארי היציב של האפליקציה (ABI) של המערכת והספק.

Vendor init נועד לסגור את החור הזה באמצעות דומיין נפרד של Linux עם אבטחה משופרת (SELinux)‏ vendor_init, כדי להריץ פקודות שנמצאות ב-/vendor עם הרשאות ספציפיות לספק.

מנגנון

Vendor init יוצר תהליך משנה של init בשלב מוקדם בתהליך האתחול, עם ההקשר של SELinux‏ u:r:vendor_init:s0. להקשר SELinux הזה יש הרבה פחות הרשאות מאשר להקשר init שמוגדר כברירת מחדל, והגישה שלו מוגבלת לקבצים, למאפיינים וכו' שספציפיים לספק או שהם חלק מ-ABI היציב של ספק המערכת.

‏init בודקת כל סקריפט שהיא טוענת כדי לראות אם הנתיב שלו מתחיל ב-/vendor. אם כן, היא מסמנת אותו עם אינדיקציה לכך שצריך להריץ את הפקודות שלו בהקשר של init של הספק. כל פונקציה מובנית של init מסומנת ב-annotation של ערך בוליאני שמציין אם צריך להריץ את הפקודה בתהליך המשנה של init של הספק או לא:

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

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

שימוש באתחול של הספק

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

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

type=1400 audit(1511821362.996:9): avc: denied { search } for pid=540 comm="init" name="nfc" dev="sda45" ino=1310721 scontext=u:r:vendor_init:s0 tcontext=u:object_r:nfc_data_file:s0 tclass=dir permissive=0
init: Command 'write /data/nfc/bad_file_access 1234' action=boot (/vendor/etc/init/hw/init.walleye.rc:422) took 2ms and failed: Unable to write to file '/data/nfc/bad_file_access': open() failed: Permission denied

אם הפקודה נכשלת, יש שתי אפשרויות:

  • אם הפקודה נכשלת בגלל הגבלה מכוונת (למשל, אם הפקודה ניגשת לקובץ או לנכס מערכת), צריך להטמיע מחדש את הפקודה באופן שמתאים ל-Treble, דרך ממשקים יציבים בלבד. כללי Neverallow מונעים הוספה של הרשאות גישה לקבצים של מערכת שלא נכללים ב-ABI היציב של ספק המערכת.
  • אם התווית SELinux חדשה ועדיין לא קיבלו הרשאות במערכת vendor_init.te וגם לא הוחרגו הרשאות באמצעות כללי אף פעם, יכול להיות שהתווית החדשה תקבל הרשאות במכשיר vendor_init.te הספציפי למכשיר.

במכשירים שהושקעו לפני Android 9, אפשר לעקוף את הכללים של neverallows על ידי הוספת מאפיין הסוג data_between_core_and_vendor_violators לקובץ vendor_init.te הספציפי למכשיר.

מיקומי קוד

רוב הלוגיקה של ה-IPC של init של הספק נמצאת בקובץ system/core/init/subcontext.cpp.

טבלת הפקודות נמצאת בכיתה BuiltinFunctionMap בקובץ system/core/init/builtins.cpp, והיא כוללת הערות שמציינות אם הפקודה צריכה לפעול בתהליך המשנה של init של הספק.

ה-SEPolicy לאתחול ספקים מחולק בין הספריות הפרטיות (system/sepolicy/private/vendor_init.te) והציבוריות (system/sepolicy/public/vendor_init.te) במערכת/sepolicy.