Skip to content

Commit 90ad4db

Browse files
robertbindarvuvova
authored andcommitted
MDEV-7597 Expiration of user passwords
This patch adds support for expiring user passwords. The following statements are extended: CREATE USER user@localhost PASSWORD EXPIRE [option] ALTER USER user@localhost PASSWORD EXPIRE [option] If no option is specified, the password is expired with immediate effect. If option is DEFAULT, global policy applies according to the default_password_lifetime system var (if 0, password never expires, if N, password expires every N days). If option is NEVER, the password never expires and if option is INTERVAL N DAY, the password expires every N days. The feature also supports the disconnect_on_expired_password system var and the --connect-expired-password client option. Closes #1166
1 parent 83de75d commit 90ad4db

34 files changed

+1259
-99
lines changed

client/mysql.cc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ static uint my_end_arg;
160160
static char * opt_mysql_unix_port=0;
161161
static int connect_flag=CLIENT_INTERACTIVE;
162162
static my_bool opt_binary_mode= FALSE;
163+
static my_bool opt_connect_expired_password= FALSE;
163164
static int interrupted_query= 0;
164165
static char *current_host,*current_db,*current_user=0,*opt_password=0,
165166
*current_prompt=0, *delimiter_str= 0,
@@ -1686,6 +1687,11 @@ static struct my_option my_long_options[] =
16861687
"piped to mysql or loaded using the 'source' command). This is necessary "
16871688
"when processing output from mysqlbinlog that may contain blobs.",
16881689
&opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
1690+
{"connect-expired-password", 0,
1691+
"Notify the server that this client is prepared to handle expired "
1692+
"password sandbox mode even if --batch was specified.",
1693+
&opt_connect_expired_password, &opt_connect_expired_password, 0, GET_BOOL,
1694+
NO_ARG, 0, 0, 0, 0, 0, 0},
16891695
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
16901696
};
16911697

@@ -4684,6 +4690,9 @@ sql_real_connect(char *host,char *database,char *user,char *password,
46844690

46854691
mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);
46864692

4693+
my_bool can_handle_expired= opt_connect_expired_password || !status.batch;
4694+
mysql_options(&mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &can_handle_expired);
4695+
46874696
if (!do_connect(&mysql, host, user, password, database,
46884697
connect_flag | CLIENT_MULTI_STATEMENTS))
46894698
{

include/mysql_com.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -333,12 +333,8 @@ enum enum_indicator_type
333333
CLIENT_DEPRECATE_EOF |\
334334
CLIENT_CONNECT_ATTRS |\
335335
MARIADB_CLIENT_COM_MULTI |\
336-
MARIADB_CLIENT_STMT_BULK_OPERATIONS)
337-
338-
/*
339-
To be added later:
340-
CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS
341-
*/
336+
MARIADB_CLIENT_STMT_BULK_OPERATIONS |\
337+
CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)
342338

343339
/*
344340
Switch off the flags that are optional and depending on build flags

mysql-test/main/mysql_upgrade.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,15 +596,15 @@ drop view mysql.user_bak;
596596
create user 'user3'@'localhost' identified with mysql_native_password as password('a_password');
597597
show create user user3@localhost;
598598
CREATE USER for user3@localhost
599-
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA'
599+
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA' PASSWORD EXPIRE NEVER
600600
update mysql.user set password=authentication_string, authentication_string='' where user='user3';
601601
select password,plugin,authentication_string from mysql.user where user='user3';
602602
password plugin authentication_string
603603
*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA mysql_native_password
604604
flush privileges;
605605
show create user user3@localhost;
606606
CREATE USER for user3@localhost
607-
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA'
607+
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA' PASSWORD EXPIRE NEVER
608608
connect con1,localhost,user3,a_password;
609609
select current_user();
610610
current_user()

mysql-test/main/mysqld--help.result

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ The following specify which files/extra groups are read (specified before remain
188188
--deadlock-timeout-short=#
189189
Short timeout for the two-step deadlock detection (in
190190
microseconds)
191+
--default-password-lifetime=#
192+
This defines the global password expiration policy. 0
193+
means automatic password expiration is disabled. If the
194+
value is a positive integer N, the passwords must be
195+
changed every N days. This behavior can be overriden
196+
using the password expiration options in ALTER USER.
191197
--default-regex-flags=name
192198
Default flags for the regex library. Any combination of:
193199
DOTALL, DUPNAMES, EXTENDED, EXTRA, MULTILINE, UNGREEDY
@@ -224,6 +230,11 @@ The following specify which files/extra groups are read (specified before remain
224230
handling INSERT DELAYED. If the queue becomes full, any
225231
client that does INSERT DELAYED will wait until there is
226232
room in the queue again
233+
--disconnect-on-expired-password
234+
This variable controls how the server handles clients
235+
that are not aware of the sandbox mode. If enabled, the
236+
server disconnects the client, otherwise the server puts
237+
the client in a sandbox mode.
227238
--div-precision-increment=#
228239
Precision of the result of '/' operator will be increased
229240
on that value
@@ -1428,6 +1439,7 @@ deadlock-search-depth-long 15
14281439
deadlock-search-depth-short 4
14291440
deadlock-timeout-long 50000000
14301441
deadlock-timeout-short 10000
1442+
default-password-lifetime 0
14311443
default-regex-flags
14321444
default-storage-engine myisam
14331445
default-time-zone (No default value)
@@ -1437,6 +1449,7 @@ delay-key-write ON
14371449
delayed-insert-limit 100
14381450
delayed-insert-timeout 300
14391451
delayed-queue-size 1000
1452+
disconnect-on-expired-password FALSE
14401453
div-precision-increment 4
14411454
encrypt-binlog FALSE
14421455
encrypt-tmp-disk-tables FALSE
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#
2+
# Only privileged users should be able to expire passwords
3+
#
4+
create user user1@localhost;
5+
alter user user1@localhost password expire;
6+
create user user2@localhost;
7+
connect con2,localhost,user2;
8+
connection con2;
9+
alter user user1@localhost password expire;
10+
ERROR 42000: Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
11+
disconnect con2;
12+
connection default;
13+
drop user user1@localhost;
14+
drop user user2@localhost;
15+
#
16+
# disconnect_on_expired_password=ON should deny a clients's connection
17+
# when the password is expired or put the client in sandbox mode if OFF
18+
#
19+
create user user1@localhost password expire;
20+
set global disconnect_on_expired_password=ON;
21+
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
22+
connect con1,localhost,user1;
23+
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
24+
set global disconnect_on_expired_password=OFF;
25+
connect con1,localhost,user1;
26+
connection con1;
27+
select 1;
28+
ERROR HY000: You must SET PASSWORD before executing this statement
29+
disconnect con1;
30+
connection default;
31+
drop user user1@localhost;
32+
#
33+
# connect-expired-password option passed to client should override
34+
# the behavior of disconnect_on_expired_password server system var.
35+
#
36+
create user user1@localhost password expire;
37+
set global disconnect_on_expired_password=ON;
38+
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
39+
connect con1,localhost,user1;
40+
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
41+
drop user user1@localhost;
42+
#
43+
# Manually expiring a password should have immediate effect
44+
#
45+
create user user1@localhost;
46+
alter user user1@localhost password expire;
47+
set global disconnect_on_expired_password=ON;
48+
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
49+
connect con1,localhost,user1;
50+
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
51+
drop user user1@localhost;
52+
#
53+
# Sandbox mode should only allow change password statements
54+
#
55+
create user user1@localhost password expire;
56+
grant create user on *.* to user1@localhost;
57+
set global disconnect_on_expired_password=OFF;
58+
connect con1,localhost,user1;
59+
connection con1;
60+
select 1;
61+
ERROR HY000: You must SET PASSWORD before executing this statement
62+
set password=password('');
63+
select 1;
64+
1
65+
1
66+
disconnect con1;
67+
connection default;
68+
drop user user1@localhost;
69+
#
70+
# Passwords are still expired after acl reload
71+
#
72+
set global disconnect_on_expired_password=ON;
73+
create user user1@localhost password expire;
74+
flush privileges;
75+
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
76+
connect con1,localhost,user1;
77+
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
78+
drop user user1@localhost;
79+
#
80+
# JSON functions on global_priv reflect the correct state
81+
# of the password expiration columns
82+
#
83+
create user user1@localhost password expire;
84+
select host, user, JSON_VALUE(Priv, '$.password_last_changed') from mysql.global_priv where user='user1';
85+
host user JSON_VALUE(Priv, '$.password_last_changed')
86+
localhost user1 0
87+
alter user user1@localhost password expire never;
88+
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
89+
host user JSON_VALUE(Priv, '$.password_lifetime')
90+
localhost user1 0
91+
alter user user1@localhost password expire default;
92+
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
93+
host user JSON_VALUE(Priv, '$.password_lifetime')
94+
localhost user1 -1
95+
alter user user1@localhost password expire interval 123 day;
96+
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
97+
host user JSON_VALUE(Priv, '$.password_lifetime')
98+
localhost user1 123
99+
drop user user1@localhost;
100+
#
101+
# SHOW CREATE USER correctly displays the locking state of an user
102+
#
103+
create user user1@localhost;
104+
show create user user1@localhost;
105+
CREATE USER for user1@localhost
106+
CREATE USER 'user1'@'localhost'
107+
alter user user1@localhost password expire;
108+
show create user user1@localhost;
109+
CREATE USER for user1@localhost
110+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
111+
set password for user1@localhost= password('');
112+
alter user user1@localhost password expire default;
113+
show create user user1@localhost;
114+
CREATE USER for user1@localhost
115+
CREATE USER 'user1'@'localhost'
116+
alter user user1@localhost password expire never;
117+
show create user user1@localhost;
118+
CREATE USER for user1@localhost
119+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
120+
alter user user1@localhost password expire interval 123 day;
121+
show create user user1@localhost;
122+
CREATE USER for user1@localhost
123+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE INTERVAL 123 DAY
124+
alter user user1@localhost password expire;
125+
show create user user1@localhost;
126+
CREATE USER for user1@localhost
127+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
128+
set password for user1@localhost= password('');
129+
show create user user1@localhost;
130+
CREATE USER for user1@localhost
131+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE INTERVAL 123 DAY
132+
drop user user1@localhost;
133+
#
134+
# Incorrect INTERVAL values should be rejected
135+
#
136+
create user user1@localhost password expire interval 0 day;
137+
ERROR HY000: Incorrect DAY value: '0'
138+
#
139+
# Password expiration fields are loaded properly on 10.3 tables
140+
#
141+
create user user1@localhost;
142+
show create user user1@localhost;
143+
CREATE USER for user1@localhost
144+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
145+
flush privileges;
146+
show create user user1@localhost;
147+
CREATE USER for user1@localhost
148+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
149+
alter user user1@localhost password expire;
150+
show create user user1@localhost;
151+
CREATE USER for user1@localhost
152+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
153+
flush privileges;
154+
show create user user1@localhost;
155+
CREATE USER for user1@localhost
156+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
157+
set password for user1@localhost= password('');
158+
alter user user1@localhost password expire default;
159+
show create user user1@localhost;
160+
CREATE USER for user1@localhost
161+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
162+
flush privileges;
163+
show create user user1@localhost;
164+
CREATE USER for user1@localhost
165+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
166+
alter user user1@localhost password expire never;
167+
show create user user1@localhost;
168+
CREATE USER for user1@localhost
169+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
170+
flush privileges;
171+
show create user user1@localhost;
172+
CREATE USER for user1@localhost
173+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
174+
alter user user1@localhost password expire interval 123 day;
175+
show create user user1@localhost;
176+
CREATE USER for user1@localhost
177+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
178+
flush privileges;
179+
show create user user1@localhost;
180+
CREATE USER for user1@localhost
181+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
182+
alter user user1@localhost password expire;
183+
show create user user1@localhost;
184+
CREATE USER for user1@localhost
185+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
186+
flush privileges;
187+
show create user user1@localhost;
188+
CREATE USER for user1@localhost
189+
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
190+
set global disconnect_on_expired_password=ON;
191+
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
192+
connect con1,localhost,user1;
193+
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
194+
set global disconnect_on_expired_password=OFF;
195+
connect con1,localhost,user1;
196+
connection con1;
197+
select 1;
198+
ERROR HY000: You must SET PASSWORD before executing this statement
199+
set password=password('');
200+
select 1;
201+
1
202+
1
203+
disconnect con1;
204+
connection default;
205+
drop user user1@localhost;
206+
set global disconnect_on_expired_password=default;
207+
set global default_password_lifetime=default;

0 commit comments

Comments
 (0)