From 885366fd3cab3fb97347e88b8c7b526b0493c1dc Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 23 Feb 2017 16:44:15 +0800 Subject: [PATCH 1/3] privilege/privileges: ignore privileges that TiDB doesn't recognise when load table --- privilege/privileges/cache.go | 36 +++++++++++------------------- privilege/privileges/privileges.go | 6 ++--- 2 files changed, 16 insertions(+), 26 deletions(-) diff --git a/privilege/privileges/cache.go b/privilege/privileges/cache.go index c2eaf534cfa08..ea5e1f16e22bd 100644 --- a/privilege/privileges/cache.go +++ b/privilege/privileges/cache.go @@ -19,6 +19,7 @@ import ( "time" "github.com/juju/errors" + "github.com/ngaut/log" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/context" "github.com/pingcap/tidb/mysql" @@ -116,22 +117,22 @@ func (p *MySQLPrivilege) LoadAll(ctx context.Context) error { // LoadUserTable loads the mysql.user table from database. func (p *MySQLPrivilege) LoadUserTable(ctx context.Context) error { - return p.loadTable(ctx, "select * from mysql.user order by host, user;", p.decodeUserTableRow) + return p.loadTable(ctx, "select Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Insert_priv,Create_user_priv from mysql.user order by host, user;", p.decodeUserTableRow) } // LoadDBTable loads the mysql.db table from database. func (p *MySQLPrivilege) LoadDBTable(ctx context.Context) error { - return p.loadTable(ctx, "select * from mysql.db order by host, db, user;", p.decodeDBTableRow) + return p.loadTable(ctx, "select Host,DB,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Index_priv,Alter_priv,Execute_priv from mysql.db order by host, db, user;", p.decodeDBTableRow) } // LoadTablesPrivTable loads the mysql.tables_priv table from database. func (p *MySQLPrivilege) LoadTablesPrivTable(ctx context.Context) error { - return p.loadTable(ctx, "select * from mysql.tables_priv", p.decodeTablesPrivTableRow) + return p.loadTable(ctx, "select Host,DB,User,Table_name,Grantor,Timestamp,Table_priv,Column_priv from mysql.tables_priv", p.decodeTablesPrivTableRow) } // LoadColumnsPrivTable loads the mysql.columns_priv table from database. func (p *MySQLPrivilege) LoadColumnsPrivTable(ctx context.Context) error { - return p.loadTable(ctx, "select * from mysql.columns_priv", p.decodeColumnsPrivTableRow) + return p.loadTable(ctx, "select Host,DB,User,Table_name,Column_name,Timestamp,Column_priv from mysql.columns_priv", p.decodeColumnsPrivTableRow) } func (p *MySQLPrivilege) loadTable(ctx context.Context, sql string, @@ -235,17 +236,9 @@ func (p *MySQLPrivilege) decodeTablesPrivTableRow(row *ast.Row, fs []*ast.Result case f.ColumnAsName.L == "table_name": value.TableName = d.GetString() case f.ColumnAsName.L == "table_priv": - priv, err := decodeSetToPrivilege(d.GetMysqlSet()) - if err != nil { - return errors.Trace(err) - } - value.TablePriv = priv + value.TablePriv = decodeSetToPrivilege(d.GetMysqlSet()) case f.ColumnAsName.L == "column_priv": - priv, err := decodeSetToPrivilege(d.GetMysqlSet()) - if err != nil { - return errors.Trace(err) - } - value.ColumnPriv = priv + value.ColumnPriv = decodeSetToPrivilege(d.GetMysqlSet()) } } p.TablesPriv = append(p.TablesPriv, value) @@ -271,30 +264,27 @@ func (p *MySQLPrivilege) decodeColumnsPrivTableRow(row *ast.Row, fs []*ast.Resul case f.ColumnAsName.L == "timestamp": value.Timestamp, _ = d.GetMysqlTime().Time.GoTime(time.Local) case f.ColumnAsName.L == "column_priv": - priv, err := decodeSetToPrivilege(d.GetMysqlSet()) - if err != nil { - return errors.Trace(err) - } - value.ColumnPriv = priv + value.ColumnPriv = decodeSetToPrivilege(d.GetMysqlSet()) } } p.ColumnsPriv = append(p.ColumnsPriv, value) return nil } -func decodeSetToPrivilege(s types.Set) (mysql.PrivilegeType, error) { +func decodeSetToPrivilege(s types.Set) mysql.PrivilegeType { var ret mysql.PrivilegeType if s.Name == "" { - return ret, nil + return ret } for _, str := range strings.Split(s.Name, ",") { priv, ok := mysql.SetStr2Priv[str] if !ok { - return ret, errInvalidPrivilegeType + log.Warn("unsupported privilege type:", str) + continue } ret |= priv } - return ret, nil + return ret } func (record *userRecord) match(user, host string) bool { diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index a8cb0d7047995..434debe580f1f 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -317,7 +317,7 @@ const userTablePrivColumnStartIndex = 3 const dbTablePrivColumnStartIndex = 3 func (p *UserPrivileges) loadGlobalPrivileges(ctx context.Context) error { - sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, + sql := fmt.Sprintf(`SELECT Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Insert_priv,Create_user_priv FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, mysql.SystemDB, mysql.UserTable, p.privs.User, p.privs.Host) rows, fs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) if err != nil { @@ -348,7 +348,7 @@ func (p *UserPrivileges) loadGlobalPrivileges(ctx context.Context) error { } func (p *UserPrivileges) loadDBScopePrivileges(ctx context.Context) error { - sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, + sql := fmt.Sprintf(`SELECT Host,DB,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Index_priv,Alter_priv,Execute_priv FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, mysql.SystemDB, mysql.DBTable, p.privs.User, p.privs.Host) rows, fs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) if err != nil { @@ -381,7 +381,7 @@ func (p *UserPrivileges) loadDBScopePrivileges(ctx context.Context) error { } func (p *UserPrivileges) loadTableScopePrivileges(ctx context.Context) error { - sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, + sql := fmt.Sprintf(`SELECT Host,DB,User,Table_name,Grantor,Timestamp,Table_priv,Column_priv FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, mysql.SystemDB, mysql.TablePrivTable, p.privs.User, p.privs.Host) rows, _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) if err != nil { From 414e91ff375735d3e22f2934098104dab4da73ae Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 23 Feb 2017 19:06:47 +0800 Subject: [PATCH 2/3] use host in connection verification --- privilege/privileges/cache_test.go | 61 ++++++++++++++++++++++++++++++ server/server_test.go | 9 +++++ session.go | 31 ++++++++++++--- 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/privilege/privileges/cache_test.go b/privilege/privileges/cache_test.go index 6eea639760979..5a0a6eff13ec7 100644 --- a/privilege/privileges/cache_test.go +++ b/privilege/privileges/cache_test.go @@ -171,3 +171,64 @@ func (s *testCacheSuite) TestCaseInsensitive(c *C) { c.Assert(p.RequestVerification("genius", "127.0.0.1", "TCTRAIN", "TCTRAINORDER", "", mysql.SelectPriv), IsTrue) c.Assert(p.RequestVerification("genius", "127.0.0.1", "tctrain", "tctrainorder", "", mysql.SelectPriv), IsTrue) } + +func (s *testCacheSuite) TestAfterSyncMySQLUser(c *C) { + se, err := tidb.CreateSession(s.store) + c.Assert(err, IsNil) + defer se.Close() + + mustExec(c, se, "DROP TABLE mysql.user;") + mustExec(c, se, `CREATE TABLE user ( + Host char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + User char(16) COLLATE utf8_bin NOT NULL DEFAULT '', + Password char(41) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL DEFAULT '', + Select_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Insert_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Update_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Delete_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Drop_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Reload_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Shutdown_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Process_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + File_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Grant_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + References_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Index_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Alter_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Show_db_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Super_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_tmp_table_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Lock_tables_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Execute_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Repl_slave_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Repl_client_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Show_view_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Alter_routine_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_user_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Event_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Trigger_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + Create_tablespace_priv enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + ssl_type enum('','ANY','X509','SPECIFIED') CHARACTER SET utf8 NOT NULL DEFAULT '', + ssl_cipher blob NOT NULL, + x509_issuer blob NOT NULL, + x509_subject blob NOT NULL, + max_questions int(11) unsigned NOT NULL DEFAULT '0', + max_updates int(11) unsigned NOT NULL DEFAULT '0', + max_connections int(11) unsigned NOT NULL DEFAULT '0', + max_user_connections int(11) unsigned NOT NULL DEFAULT '0', + plugin char(64) COLLATE utf8_bin DEFAULT 'mysql_native_password', + authentication_string text COLLATE utf8_bin, + password_expired enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N', + PRIMARY KEY (Host,User) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges';`) + mustExec(c, se, `INSERT INTO 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,'mysql_native_password','','N'); +`) + var p privileges.MySQLPrivilege + err = p.LoadUserTable(se) + c.Assert(err, IsNil) + // MySQL mysql.user table schema is not identical to TiDB, check it doesn't break privilege. + c.Assert(p.RequestVerification("root", "localhost", "test", "", "", mysql.SelectPriv), IsTrue) +} diff --git a/server/server_test.go b/server/server_test.go index 077992565d25d..b78722ba4e92c 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -472,6 +472,15 @@ func runTestAuth(c *C) { _, err = db.Query("USE mysql;") c.Assert(err, NotNil, Commentf("Wrong password should be failed")) db.Close() + + // Test login use IP but user record is host. + runTests(c, dsn, func(dbt *DBTest) { + dbt.mustExec(`CREATE USER 'xxx'@'localhost' IDENTIFIED BY 'yyy';`) + }) + newDsn := "test:123@tcp(127.0.0.1:4001)/test?strict=true" + runTests(c, newDsn, func(dbt *DBTest) { + dbt.mustExec(`USE mysql;`) + }) } func runTestIssues(c *C) { diff --git a/session.go b/session.go index 0b68460d923cc..dce1c23469289 100644 --- a/session.go +++ b/session.go @@ -21,6 +21,7 @@ import ( goctx "context" "encoding/json" "fmt" + "net" "strings" "sync" "time" @@ -707,14 +708,32 @@ func (s *session) Auth(user string, auth []byte, salt []byte) bool { // Get user password. name := strs[0] host := strs[1] - checker := privilege.GetPrivilegeChecker(s) - if !checker.ConnectionVerification(name, host, auth, salt) { - log.Errorf("User connection verification failed %v", name) - return false + + // Check IP. + if checker.ConnectionVerification(name, host, auth, salt) { + s.sessionVars.User = name + "@" + host + return true + } + + // Check Hostname. + for _, addr := range getHostByIP(host) { + if checker.ConnectionVerification(name, addr, auth, salt) { + s.sessionVars.User = name + "@" + addr + return true + } + } + + log.Errorf("User connection verification failed %v", user) + return false +} + +func getHostByIP(ip string) []string { + if ip == "127.0.0.1" { + return []string{"localhost"} } - s.sessionVars.User = user - return true + addrs, _ := net.LookupAddr(ip) + return addrs } // Some vars name for debug. From 6578815900b24d82b362c9cea7b2b91d0f321674 Mon Sep 17 00:00:00 2001 From: tiancaiamao Date: Thu, 23 Feb 2017 20:00:34 +0800 Subject: [PATCH 3/3] fix ci --- privilege/privileges/cache.go | 2 +- privilege/privileges/cache_test.go | 10 +++++++++- privilege/privileges/privileges.go | 2 +- server/server_test.go | 4 ++-- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/privilege/privileges/cache.go b/privilege/privileges/cache.go index ea5e1f16e22bd..4be39cb684d4f 100644 --- a/privilege/privileges/cache.go +++ b/privilege/privileges/cache.go @@ -117,7 +117,7 @@ func (p *MySQLPrivilege) LoadAll(ctx context.Context) error { // LoadUserTable loads the mysql.user table from database. func (p *MySQLPrivilege) LoadUserTable(ctx context.Context) error { - return p.loadTable(ctx, "select Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Insert_priv,Create_user_priv from mysql.user order by host, user;", p.decodeUserTableRow) + return p.loadTable(ctx, "select Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Index_priv,Create_user_priv from mysql.user order by host, user;", p.decodeUserTableRow) } // LoadDBTable loads the mysql.db table from database. diff --git a/privilege/privileges/cache_test.go b/privilege/privileges/cache_test.go index 5a0a6eff13ec7..704d9372f3dd5 100644 --- a/privilege/privileges/cache_test.go +++ b/privilege/privileges/cache_test.go @@ -173,11 +173,19 @@ func (s *testCacheSuite) TestCaseInsensitive(c *C) { } func (s *testCacheSuite) TestAfterSyncMySQLUser(c *C) { - se, err := tidb.CreateSession(s.store) + privileges.Enable = true + store, err := tidb.NewStore("memory://sync_mysql_user") + c.Assert(err, IsNil) + domain, err := tidb.BootstrapSession(store) + c.Assert(err, IsNil) + defer domain.Close() + + se, err := tidb.CreateSession(store) c.Assert(err, IsNil) defer se.Close() mustExec(c, se, "DROP TABLE mysql.user;") + mustExec(c, se, "USE mysql;") mustExec(c, se, `CREATE TABLE user ( Host char(60) COLLATE utf8_bin NOT NULL DEFAULT '', User char(16) COLLATE utf8_bin NOT NULL DEFAULT '', diff --git a/privilege/privileges/privileges.go b/privilege/privileges/privileges.go index 434debe580f1f..6a2107c317103 100644 --- a/privilege/privileges/privileges.go +++ b/privilege/privileges/privileges.go @@ -317,7 +317,7 @@ const userTablePrivColumnStartIndex = 3 const dbTablePrivColumnStartIndex = 3 func (p *UserPrivileges) loadGlobalPrivileges(ctx context.Context) error { - sql := fmt.Sprintf(`SELECT Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Insert_priv,Create_user_priv FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, + sql := fmt.Sprintf(`SELECT Host,User,Password,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,Alter_priv,Show_db_priv,Execute_priv,Index_priv,Create_user_priv FROM %s.%s WHERE User="%s" AND (Host="%s" OR Host="%%");`, mysql.SystemDB, mysql.UserTable, p.privs.User, p.privs.Host) rows, fs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) if err != nil { diff --git a/server/server_test.go b/server/server_test.go index b78722ba4e92c..9bbfc0443daef 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -473,11 +473,11 @@ func runTestAuth(c *C) { c.Assert(err, NotNil, Commentf("Wrong password should be failed")) db.Close() - // Test login use IP but user record is host. + // Test login use IP that not exists in mysql.user. runTests(c, dsn, func(dbt *DBTest) { dbt.mustExec(`CREATE USER 'xxx'@'localhost' IDENTIFIED BY 'yyy';`) }) - newDsn := "test:123@tcp(127.0.0.1:4001)/test?strict=true" + newDsn = "xxx:yyy@tcp(127.0.0.1:4001)/test?strict=true" runTests(c, newDsn, func(dbt *DBTest) { dbt.mustExec(`USE mysql;`) })