From f27eccabbd00f628f6e4195be7e1e4e5c463cba2 Mon Sep 17 00:00:00 2001 From: Sam Doran Date: Mon, 22 Apr 2019 15:24:10 -0400 Subject: [PATCH] User - Fix shadow file parsing on AIX (#55230) Implement a new method for shadow file parsing so it can be subclassed. --- .../user-aix-shadow-file-parsing.yaml | 2 + lib/ansible/modules/system/user.py | 65 +++++++++++++++++-- 2 files changed, 60 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/user-aix-shadow-file-parsing.yaml diff --git a/changelogs/fragments/user-aix-shadow-file-parsing.yaml b/changelogs/fragments/user-aix-shadow-file-parsing.yaml new file mode 100644 index 00000000000..432abc63fcf --- /dev/null +++ b/changelogs/fragments/user-aix-shadow-file-parsing.yaml @@ -0,0 +1,2 @@ +bugfixes: + - user - properly parse the shadow file on AIX (https://github.com/ansible/ansible/issues/54461) diff --git a/lib/ansible/modules/system/user.py b/lib/ansible/modules/system/user.py index 31757389a08..cc1ab81190c 100644 --- a/lib/ansible/modules/system/user.py +++ b/lib/ansible/modules/system/user.py @@ -907,13 +907,19 @@ class User(object): if not self.user_exists(): return passwd, expires elif self.SHADOWFILE: - # Read shadow file for user's encrypted password string - if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK): - with open(self.SHADOWFILE, 'r') as f: - for line in f: - if line.startswith('%s:' % self.name): - passwd = line.split(':')[1] - expires = line.split(':')[self.SHADOWFILE_EXPIRE_INDEX] or -1 + passwd, expires = self.parse_shadow_file() + + return passwd, expires + + def parse_shadow_file(self): + passwd = '' + expires = '' + if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK): + with open(self.SHADOWFILE, 'r') as f: + for line in f: + if line.startswith('%s:' % self.name): + passwd = line.split(':')[1] + expires = line.split(':')[self.SHADOWFILE_EXPIRE_INDEX] or -1 return passwd, expires def get_ssh_key_path(self): @@ -2326,6 +2332,7 @@ class AIX(User): - create_user() - remove_user() - modify_user() + - parse_shadow_file() """ platform = 'AIX' @@ -2467,6 +2474,50 @@ class AIX(User): else: return (rc2, out + out2, err + err2) + def parse_shadow_file(self): + """Example AIX shadowfile data: + nobody: + password = * + + operator1: + password = {ssha512}06$xxxxxxxxxxxx.... + lastupdate = 1549558094 + + test1: + password = * + lastupdate = 1553695126 + + """ + + b_name = to_bytes(self.name) + if os.path.exists(self.SHADOWFILE) and os.access(self.SHADOWFILE, os.R_OK): + with open(self.SHADOWFILE, 'rb') as bf: + b_lines = bf.readlines() + + b_passwd_line = b'' + b_expires_line = b'' + for index, b_line in enumerate(b_lines): + # Get password and lastupdate lines which come after the username + if b_line.startswith(b'%s:' % b_name): + b_passwd_line = b_lines[index + 1] + b_expires_line = b_lines[index + 2] + break + + # Sanity check the lines because sometimes both are not present + if b' = ' in b_passwd_line: + b_passwd = b_passwd_line.split(b' = ', 1)[-1].strip() + else: + b_passwd = b'' + + if b' = ' in b_expires_line: + b_expires = b_expires_line.split(b' = ', 1)[-1].strip() + else: + b_expires = b'' + + passwd = to_native(b_passwd) + expires = to_native(b_expires) or -1 + return passwd, expires + class HPUX(User): """