פתרון מדיניות קריאות מערכת¶
פתרון ראשון¶
הארכיטקטורה השגויה מגיעה להוראת ההרג הראשונה. הארכיטקטורה הנתמכת מדלגת עליה, ומספר רגיל של הקריאה האסורה מגיע להחזרת שגיאת ההרשאה. הקריאה המותרת מדלגת על החזרת השגיאה ומגיעה לאישור. הממשק הנוסף בעל הסיבית המיוחדת מגיע להוראת ההרג השנייה עוד לפני השוואת מספר הקריאה האסורה.
מספר קריאה הוא מזהה בתוך ממשק מסוים. השוואת המספר בלי זיהוי הממשק מניחה הנחה שלא נבדקה על משמעותו, ולכן עלולה לאשר פעולה שהתכוונו לסרב לה.
פתרון שני¶
נחליף בתוך install את SYS_getppid ב־SYS_socket. נשאיר את הגדרות המסנן ונחליף את הפונקציה הראשית בקוד הבא. התוכנית משתמשת בסוקט זוגי שנפתח לפני ההתקנה, ולכן אינה זקוקה לתהליך אחר.
fn main() -> std::io::Result<()> {
use std::io::{Read, Write};
let (mut left, mut right) = std::os::unix::net::UnixStream::pair()?;
install()?;
let new_socket = unsafe { libc::socket(libc::AF_UNIX,
libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0) };
assert_eq!(new_socket, -1);
assert_eq!(std::io::Error::last_os_error().raw_os_error(), Some(libc::EPERM));
left.write_all(b"ok")?;
let mut bytes = [0_u8; 2];
right.read_exact(&mut bytes)?;
assert_eq!(&bytes, b"ok");
Ok(())
}
המסנן בדוגמה מסרב רק לקריאה socket. הוא אינו מסרב ל־socketpair, ולכן אפילו אינו מהווה מדיניות מלאה של איסור יצירת סוקטים. ההבחנה הזאת היא חלק מהפתרון: נדרש לאפיין את כל הדרכים לבצע את הפעולה ברמת האבטחה, ולא להסתפק בשם של פונקציה אחת.
פתרון שלישי¶
בשלב הראשון נברר את גרסת הממשק ואת קבוצת הפעולות שניתן להגביל. בשלב השני נבנה ונחיל מדיניות, וכל כשל יפסיק את ההפעלה לפני שהשירות מקבל לקוחות. בשלב השלישי נפעיל בדיקת גישה מותרת ואסורה מאותו הקשר שבו יעבדו הבקשות.
חוסר תמיכה אינו שקול להפרת מדיניות, משום שלא הותקן כלל שיוכל להיאכף. תהליכון אחר שאינו נושא את המסנן עשוי להמשיך לבצע קריאות שהראשון אינו רשאי לבצע. לכן נדרשת התקנה מוקדמת לפני יצירת עובדים או התקנה מסונכרנת שהצלחתה נבדקה.