1. Sự phát triển của bảo mật kiểm soát truy cập trong hệ điều hành
Trước đây, các hệ điều hành gần như không có cơ chế bảo mật, người dùng có thể truy cập bất kỳ tệp hoặc tài nguyên nào chỉ bằng cách biết tên của chúng. Không lâu sau, các cơ chế kiểm soát truy cập bắt đầu xuất hiện để tăng độ bảo mật. Loại kiểm soát truy cập chủ yếu mà chúng ta có ngày nay được gọi là kiểm soát truy cập tùy ý (DAC). Tính năng chính của DAC là chủ sở hữu tài nguyên có thể chỉ định ai có thể hoặc không thể truy cập tài nguyên. Tuy nhiên, DAC có điểm yếu lớn là nó dựa vào sự tin tưởng vào người dùng, điều này có thể dẫn đến các vấn đề bảo mật nếu phần mềm hoặc người dùng gặp sự cố. Để khắc phục điều này, kiểm soát truy cập bắt buộc (MAC) được phát triển, nơi quyền truy cập được kiểm soát bởi chính sách bảo mật cứng nhắc không thể thay đổi bởi người dùng. SELinux là một cơ chế MAC linh hoạt và cấu hình được, cung cấp khả năng kiểm soát truy cập mạnh mẽ và có thể điều chỉnh để phù hợp với các yêu cầu bảo mật khác nhau, khắc phục những điểm yếu của DAC và làm cho bảo mật hệ thống trở nên hiệu quả hơn.
1.1. Mô hình Reference Monitor
Để hiểu về kiểm soát truy cập, chúng ta cần hiểu rõ mô hình Reference Monitor
- Mục tiêu của Reference Monitor: Đảm bảo hệ thống bảo mật luôn hoạt động đúng cách, luôn thực hiện kiểm soát truy cập đối với mọi đối tượng.
- Subjects: Là các thực thể chủ động như các tiến trình.
- Objects: Là các tài nguyên thụ động như tập tin, thư mục.
- RVM (Reference Validation Mechanism) là thành phần kiểm soát việc truy cập giữa các subjects và objects. Nó áp dụng chính sách bảo mật thông qua một bộ các quy tắc kiểm soát truy cập để đảm bảo rằng chỉ những quyền truy cập phù hợp với chính sách mới được phép.
1.2. Những vấn đề của kiểm soát truy cập tùy ý (DAC)
Discretionary Access Control (DAC) cho phép người dùng có quyền sở hữu tài nguyên quyết định ai có thể truy cập vào các tài nguyên đó. Cụ thể hơn, người dùng có thể thay đổi các thuộc tính kiểm soát truy cập của các đối tượng như tệp tin, thư mục, thông qua các cơ chế như phân quyền sở hữu (owner-group-world) trong Linux hoặc danh sách kiểm soát truy cập (ACL). Tuy nhiên, DAC có một điểm yếu cơ bản là nó không phân biệt được giữa người dùng thực sự và các chương trình máy tính mà người dùng chạy.
Nhược điểm của DAC:
Môi trường không an toàn: DAC hoạt động dựa trên giả định rằng môi trường là an toàn và các phần mềm không có lỗi, nhưng thực tế, luôn có những người sẽ khai thác lỗ hổng trong phần mềm để thực hiện các hành động xấu.
- Tin tưởng vào phần mềm: DAC giả định rằng tất cả các chương trình đều đáng tin cậy và không có lỗi, nhưng thực tế là người dùng thường dựa vào phần mềm (không phải do họ tạo ra) để thực hiện các chức năng trên máy tính. Điều này có nghĩa là nếu người dùng có quyền truy cập, các chương trình mà họ sử dụng cũng có quyền truy cập, bao gồm cả các chương trình độc hại.
- Môi trường không an toàn: DAC hoạt động dựa trên giả định rằng môi trường là an toàn và các phần mềm không có lỗi, nhưng thực tế, luôn có những người sẽ khai thác lỗ hổng trong phần mềm để thực hiện các hành động xấu.
1.3 Nguồn gốc của kiểm soát truy cập bắt buộc (MAC)
- Trong những năm 1970 và 1980, người ta đã nỗ lực rất nhiều để giải quyết vấn đề phần mềm độc hại và các lỗ hổng trong phần mềm. Mục tiêu là phát triển MAC, nơi mà các quyết định kiểm soát truy cập không phụ thuộc vào quyết định của người dùng hay quản trị viên hệ thống. Thay vào đó, thực hiện một chính sách bảo mật của tổ chức để kiểm soát việc truy cập vào các đối tượng mà không thể bị thay đổi bởi các cá nhân hoặc chương trình. Hầu hết công việc này được tài trợ bởi quân đội, tập trung vào việc bảo vệ tính bảo mật dữ liệu của chính phủ. Cơ chế MAC phổ biến nhất được triển khai đến nay là bảo mật đa cấp (MLS), dựa trên mô hình Bell-LaPadula.
- Đặc điểm chính của MAC: Áp dụng một chính sách bảo mật tổ chức nghiêm ngặt, thường được thiết lập để bảo vệ tính bảo mật của dữ liệu ở nhiều cấp độ khác nhau.
- Hạn chế của MAC: Mặc dù MAC cung cấp bảo mật mạnh mẽ, nó thường bị giới hạn ở một mục tiêu bảo mật duy nhất, như bảo vệ tính bảo mật của dữ liệu. Điều này dẫn đến việc khó áp dụng MAC trong các hệ thố
1.4 SELinux
- SELinux sử dụng một cơ chế MAC linh hoạt gọi là kiểm soát theo kiểu (Type Enforcement – TE). Cơ chế này cho phép bảo mật mạnh mẽ và có thể điều chỉnh để phù hợp với nhiều mục tiêu bảo mật khác nhau. Trong SELinux, mọi chương trình và tệp đều có một “kiểu” (type) xác định. Để một chương trình truy cập tệp, kiểu của chương trình đó phải được phép truy cập kiểu của tệp.
- Điều làm cho SELinux nổi bật hơn so với các giải pháp khác là các quy tắc truy cập trong SELinux không cố định hoặc được mã hóa sẵn trong kernel. Thay vào đó, có thể tự tạo ra các quy tắc để xác định quyền truy cập, giúp SELinux dễ dàng thích ứng với nhiều loại chính sách bảo mật khác nhau.
- Các quy tắc này được gọi là “SELinux Policy” và được lưu trong một tệp đặc biệt. Tệp này sẽ được nạp vào kernel khi khởi động và sau đó được sử dụng để quyết định quyền truy cập.
Chế độ hoạt động:
- Enforcing (Bắt buộc): SELinux thực thi các chính sách bảo mật và chặn mọi hành động không được phép theo chính sách.
- Permissive (Cho phép): SELinux không chặn các hành động không được phép mà chỉ ghi lại chúng để kiểm tra.
- Disabled (Vô hiệu hóa): SELinux hoàn toàn bị tắt và không thực hiện bất kỳ kiểm soát truy cập nào.
2. Kiến trúc của Kernel
SELinux mở rộng khả năng kiểm soát truy cập trong kernel của Linux thông qua một hệ thống gọi là LSM (Linux Security Modules).
2.1 LSM Framework
LSM Framework là một cơ chế cho phép các module bảo mật như SELinux nạp vào kernel của Linux. Nó cho phép các module này kiểm tra quyền truy cập trước khi kernel thực sự cấp quyền truy cập cho một tài nguyên. LSM cung cấp một tập hợp các điểm móc (hooks) trong system call của kernel.
Hình 2: Kiến trúc LSM hook
Quy trình xử lý:
- Usermode Process: Đây là một tiến trình đang chạy trong không gian người dùng.
- Open System Call: Khi tiến trình người dùng cần truy cập một tệp hoặc tài nguyên khác, nó sẽ thực hiện một system call để mở tài nguyên đó.
- Lookup Inode: Hệ điều hành tìm kiếm và xác định inode của tệp hoặc tài nguyên. Inode là cấu trúc dữ liệu trong hệ thống tập tin dùng để lưu trữ thông tin về tệp, như quyền truy cập, chủ sở hữu, và các thuộc tính khác.
- Error Check: Hệ điều hành thực hiện kiểm tra lỗi để đảm bảo rằng inode tồn tại và có thể được truy cập.
- DAC Check: Hệ điều hành thực hiện kiểm tra quyền truy cập theo mô hình DAC (Discretionary Access Control). Đây là bước xác thực quyền truy cập dựa trên các thuộc tính của người dùng và tệp, chẳng hạn như quyền đọc, ghi hoặc thực thi.
- LSM Hook: Hệ điều hành kiểm tra các hook của LSM. Nếu các hook này yêu cầu, SELinux sẽ được tham khảo để thực hiện kiểm tra quyền truy cập bổ sung. SELinux sẽ quyết định xem truy cập có được phép hay không dựa trên chính sách của nó.
- Access Inode: Nếu tất cả các kiểm tra quyền truy cập (DAC và SELinux) đều cho phép, hệ điều hành sẽ thực hiện việc truy cập inode và tiến trình người dùng có thể tiếp tục thực hiện hành động trên tài nguyên (như đọc hoặc ghi tệp).
2.2. SELinux LSM Module
- Kiến trúc của SELinux LSM Module dựa trên thiết kế Flask, vốn được phát triển cho các hệ điều hành microkernel. Thiết kế này bao gồm ba thành phần chính: security server, object managers và the access vector cache.
Hình 3: Kiến trúc của SELinux LSM Module
Security Server:
- Security Server lưu trữ và quản lý các quy tắc chính sách bảo mật (policy rules). Những quy tắc này xác định các điều kiện và yêu cầu về quyền truy cập vào các tài nguyên hệ thống.
- Security Server tra cứu các quy tắc trong chính sách để kiểm tra xem yêu cầu truy cập có được phép hay không. Sau khi kiểm tra, Security Server đưa ra một quyết định về việc cấp phép hoặc từ chối yêu cầu truy cập. Quyết định này sẽ được trả về cho LSM hook, và từ đó, kernel sẽ thực hiện yêu cầu truy cập dựa trên quyết định của Security Server.
- Object Managers: Thực thi các quyết định chính sách của Security Server đối với các tài nguyên mà chúng quản lý. Trong kernel, Object Managers có thể được coi là các subsystem của kernel quản lý các đối tượng ở cấp kernel.
- Access Vector Cache:
- AVC hoạt động như một trung gian giữa LSM hooks trong kernel và Security Server. Khi một yêu cầu truy cập được gửi đến, AVC sẽ tra cứu cache để tìm quyết định trước đó, nếu có, và trả về quyết định đó.
- Nếu quyết định không có trong cache, AVC sẽ yêu cầu Security Server thực hiện kiểm tra quyền truy cập và sau đó lưu trữ quyết định vào cache để sử dụng sau này.
3. SELinux Policy
- SELinux Policy là tập hợp các quy tắc xác định cách các tài nguyên trong hệ thống (như tệp tin và quy trình) có thể được truy cập và sử dụng. Chính sách này được nạp vào Security Server và được sử dụng để đưa ra các quyết định kiểm soát quyền truy cập.
3.1. Trình biên dịch SELinux Policy
Để tạo ra chính sách cho SELinux mà kernel có thể sử dụng, cần phải biên dịch tệp chính sách nguồn thành một định dạng mà kernel hiểu được. Để làm điều này, bạn sử dụng một công cụ gọi là checkpolicy. Công cụ này kiểm tra cú pháp và ngữ nghĩa của tệp chính sách nguồn và sau đó chuyển đổi nó thành một tệp nhị phân mà kernel có thể sử dụng.
Hình 4: Cấu trúc của policy.conf
- Classes and Permissions: định nghĩa các lớp đối tượng và quyền liên quan đến chúng. Đối với kernel, các lớp này liên quan trực tiếp đến các tệp nguồn của kernel, thường không thay đổi các định nghĩa này.
- Type Enforcement and Role Statement: Xác định cách các loại đối tượng tương tác với nhau và quyền truy cập của các đối tượng này. Các khai báo vai trò và người dùng cũng nằm trong phần này, xác định các vai trò và quyền của người dùng trong hệ thống. Định nghĩa các vai trò trong chính sách và các quyền của các vai trò này.
- User: Xác định các người dùng cụ thể và vai trò trong chính sách SELinux, cách tương tác với các đối tượng trong hệ thống.
- Constraints: Giúp áp đặt các yêu cầu bảo mật đặc biệt ngoài các quy tắc kiểm soát loại cơ bản.
- Resource Labeling Specification: Để SELinux có thể thực thi các quy tắc kiểm soát truy cập, tất cả các đối tượng (như tệp, thư mục, thiết bị) phải được gán nhãn với một bối cảnh bảo mật cụ thể. Bối cảnh này bao gồm các thông tin về quyền hạn và mức độ bảo mật của đối tượng đó.
3.2. Xây dựng và cài đặt Monolithic Policies
Hình 5: Quá trình xây dựng và nạp SELinux Policy
- Source Modules: Phương pháp cổ điển và phổ biến, nơi các policy module được phát triển dưới dạng các tập tin văn bản. Các module này được kết hợp thông qua shell scripts, macros, và Makefiles để tạo ra một tệp policy duy nhất (policy.conf).
- Biên dịch policy: Sử dụng công cụ checkpolicy để biên dịch policy.conf thành binary policy file, sẵn sàng để tải vào kernel.
- Tải policy vào Kernel: Sử dụng công cụ load_policy để tải binary policy file vào kernel, bắt đầu thực thi các quy tắc kiểm soát truy cập theo chính sách mới.
4. Các khái niệm về SELinux
4.1. Các Loại (Types), Thuộc Tính (Attributes), và Quy Tắc (Rules)
- Type Enforcement (TE): Là phần chính của chính sách SELinux, nơi mà tất cả các đối tượng trong hệ thống (file, process, socket) đều được gán một loại (type). Ví dụ, một ứng dụng có thể có loại untrusted_app.
- Thuộc tính (Attributes): Là các nhãn được gán cho một nhóm loại (types) nhằm mục đích dễ quản lý. Ví dụ, thuộc tính appdomain có thể được gán cho tất cả các loại ứng dụng (untrusted_app, isolated_app).
- Quy tắc (Rules): Quy tắc là cách mà SELinux kiểm soát quyền truy cập.
Ví dụ: allow untrusted_app app_data_file:file { read write };
- Quy tắc này cho phép các ứng dụng có loại “untrusted_app” đọc và ghi các tệp được gán nhãn “app_data_file”.
4.2. Macros
Đối với việc truy cập tệp, có rất nhiều loại quyền cần xem xét. Ví dụ, quyền đọc (read permission) không đủ để mở tệp hoặc gọi hàm start trên tệp đó. Để đơn giản hóa việc định nghĩa quy tắc, Android cung cấp một tập hợp các macro để xử lý các trường hợp phổ biến.
Ví dụ: Quy tắc bên trên có thể được viết lại như sau để bao gồm các quyền còn thiếu như open
allow appdomain app_data_file:file rw_file_perms;
4.3. Ngữ Cảnh Bảo Mật (Security Context) và Các Danh Mục (Categories)
- Khi gỡ lỗi các SELinux policy hoặc gán nhãn cho các tệp (thông qua file_contexts hoặc khi sử dụng lệnh ls -Z), có thể bắt gặp security context (còn gọi là label).
- Security context có định dạng như sau:
User:role:type:sensitivity[:categories]
Nhưng trong hầu hết các trường hợp, ta chỉ cần quan tâm đến type và categories.
- Danh mục (Categories) là một phần của hỗ trợ bảo mật nhiều cấp độ (Multi-Level Security – MLS) trong SELinux. Kể từ Android S, các danh mục này được sử dụng để:
- Cách ly dữ liệu của ứng dụng khỏi việc truy cập bởi ứng dụng khác.
- Cách ly dữ liệu của ứng dụng giữa các người dùng vật lý khác nhau.
- Điều này giúp tăng cường bảo mật, đảm bảo rằng dữ liệu của một ứng dụng hoặc người dùng không bị truy cập trái phép bởi các ứng dụng hoặc người dùng khác trên cùng một thiết bị.
5. Tùy chỉnh SELinux
5.1. Các bước thực hiện
- Viết chính sách SELinux cho các daemon mới: Nếu thêm các dịch vụ mới vào hệ thống, cần phải viết chính sách SELinux tương ứng để đảm bảo các dịch vụ này hoạt động một cách an toàn và được bảo vệ.
- Sử dụng các miền (domains) đã được định nghĩa sẵn: Nếu có thể, hãy sử dụng các miền SELinux đã có sẵn để không phải định nghĩa lại từ đầu.
- Gán domain cho bất kỳ tiến trình nào được từ init service: Đảm bảo rằng các tiến trình được khởi tạo từ init service đều có chính sách SELinux phù hợp.
- Làm quen với các macro trước khi viết SEPolicy: Các macro giúp đơn giản hóa việc viết chính sách SELinux và đảm bảo tính nhất quán.
- Lưu ý:
- Chia thành các module: Tách biệt các module để các chính sách SELinux có thể quản lý chúng một cách độc lập.
- Tạo chính sách SELinux cho từng nhiệm vụ riêng biệt: Đảm bảo các nhiệm vụ trong hệ thống được phân chia rõ ràng và các chính sách SELinux được áp dụng cho từng nhiệm vụ một cách hiệu quả.
- Khởi tạo các domain ở chế độ permissive: Bắt đầu ở chế độ permissive để theo dõi và điều chỉnh trước khi chuyển sang chế độ enforcing.
- Xóa khai báo permissive khi không còn lỗi: Khi không còn thông báo lỗi trong các build userdebug, bạn có thể loại bỏ khai báo permissive và chuyển sang chế độ enforcing.
- Tích hợp thay đổi vào quy trình phát triển: Đảm bảo rằng các thay đổi chính sách SELinux được tích hợp vào quy trình phát triển phần mềm để duy trì tính tương thích với các phiên bản Android sau này.
5.2. Các quyền kiểm soát
- Capability: chown, dac_override, dac_read_search, fowner, fsetid, kill, setgid, setuid, setpcap, linux_immutable, net_bind_service, net_broadcast, net_admin, net_raw, ipc_lock, ipc_owner, sys_module, sys_rawio, sys_chroot, sys_ptrace, sys_pacct, sys_admin, sys_boot, sys_nice, sys_resource, sys_time, sys_tty_config, mknod, lease, audit_write, audit_control, setfcap.
- File: ioctl, read, write, create, getattr, setattr, lock, relabelfrom, relabelto, append, unlink, link, rename, execute, swapon, quotaon.
- Directory: add_name, remove_name, reparent, search, rmdir, open, audit_access, execmod.
- Socket: ioctl, read, write, create, getattr, setattr, lock, relabelfrom, relabelto, append, bind, connect, listen, accept, getopt, setopt, shutdown, recvfrom, sendto, recv_msg, send_msg, name_bind.
- Filesystem: mount, remount, unmount, getattr, relabelfrom, relabelto, transition, associate, quotamod, quotaget.
- Process: fork, transition, sigchld, sigkill, sigstop, signull, signal, ptrace, getsched, setsched, getsession, getpgid, setpgid, getcap, setcap, share, getattr, setexec, setfscreate, noatsecure, siginh, setrlimit, rlimitinh, dyntransition, setcurrent, execmem, execstack, execheap, setkeycreate, setsockcreate.
- Security: compute_av, compute_create, compute_member, check_context, load_policy, compute_relabel, compute_user, setenforce, setbool, setsecparam, setcheckreqprot, read_policy.
6. Viết một SELinux Policy
Bước 1: Viết file my_policy.te
- Thêm các SELinux Policy vào đường dẫn “device/manufacturer/device-name/sepolicy”, trong trường hợp này là “device/brcm/rpi4/sepolicy”
- cd aosp/source/device/brcm/rpi4/sepolicy
- code my_policy.te
- type my_app_exec, exec_type, file_type: Định nghĩa một loại mới “my_app_exec” được dùng cho các đối tượng thực thi và file.
- type my_app_data_file, file_type: Định nghĩa một loại mới “my_app_data_file” được dùng cho file.
- allow my_app_exec my_app_data_file:file { read write }: cho phép các đối tượng “my_app_exec” có quyền đọc ghi các đối tượng “my_app_data_file”.
Bước 2: Xây dựng lại SEPolicy
- cd aosp/source
- source build/envsetup.sh
- lunch aosp_rpi4-userdebug
- m sepolicy
Bước 3: Xây dựng lại Image
- m -j$(nproc)
- ./rpi4-mkimg.sh
- Flash Image
Bước 4: Khởi động rpi4 và kiểm tra đã có SEPolicy mới hay chưa
- Tạo 1 file mới để kiểm tra
- adb root
- adb shell
- touch data/file_test
- Gán context bất kì cho file_test
- chcon u:object_r:context_test:s0 data/file_test
- Kiểm tra log: dmesg | grep -i “selinux”
- Loại “context_test” không hợp lệ vì chưa được định nghĩa trong SEPolicy
- Gán context với loại “my_app_data_file”
- chcon u:object_r:my_app_data_file:s0 data/file_test
- Kiểm tra log: dmesg | grep -i “selinux” không có lỗi, SEPolicy mới đã được áp dụng.