feat: complete basic functions

Support http,dns and mysql connection. Support custom http, dns response. Support dns rebinding.
Support mysql load local files and jdbc deserialize exploit.
This commit is contained in:
Li4n0
2021-04-21 18:55:16 +08:00
parent e9db8ddb24
commit 3559894acc
114 changed files with 41617 additions and 0 deletions
+158
View File
@@ -0,0 +1,158 @@
/*
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.
*/
package vmysql
import (
"bytes"
"flag"
"net"
"sync"
querypb "vitess.io/vitess/go/vt/proto/query"
)
var (
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.")
mysqlAuthServerStaticReloadInterval = flag.Duration("mysql_auth_static_reload_interval", 0, "Ticker to reload credentials")
)
const (
localhostName = "localhost"
)
// AuthServerStatic implements AuthServer using a static configuration.
type AuthServerStatic struct {
// Method can be set to:
// - MysqlNativePassword
// - MysqlClearPassword
// - MysqlDialog
// It defaults to MysqlNativePassword.
Method string
// This mutex helps us prevent data races between the multiple updates of Entries.
mu sync.Mutex
// Entries contains the users, passwords and user data.
Entries map[string][]*AuthServerStaticEntry
}
// AuthServerStaticEntry stores the values for a given user.
type AuthServerStaticEntry struct {
// MysqlNativePassword is generated by password hashing methods in MySQL.
// These changes are illustrated by changes in the result from the PASSWORD() function
// that computes password hash values and in the structure of the user table where passwords are stored.
// mysql> SELECT PASSWORD('mypass');
// +-------------------------------------------+
// | PASSWORD('mypass') |
// +-------------------------------------------+
// | *6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4 |
// +-------------------------------------------+
// MysqlNativePassword's format looks like "*6C8989366EAF75BB670AD8EA7A7FC1176A95CEF4", it store a hashing value.
// Use MysqlNativePassword in auth config, maybe more secure. After all, it is cryptographic storage.
MysqlNativePassword string
Password string
UserData string
SourceHost string
Groups []string
}
// AuthMethod is part of the AuthServer interface.
func (a *AuthServerStatic) AuthMethod(user string) (string, error) {
return a.Method, nil
}
// Salt is part of the AuthServer interface.
func (a *AuthServerStatic) Salt() ([]byte, error) {
return NewSalt()
}
// ValidateHash is part of the AuthServer interface.
func (a *AuthServerStatic) ValidateHash(salt []byte, user string, authResponse []byte, remoteAddr net.Addr) (Getter, error) {
a.mu.Lock()
entries, ok := a.Entries[user]
a.mu.Unlock()
if !ok {
return &StaticUserData{}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
}
for _, entry := range entries {
if entry.MysqlNativePassword != "" {
isPass := isPassScrambleMysqlNativePassword(authResponse, salt, entry.MysqlNativePassword)
if matchSourceHost(remoteAddr, entry.SourceHost) && isPass {
return &StaticUserData{entry.UserData, entry.Groups}, nil
}
} else {
computedAuthResponse := ScramblePassword(salt, []byte(entry.Password))
// Validate the password.
if matchSourceHost(remoteAddr, entry.SourceHost) && bytes.Equal(authResponse, computedAuthResponse) {
return &StaticUserData{entry.UserData, entry.Groups}, nil
}
}
}
return &StaticUserData{}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
}
// 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, remoteAddr net.Addr) (Getter, error) {
// Finish the negotiation.
password, err := AuthServerNegotiateClearOrDialog(c, a.Method)
if err != nil {
return nil, err
}
a.mu.Lock()
entries, ok := a.Entries[user]
a.mu.Unlock()
if !ok {
return &StaticUserData{}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
}
for _, entry := range entries {
// Validate the password.
if matchSourceHost(remoteAddr, entry.SourceHost) && entry.Password == password {
return &StaticUserData{entry.UserData, entry.Groups}, nil
}
}
return &StaticUserData{}, NewSQLError(ERAccessDeniedError, SSAccessDeniedError, "Access denied for user '%v'", user)
}
func matchSourceHost(remoteAddr net.Addr, targetSourceHost string) bool {
// Legacy support, there was not matcher defined default to true
if targetSourceHost == "" {
return true
}
switch remoteAddr.(type) {
case *net.UnixAddr:
if targetSourceHost == localhostName {
return true
}
}
return false
}
// StaticUserData holds the username and groups
type StaticUserData struct {
username string
groups []string
}
// Get returns the wrapped username and groups
func (sud *StaticUserData) Get() *querypb.VTGateCallerID {
return &querypb.VTGateCallerID{Username: sud.username, Groups: sud.groups}
}