selinux.md: Add how to write actual policy rules

BUG=none.
TEST=scripts/preview_docs security/selinux.md

Change-Id: I4f566cd89249dad35ce92c3123c261dff9b10164
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/docs/+/2851105
Tested-by: Betul Soysal <betuls@google.com>
Reviewed-by: Jorge Lucangeli Obes <jorgelo@chromium.org>
Reviewed-by: Nicole Anderson-Au <nvaa@google.com>
Commit-Queue: Betul Soysal <betuls@google.com>
diff --git a/security/selinux.md b/security/selinux.md
index 2bc408d..c6bd6c3 100644
--- a/security/selinux.md
+++ b/security/selinux.md
@@ -691,9 +691,138 @@
 
 1.  Write actual rules
 
-    TODO
+    After creating a permissive domain for your daemon, now you need to write
+    actual rules before making the daemon domain SELinux enforcing.
+    Follow these steps to add the needed access rules:
 
-    Go back to previous step to update SELinux tests for stateful files too.
+    - Build and flash a new image with the USE flag ‘selinux_develop’. You need
+    this flag to log permissive denials.
+
+    - Run all the tests that are related to your daemon on the test device.
+
+    - Check the SELinux violations logged at /var/log/audit/audit.log on your
+    test device and add the required access rules to the policy as explained in
+    [How to read the denials in audit logs].
+
+    - You can use 'audit2allow' script instead to define the access rules. Run
+    'audit2allow’ script on the audit.log file in your chroot:
+
+    ```
+    audit2allow -xp /build/$BOARD/etc/selinux/arc/policy/policy.30 -i /path_to_audit_log/audit.log
+    ```
+
+    - Add the rules created by the audit2allow to the policy.
+
+    - Use macros where possible and group the rules that apply to the same file
+    context. Macros are defined in [base/imported/global_macros],
+    [base/imported/te_macros] and [chromeos/te_macros]. Find the appropriate
+    macro that includes all the access vectors reported by audit2allow. For
+    example if audit2allow generates the following allow rule:
+
+    ```
+    allow <DomainA> <ContextA>:file { getattr open read ioctl lock map }
+    ```
+
+    Use 'r_file_perms' macro instead:
+
+    ```
+    allow <DomainA> <ContextA>:file r_file_perms
+    ```
+
+    - emerge and deploy the updated policy and run the tests again. You might
+    want to remove /var/log/audit/audit.log file before running the tests to
+    avoid any confusion.
+
+    If you receive the same SELinux violation log despite adding the required
+    access rule this means you need to add an additional rule related to the
+    same violation. Right place to start the investigation is the audit logs at
+    '/var/log/audit/audit.log' and system log messages at '/var/log/messages'
+    together. Identify the step causing the violation. Examples could be the
+    need to define an ioctl or a key search permission requirement or you may
+    need to change how the file context is defined.
+
+    - Once the policy is complete for the specific board you are testing the
+    policy on, you need to make sure policy doesn't fail for other boards as
+    well. There are macros to restrict the scope of the access rules for
+    different cases. 'has_arc' or 'is_arc_vm' are the two examples for these
+    macros. For example, /home/root/hash/android-data/data directory only
+    exists if ARC is enabled on the device. Therefore if you need to define an
+    access rule for the file context 'media_rw_data_file' you need to use
+    ‘has_arc’ macro.
+
+    - If SELinux logs report a violation against an unlabeled or an unconfined
+    context define the context properly. Run the specific test triggering this
+    rule and identify the specific step to find out the unlabeled/unconfined
+    object. Label them properly following the steps defined in [File Contexts].
+
+    __Testing after adding new rules:__
+
+    - emerge and deploy selinux-policy after adding the new rules. Don't forget
+    to run `cros-workon --board_name=$BOARD --start selinux-policy` before
+    emerging the selinux-policy. And repeat the tests.
+
+    - Domain is ready to become enforcing once there are no more SELinux
+    violations logged at /var/log/audit/audit.log for your domain (or when
+    audit2allow doesn't generate any rules for your domain anymore).
+
+    - Remove 'permissive cros_tcsd' line to make the domain enforcing.
+
+    - If you've added or updated any file context you need to build a new image.
+    Even if you haven't changed any file context you still need to build a new
+    image without the USE flag 'selinux_develop' since the daemon will be
+    running in an enforcing domain (remember this flag is to log permissive
+    denials). if there is any missing access rule for the daemon that will
+    be logged since the daemon will be running in an enforcing domain.
+
+    - Repeat the tests. All the tests need to pass and no more violations should
+    be logged.
+
+    - If any of the tests fail and SELinux violations are logged for your
+    daemon, that is possibly because some of the violation logs were overridden
+    on audit.log when you were running the tests earlier. In this case you may
+    need to run the tests in smallers test sets. Or you can use a simple script
+    to filter out the logs you are looking for. A sample script could be like
+    this:
+
+    ```
+    #!/bin/bash
+
+    touch selinux_rules
+    chmod 666 selinux_rules
+    for (( i=1; i<=360; i++ ))
+    do
+    scp DUT://var/log/audit/audit.log ./audit.log
+    audit2allow -p /build/puff/etc/selinux/arc/policy/policy.30 -i ./audit.log | grep 'allow cros_cryptohomed' | tee -a selinux_rules
+    echo $i
+    sleep 30
+    done
+    ```
+
+    This script copies the audit.log file from a puff board, converts the
+    violation logs to allowrules, greps only the rules that are part of
+    cros_cryptohome domain policy and dumps them to a separate file. Repeat the
+    previous steps until all the tests are passing and no more violations are
+    logged on audit.log for your daemon.
+
+
+    - Before uploading the updated policy, make sure unit tests pass as well.
+    Android neverallow rules apply to Chrome OS since Chrome OS and Android
+    share the same kernel when Android is running in the ARC container. These
+    neverallow rules are not merged to the policy and don't take effect at
+    compile time. They are rather injected into the selinux-policy unit tests
+    and that's why you need to run the unit tests to test against the Android
+    neverallow rules:
+
+    ```
+    FEATURES=test emerge-DUT-with-ARC-Container selinux-policy
+    ```
+
+    - Note that the neverallow rules are not imported by the VM unittests like
+    amd64-generic or betty-pi-arc boards, so you need to use a physical ARC
+    container device build to run the unit tests for the updated policy.
+
+    After updating the policy, go back to previous step to update SELinux tests
+    for stateful files too.
 
     If your process relates to after-login behavior, you may also need to update
     `selinux_files_arc.go` and `selinux_files_non_arc.go`. Non-ARC specified
@@ -1024,3 +1153,10 @@
 For Googlers, there's a nice introduction presentation slides how debugging
 SELinux policies to refer to though it's for Android, at
 [go/sepolicy-debug](https://goto.google.com/sepolicy-debug)
+
+[File Contexts]: https://chromium.googlesource.com/chromiumos/docs/+/main/security/selinux.md#File-Contexts
+[How to read the denials in audit logs]: https://chromium.googlesource.com/chromiumos/docs/+/main/security/selinux.md#How-to-read-the-denials-in-audit-logs
+
+[base/imported/global_macros]: https://source.corp.google.com/chromeos_public/src/platform2/sepolicy/policy/base/imported/global_macros
+[base/imported/te_macros]: https://source.corp.google.com/chromeos_public/src/platform2/sepolicy/policy/base/imported/te_macros
+[chromeos/te_macros]: https://source.corp.google.com/chromeos_public/src/platform2/sepolicy/policy/chromeos/te_macros