2017-05-06 02:21:12 +03:00
|
|
|
/*
|
|
|
|
Copyright 2017 Google Inc.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreedto in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2017-05-18 01:17:40 +03:00
|
|
|
package mysql
|
2017-02-24 23:37:53 +03:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2017-02-27 18:21:44 +03:00
|
|
|
"encoding/json"
|
2017-03-28 22:51:03 +03:00
|
|
|
"flag"
|
2017-02-27 18:21:44 +03:00
|
|
|
"io/ioutil"
|
|
|
|
|
|
|
|
log "github.com/golang/glog"
|
2017-02-24 23:37:53 +03:00
|
|
|
|
2017-03-24 02:26:28 +03:00
|
|
|
querypb "github.com/youtube/vitess/go/vt/proto/query"
|
2017-02-24 23:37:53 +03:00
|
|
|
)
|
|
|
|
|
2017-03-28 22:51:03 +03:00
|
|
|
var (
|
2017-03-29 21:38:49 +03:00
|
|
|
mysqlAuthServerStaticFile = flag.String("mysql_auth_server_static_file", "", "JSON File to read the users/passwords from.")
|
|
|
|
mysqlAuthServerStaticString = flag.String("mysql_auth_server_static_string", "", "JSON representation of the users/passwords config.")
|
2017-03-28 22:51:03 +03:00
|
|
|
)
|
|
|
|
|
2017-03-17 19:23:16 +03:00
|
|
|
// AuthServerStatic implements AuthServer using a static configuration.
|
|
|
|
type AuthServerStatic struct {
|
2017-03-29 21:38:49 +03:00
|
|
|
// Method can be set to:
|
|
|
|
// - MysqlNativePassword
|
|
|
|
// - MysqlClearPassword
|
|
|
|
// - MysqlDialog
|
|
|
|
// It defaults to MysqlNativePassword.
|
|
|
|
Method string
|
2017-02-24 23:37:53 +03:00
|
|
|
|
|
|
|
// Entries contains the users, passwords and user data.
|
2017-03-17 19:23:16 +03:00
|
|
|
Entries map[string]*AuthServerStaticEntry
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
2017-03-17 19:23:16 +03:00
|
|
|
// AuthServerStaticEntry stores the values for a given user.
|
|
|
|
type AuthServerStaticEntry struct {
|
2017-02-24 23:37:53 +03:00
|
|
|
Password string
|
|
|
|
UserData string
|
|
|
|
}
|
|
|
|
|
2017-03-29 21:38:49 +03:00
|
|
|
// InitAuthServerStatic Handles initializing the AuthServerStatic if necessary.
|
2017-03-29 02:18:51 +03:00
|
|
|
func InitAuthServerStatic() {
|
2017-03-28 22:51:03 +03:00
|
|
|
// Check parameters.
|
|
|
|
if *mysqlAuthServerStaticFile == "" && *mysqlAuthServerStaticString == "" {
|
|
|
|
// Not configured, nothing to do.
|
|
|
|
log.Infof("Not configuring AuthServerStatic, as mysql_auth_server_static_file and mysql_auth_server_static_string are empty")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if *mysqlAuthServerStaticFile != "" && *mysqlAuthServerStaticString != "" {
|
|
|
|
// Both parameters specified, can only use one.
|
|
|
|
log.Fatalf("Both mysql_auth_server_static_file and mysql_auth_server_static_string specified, can only use one.")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create and register auth server.
|
|
|
|
RegisterAuthServerStaticFromParams(*mysqlAuthServerStaticFile, *mysqlAuthServerStaticString)
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:23:16 +03:00
|
|
|
// NewAuthServerStatic returns a new empty AuthServerStatic.
|
|
|
|
func NewAuthServerStatic() *AuthServerStatic {
|
|
|
|
return &AuthServerStatic{
|
2017-03-29 21:38:49 +03:00
|
|
|
Method: MysqlNativePassword,
|
|
|
|
Entries: make(map[string]*AuthServerStaticEntry),
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-17 19:23:16 +03:00
|
|
|
// RegisterAuthServerStaticFromParams creates and registers a new
|
|
|
|
// AuthServerStatic, loaded for a JSON file or string. If file is set,
|
2017-02-27 18:21:44 +03:00
|
|
|
// it uses file. Otherwise, load the string. It log.Fatals out in case
|
|
|
|
// of error.
|
2017-03-17 19:23:16 +03:00
|
|
|
func RegisterAuthServerStaticFromParams(file, str string) {
|
|
|
|
authServerStatic := NewAuthServerStatic()
|
2017-02-27 18:21:44 +03:00
|
|
|
jsonConfig := []byte(str)
|
|
|
|
if file != "" {
|
|
|
|
data, err := ioutil.ReadFile(file)
|
|
|
|
if err != nil {
|
2017-03-17 19:23:16 +03:00
|
|
|
log.Fatalf("Failed to read mysql_auth_server_static_file file: %v", err)
|
2017-02-27 18:21:44 +03:00
|
|
|
}
|
|
|
|
jsonConfig = data
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse JSON config.
|
2017-03-17 19:23:16 +03:00
|
|
|
if err := json.Unmarshal(jsonConfig, &authServerStatic.Entries); err != nil {
|
2017-02-27 18:21:44 +03:00
|
|
|
log.Fatalf("Error parsing auth server config: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// And register the server.
|
2017-03-29 02:18:51 +03:00
|
|
|
RegisterAuthServerImpl("static", authServerStatic)
|
2017-02-27 18:21:44 +03:00
|
|
|
}
|
|
|
|
|
2017-03-29 21:38:49 +03:00
|
|
|
// AuthMethod is part of the AuthServer interface.
|
|
|
|
func (a *AuthServerStatic) AuthMethod(user string) (string, error) {
|
|
|
|
return a.Method, nil
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Salt is part of the AuthServer interface.
|
2017-03-17 19:23:16 +03:00
|
|
|
func (a *AuthServerStatic) Salt() ([]byte, error) {
|
2017-03-29 21:38:49 +03:00
|
|
|
return NewSalt()
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateHash is part of the AuthServer interface.
|
2017-03-29 02:18:51 +03:00
|
|
|
func (a *AuthServerStatic) ValidateHash(salt []byte, user string, authResponse []byte) (Getter, error) {
|
2017-02-24 23:37:53 +03:00
|
|
|
// Find the entry.
|
|
|
|
entry, ok := a.Entries[user]
|
|
|
|
if !ok {
|
2017-05-18 18:35:48 +03:00
|
|
|
return &StaticUserData{""}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the password.
|
2017-03-29 02:18:51 +03:00
|
|
|
computedAuthResponse := scramblePassword(salt, []byte(entry.Password))
|
2017-02-24 23:37:53 +03:00
|
|
|
if bytes.Compare(authResponse, computedAuthResponse) != 0 {
|
2017-05-18 18:35:48 +03:00
|
|
|
return &StaticUserData{""}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
2017-03-24 02:26:28 +03:00
|
|
|
return &StaticUserData{entry.UserData}, nil
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
2017-03-29 21:38:49 +03:00
|
|
|
// Negotiate is part of the AuthServer interface.
|
|
|
|
// It will be called if Method is anything else than MysqlNativePassword.
|
|
|
|
// We only recognize MysqlClearPassword and MysqlDialog here.
|
|
|
|
func (a *AuthServerStatic) Negotiate(c *Conn, user string) (Getter, error) {
|
|
|
|
// Finish the negotiation.
|
|
|
|
password, err := AuthServerNegotiateClearOrDialog(c, a.Method)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-02-24 23:37:53 +03:00
|
|
|
// Find the entry.
|
|
|
|
entry, ok := a.Entries[user]
|
|
|
|
if !ok {
|
2017-05-18 18:35:48 +03:00
|
|
|
return &StaticUserData{""}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Validate the password.
|
|
|
|
if entry.Password != password {
|
2017-05-18 18:35:48 +03:00
|
|
|
return &StaticUserData{""}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|
|
|
|
|
2017-03-24 02:26:28 +03:00
|
|
|
return &StaticUserData{entry.UserData}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// StaticUserData holds the username
|
|
|
|
type StaticUserData struct {
|
|
|
|
value string
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get returns the wrapped username
|
|
|
|
func (sud *StaticUserData) Get() *querypb.VTGateCallerID {
|
|
|
|
return &querypb.VTGateCallerID{Username: sud.value}
|
2017-02-24 23:37:53 +03:00
|
|
|
}
|