From 3030c793311ab8a8bcb47c04bf73d3e754aa8de1 Mon Sep 17 00:00:00 2001 From: Lee Garrett Date: Tue, 14 Jan 2025 20:44:16 +0100 Subject: [PATCH] user: Fix homedir permissions when UMASK is unset in /etc/login.defs When a user doesn't exist and user module is used to create the user and the homedir, adduser is called which parses HOME_MODE from /etc/login.defs, and when not set calculates the mode from UMASK from the same file. When a user already exists without homedir, and the user module is used to add a home dir, it incorrectly ignores HOME_MODE, resulting in a world-readable home dir when UMASK is not set. This is for example the case in Debian trixie and later, and likely Ubuntu 25.04 and later. Signed-off-by: Lee Garrett Co-authored-by: Brian Coca Co-authored-by: Abhijeet Kasurde --- changelogs/fragments/user_module.yml | 3 +++ lib/ansible/modules/user.py | 26 ++++++++++++------- .../user/tasks/test_no_home_fallback.yml | 7 ++++- 3 files changed, 26 insertions(+), 10 deletions(-) create mode 100644 changelogs/fragments/user_module.yml diff --git a/changelogs/fragments/user_module.yml b/changelogs/fragments/user_module.yml new file mode 100644 index 00000000000..e192234f5f9 --- /dev/null +++ b/changelogs/fragments/user_module.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - user - Use higher precedence HOME_MODE as UMASK for path provided (https://github.com/ansible/ansible/pull/84482). diff --git a/lib/ansible/modules/user.py b/lib/ansible/modules/user.py index 376d15f7722..90ecd04b8d9 100644 --- a/lib/ansible/modules/user.py +++ b/lib/ansible/modules/user.py @@ -1376,16 +1376,24 @@ class User(object): self.module.exit_json(failed=True, msg="%s" % to_native(e)) # get umask from /etc/login.defs and set correct home mode if os.path.exists(self.LOGIN_DEFS): - with open(self.LOGIN_DEFS, 'r') as f: - for line in f: - m = re.match(r'^UMASK\s+(\d+)$', line) - if m: - umask = int(m.group(1), 8) + # fallback if neither HOME_MODE nor UMASK are set; + # follow behaviour of useradd initializing UMASK = 022 + mode = 0o755 + with open(self.LOGIN_DEFS, 'r') as fh: + for line in fh: + # HOME_MODE has higher precedence as UMASK + match = re.match(r'^HOME_MODE\s+(\d+)$', line) + if match: + mode = int(match.group(1), 8) + break # higher precedence + match = re.match(r'^UMASK\s+(\d+)$', line) + if match: + umask = int(match.group(1), 8) mode = 0o777 & ~umask - try: - os.chmod(path, mode) - except OSError as e: - self.module.exit_json(failed=True, msg="%s" % to_native(e)) + try: + os.chmod(path, mode) + except OSError as e: + self.module.exit_json(failed=True, msg=to_native(e)) def chown_homedir(self, uid, gid, path): try: diff --git a/test/integration/targets/user/tasks/test_no_home_fallback.yml b/test/integration/targets/user/tasks/test_no_home_fallback.yml index f7627fae1e3..0783ec1b6c8 100644 --- a/test/integration/targets/user/tasks/test_no_home_fallback.yml +++ b/test/integration/targets/user/tasks/test_no_home_fallback.yml @@ -35,12 +35,17 @@ import os try: for line in open('/etc/login.defs').readlines(): + m = re.match(r'^HOME_MODE\s+(\d+)$', line) + if m: + mode = oct(int(m.group(1), 8)) + break m = re.match(r'^UMASK\s+(\d+)$', line) if m: umask = int(m.group(1), 8) + mode = oct(0o777 & ~umask) except: umask = os.umask(0) - mode = oct(0o777 & ~umask) + mode = oct(0o777 & ~umask) print(str(mode).replace('o', '')) args: executable: "{{ ansible_python_interpreter }}"