אימות SELinux

מערכת Android ממליצה בחום ליצרני ציוד מקורי (OEM) לבדוק ביסודיות את ההטמעות של SELinux. יצרנים שמטמיעים SELinux צריכים להחיל את המדיניות החדשה קודם על קבוצת מכשירי בדיקה.

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

הפקודה הזו מדפיסה את מצב SELinux הגלובלי: Enforcing (אכיפה) או Permissive (מתיר). כדי לקבוע את מצב SELinux לכל דומיין, צריך לבדוק את הקבצים המתאימים או להריץ את הגרסה העדכנית של sepolicy-analyze עם הדגל המתאים (-p), שמופיע ב- /platform/system/sepolicy/tools/.

קריאת נתוני הדחיות

בודקים אם יש שגיאות, שמנותבות כיומני אירועים אל dmesg ואל logcat וניתן לראות אותן באופן מקומי במכשיר. יצרנים צריכים לבדוק את הפלט של SELinux ב-dmesg במכשירים האלה ולשפר את ההגדרות לפני השחרור לציבור במצב מתירני, ובסופו של דבר לעבור למצב אכיפה. הודעות ביומן SELinux מכילות את avc: ולכן אפשר למצוא אותן בקלות באמצעות grep. אפשר לתעד את יומני הדחייה הפעילים על ידי הפעלת הפקודה cat /proc/kmsg, או לתעד את יומני הדחייה מההפעלה הקודמת על ידי הפעלת הפקודה cat /sys/fs/pstore/console-ramoops.

הודעות השגיאה של SELinux מוגבלות בקצב אחרי השלמת האתחול, כדי למנוע הצפה של היומנים. כדי לוודא שאתם רואים את כל ההודעות הרלוונטיות, אתם יכולים להשבית את ההגדרה הזו על ידי הפעלת הפקודה adb shell auditctl -r 0.

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

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

avc: denied  { connectto } for  pid=2671 comm="ping" path="/dev/socket/dnsproxyd"
scontext=u:r:shell:s0 tcontext=u:r:netd:s0 tclass=unix_stream_socket

הסבר לפלט:

  • הפעולה שמתבצעת מיוצגת על ידי { connectto }. יחד עם tclass בסוף (unix_stream_socket), הוא מציין בערך מה נעשה למה. במקרה הזה, משהו ניסה להתחבר לשקע של זרם יוניקס.
  • הערך scontext (u:r:shell:s0) מציין את ההקשר שבו הפעולה התחילה. במקרה הזה, מדובר במשהו שפועל כמעטפת.
  • השדה tcontext (u:r:netd:s0) מציין את ההקשר של יעד הפעולה. במקרה הזה, זהו unix_stream_socket שנמצא בבעלות netd.
  • הסמל comm="ping" בחלק העליון מספק רמז נוסף לגבי מה שהופעל בזמן שנוצרה הדחייה. במקרה הזה, זו רמז די טוב.

דוגמה נוספת:

adb shell su root dmesg | grep 'avc: '

אודיו יוצא:

<5> type=1400 audit: avc:  denied  { read write } for  pid=177
comm="rmt_storage" name="mem" dev="tmpfs" ino=6004 scontext=u:r:rmt:s0
tcontext=u:object_r:kmem_device:s0 tclass=chr_file

אלה הרכיבים העיקריים מהדחייה הזו:

  • פעולה – הפעולה שניסו לבצע מודגשת בסוגריים, read write או setenforce.
  • Actor – הרשומה scontext (הקשר המקור) מייצגת את הגורם המבצע, ובמקרה הזה את דמון rmt_storage.
  • Object (אובייקט) – הרשומה tcontext (הקשר של היעד) מייצגת את האובייקט שעליו מתבצעת הפעולה, במקרה הזה kmem.
  • תוצאה – הרשומה tclass (target class) מציינת את סוג האובייקט שעליו מתבצעת הפעולה, ובמקרה הזה מדובר ב-chr_file (character device).

העברה של מחסניות של משתמשים וליבות

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

בליבות עדכניות מוגדרת נקודת מעקב בשם avc:selinux_audited. משתמשים ב-Android simpleperf כדי להפעיל את נקודת המעקב הזו ולתעד את שרשרת הקריאות.

הגדרה נתמכת

  • יש תמיכה בליבת Linux מגרסה 5.10 ואילך, במיוחד בענפים של ליבת Android Common Kernel‏: mainline ו- android12-5.10. יש תמיכה גם בענף android12-5.4. אפשר להשתמש ב-simpleperf כדי לבדוק אם נקודת המעקב מוגדרת במכשיר שלכם: adb root && adb shell simpleperf list | grep avc:selinux_audited. בגרסאות ליבה אחרות, אפשר לבחור קומיטים (commit) dd81662 ו-30969bc.
  • צריכה להיות אפשרות לשחזר את האירוע שמבצעים בו ניפוי באגים. אי אפשר לתמוך באירועים של זמן האתחול באמצעות simpleperf, אבל יכול להיות שעדיין תוכלו להפעיל מחדש את השירות כדי להפעיל את האירוע.

תיעוד שרשרת השיחות

השלב הראשון הוא לתעד את האירוע באמצעות simpleperf record:

adb shell -t "cd /data/local/tmp && su root simpleperf record -a -g -e avc:selinux_audited"

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

^Csimpleperf I cmd_record.cpp:751] Samples recorded: 1. Samples lost: 0.

לבסוף, אפשר להשתמש ב-simpleperf report כדי לבדוק את ה-stacktrace שנתפס. לדוגמה:

adb shell -t "cd /data/local/tmp && su root simpleperf report -g --full-callgraph"
[...]
Children  Self     Command  Pid   Tid   Shared Object                                   Symbol
100.00%   0.00%    dmesg    3318  3318  /apex/com.android.runtime/lib64/bionic/libc.so  __libc_init
       |
       -- __libc_init
          |
           -- main
              toybox_main
              toy_exec_which
              dmesg_main
              klogctl
              entry_SYSCALL_64_after_hwframe
              do_syscall_64
              __x64_sys_syslog
              do_syslog
              selinux_syslog
              slow_avc_audit
              common_lsm_audit
              avc_audit_post_callback
              avc_audit_post_callback

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

מעבר להרשאה מתירה

אפשר להשבית את האכיפה של SELinux באמצעות adb בגרסאות userdebug או eng. כדי לעשות את זה, קודם צריך להעביר את ADB למצב root על ידי הפעלת adb root. לאחר מכן, כדי להשבית את האכיפה של SELinux, מריצים את הפקודה:

adb shell setenforce 0

או בשורת הפקודה של ליבת המערכת (במהלך הפעלה מוקדמת של המכשיר):

androidboot.selinux=permissive
androidboot.selinux=enforcing

או באמצעות bootconfig ב-Android 12:

androidboot.selinux=permissive
androidboot.selinux=enforcing

שימוש ב-audit2allow

הכלי audit2allow לוקח דחיות של dmesg וממיר אותן להצהרות מדיניות תואמות של SELinux. לכן, הוא יכול להאיץ מאוד את פיתוח SELinux.

כדי להשתמש בו, מריצים את הפקודה:

adb pull /sys/fs/selinux/policy
adb logcat -b events -d | audit2allow -p policy

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

#============= shell ==============
allow shell kernel:security setenforce;
#============= rmt ==============
allow rmt kmem_device:chr_file { read write };

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