כתיבת מדיניות SELinux

פרויקט Android Open Source Project‏ (AOSP) מספק מדיניות בסיסית מוצקה לאפליקציות ולשירותים הנפוצים בכל מכשירי Android. שותפי AOSP משדרגים את המדיניות הזו באופן קבוע. המדיניות הבסיסית צפויה להוות כ-90%-95% מהמדיניות הסופית במכשיר, וההתאמות אישיות הספציפיות למכשיר ייצגו את 5%-10% הנותרים. המאמר הזה מתמקד בהתאמה אישית שספציפית למכשיר, בכתיבת מדיניות ספציפית למכשיר ובכמה מהמלכודות שיש להימנע מהן בדרך.

הפעלת מכשיר

כשכותבים מדיניות ספציפית למכשיר, פועלים לפי השלבים הבאים.

הפעלה במצב הרשאות רחבות

כשמכשיר נמצא במצב הרשאה, דחיות מתועדות ביומן אבל לא נאכפות. מצב מתירנות חשוב משתי סיבות:

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

הדרך הפשוטה ביותר להעביר מכשיר למצב הרשאה היא באמצעות שורת הפקודה של הליבה. אפשר להוסיף את זה לקובץ BoardConfig.mk של המכשיר: platform/device/<vendor>/<target>/BoardConfig.mk. אחרי שמבצעים שינוי בשורת הפקודה, מריצים את הפקודה make clean ואז את הפקודה make bootimage ומבצעים את הפלאש של קובץ האימג' החדש להפעלה.

לאחר מכן, מאשרים את מצב ההרשאה:

adb shell getenforce

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

אכיפה מוקדמת

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

הסרה או מחיקה של מדיניות קיימת

יש כמה סיבות טובות ליצור מדיניות ספציפית למכשיר מאפס במכשיר חדש, כולל:

טיפול בבקשות דחייה של שירותי ליבה

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

avc: denied { open } for pid=1003 comm=”mediaserver” path="/dev/kgsl-3d0”
dev="tmpfs" scontext=u:r:mediaserver:s0 tcontext=u:object_r:device:s0
tclass=chr_file permissive=1
avc: denied { read write } for pid=1003 name="kgsl-3d0" dev="tmpfs"
scontext=u:r:mediaserver:s0
tcontext=u:object_r:device:s0 tclass=chr_file permissive=1

ניתן לטפל בבעיה הזו באופן מלא על ידי סימון /dev/kgsl-3d0 בצורה נכונה. בדוגמה הזו, tcontext הוא device. זהו הקשר שמוגדרת לו ברירת המחדל, שבו כל מה שמופיע ב-/dev מקבל את התווית device, אלא אם הוקצה לו תווית ספציפית יותר. קבלה פשוטה של הפלט מ-audit2allow תוביל לכלל שגוי ומתיר מדי.

כדי לפתור בעיות כאלה, צריך לתת לקובץ תווית ספציפית יותר. במקרה הזה, התווית היא gpu_device. אין צורך בהרשאות נוספות, כי ל-mediaserver כבר יש את ההרשאות הנדרשות במדיניות הליבה כדי לגשת ל-gpu_device.

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

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

תיוג שירותים חדשים וטיפול בדחיות

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

השירות מופעל בקובץ init.device.rc של המכשיר שלנו בתור:

service foo /system/bin/foo
    class core
  1. יוצרים דומיין חדש בשם 'foo'

    יוצרים את הקובץ device/manufacturer/device-name/sepolicy/foo.te עם התוכן הבא:

    # foo service
    type foo, domain;
    type foo_exec, exec_type, file_type;
    
    init_daemon_domain(foo)
    

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

  2. התווית /system/bin/foo

    מוסיפים את הפרטים הבאים ל-device/manufacturer/device-name/sepolicy/file_contexts:

    /system/bin/foo   u:object_r:foo_exec:s0
    

    כך ניתן לוודא שקובץ ההפעלה מסומן כראוי כדי ש-SELinux יריץ את השירות בדומיין הנכון.

  3. יצירת קובצי האימג' של האתחול והמערכת והעברתם (flash) למכשיר.
  4. מצמצמים את כללי SELinux לדומיין.

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

חזרה למצב אכיפה

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

טעויות נפוצות

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

שימוש יתר בביטויים של הכחשה

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

allow { domain -untrusted_app } scary_debug_device:chr_file rw_file_perms

הכוונה ברורה: לכולם יכולה להיות גישה למכשיר לניפוי באגים, חוץ מאפליקציות צד שלישי.

הכלל הזה פגום בכמה דרכים. קל לעקוף את ההחרגה של untrusted_app, כי כל האפליקציות יכולות להפעיל שירותים בדומיין isolated_app. באופן דומה, אם יתווספו ל-AOSP דומיינים חדשים של אפליקציות צד שלישי, גם להם תהיה גישה ל-scary_debug_device. הכלל מתירני מדי. רוב הדומיינים לא ייהנו מגישה לכלי לניפוי באגים. צריך לכתוב את הכלל כך שיאפשר רק לדומיינים שדורשים גישה.

תכונות של ניפוי באגים בסביבת ייצור

אסור לכלול תכונות ניפוי באגים בגרסאות build בסביבת הייצור, וגם לא את המדיניות שלהן.

האפשרות הפשוטה ביותר היא לאפשר את תכונת ניפוי הבאגים רק כש-SELinux מושבת בגרסאות build של eng/userdebug, כמו adb root ו-adb shell setenforce 0.

אפשרות בטוחה אחרת היא להכניס את הרשאות ניפוי הבאגים לתוך טענת userdebug_or_eng.

הגדלת נפח המדיניות

במאמר Characterizing SEAndroid Policies in the Wild מתוארת מגמה מדאיגה של גידול בהתאמות אישיות של מדיניות המכשיר. המדיניות הספציפית למכשיר צריכה להוות 5-10% מהמדיניות הכוללת שפועלת במכשיר. כמעט בטוח שהתאמות אישיות בטווח של 20%ומעלה מכילות דומיינים עם הרשאות יתר ומדיניות לא תקינה.

מדיניות גדולה מדי:

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

בדוגמה הבאה מוצגים שני מכשירים שבהם המדיניות הספציפית ליצרן כללה 50% ו-40% מהמדיניות במכשיר. כתיבת מחדש של המדיניות הביאה לשיפורים משמעותיים באבטחה ללא פגיעה בפונקציונליות, כפי שמוצג בהמשך. (מכשירי AOSP Shamu ו-Flounder נכללים לצורך השוואה).

איור 1: השוואה בין הגודל של מדיניות ספציפית למכשיר אחרי ביקורת אבטחה.

איור 1. השוואה של גודל המדיניות הספציפית למכשיר לאחר ביקורת האבטחה.

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

הענקת היכולת dac_override

דחייה מסוג dac_override פירושה שהתהליך הפוגע מנסה לגשת לקובץ עם הרשאות שגויות של משתמש/קבוצה/כל העולם ב-Unix. כמעט אף פעם הפתרון הנכון הוא להעניק את ההרשאה dac_override. במקום זאת, יש לשנות את הרשאות ה-Unix בקובץ או בתהליך. לדומיינים מסוימים, כמו init,‏ vold ו-installd, באמת יש צורך ביכולת לשנות את הרשאות הקבצים ב-Unix כדי לגשת לקבצים של תהליכים אחרים. הסבר מפורט יותר זמין בבלוג של Dan Walsh.