blob: c554246c2721c464305b1c6a218255a77bba7ee1 [file] [log] [blame]
Dmitry Teselkinf8f2a592019-07-31 16:22:01 +03001#!/usr/bin/python
2
3from collections import namedtuple
4import datetime
5
6
7PasswdEntry = namedtuple('PasswdEntry', [
8 'pw_name', # username
9 'pw_passwd', # user password
10 'pw_uid', # user ID
11 'pw_gid', # group ID
12 'pw_gecos', # user information
13 'pw_dir', # home directory
14 'pw_shell', # shell program
15])
16
17ShadowEntry = namedtuple('ShadowEntry', [
18 'sp_namp', # user login name
19 'sp_pwdp', # encrypted password
20 'sp_lstchg', # last password change
21 'sp_min', # days until change allowed
22 'sp_max', # days before change required
23 'sp_warn', # days warning for expiration
24 'sp_inact', # days before account inactive
25 'sp_expire', # date when account expires
26 'sp_flag', # reserved for future use
27])
28
29
30def fix_last_password_change(*args, **kwargs):
31 """
32 This helper function updates last password change timestamp of a user(s).
33 It doesn't update password itself.
34
35 In case username (a list of usernames) passed as input to this function,
36 only these users might be affected.
37
38 If there is no input, then all the users in the system will be tried.
39
40 Regardless of how username(s) was/were get, all the following rules SHOULD
41 match in order to succeed:
42 * user ID MUST be >= 1000
43 * user's password from /etc/shadow MUST be '*'. This is an indicator that
44 password is disabled for this user, and last password change timestamp
45 can safely be updated.
46
47 If an account ID is < 1000 (system account) or a user have locked
48 password (that starts with exclamation mark) or have a valid password
49 string (even if it's an empty string) then last password change timestamp
50 won't be updated.
51
52 Returns dictionary where key is account username and value is status of
53 update operation.
54
55 Possible status values are:
56 * None - user doesn't exist on the system or an error occured
57 * True - updated successfully
58 * False - not updated (UID < 1000 or password is locked / set)
59
60 Usage:
61
62 * to fix last password change timestamp for all accounts
63
64 .. code-block:: shell
65
66 # salt '*' sharedlib.call cis.fix_last_password_change
67
68 * to fix last password change timestamp for specified account(s)
69
70 .. code-block:: shell
71
72 # salt '*' sharedlib.call cis.fix_last_password_change <account1> [<account2>]
73
74
75 """
76 def _update_last_password_change(username):
77 if username not in passwd:
78 return None
79
80 if int(passwd[username].pw_uid) < 1000:
81 # Ignore 'system' accounts
82 return False
83
84 if shadow[username].sp_pwdp != '*':
85 # Do not touch password for accounts that are locked
86 # or use valid password
87 return False
88
89 try:
90 __salt__['cmd.run']('chage -d {} {}'.format(today, username))
91 return True
92 except:
93 return None
94
95
96 today = datetime.date.today().strftime('%Y-%m-%d')
97
98 passwd = {}
99 with open('/etc/passwd') as f:
100 for line in f:
101 entry = PasswdEntry(*line.strip().split(':'))
102 passwd[entry.pw_name] = entry
103
104 shadow = {}
105 with open('/etc/shadow') as f:
106 for line in f:
107 entry = ShadowEntry(*line.strip().split(':'))
108 shadow[entry.sp_namp] = entry
109
110 result = {}
111 if args:
112 for username in args:
113 result[username] = _update_last_password_change(username)
114 else:
115 for username in passwd:
116 result[username] = _update_last_password_change(username)
117
118 return result