Skip to content

Commit

Permalink
MDEV-7597 Expiration of user passwords
Browse files Browse the repository at this point in the history
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
  • Loading branch information
robertbindar authored and vuvova committed Feb 21, 2019
1 parent 83de75d commit 90ad4db
Show file tree
Hide file tree
Showing 34 changed files with 1,259 additions and 99 deletions.
9 changes: 9 additions & 0 deletions client/mysql.cc
Expand Up @@ -160,6 +160,7 @@ static uint my_end_arg;
static char * opt_mysql_unix_port=0;
static int connect_flag=CLIENT_INTERACTIVE;
static my_bool opt_binary_mode= FALSE;
static my_bool opt_connect_expired_password= FALSE;
static int interrupted_query= 0;
static char *current_host,*current_db,*current_user=0,*opt_password=0,
*current_prompt=0, *delimiter_str= 0,
Expand Down Expand Up @@ -1686,6 +1687,11 @@ static struct my_option my_long_options[] =
"piped to mysql or loaded using the 'source' command). This is necessary "
"when processing output from mysqlbinlog that may contain blobs.",
&opt_binary_mode, &opt_binary_mode, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"connect-expired-password", 0,
"Notify the server that this client is prepared to handle expired "
"password sandbox mode even if --batch was specified.",
&opt_connect_expired_password, &opt_connect_expired_password, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
{ 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0}
};

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

mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset);

my_bool can_handle_expired= opt_connect_expired_password || !status.batch;
mysql_options(&mysql, MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, &can_handle_expired);

if (!do_connect(&mysql, host, user, password, database,
connect_flag | CLIENT_MULTI_STATEMENTS))
{
Expand Down
8 changes: 2 additions & 6 deletions include/mysql_com.h
Expand Up @@ -333,12 +333,8 @@ enum enum_indicator_type
CLIENT_DEPRECATE_EOF |\
CLIENT_CONNECT_ATTRS |\
MARIADB_CLIENT_COM_MULTI |\
MARIADB_CLIENT_STMT_BULK_OPERATIONS)

/*
To be added later:
CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS
*/
MARIADB_CLIENT_STMT_BULK_OPERATIONS |\
CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS)

/*
Switch off the flags that are optional and depending on build flags
Expand Down
4 changes: 2 additions & 2 deletions mysql-test/main/mysql_upgrade.result
Expand Up @@ -596,15 +596,15 @@ drop view mysql.user_bak;
create user 'user3'@'localhost' identified with mysql_native_password as password('a_password');
show create user user3@localhost;
CREATE USER for user3@localhost
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA'
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA' PASSWORD EXPIRE NEVER
update mysql.user set password=authentication_string, authentication_string='' where user='user3';
select password,plugin,authentication_string from mysql.user where user='user3';
password plugin authentication_string
*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA mysql_native_password
flush privileges;
show create user user3@localhost;
CREATE USER for user3@localhost
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA'
CREATE USER 'user3'@'localhost' IDENTIFIED BY PASSWORD '*5DC1D11F45824A9DD613961F05C1EC1E7A1601AA' PASSWORD EXPIRE NEVER
connect con1,localhost,user3,a_password;
select current_user();
current_user()
Expand Down
13 changes: 13 additions & 0 deletions mysql-test/main/mysqld--help.result
Expand Up @@ -188,6 +188,12 @@ The following specify which files/extra groups are read (specified before remain
--deadlock-timeout-short=#
Short timeout for the two-step deadlock detection (in
microseconds)
--default-password-lifetime=#
This defines the global password expiration policy. 0
means automatic password expiration is disabled. If the
value is a positive integer N, the passwords must be
changed every N days. This behavior can be overriden
using the password expiration options in ALTER USER.
--default-regex-flags=name
Default flags for the regex library. Any combination of:
DOTALL, DUPNAMES, EXTENDED, EXTRA, MULTILINE, UNGREEDY
Expand Down Expand Up @@ -224,6 +230,11 @@ The following specify which files/extra groups are read (specified before remain
handling INSERT DELAYED. If the queue becomes full, any
client that does INSERT DELAYED will wait until there is
room in the queue again
--disconnect-on-expired-password
This variable controls how the server handles clients
that are not aware of the sandbox mode. If enabled, the
server disconnects the client, otherwise the server puts
the client in a sandbox mode.
--div-precision-increment=#
Precision of the result of '/' operator will be increased
on that value
Expand Down Expand Up @@ -1428,6 +1439,7 @@ deadlock-search-depth-long 15
deadlock-search-depth-short 4
deadlock-timeout-long 50000000
deadlock-timeout-short 10000
default-password-lifetime 0
default-regex-flags
default-storage-engine myisam
default-time-zone (No default value)
Expand All @@ -1437,6 +1449,7 @@ delay-key-write ON
delayed-insert-limit 100
delayed-insert-timeout 300
delayed-queue-size 1000
disconnect-on-expired-password FALSE
div-precision-increment 4
encrypt-binlog FALSE
encrypt-tmp-disk-tables FALSE
Expand Down
207 changes: 207 additions & 0 deletions mysql-test/main/password_expiration.result
@@ -0,0 +1,207 @@
#
# Only privileged users should be able to expire passwords
#
create user user1@localhost;
alter user user1@localhost password expire;
create user user2@localhost;
connect con2,localhost,user2;
connection con2;
alter user user1@localhost password expire;
ERROR 42000: Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
disconnect con2;
connection default;
drop user user1@localhost;
drop user user2@localhost;
#
# disconnect_on_expired_password=ON should deny a clients's connection
# when the password is expired or put the client in sandbox mode if OFF
#
create user user1@localhost password expire;
set global disconnect_on_expired_password=ON;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
set global disconnect_on_expired_password=OFF;
connect con1,localhost,user1;
connection con1;
select 1;
ERROR HY000: You must SET PASSWORD before executing this statement
disconnect con1;
connection default;
drop user user1@localhost;
#
# connect-expired-password option passed to client should override
# the behavior of disconnect_on_expired_password server system var.
#
create user user1@localhost password expire;
set global disconnect_on_expired_password=ON;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
drop user user1@localhost;
#
# Manually expiring a password should have immediate effect
#
create user user1@localhost;
alter user user1@localhost password expire;
set global disconnect_on_expired_password=ON;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
drop user user1@localhost;
#
# Sandbox mode should only allow change password statements
#
create user user1@localhost password expire;
grant create user on *.* to user1@localhost;
set global disconnect_on_expired_password=OFF;
connect con1,localhost,user1;
connection con1;
select 1;
ERROR HY000: You must SET PASSWORD before executing this statement
set password=password('');
select 1;
1
1
disconnect con1;
connection default;
drop user user1@localhost;
#
# Passwords are still expired after acl reload
#
set global disconnect_on_expired_password=ON;
create user user1@localhost password expire;
flush privileges;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
drop user user1@localhost;
#
# JSON functions on global_priv reflect the correct state
# of the password expiration columns
#
create user user1@localhost password expire;
select host, user, JSON_VALUE(Priv, '$.password_last_changed') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.password_last_changed')
localhost user1 0
alter user user1@localhost password expire never;
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.password_lifetime')
localhost user1 0
alter user user1@localhost password expire default;
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.password_lifetime')
localhost user1 -1
alter user user1@localhost password expire interval 123 day;
select host, user, JSON_VALUE(Priv, '$.password_lifetime') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.password_lifetime')
localhost user1 123
drop user user1@localhost;
#
# SHOW CREATE USER correctly displays the locking state of an user
#
create user user1@localhost;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
alter user user1@localhost password expire;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
set password for user1@localhost= password('');
alter user user1@localhost password expire default;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
alter user user1@localhost password expire never;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
alter user user1@localhost password expire interval 123 day;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE INTERVAL 123 DAY
alter user user1@localhost password expire;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
set password for user1@localhost= password('');
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE INTERVAL 123 DAY
drop user user1@localhost;
#
# Incorrect INTERVAL values should be rejected
#
create user user1@localhost password expire interval 0 day;
ERROR HY000: Incorrect DAY value: '0'
#
# Password expiration fields are loaded properly on 10.3 tables
#
create user user1@localhost;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
alter user user1@localhost password expire;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
set password for user1@localhost= password('');
alter user user1@localhost password expire default;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
alter user user1@localhost password expire never;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
alter user user1@localhost password expire interval 123 day;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE NEVER
alter user user1@localhost password expire;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
flush privileges;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' PASSWORD EXPIRE
set global disconnect_on_expired_password=ON;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Your password has expired. To log in you must change it using a client that supports expired passwords
set global disconnect_on_expired_password=OFF;
connect con1,localhost,user1;
connection con1;
select 1;
ERROR HY000: You must SET PASSWORD before executing this statement
set password=password('');
select 1;
1
1
disconnect con1;
connection default;
drop user user1@localhost;
set global disconnect_on_expired_password=default;
set global default_password_lifetime=default;

0 comments on commit 90ad4db

Please sign in to comment.