Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,32 @@
# complexity = low
# disruption = low

- name: Get all local users from /etc/passwd
- name: Get all users from getent to support remote users (LDAP, SSSD, etc.)
ansible.builtin.getent:
database: passwd
split: ':'

- name: Create local_users variable from the getent output
- name: Create interactive_users variable filtering out non-interactive shells
ansible.builtin.set_fact:
local_users: '{{ ansible_facts.getent_passwd|dict2items }}'
# Creates a dictionary where the key is the first field of the /etc/passwd file, the username.
# The list of values are the next 6 fields from /etc/passwd. Example for the root entry:
# The "root" key would have these values: ["x", "0", "0", "root", "/root", "/bin/bash"]
interactive_users: '{{ ansible_facts.getent_passwd|dict2items|rejectattr("value.5", "match", ".*/sbin/nologin$")|rejectattr("value.5", "match", ".*/usr/sbin/nologin$")|rejectattr("value.5", "match", ".*/bin/false$")|rejectattr("value.5", "match", ".*/usr/bin/false$")|list }}'
# Creates a dictionary where the key is the username and values are fields from getent passwd.
# Filters out users with non-interactive shells: nologin and false.
# Example for the admin entry: ["x", "1000", "1000", "", "/home/admin", "/bin/bash"]

- 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 }}'
loop: '{{ interactive_users }}'
when:
- item.value[1]|int >= {{{ uid_min }}}
- item.value[1]|int != {{{ nobody_uid }}}
- item.value[4] != "/"

- name: Ensure interactive local users are the group-owners of their respective home directories
- name: Ensure interactive 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 }}'
loop: '{{ interactive_users|zip(path_exists.results)|list }}'
when:
- item.1.stat is defined and item.1.stat.exists
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,14 @@
# complexity = low
# disruption = low

awk -F':' '{ if ($3 >= {{{ uid_min }}} && $3 != {{{ nobody_uid }}} && $6 != "/") system("chgrp -f " $4" "$6) }' /etc/passwd
# Get interactive users using getent to support remote users (LDAP, SSSD, etc.)
while IFS=: read -r _ _ uid gid _ home shell; do
# Filter interactive users: UID >= {{{ uid_min }}}, not nobody, has valid home dir, not nologin/false shell
if [ "$uid" -ge {{{ uid_min }}} ] && [ "$uid" -ne {{{ nobody_uid }}} ] && \
[ "$home" != "/" ] && \
[ "$shell" != "/sbin/nologin" ] && [ "$shell" != "/usr/sbin/nologin" ] && \
[ "$shell" != "/bin/false" ] && [ "$shell" != "/usr/bin/false" ]; then
# Arguments are properly quoted to prevent command injection
chgrp -f -- "$gid" "$home"
fi
done < <(getent passwd)
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,20 @@
</criteria>
</definition>

{{%- set interactive_users_home_dirs_object = "object_" ~ rule_id ~ "_home_dirs" -%}}
{{{ create_local_interactive_users_home_dirs_list_object(interactive_users_home_dirs_object) }}}
{{%- set interactive_users_gids_object = "object_" ~ rule_id ~ "_gids" -%}}
{{{ create_local_interactive_users_gids_list_object(interactive_users_gids_object) }}}
{{%- set interactive_users_object = "object_" ~ rule_id ~ "_interactive_users" -%}}
{{{ create_interactive_users_list_object(interactive_users_object, rule_id=rule_id) }}}

<!-- #### prepare for test_file_groupownership_home_directories #### -->
<!-- Extract home directories from password_object (supports remote users via getpwent) -->
<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="subexpression" object_ref="{{{ interactive_users_home_dirs_object }}}"/>
comment="Home directories of interactive users (local and remote)">
<object_component item_field="home_dir" object_ref="{{{ interactive_users_object }}}"/>
</local_variable>

<!-- Extract primary group IDs from password_object (supports remote users via getpwent) -->
<local_variable id="var_file_groupownership_home_directories_gids" datatype="int" version="1"
comment="Variable including all gids from primary interactive group">
comment="Primary group IDs of interactive users (local and remote)">
<unique>
<object_component item_field="subexpression" object_ref="{{{ interactive_users_gids_object }}}"/>
<object_component item_field="group_id" object_ref="{{{ interactive_users_object }}}"/>
</unique>
</local_variable>

Expand All @@ -37,9 +36,8 @@
</unix:file_state>

<!-- #### creation of test #### -->
<!-- #### creation 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">
version="1" comment="All home directories are group-owned by an interactive user's primary group">
<unix:object object_ref="object_file_groupownership_home_directories_dirs"/>
<unix:state state_ref="state_file_groupownership_home_directories_gids"/>
</unix:file_test>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ documentation_complete: true
title: 'All Interactive User Home Directories Must Be Group-Owned By The Primary Group'

description: |-
Change the group owner of interactive users home directory to the
group found in <tt>/etc/passwd</tt>. To change the group owner of
interactive users home directory, use the following command:
Change the group owner of interactive user home directories to the
group found in <tt>getent passwd</tt>. To change the group owner of
an interactive user's 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
group-owned by that user's primary group. It also ensures that interactive users
are group-owners of one and only one home directory.

Interactive users are those with a UID greater than or equal to {{{ uid_min }}}
and a valid interactive shell (not <tt>/sbin/nologin</tt>, <tt>/usr/sbin/nologin</tt>,
<tt>/bin/false</tt>, or <tt>/usr/bin/false</tt>).

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
Expand Down Expand Up @@ -43,15 +47,27 @@ ocil_clause: 'the group ownership is incorrect'

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&gt;={{{ uid_min }}})&amp;&amp;($7 !~ /nologin/){print $6}' /etc/passwd)</pre>
owned by that user's primary GID, run the following command:
<pre># ls -ld $(getent passwd | awk -F: '($3&gt;={{{ uid_min }}})&amp;&amp;($7 !~ /nologin/)&amp;&amp;($7 !~ /\/false$/){print $6}')</pre>

This should return a list of home directories with group ownership matching each user's primary group.
Using <tt>getent passwd</tt> instead of reading <tt>/etc/passwd</tt> directly ensures that remote users
(LDAP, SSSD, NIS, etc.) are also checked.

fixtext: |-
Change the group owner of a local interactive user’s home directory to the group found in "/etc/passwd". To change the group owner of a local interactive user’s home directory, use the following command:
Change the group owner of an interactive user’s home directory to the group found in the system user database.

Identify the user’s primary group:
<pre>$ getent passwd smithj
smithj:x:1000:1000:John Smith:/home/smithj:/bin/bash</pre>

The fourth field (1000) is the primary group ID (GID). Change the home directory group ownership:
<pre>$ sudo chgrp 1000 /home/smithj</pre>

Note: The example will be for the user "smithj", who has a home directory of "/home/smithj", and has a primary group of users.
Or use the group name:
<pre>$ sudo chgrp users /home/smithj</pre>

$ sudo chgrp users /home/smithj
Note: This applies to all interactive users, including those in remote directories (LDAP, SSSD, NIS, etc.).

srg_requirement: 'All {{{ full_name }}} local interactive user home directories must be group-owned by the home directory owner’s primary group.'

Expand Down
2 changes: 1 addition & 1 deletion shared/macros/10-oval.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -1276,7 +1276,7 @@ Generates the :code:`<affected>` tag for OVAL check using correct product platfo
</unix:password_state>

<unix:password_state id="state_{{{ rule_id }}}_users_nologin_shell" version="1">
<unix:login_shell datatype="string" operation="pattern match">^(?:/usr)?/sbin/nologin$</unix:login_shell>
<unix:login_shell datatype="string" operation="pattern match">^(?:/usr)?(?:/sbin/nologin|/bin/false)$</unix:login_shell>
</unix:password_state>
{{%- endmacro %}}

Expand Down
Loading