Skip to content

Commit 48f3e66

Browse files
author
Atanu Ghosh
committed
WL#7131:Add timestamp in mysql.user on the last time the
password was changed and implement password rotation We need to track when the password was last changed and implement password rotation. Put a TIMESTAMP column inside mysql.user table and update it when the password is updated. Put another column in mysql.user, holding the number of days after which the password must expire. Introduce extension of query ALTER USER as: ALTER USER foo PASSWORD EXPIRE EVERY <day> DAY; ALTER USER foo PASSWORD EXPIRE NEVER; ALTER USER foo PASSWORD EXPIRE DEFAULT;
1 parent 5c40177 commit 48f3e66

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1250
-104
lines changed

libmysqld/lib_sql.cc

+3
Original file line numberDiff line numberDiff line change
@@ -1304,6 +1304,9 @@ void Protocol_text::prepare_for_resend()
13041304

13051305
bool Protocol_text::store_null()
13061306
{
1307+
if (!thd->mysql) // bootstrap file handling
1308+
return false;
1309+
13071310
*(next_field++)= NULL;
13081311
++next_mysql_field;
13091312
return false;

mysql-test/extra/rpl_tests/rpl_sp.test

+3
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ call foo2();
7272
# check that this is allowed (it's not for functions):
7373
alter procedure foo2 contains sql;
7474

75+
# Change the timestamp back to current
76+
set timestamp = DEFAULT;
77+
7578
# SP with definer's right
7679

7780
drop table t1;

mysql-test/include/mtr_system_tables_data.sql

+4-4
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ DROP TABLE tmp_db;
3535
-- Fill "user" table with default users allowing root access
3636
-- from local machine if "user" table didn't exist before
3737
CREATE TEMPORARY TABLE tmp_user LIKE user;
38-
INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N');
39-
REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N' FROM dual WHERE @current_hostname != 'localhost';
40-
REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N');
41-
REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N');
38+
INSERT INTO tmp_user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N',CURRENT_TIMESTAMP,NULL);
39+
REPLACE INTO tmp_user SELECT @current_hostname,'root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N',CURRENT_TIMESTAMP, NULL FROM dual WHERE @current_hostname != 'localhost';
40+
REPLACE INTO tmp_user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N',CURRENT_TIMESTAMP,NULL);
41+
REPLACE INTO tmp_user VALUES ('::1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0,0,@@default_authentication_plugin,'','N',CURRENT_TIMESTAMP,NULL);
4242
INSERT INTO tmp_user (host,user) SELECT @current_hostname,'' FROM dual WHERE @current_hostname != 'localhost';
4343
INSERT INTO user SELECT * FROM tmp_user WHERE @had_user_table=0;
4444
DROP TABLE tmp_user;

mysql-test/r/grant.result

+109
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ max_user_connections 0
5757
plugin mysql_native_password
5858
authentication_string
5959
password_expired N
60+
password_last_changed DTVALUE
61+
password_lifetime NULL
6062
show grants for mysqltest_1@localhost;
6163
Grants for mysqltest_1@localhost
6264
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA'
@@ -129,6 +131,8 @@ max_user_connections 0
129131
plugin mysql_native_password
130132
authentication_string
131133
password_expired N
134+
password_last_changed DTVALUE
135+
password_lifetime NULL
132136
show grants for mysqltest_1@localhost;
133137
Grants for mysqltest_1@localhost
134138
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10
@@ -177,6 +181,8 @@ max_user_connections 0
177181
plugin mysql_native_password
178182
authentication_string
179183
password_expired N
184+
password_last_changed DTVALUE
185+
password_lifetime NULL
180186
show grants for mysqltest_1@localhost;
181187
Grants for mysqltest_1@localhost
182188
GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30
@@ -2615,3 +2621,106 @@ foo@localhost foo@127.0.0.1
26152621
# Clean-up
26162622
DROP USER foo@'127.0.0.1';
26172623
# End of Bug#12766319
2624+
#
2625+
# WL#7131: Add timestamp in mysql.user on the last time the
2626+
# password was changed and implement password rotation.
2627+
#
2628+
SET @saved_value = @@global.default_password_lifetime;
2629+
SET GLOBAL default_password_lifetime = 2;
2630+
SHOW VARIABLES LIKE 'default_password_lifetime';
2631+
Variable_name Value
2632+
default_password_lifetime 2
2633+
CREATE USER 'wl7131' IDENTIFIED BY 'wl7131';
2634+
# This should report 1.
2635+
SELECT (SELECT now()-(SELECT password_last_changed from mysql.user where user='wl7131')) <= 2;
2636+
(SELECT now()-(SELECT password_last_changed from mysql.user where user='wl7131')) <= 2
2637+
1
2638+
UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 3 DAY) where user='wl7131';
2639+
FLUSH PRIVILEGES;
2640+
# Attempt to execute query should fail
2641+
mysql: [Warning] Using a password on the command line interface can be insecure.
2642+
ERROR 1862 (HY000): Your password has expired. To log in you must change it using a client that supports expired passwords.
2643+
# Doing something should fail
2644+
SELECT 1;
2645+
ERROR HY000: You must SET PASSWORD before executing this statement
2646+
# Setting variables should work
2647+
SET old_passwords=0;
2648+
# Setting password should work
2649+
SET PASSWORD = PASSWORD('new_wl7131');
2650+
# Doing something should pass
2651+
SELECT 1;
2652+
1
2653+
1
2654+
# Reconnecting with same user should pass now
2655+
SELECT 1;
2656+
1
2657+
1
2658+
DROP USER 'wl7131';
2659+
CREATE USER 'wl7131' IDENTIFIED BY 'wl7131';
2660+
# Issue alter user and check the value of
2661+
# password_lifetime column
2662+
ALTER USER 'wl7131' PASSWORD EXPIRE NEVER;
2663+
# This should report 0
2664+
SELECT password_lifetime FROM mysql.user where user='wl7131';
2665+
password_lifetime
2666+
0
2667+
UPDATE mysql.user SET password_last_changed = (now() - INTERVAL 5 DAY) where user='wl7131';
2668+
FLUSH PRIVILEGES;
2669+
# This should pass as password is never expired.
2670+
mysql: [Warning] Using a password on the command line interface can be insecure.
2671+
ALTER USER 'wl7131' PASSWORD EXPIRE DEFAULT;
2672+
# This should report NULL
2673+
SELECT password_lifetime FROM mysql.user where user='wl7131';
2674+
password_lifetime
2675+
NULL
2676+
# This should not pass as default_password_lifetime
2677+
# (which is 2 now) is being used.
2678+
mysql: [Warning] Using a password on the command line interface can be insecure.
2679+
ERROR 1862 (HY000): Your password has expired. To log in you must change it using a client that supports expired passwords.
2680+
SET GLOBAL default_password_lifetime = 0;
2681+
ALTER USER 'wl7131' PASSWORD EXPIRE INTERVAL 4 DAY;
2682+
# Should report 4
2683+
SELECT password_lifetime FROM mysql.user where user='wl7131';
2684+
password_lifetime
2685+
4
2686+
# This should not pass.
2687+
mysql: [Warning] Using a password on the command line interface can be insecure.
2688+
ERROR 1862 (HY000): Your password has expired. To log in you must change it using a client that supports expired passwords.
2689+
SET GLOBAL default_password_lifetime = @saved_value;
2690+
ALTER USER 'wl7131' PASSWORD EXPIRE INTERVAL 6 DAY;
2691+
# Should report 6
2692+
select password_lifetime from mysql.user where user='wl7131';
2693+
password_lifetime
2694+
6
2695+
# This should pass.
2696+
mysql: [Warning] Using a password on the command line interface can be insecure.
2697+
DROP USER 'wl7131';
2698+
CREATE USER 'wl7131';
2699+
# This should not report NULL
2700+
'DTVALUE' IS NOT NULL
2701+
1
2702+
GRANT USAGE ON *.* TO 'wl7131' REQUIRE SSL;
2703+
# This should report 0 as it must have the same value as above
2704+
TIMESTAMPDIFF(SECOND,'DTVALUE','DTVALUE') <> 0
2705+
0
2706+
# Should report errors
2707+
ALTER USER 'wl7131' PASSWORD EXPIRE INTERVAL -2 DAY;
2708+
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '-2 DAY' at line 1
2709+
ALTER USER 'wl7131' PASSWORD EXPIRE INTERVAL 0 DAY;
2710+
ERROR HY000: Incorrect DAY value: '0'
2711+
ALTER USER 'wl7131' PASSWORD EXPIRE INTERVAL 65536 DAY;
2712+
ERROR HY000: Incorrect DAY value: '65536'
2713+
# Setting an empty password. It should update the timestamp column.
2714+
SET PASSWORD FOR 'wl7131' = PASSWORD('');
2715+
# This should report 1.
2716+
SELECT (SELECT now()-(SELECT password_last_changed from mysql.user where user='wl7131')) <= 2;
2717+
(SELECT now()-(SELECT password_last_changed from mysql.user where user='wl7131')) <= 2
2718+
1
2719+
DROP USER 'wl7131';
2720+
GRANT USAGE ON *.* TO 'wl7131'@'localhost' IDENTIFIED BY 'wl7131';
2721+
# Must report 1
2722+
SELECT (SELECT password_last_changed FROM mysql.user where user='wl7131') IS NOT NULL;
2723+
(SELECT password_last_changed FROM mysql.user where user='wl7131') IS NOT NULL
2724+
1
2725+
DROP USER 'wl7131'@'localhost';
2726+
# mysql.user table restored to original values.

mysql-test/r/grant2.result

+1
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,4 @@ DROP USER mysqltest_u2@localhost;
847847
DROP USER mysqltest_u3@localhost;
848848
DROP USER mysqltest_u4@localhost;
849849
DROP USER mysqltest_u5@localhost;
850+
# mysql.user table restored to original values.

mysql-test/r/information_schema.result

+2
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,8 @@ max_connections select,insert,update,references
706706
max_user_connections select,insert,update,references
707707
authentication_string select,insert,update,references
708708
password_expired select,insert,update,references
709+
password_last_changed select,insert,update,references
710+
password_lifetime select,insert,update,references
709711
use test;
710712
create function sub1(i int) returns int
711713
return i+1;

mysql-test/r/mysqlbinlog.result

+1
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,7 @@ DROP TABLE t1;
653653
1
654654
DROP TABLE t1;
655655
shell> mysqlbinlog std_data/corrupt-relay-bin.000624 > var/tmp/bug31793.sql
656+
set timestamp= default;
656657
FLUSH LOGS;
657658
Bug#31611 Security risk with BINLOG statement
658659
SET BINLOG_FORMAT=ROW;

mysql-test/r/mysqld--help-notwin.result

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ The following options may be given as the first argument:
140140
--default-authentication-plugin=name
141141
The default authentication plugin used by the server to
142142
hash the password.
143+
--default-password-lifetime=#
144+
The number of days after which the password will expire.
143145
--default-storage-engine=name
144146
The default storage engine for new tables
145147
--default-time-zone=name
@@ -1063,6 +1065,7 @@ console FALSE
10631065
date-format %Y-%m-%d
10641066
datetime-format %Y-%m-%d %H:%i:%s
10651067
default-authentication-plugin mysql_native_password
1068+
default-password-lifetime 360
10661069
default-storage-engine InnoDB
10671070
default-time-zone (No default value)
10681071
default-tmp-storage-engine InnoDB

mysql-test/r/mysqld--help-win.result

+3
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ The following options may be given as the first argument:
140140
--default-authentication-plugin=name
141141
The default authentication plugin used by the server to
142142
hash the password.
143+
--default-password-lifetime=#
144+
The number of days after which the password will expire.
143145
--default-storage-engine=name
144146
The default storage engine for new tables
145147
--default-time-zone=name
@@ -1071,6 +1073,7 @@ console FALSE
10711073
date-format %Y-%m-%d
10721074
datetime-format %Y-%m-%d %H:%i:%s
10731075
default-authentication-plugin mysql_native_password
1076+
default-password-lifetime 360
10741077
default-storage-engine InnoDB
10751078
default-time-zone (No default value)
10761079
default-tmp-storage-engine InnoDB

mysql-test/r/ps.result

+3-3
Original file line numberDiff line numberDiff line change
@@ -1226,13 +1226,13 @@ SET @aux= "SELECT COUNT(*)
12261226
prepare my_stmt from @aux;
12271227
execute my_stmt;
12281228
COUNT(*)
1229-
43
1229+
45
12301230
execute my_stmt;
12311231
COUNT(*)
1232-
43
1232+
45
12331233
execute my_stmt;
12341234
COUNT(*)
1235-
43
1235+
45
12361236
deallocate prepare my_stmt;
12371237
drop procedure if exists p1|
12381238
drop table if exists t1|

mysql-test/r/system_mysql_db.result

+2
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ user CREATE TABLE `user` (
102102
`plugin` char(64) COLLATE utf8_bin NOT NULL DEFAULT 'mysql_native_password',
103103
`authentication_string` text COLLATE utf8_bin,
104104
`password_expired` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N',
105+
`password_last_changed` timestamp NULL DEFAULT NULL,
106+
`password_lifetime` smallint(5) unsigned DEFAULT NULL,
105107
PRIMARY KEY (`Host`,`User`)
106108
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
107109
show create table func;

0 commit comments

Comments
 (0)