Skip to content

Commit 5b15cc6

Browse files
committed
MDEV-11340 Allow multiple alternative authentication methods for the same user
introduce the syntax ... IDENTIFIED { WITH | VIA } plugin [ { USING | AS } auth ] [ OR plugin [ { USING | AS } auth ] [ OR ... ]] Server will try auth plugins in the specified order until the first success. No protocol changes, server uses the existing "switch plugin" packet. The auth chain is stored in json as "auth_or":[{"plugin":"xxx","authentication_string":"yyy"}, {}, {"plugin":"foo","authentication_string":"bar"}, ...], "plugin":"aaa", "authentication_string":"bbb" Note: * "auth_or" implies that there might be "auth_and" someday; * one entry in the array is an empty object, meaning to take plugin/auth from the main json object. This preserves compatibility with the existing mysql.global_priv table and with the mysql.user view. This entry is preferrably a mysql_native_password plugin for a non-empty mysql.user.password column. SET PASSWORD is supported and changes the password for the *first* plugin in the chain that has a notion of a "password"
1 parent 798d1a9 commit 5b15cc6

File tree

12 files changed

+1048
-406
lines changed

12 files changed

+1048
-406
lines changed

libmariadb

mysql-test/main/alter_user.result

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ alter user foo identified with 'somecoolplugin';
6565
ERROR HY000: Operation ALTER USER failed for 'foo'@'%'
6666
show warnings;
6767
Level Code Message
68-
Warning 1524 Plugin 'somecoolplugin' is not loaded
68+
Error 1524 Plugin 'somecoolplugin' is not loaded
6969
Error 1396 Operation ALTER USER failed for 'foo'@'%'
7070
alter user foo identified with 'mysql_old_password';
7171
select * from mysql.user where user = 'foo';
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
install soname 'auth_socket';
2+
install soname 'auth_ed25519';
3+
create user USER identified via unix_socket OR mysql_native_password as password("GOOD");
4+
create user mysqltest1 identified via unix_socket OR mysql_native_password as password("good");
5+
show create user mysqltest1;
6+
CREATE USER for mysqltest1@%
7+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA unix_socket OR mysql_native_password USING '*8409037B3E362D6DAE24C8E667F4D3B66716144E'
8+
# name match = ok
9+
select user(), current_user(), database();
10+
user() current_user() database()
11+
USER@localhost USER@% test
12+
# name does not match, password good = ok
13+
select user(), current_user(), database();
14+
user() current_user() database()
15+
mysqltest1@localhost mysqltest1@% test
16+
# name does not match, password bad = failure
17+
drop user USER, mysqltest1;
18+
create user USER identified via mysql_native_password as password("GOOD") OR unix_socket;
19+
create user mysqltest1 identified via mysql_native_password as password("good") OR unix_socket;
20+
show create user mysqltest1;
21+
CREATE USER for mysqltest1@%
22+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA mysql_native_password USING '*8409037B3E362D6DAE24C8E667F4D3B66716144E' OR unix_socket
23+
# name match = ok
24+
select user(), current_user(), database();
25+
user() current_user() database()
26+
USER@localhost USER@% test
27+
# name does not match, password good = ok
28+
select user(), current_user(), database();
29+
user() current_user() database()
30+
mysqltest1@localhost mysqltest1@% test
31+
# name does not match, password bad = failure
32+
drop user USER, mysqltest1;
33+
create user USER identified via unix_socket OR ed25519 as password("GOOD");
34+
create user mysqltest1 identified via unix_socket OR ed25519 as password("good");
35+
show create user mysqltest1;
36+
CREATE USER for mysqltest1@%
37+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA unix_socket OR ed25519 USING 'F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc'
38+
# name match = ok
39+
select user(), current_user(), database();
40+
user() current_user() database()
41+
USER@localhost USER@% test
42+
# name does not match, password good = ok
43+
select user(), current_user(), database();
44+
user() current_user() database()
45+
mysqltest1@localhost mysqltest1@% test
46+
# name does not match, password bad = failure
47+
drop user USER, mysqltest1;
48+
create user USER identified via ed25519 as password("GOOD") OR unix_socket;
49+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket;
50+
show create user mysqltest1;
51+
CREATE USER for mysqltest1@%
52+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA ed25519 USING 'F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc' OR unix_socket
53+
# name match = ok
54+
select user(), current_user(), database();
55+
user() current_user() database()
56+
USER@localhost USER@% test
57+
# name does not match, password good = ok
58+
select user(), current_user(), database();
59+
user() current_user() database()
60+
mysqltest1@localhost mysqltest1@% test
61+
# name does not match, password bad = failure
62+
drop user USER, mysqltest1;
63+
create user USER identified via ed25519 as password("GOOD") OR unix_socket OR mysql_native_password as password("works");
64+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
65+
show create user mysqltest1;
66+
CREATE USER for mysqltest1@%
67+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA ed25519 USING 'F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc' OR unix_socket OR mysql_native_password USING '*7D8C3DF236D9163B6C274A9D47704BC496988460'
68+
# name match = ok
69+
select user(), current_user(), database();
70+
user() current_user() database()
71+
USER@localhost USER@% test
72+
# name does not match, password good = ok
73+
select user(), current_user(), database();
74+
user() current_user() database()
75+
mysqltest1@localhost mysqltest1@% test
76+
# name does not match, second password works = ok
77+
select user(), current_user(), database();
78+
user() current_user() database()
79+
mysqltest1@localhost mysqltest1@% test
80+
# name does not match, password bad = failure
81+
drop user USER, mysqltest1;
82+
create user mysqltest1 identified via mysql_native_password as password("good") OR mysql_native_password as password("works");
83+
show create user mysqltest1;
84+
CREATE USER for mysqltest1@%
85+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA mysql_native_password USING '*8409037B3E362D6DAE24C8E667F4D3B66716144E' OR mysql_native_password USING '*7D8C3DF236D9163B6C274A9D47704BC496988460'
86+
# password good = ok
87+
select user(), current_user(), database();
88+
user() current_user() database()
89+
mysqltest1@localhost mysqltest1@% test
90+
# second password works = ok
91+
select user(), current_user(), database();
92+
user() current_user() database()
93+
mysqltest1@localhost mysqltest1@% test
94+
# password bad = failure
95+
drop user mysqltest1;
96+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
97+
show grants for mysqltest1;
98+
Grants for mysqltest1@%
99+
GRANT USAGE ON *.* TO 'mysqltest1'@'%' IDENTIFIED VIA ed25519 USING 'F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc' OR unix_socket OR mysql_native_password USING '*7D8C3DF236D9163B6C274A9D47704BC496988460'
100+
select json_detailed(priv) from mysql.global_priv where user='mysqltest1';
101+
json_detailed(priv)
102+
{
103+
"access": 0,
104+
"plugin": "mysql_native_password",
105+
"authentication_string": "*7D8C3DF236D9163B6C274A9D47704BC496988460",
106+
"auth_or":
107+
[
108+
109+
{
110+
"plugin": "ed25519",
111+
"authentication_string": "F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc"
112+
},
113+
114+
{
115+
"plugin": "unix_socket"
116+
},
117+
118+
{
119+
}
120+
]
121+
}
122+
select password,plugin,authentication_string from mysql.user where user='mysqltest1';
123+
Password plugin authentication_string
124+
*7D8C3DF236D9163B6C274A9D47704BC496988460 mysql_native_password *7D8C3DF236D9163B6C274A9D47704BC496988460
125+
flush privileges;
126+
show create user mysqltest1;
127+
CREATE USER for mysqltest1@%
128+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA ed25519 USING 'F4aF8bw7130VaRbdLCl4f/P/wkjDmgJXwWvpJ5gmsZc' OR unix_socket OR mysql_native_password USING '*7D8C3DF236D9163B6C274A9D47704BC496988460'
129+
set password for mysqltest1 = password('foobar');
130+
show create user mysqltest1;
131+
CREATE USER for mysqltest1@%
132+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA ed25519 USING 'qv2mG6HWCuy32Slb5xhV4THStewNz2VINVPbgk+XAJ8' OR unix_socket OR mysql_native_password USING '*7D8C3DF236D9163B6C274A9D47704BC496988460'
133+
alter user mysqltest1 identified via unix_socket OR mysql_native_password as password("some");
134+
show create user mysqltest1;
135+
CREATE USER for mysqltest1@%
136+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA unix_socket OR mysql_native_password USING '*BFE3F4604CFD21E6595080A261D92EF0183B5971'
137+
set password for mysqltest1 = password('foobar');
138+
show create user mysqltest1;
139+
CREATE USER for mysqltest1@%
140+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA unix_socket OR mysql_native_password USING '*9B500343BC52E2911172EB52AE5CF4847604C6E5'
141+
alter user mysqltest1 identified via unix_socket;
142+
set password for mysqltest1 = password('bla');
143+
ERROR HY000: SET PASSWORD is ignored for users authenticating via unix_socket plugin
144+
alter user mysqltest1 identified via mysql_native_password as password("some") or unix_socket;
145+
show create user mysqltest1;
146+
CREATE USER for mysqltest1@%
147+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA mysql_native_password USING '*BFE3F4604CFD21E6595080A261D92EF0183B5971' OR unix_socket
148+
drop user mysqltest1;
149+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
150+
ERROR HY000: Column count of mysql.user is wrong. Expected 3, found 47. Created with MariaDB XX.YY.ZZ, now running XX.YY.ZZ. Please use mysql_upgrade to fix this error
151+
create user USER identified via mysql_native_password as '1234567890123456789012345678901234567890a' OR unix_socket;
152+
create user mysqltest1 identified via mysql_native_password as '1234567890123456789012345678901234567890a' OR unix_socket;
153+
update mysql.global_priv set priv=replace(priv, '1234567890123456789012345678901234567890a', 'invalid password');
154+
flush privileges;
155+
show create user mysqltest1;
156+
CREATE USER for mysqltest1@%
157+
CREATE USER 'mysqltest1'@'%' IDENTIFIED VIA mysql_native_password USING 'invalid password' OR unix_socket
158+
# name match = ok
159+
select user(), current_user(), database();
160+
user() current_user() database()
161+
USER@localhost USER@% test
162+
# name does not match = failure
163+
# SET PASSWORD helps
164+
set password for mysqltest1 = password('bla');
165+
select user(), current_user(), database();
166+
user() current_user() database()
167+
mysqltest1@localhost mysqltest1@% test
168+
drop user USER, mysqltest1;
169+
uninstall soname 'auth_socket';
170+
uninstall soname 'auth_ed25519';
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#
2+
# MDEV-11340 Allow multiple alternative authentication methods for the same user
3+
#
4+
--source include/have_unix_socket.inc
5+
if (`SELECT '$USER' = 'mysqltest1'`) {
6+
skip USER is mysqltest1;
7+
}
8+
if (!$AUTH_ED25519_SO) {
9+
skip No auth_ed25519 plugin;
10+
}
11+
12+
--let $plugindir=`SELECT @@global.plugin_dir`
13+
install soname 'auth_socket';
14+
install soname 'auth_ed25519';
15+
16+
--let $try_auth=$MYSQL_TEST < $MYSQLTEST_VARDIR/tmp/peercred_test.txt
17+
18+
--write_file $MYSQLTEST_VARDIR/tmp/peercred_test.txt
19+
--let $replace1=$USER@localhost
20+
--let $replace2=$USER@%
21+
--replace_result $replace1 "USER@localhost" $replace2 "USER@%"
22+
select user(), current_user(), database();
23+
EOF
24+
25+
--let $creplace=create user $USER
26+
--let $dreplace=drop user $USER
27+
28+
#
29+
# socket,password
30+
#
31+
--replace_result $creplace "create user USER"
32+
eval $creplace identified via unix_socket OR mysql_native_password as password("GOOD");
33+
create user mysqltest1 identified via unix_socket OR mysql_native_password as password("good");
34+
show create user mysqltest1;
35+
--echo # name match = ok
36+
--exec $try_auth -u $USER
37+
--echo # name does not match, password good = ok
38+
--exec $try_auth -u mysqltest1 -pgood
39+
--echo # name does not match, password bad = failure
40+
--error 1
41+
--exec $try_auth -u mysqltest1 -pbad
42+
--replace_result $dreplace "drop user USER"
43+
eval $dreplace, mysqltest1;
44+
45+
#
46+
# password,socket
47+
#
48+
--replace_result $creplace "create user USER"
49+
eval $creplace identified via mysql_native_password as password("GOOD") OR unix_socket;
50+
create user mysqltest1 identified via mysql_native_password as password("good") OR unix_socket;
51+
show create user mysqltest1;
52+
--echo # name match = ok
53+
--exec $try_auth -u $USER
54+
--echo # name does not match, password good = ok
55+
--exec $try_auth -u mysqltest1 -pgood
56+
--echo # name does not match, password bad = failure
57+
--error 1
58+
--exec $try_auth -u mysqltest1 -pbad
59+
--replace_result $dreplace "drop user USER"
60+
eval $dreplace, mysqltest1;
61+
62+
#
63+
# socket,ed25519
64+
#
65+
--replace_result $creplace "create user USER"
66+
eval $creplace identified via unix_socket OR ed25519 as password("GOOD");
67+
create user mysqltest1 identified via unix_socket OR ed25519 as password("good");
68+
show create user mysqltest1;
69+
--echo # name match = ok
70+
--exec $try_auth -u $USER
71+
--echo # name does not match, password good = ok
72+
--exec $try_auth -u mysqltest1 -pgood
73+
--echo # name does not match, password bad = failure
74+
--error 1
75+
--exec $try_auth -u mysqltest1 -pbad
76+
--replace_result $dreplace "drop user USER"
77+
eval $dreplace, mysqltest1;
78+
79+
#
80+
# ed25519,socket
81+
#
82+
--replace_result $creplace "create user USER"
83+
eval $creplace identified via ed25519 as password("GOOD") OR unix_socket;
84+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket;
85+
show create user mysqltest1;
86+
--echo # name match = ok
87+
--exec $try_auth -u $USER
88+
--echo # name does not match, password good = ok
89+
--exec $try_auth -u mysqltest1 -pgood
90+
--echo # name does not match, password bad = failure
91+
--error 1
92+
--exec $try_auth -u mysqltest1 -pbad
93+
--replace_result $dreplace "drop user USER"
94+
eval $dreplace, mysqltest1;
95+
96+
#
97+
# ed25519,socket,password
98+
#
99+
--replace_result $creplace "create user USER"
100+
eval $creplace identified via ed25519 as password("GOOD") OR unix_socket OR mysql_native_password as password("works");
101+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
102+
show create user mysqltest1;
103+
--echo # name match = ok
104+
--exec $try_auth -u $USER
105+
--echo # name does not match, password good = ok
106+
--exec $try_auth -u mysqltest1 -pgood
107+
--echo # name does not match, second password works = ok
108+
--exec $try_auth -u mysqltest1 -pworks
109+
--echo # name does not match, password bad = failure
110+
--error 1
111+
--exec $try_auth -u mysqltest1 -pbad
112+
--replace_result $dreplace "drop user USER"
113+
eval $dreplace, mysqltest1;
114+
115+
#
116+
# password,password
117+
#
118+
create user mysqltest1 identified via mysql_native_password as password("good") OR mysql_native_password as password("works");
119+
show create user mysqltest1;
120+
--echo # password good = ok
121+
--exec $try_auth -u mysqltest1 -pgood
122+
--echo # second password works = ok
123+
--exec $try_auth -u mysqltest1 -pworks
124+
--echo # password bad = failure
125+
--error 1
126+
--exec $try_auth -u mysqltest1 -pbad
127+
drop user mysqltest1;
128+
129+
#
130+
# show grants, flush privileges, set password, alter user
131+
#
132+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
133+
show grants for mysqltest1;
134+
select json_detailed(priv) from mysql.global_priv where user='mysqltest1';
135+
select password,plugin,authentication_string from mysql.user where user='mysqltest1';
136+
flush privileges;
137+
show create user mysqltest1;
138+
set password for mysqltest1 = password('foobar');
139+
show create user mysqltest1;
140+
alter user mysqltest1 identified via unix_socket OR mysql_native_password as password("some");
141+
show create user mysqltest1;
142+
set password for mysqltest1 = password('foobar');
143+
show create user mysqltest1;
144+
alter user mysqltest1 identified via unix_socket;
145+
--error ER_SET_PASSWORD_AUTH_PLUGIN
146+
set password for mysqltest1 = password('bla');
147+
alter user mysqltest1 identified via mysql_native_password as password("some") or unix_socket;
148+
show create user mysqltest1;
149+
drop user mysqltest1;
150+
151+
--source include/switch_to_mysql_user.inc
152+
--replace_regex /\d{6}/XX.YY.ZZ/
153+
--error ER_COL_COUNT_DOESNT_MATCH_PLEASE_UPDATE
154+
create user mysqltest1 identified via ed25519 as password("good") OR unix_socket OR mysql_native_password as password("works");
155+
--source include/switch_to_mysql_global_priv.inc
156+
157+
#
158+
# invalid password,socket
159+
#
160+
--replace_result $creplace "create user USER"
161+
eval $creplace identified via mysql_native_password as '1234567890123456789012345678901234567890a' OR unix_socket;
162+
create user mysqltest1 identified via mysql_native_password as '1234567890123456789012345678901234567890a' OR unix_socket;
163+
update mysql.global_priv set priv=replace(priv, '1234567890123456789012345678901234567890a', 'invalid password');
164+
flush privileges;
165+
show create user mysqltest1;
166+
--echo # name match = ok
167+
--exec $try_auth -u $USER
168+
--echo # name does not match = failure
169+
--error 1
170+
--exec $try_auth -u mysqltest1
171+
--echo # SET PASSWORD helps
172+
set password for mysqltest1 = password('bla');
173+
--exec $try_auth -u mysqltest1 -pbla
174+
--replace_result $dreplace "drop user USER"
175+
eval $dreplace, mysqltest1;
176+
177+
uninstall soname 'auth_socket';
178+
uninstall soname 'auth_ed25519';
179+
--remove_file $MYSQLTEST_VARDIR/tmp/peercred_test.txt
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
INSTALL SONAME 'auth_gssapi';
2+
Warnings:
3+
Note 1105 SSPI: using principal name 'localhost', mech 'Negotiate'
4+
CREATE USER 'nosuchuser' IDENTIFIED WITH gssapi OR mysql_native_password as password("good");
5+
connect(localhost,nosuchuser,,test,MASTER_MYPORT,MASTER_MYSOCK);
6+
connect con1,localhost,nosuchuser,,;
7+
ERROR 28000: Access denied for user 'nosuchuser'@'localhost' (using password: NO)
8+
connect con1,localhost,nosuchuser,good,;
9+
SELECT USER(),CURRENT_USER();
10+
USER() CURRENT_USER()
11+
nosuchuser@localhost nosuchuser@%
12+
disconnect con1;
13+
connection default;
14+
DROP USER nosuchuser;
15+
CREATE USER 'nosuchuser' IDENTIFIED WITH mysql_native_password as password("good") OR gssapi;
16+
connect(localhost,nosuchuser,,test,MASTER_MYPORT,MASTER_MYSOCK);
17+
connect con1,localhost,nosuchuser,,;
18+
ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser', actual name 'GSSAPI_SHORTNAME'
19+
connect con1,localhost,nosuchuser,good,;
20+
SELECT USER(),CURRENT_USER();
21+
USER() CURRENT_USER()
22+
nosuchuser@localhost nosuchuser@%
23+
disconnect con1;
24+
connection default;
25+
DROP USER nosuchuser;
26+
CREATE USER 'GSSAPI_SHORTNAME' IDENTIFIED WITH mysql_native_password as password("good") OR gssapi;
27+
connect con1,localhost,$GSSAPI_SHORTNAME,,;
28+
SELECT USER(),CURRENT_USER();
29+
USER() CURRENT_USER()
30+
GSSAPI_SHORTNAME@localhost GSSAPI_SHORTNAME@%
31+
disconnect con1;
32+
connection default;
33+
DROP USER 'GSSAPI_SHORTNAME';
34+
UNINSTALL SONAME 'auth_gssapi';

0 commit comments

Comments
 (0)