commit 549241cec9404bd211a580454fdd28cb72dfe520
Author: Gabriel Becker <ggasparb@redhat.com>
Date: Thu Feb 24 17:24:17 2022 +0100
Manual edited patch scap-security-guide-0.1.59-BZ1884687-PR_7770.patch.
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/ansible/shared.yml b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/ansible/shared.yml
new file mode 100644
index 0000000..09d1984
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/ansible/shared.yml
@@ -0,0 +1,31 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+- name: Get all local users from /etc/passwd
+ ansible.builtin.getent:
+ database: passwd
+ split: ':'
+
+- name: Create local_users variable from the getent output
+ ansible.builtin.set_fact:
+ local_users: '{{ ansible_facts.getent_passwd|dict2items }}'
+
+- name: Test for existence of home directories to avoid creating them, but only fixing group ownership
+ ansible.builtin.stat:
+ path: '{{ item.value[4] }}'
+ register: path_exists
+ loop: '{{ local_users }}'
+ when:
+ - item.value[2]|int >= {{{ gid_min }}}
+ - item.value[2]|int != 65534
+
+- name: Ensure interactive local users are the group-owners of their respective home directories
+ ansible.builtin.file:
+ path: '{{ item.0.value[4] }}'
+ group: '{{ item.0.value[2] }}'
+ loop: '{{ local_users|zip(path_exists.results)|list }}'
+ when:
+ - item.1.stat is defined and item.1.stat.exists
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/bash/shared.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/bash/shared.sh
new file mode 100644
index 0000000..08f7307
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/bash/shared.sh
@@ -0,0 +1,7 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+awk -F':' '{ if ($4 >= {{{ gid_min }}} && $4 != 65534) system("chgrp -f " $4" "$6) }' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/oval/shared.xml b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/oval/shared.xml
new file mode 100644
index 0000000..a1d1f2e
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/oval/shared.xml
@@ -0,0 +1,89 @@
+<def-group>
+ <definition class="compliance" id="{{{ rule_id }}}" version="1">
+ {{{ oval_metadata("All interactive user's Home Directories must be group-owned by its user") }}}
+ <criteria operator="AND">
+ <criterion test_ref="test_file_groupownership_home_directories"
+ comment="All interactive user's Home Directories must be group-owned by its user"/>
+ <criterion test_ref="test_file_groupownership_home_directories_duplicated"
+ comment="Interactive users should group-own only one Home Directory"/>
+ </criteria>
+ </definition>
+
+ <!-- For detailed comments about logic used in this OVAL, check the
+ "file_ownership_home_directories" rule. -->
+ <unix:password_object id="object_file_groupownership_home_directories_objects" version="1">
+ <unix:username datatype="string" operation="not equal">nobody</unix:username>
+ <filter action="include">state_file_groupownership_home_directories_interactive_gids</filter>
+ </unix:password_object>
+
+ <unix:password_state id="state_file_groupownership_home_directories_interactive_gids" version="1">
+ <unix:group_id datatype="int" operation="greater than or equal">{{{ gid_min }}}</unix:group_id>
+ </unix:password_state>
+
+ <!-- #### prepare for test_file_groupownership_home_directories #### -->
+ <local_variable id="var_file_groupownership_home_directories_dirs" datatype="string" version="1"
+ comment="Variable including all home dirs from primary interactive groups">
+ <object_component item_field="home_dir" object_ref="object_file_groupownership_home_directories_objects"/>
+ </local_variable>
+
+ <local_variable id="var_file_groupownership_home_directories_gids" datatype="int" version="1"
+ comment="Variable including all gids from primary interactive group">
+ <object_component item_field="group_id" object_ref="object_file_groupownership_home_directories_objects"/>
+ </local_variable>
+
+ <!-- #### creation of object #### -->
+ <unix:file_object id="object_file_groupownership_home_directories_dirs" version="1">
+ <unix:path var_ref="var_file_groupownership_home_directories_dirs" var_check="at least one"/>
+ <unix:filename xsi:nil="true"/>
+ </unix:file_object>
+
+ <!-- #### creation of state #### -->
+ <unix:file_state id="state_file_groupownership_home_directories_gids" version="1">
+ <unix:group_id datatype="int" var_check="only one" var_ref="var_file_groupownership_home_directories_gids"/>
+ </unix:file_state>
+
+ <!-- #### creation of test #### -->
+ <!-- #### creatin of test_file_groupownership_home_directories #### -->
+ <unix:file_test id="test_file_groupownership_home_directories" check="all" check_existence="any_exist"
+ version="1" comment="All home directories are group-owned by a local interactive group">
+ <unix:object object_ref="object_file_groupownership_home_directories_dirs"/>
+ <unix:state state_ref="state_file_groupownership_home_directories_gids"/>
+ </unix:file_test>
+
+ <!-- #### prepare for test_file_groupownership_home_directories_duplicated #### -->
+ <local_variable id="var_file_groupownership_home_directories_gids_count" datatype="int" version="1"
+ comment="Variable including count of gids from interactive group-owners">
+ <count>
+ <object_component item_field="group_id" object_ref="object_file_groupownership_home_directories_dirs"/>
+ </count>
+ </local_variable>
+
+ <local_variable id="var_file_groupownership_home_directories_gids_count_uniq" datatype="int" version="1"
+ comment="Variable including count of uniq gids from interactive group-owners">
+ <count>
+ <unique>
+ <object_component item_field="group_id" object_ref="object_file_groupownership_home_directories_dirs"/>
+ </unique>
+ </count>
+ </local_variable>
+
+ <!-- #### creation of object #### -->
+ <ind:variable_object id="object_file_groupownership_home_directories_gids_count" version="1">
+ <ind:var_ref>var_file_groupownership_home_directories_gids_count</ind:var_ref>
+ </ind:variable_object>
+
+ <!-- #### creation of state #### -->
+ <!-- #### creation of state_no_duplicate_groupowners #### -->
+ <ind:variable_state id="state_file_groupownership_home_directories_gids_count_uniq" version="1">
+ <ind:value datatype="int" operation="equals" var_check="at least one"
+ var_ref="var_file_groupownership_home_directories_gids_count_uniq"/>
+ </ind:variable_state>
+
+ <!-- #### creation of test #### -->
+ <ind:variable_test id="test_file_groupownership_home_directories_duplicated" check="all"
+ check_existence="any_exist" version="1"
+ comment="It should not exist duplicated group-owners of home dirs">
+ <ind:object object_ref="object_file_groupownership_home_directories_gids_count"/>
+ <ind:state state_ref="state_file_groupownership_home_directories_gids_count_uniq"/>
+ </ind:variable_test>
+</def-group>
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/rule.yml b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/rule.yml
index 2e6ce60..e33660f 100644
--- a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/rule.yml
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/rule.yml
@@ -10,6 +10,10 @@ description: |-
interactive users home directory, use the following command:
<pre>$ sudo chgrp <i>USER_GROUP</i> /home/<i>USER</i></pre>
+ This rule ensures every home directory related to an interactive user is
+ group-owned by an interactive user. It also ensures that interactive users
+ are group-owners of one and only one home directory.
+
rationale: |-
If the Group Identifier (GID) of a local interactive users home directory is
not the same as the primary GID of the user, this would allow unauthorized
@@ -42,3 +46,9 @@ ocil: |-
To verify the assigned home directory of all interactive users is group-
owned by that users primary GID, run the following command:
<pre># ls -ld $(awk -F: '($3>=1000)&&($7 !~ /nologin/){print $6}' /etc/passwd)</pre>
+
+warnings:
+ - general: |-
+ Due to OVAL limitation, this rule can report a false negative in a
+ specific situation where two interactive users swap the group-ownership
+ of their respective home directories.
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/expected_groupowner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/expected_groupowner.pass.sh
new file mode 100644
index 0000000..1605339
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/expected_groupowner.pass.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chgrp $USER /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_all_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_all_absent.pass.sh
new file mode 100644
index 0000000..af24025
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_all_absent.pass.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -M $USER
+# This make sure home dirs related to test environment users are also removed.
+rm -Rf /home/*
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_one_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_one_absent.pass.sh
new file mode 100644
index 0000000..5bce517
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_one_absent.pass.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -M $USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_with_same_groupowner.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_with_same_groupowner.fail.sh
new file mode 100644
index 0000000..9d0f765
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/home_dirs_with_same_groupowner.fail.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Define the same owner for two home directories
+chgrp $USER1 /home/$USER1
+chgrp $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/interactive_users_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/interactive_users_absent.pass.sh
new file mode 100644
index 0000000..ed34f09
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/interactive_users_absent.pass.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# remove all interactive users (ID >= 1000) from /etc/passwd
+sed -i '/.*:[0-9]\{4,\}:/d' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_system_id.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_system_id.fail.sh
new file mode 100644
index 0000000..c1a87c1
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_system_id.fail.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chgrp 2 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_unknown_id.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_unknown_id.fail.sh
new file mode 100644
index 0000000..d352011
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/unexpected_groupowner_unknown_id.fail.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chgrp 10005 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_crossed_groupowner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_crossed_groupowner.pass.sh
new file mode 100644
index 0000000..0cffa4a
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_crossed_groupowner.pass.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Define the same owner for two home directories
+chgrp $USER2 /home/$USER1
+chgrp $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_swapped_groupowner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_swapped_groupowner.pass.sh
new file mode 100644
index 0000000..3e5b778
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_groupownership_home_directories/tests/warning_home_dirs_swapped_groupowner.pass.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Swap the group-ownership of two home directories
+# WARNING: This test scenario will report a false negative, as explained in the
+# warning section of this rule.
+chgrp $USER2 /home/$USER1
+chgrp $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/ansible/shared.yml b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/ansible/shared.yml
new file mode 100644
index 0000000..97d4274
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/ansible/shared.yml
@@ -0,0 +1,31 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+- name: Get all local users from /etc/passwd
+ ansible.builtin.getent:
+ database: passwd
+ split: ':'
+
+- name: Create local_users variable from the getent output
+ ansible.builtin.set_fact:
+ local_users: '{{ ansible_facts.getent_passwd|dict2items }}'
+
+- name: Test for existence home directories to avoid creating them, but only fixing ownership
+ ansible.builtin.stat:
+ path: '{{ item.value[4] }}'
+ register: path_exists
+ loop: '{{ local_users }}'
+ when:
+ - item.value[1]|int >= {{{ uid_min }}}
+ - item.value[1]|int != 65534
+
+- name: Ensure interactive local users are the owners of their respective home directories
+ ansible.builtin.file:
+ path: '{{ item.0.value[4] }}'
+ owner: '{{ item.0.value[1] }}'
+ loop: '{{ local_users|zip(path_exists.results)|list }}'
+ when:
+ - item.1.stat is defined and item.1.stat.exists
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/bash/shared.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/bash/shared.sh
new file mode 100644
index 0000000..1d1e675
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/bash/shared.sh
@@ -0,0 +1,7 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+awk -F':' '{ if ($3 >= {{{ uid_min }}} && $3 != 65534) system("chown -f " $3" "$6) }' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/oval/shared.xml b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/oval/shared.xml
new file mode 100644
index 0000000..3d0b9ae
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/oval/shared.xml
@@ -0,0 +1,142 @@
+<def-group>
+ <!-- Updated references of the OVAL language used in this file can be found in this link:
+ https://oval-community-guidelines.readthedocs.io/en/latest/oval-schema-documentation/oval-definitions-schema.html
+ -->
+
+ <definition class="compliance" id="{{{ rule_id }}}" version="1">
+ {{{ oval_metadata("All interactive user's Home Directories must be owned by its user") }}}
+ <criteria operator="AND">
+ <criterion test_ref="test_file_ownership_home_directories"
+ comment="All interactive user's Home Directories must be owned by its user"/>
+ <criterion test_ref="test_file_ownership_home_directories_duplicated"
+ comment="Interactive users should own only one Home Directory"/>
+ </criteria>
+ </definition>
+
+ <!--
+ Extract a list composed of password objects filtered by UIDs starting in {{{ uid_min }}} and
+ not equal to "nobody". Most of (if not all) distros have the special user "nobody" with uid
+ 65354. Despite it be technically classified as an interactive user, it is a special case with
+ very limited access. So, we ignore it. The resulted password object will be further used to
+ create local variables composed by UIDs e Home Dirs.
+ -->
+ <unix:password_object id="object_file_ownership_home_directories_objects" version="1">
+ <unix:username datatype="string" operation="not equal">nobody</unix:username>
+ <filter action="include">state_file_ownership_home_directories_interactive_uids</filter>
+ </unix:password_object>
+
+ <!--
+ In distros which uses PAM (almost all), by default, the uid of interactive users and groups
+ starts at 1000. We use this information to make sure this password_state object will be
+ composed only with objects related to interactive users.
+ -->
+ <unix:password_state id="state_file_ownership_home_directories_interactive_uids" version="1">
+ <unix:user_id datatype="int" operation="greater than or equal">{{{ uid_min }}}</unix:user_id>
+ </unix:password_state>
+
+ <!--
+ #### prepare for test_file_groupownership_home_directories ####
+ From the list of interactive users objects we create a local variable composed of their home dirs.
+ -->
+ <local_variable id="var_file_ownership_home_directories_dirs" datatype="string" version="1"
+ comment="Variable including all home dirs from interactive users">
+ <object_component item_field="home_dir" object_ref="object_file_ownership_home_directories_objects"/>
+ </local_variable>
+
+ <!--
+ From the list of interactive users objects we create a local variable composed of their uids.
+ -->
+ <local_variable id="var_file_ownership_home_directories_uids" datatype="int" version="1"
+ comment="List of interactive users uids">
+ <object_component item_field="user_id" object_ref="object_file_ownership_home_directories_objects"/>
+ </local_variable>
+
+ <!--
+ #### creation of object ####
+ We have the home dirs, but to test their ownership we need a "file_object" and not a password
+ object, as the initial source of this information is. So, we create this file_object based on
+ content from the previous local variable.
+ -->
+ <unix:file_object id="object_file_ownership_home_directories_dirs" version="1">
+ <unix:path var_ref="var_file_ownership_home_directories_dirs" var_check="at least one"/>
+ <unix:filename xsi:nil="true"/>
+ </unix:file_object>
+
+ <!--
+ #### creation of state ####
+ We have the relevant uids, but we need a "file_state" object to use in our intendend test.
+ So, we create this file_state based on content from the previous local variable.
+ -->
+ <unix:file_state id="state_file_ownership_home_directories_uids" version="1">
+ <unix:user_id datatype="int" var_check="only one" var_ref="var_file_ownership_home_directories_uids"/>
+ </unix:file_state>
+
+ <!--
+ #### creation of test ####
+ Perform the test to ensure that all home dirs are owned by an interactive user.
+ This test will make sure that no foreign or system user is owner of an existing home dir.
+ However, this can't ensure that one local interactive user is the owner of only one home dir.
+ Currently this is an OVAL limitation which we try to mitigate with a second test below.
+ -->
+ <unix:file_test id="test_file_ownership_home_directories" check="all" check_existence="any_exist"
+ version="1" comment="All home directories are owned by a local interactive user">
+ <unix:object object_ref="object_file_ownership_home_directories_dirs"/>
+ <unix:state state_ref="state_file_ownership_home_directories_uids"/>
+ </unix:file_test>
+
+ <!--
+ We create an extra test to make sure that the number of home dirs and their respective owners
+ are the same. This is to catch situations where one local user owns more than one home dir.
+ However, we still can have a situation where two local users cross the ownership of their
+ respective home dirs. Although very atypical, we should be aware of this possible false
+ positive and that it is not possible to be solved with the current OVAL capabilities.
+ -->
+ <!--
+ This create an int variable composed by the count of file_object items.
+ -->
+ <local_variable id="var_file_ownership_home_directories_uids_count" datatype="int" version="1"
+ comment="Count home dirs related to interactive users">
+ <count>
+ <object_component item_field="user_id" object_ref="object_file_ownership_home_directories_dirs"/>
+ </count>
+ </local_variable>
+
+ <!--
+ This create an int variable composed by the count of unique user_ids collected from
+ file_object items.
+ -->
+ <local_variable id="var_file_ownership_home_directories_uids_count_uniq" datatype="int" version="1"
+ comment="Count current owners of relevant home dirs">
+ <count>
+ <unique>
+ <object_component item_field="user_id" object_ref="object_file_ownership_home_directories_dirs"/>
+ </unique>
+ </count>
+ </local_variable>
+
+ <!--
+ #### creation of object ####
+ Turn the OVAL variable representing count of home dirs into OVAL object.
+ This way we can test it further.
+ -->
+ <ind:variable_object id="object_file_ownership_home_directories_uids_count" version="1">
+ <ind:var_ref>var_file_ownership_home_directories_uids_count</ind:var_ref>
+ </ind:variable_object>
+
+ <!--
+ #### creation of state ####
+ this state checks that both counts (unique and non-unique) are the same
+ -->
+ <ind:variable_state id="state_file_ownership_home_directories_uids_count_uniq" version="1">
+ <ind:value datatype="int" operation="equals" var_check="at least one"
+ var_ref="var_file_ownership_home_directories_uids_count_uniq"/>
+ </ind:variable_state>
+
+ <!-- #### creation of test #### -->
+ <ind:variable_test id="test_file_ownership_home_directories_duplicated" check="all"
+ check_existence="any_exist" version="1"
+ comment="It should not exist duplicated owners of home dirs">
+ <ind:object object_ref="object_file_ownership_home_directories_uids_count"/>
+ <ind:state state_ref="state_file_ownership_home_directories_uids_count_uniq"/>
+ </ind:variable_test>
+</def-group>
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/rule.yml b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/rule.yml
index 198a9be..042f484 100644
--- a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/rule.yml
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/rule.yml
@@ -10,6 +10,10 @@ description: |-
the following command:
<pre>$ sudo chown <i>USER</i> /home/<i>USER</i></pre>
+ This rule ensures every home directory related to an interactive user is
+ owned by an interactive user. It also ensures that interactive users are
+ owners of one and only one home directory.
+
rationale: |-
If a local interactive user does not own their home directory, unauthorized
users could access or modify the user's files, and the users may not be able to
@@ -31,3 +35,9 @@ ocil_clause: 'the user ownership is incorrect'
ocil: |-
To verify the home directory ownership, run the following command:
<pre># ls -ld $(awk -F: '($3>=1000)&&($7 !~ /nologin/){print $6}' /etc/passwd)</pre>
+
+warnings:
+ - general: |-
+ Due to OVAL limitation, this rule can report a false negative in a
+ specific situation where two interactive users swap the ownership of
+ their respective home directories.
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/expected_owner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/expected_owner.pass.sh
new file mode 100644
index 0000000..585f759
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/expected_owner.pass.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chown $USER /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dir_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dir_absent.pass.sh
new file mode 100644
index 0000000..7c181af
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dir_absent.pass.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -M $USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_all_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_all_absent.pass.sh
new file mode 100644
index 0000000..af24025
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_all_absent.pass.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -M $USER
+# This make sure home dirs related to test environment users are also removed.
+rm -Rf /home/*
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_one_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_one_absent.pass.sh
new file mode 100644
index 0000000..5bce517
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_one_absent.pass.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -M $USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_with_same_owner.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_with_same_owner.fail.sh
new file mode 100644
index 0000000..e6aef9e
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/home_dirs_with_same_owner.fail.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Define the same owner for two home directories
+chown $USER1 /home/$USER1
+chown $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/interactive_users_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/interactive_users_absent.pass.sh
new file mode 100644
index 0000000..ed34f09
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/interactive_users_absent.pass.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# remove all interactive users (ID >= 1000) from /etc/passwd
+sed -i '/.*:[0-9]\{4,\}:/d' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_system_id.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_system_id.fail.sh
new file mode 100644
index 0000000..011b315
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_system_id.fail.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chown 2 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_unknown_id.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_unknown_id.fail.sh
new file mode 100644
index 0000000..733af78
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/unexpected_owner_unknown_id.fail.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chown 10005 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_crossed_owner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_crossed_owner.pass.sh
new file mode 100644
index 0000000..df5655f
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_crossed_owner.pass.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Define the same owner for two home directories
+chown $USER2 /home/$USER1
+chown $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_swapped_owner.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_swapped_owner.pass.sh
new file mode 100644
index 0000000..e9cfd5b
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_ownership_home_directories/tests/warning_home_dirs_swapped_owner.pass.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+USER1="cac_user1"
+USER2="cac_user2"
+
+useradd -m $USER1
+useradd -m $USER2
+# Swap the ownership of two home directories
+# WARNING: This test scenario will report a false negative, as explained in the
+# warning section of this rule.
+chown $USER2 /home/$USER1
+chown $USER1 /home/$USER2
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/ansible/shared.yml b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/ansible/shared.yml
new file mode 100644
index 0000000..945ed7e
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/ansible/shared.yml
@@ -0,0 +1,31 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+- name: Get all local users from /etc/passwd
+ ansible.builtin.getent:
+ database: passwd
+ split: ':'
+
+- name: Create local_users variable from the getent output
+ ansible.builtin.set_fact:
+ local_users: '{{ ansible_facts.getent_passwd|dict2items }}'
+
+- name: Test for existence home directories to avoid creating them, but only fixing group ownership
+ ansible.builtin.stat:
+ path: '{{ item.value[4] }}'
+ register: path_exists
+ loop: '{{ local_users }}'
+ when:
+ - item.value[2]|int >= {{{ uid_min }}}
+ - item.value[2]|int != 65534
+
+- name: Ensure interactive local users are the group-owners of their respective home directories
+ ansible.builtin.file:
+ path: '{{ item.0.value[4] }}'
+ mode: '0700'
+ loop: '{{ local_users|zip(path_exists.results)|list }}'
+ when:
+ - item.1.stat is defined and item.1.stat.exists
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/bash/shared.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/bash/shared.sh
new file mode 100644
index 0000000..4ebc674
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/bash/shared.sh
@@ -0,0 +1,7 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+
+awk -F':' '{ if ($4 >= {{{ uid_min }}} && $4 != 65534) system("chmod -f 700 "$6) }' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/oval/shared.xml b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/oval/shared.xml
new file mode 100644
index 0000000..0cb261e
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/oval/shared.xml
@@ -0,0 +1,51 @@
+<def-group>
+ <definition class="compliance" id="{{{ rule_id }}}" version="1">
+ {{{ oval_metadata("All Interactive User Home Directories Must Have mode 0750 Or Less Permissive") }}}
+ <criteria>
+ <criterion test_ref="test_file_permissions_home_directories"
+ comment="All interactive user's Home Directories must have proper permissions"/>
+ </criteria>
+ </definition>
+
+ <!-- For detailed comments about logic used in this OVAL, check the
+ "file_ownership_home_directories" rule. -->
+ <unix:password_object id="object_file_permissions_home_directories_objects" version="1">
+ <unix:username datatype="string" operation="not equal">nobody</unix:username>
+ <filter action="include">state_file_permissions_home_directories_interactive_uids</filter>
+ </unix:password_object>
+
+ <unix:password_state id="state_file_permissions_home_directories_interactive_uids" version="1">
+ <unix:user_id datatype="int" operation="greater than or equal">{{{ uid_min }}}</unix:user_id>
+ </unix:password_state>
+
+ <!-- #### prepare for test_file_permissions_home_directories #### -->
+ <local_variable id="var_file_permissions_home_directories_dirs" datatype="string" version="1"
+ comment="Variable including all home dirs from interactive users">
+ <object_component item_field="home_dir" object_ref="object_file_permissions_home_directories_objects"/>
+ </local_variable>
+
+ <!-- #### creation of object #### -->
+ <unix:file_object id="object_file_permissions_home_directories_dirs" version="1">
+ <unix:path var_ref="var_file_permissions_home_directories_dirs" var_check="at least one"/>
+ <unix:filename xsi:nil="true"/>
+ </unix:file_object>
+
+ <!-- #### creation of state #### -->
+ <unix:file_state id="state_file_permissions_home_directories_dirs" version="1" operator='AND'>
+ <unix:type operation="equals">directory</unix:type>
+ <unix:suid datatype="boolean">false</unix:suid>
+ <unix:sgid datatype="boolean">false</unix:sgid>
+ <unix:sticky datatype="boolean">false</unix:sticky>
+ <unix:gwrite datatype="boolean">false</unix:gwrite>
+ <unix:oread datatype="boolean">false</unix:oread>
+ <unix:owrite datatype="boolean">false</unix:owrite>
+ <unix:oexec datatype="boolean">false</unix:oexec>
+ </unix:file_state>
+
+ <!-- #### creation of test #### -->
+ <unix:file_test id="test_file_permissions_home_directories" check="all" check_existence="any_exist"
+ version="1" comment="All home directories have proper permissions">
+ <unix:object object_ref="object_file_permissions_home_directories_dirs"/>
+ <unix:state state_ref="state_file_permissions_home_directories_dirs"/>
+ </unix:file_test>
+</def-group>
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/acceptable_permission.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/acceptable_permission.pass.sh
new file mode 100644
index 0000000..aaf939e
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/acceptable_permission.pass.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chmod 750 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/expected_permissions.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/expected_permissions.pass.sh
new file mode 100644
index 0000000..5dfd426
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/expected_permissions.pass.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chmod 700 /home/$USER
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/home_dirs_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/home_dirs_absent.pass.sh
new file mode 100644
index 0000000..af24025
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/home_dirs_absent.pass.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -M $USER
+# This make sure home dirs related to test environment users are also removed.
+rm -Rf /home/*
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/interactive_users_absent.pass.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/interactive_users_absent.pass.sh
new file mode 100644
index 0000000..ed34f09
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/interactive_users_absent.pass.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# remove all interactive users (ID >= 1000) from /etc/passwd
+sed -i '/.*:[0-9]\{4,\}:/d' /etc/passwd
diff --git a/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/lenient_permission.fail.sh b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/lenient_permission.fail.sh
new file mode 100644
index 0000000..2f337d2
--- /dev/null
+++ b/linux_os/guide/system/accounts/accounts-session/file_permissions_home_directories/tests/lenient_permission.fail.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+
+USER="cac_user"
+useradd -m $USER
+chmod 755 /home/$USER
diff --git a/ssg/constants.py b/ssg/constants.py
index e2d3077..64e2712 100644
--- a/ssg/constants.py
+++ b/ssg/constants.py
@@ -380,6 +380,7 @@ MAKEFILE_ID_TO_PRODUCT_MAP = {
# Application constants
+DEFAULT_GID_MIN = 1000
DEFAULT_UID_MIN = 1000
DEFAULT_GRUB2_BOOT_PATH = '/boot/grub2'
DEFAULT_DCONF_GDM_DIR = 'gdm.d'
diff --git a/ssg/products.py b/ssg/products.py
index 25178b7..e410e06 100644
--- a/ssg/products.py
+++ b/ssg/products.py
@@ -7,6 +7,7 @@ from glob import glob
from .build_cpe import ProductCPEs
from .constants import (product_directories,
+ DEFAULT_GID_MIN,
DEFAULT_UID_MIN,
DEFAULT_GRUB2_BOOT_PATH,
DEFAULT_DCONF_GDM_DIR,
@@ -39,6 +40,9 @@ def _get_implied_properties(existing_properties):
if pkg_manager in PKG_MANAGER_TO_CONFIG_FILE:
result["pkg_manager_config_file"] = PKG_MANAGER_TO_CONFIG_FILE[pkg_manager]
+ if "gid_min" not in existing_properties:
+ result["gid_min"] = DEFAULT_GID_MIN
+
if "uid_min" not in existing_properties:
result["uid_min"] = DEFAULT_UID_MIN