You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
185 lines
3.8 KiB
185 lines
3.8 KiB
package main |
|
|
|
import ( |
|
"crypto/rand" |
|
"database/sql" |
|
"encoding/base64" |
|
"errors" |
|
"log" |
|
"strings" |
|
|
|
_ "github.com/mattn/go-sqlite3" |
|
) |
|
|
|
const ( |
|
_ = 1 << iota |
|
AuthMechSRP |
|
AuthMechFirstSRP |
|
) |
|
|
|
var passPhrase []byte |
|
|
|
func encodeVerifierAndSalt(s, v []byte) string { |
|
return base64.StdEncoding.EncodeToString(s) + "#" + base64.StdEncoding.EncodeToString(v) |
|
} |
|
|
|
func decodeVerifierAndSalt(src string) ([]byte, []byte, error) { |
|
sString := strings.Split(src, "#")[0] |
|
vString := strings.Split(src, "#")[1] |
|
|
|
s, err := base64.StdEncoding.DecodeString(sString) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
v, err := base64.StdEncoding.DecodeString(vString) |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
|
|
return s, v, nil |
|
} |
|
|
|
func authDB() (*DB, error) { |
|
sqlite3 := func() (*DB, error) { |
|
return OpenSQLite3("auth.sqlite", `CREATE TABLE IF NOT EXISTS auth ( |
|
name VARCHAR(32) PRIMARY KEY NOT NULL, |
|
password VARCHAR(512) NOT NULL |
|
); |
|
CREATE TABLE IF NOT EXISTS privileges ( |
|
name VARCHAR(32) PRIMARY KEY NOT NULL, |
|
privileges VARCHAR(1024) |
|
); |
|
CREATE TABLE IF NOT EXISTS ban ( |
|
addr VARCHAR(39) PRIMARY KEY NOT NULL, |
|
name VARCHAR(32) NOT NULL |
|
);`) |
|
} |
|
|
|
psql := func(host, name, user, password string, port int) (*DB, error) { |
|
return OpenPSQL(host, name, user, password, `CREATE TABLE IF NOT EXISTS auth ( |
|
name VARCHAR(32) PRIMARY KEY NOT NULL, |
|
password VARCHAR(512) NOT NULL |
|
); |
|
CREATE TABLE IF NOT EXISTS privileges ( |
|
name VARCHAR(32) PRIMARY KEY NOT NULL, |
|
privileges VARCHAR(1024) |
|
); |
|
CREATE TABLE IF NOT EXISTS ban ( |
|
addr VARCHAR(39) PRIMARY KEY NOT NULL, |
|
name VARCHAR(32) NOT NULL |
|
);`, port) |
|
} |
|
|
|
db, ok := ConfKey("psql_db").(string) |
|
if !ok { |
|
return sqlite3() |
|
} |
|
|
|
host, ok := ConfKey("psql_host").(string) |
|
if !ok { |
|
log.Print("PostgreSQL host not set or not a string") |
|
return sqlite3() |
|
} |
|
|
|
port, ok := ConfKey("psql_port").(int) |
|
if !ok { |
|
log.Print("PostgreSQL port not set or not an integer") |
|
return sqlite3() |
|
} |
|
|
|
user, ok := ConfKey("psql_user").(string) |
|
if !ok { |
|
log.Print("PostgreSQL user not set or not a string") |
|
return sqlite3() |
|
} |
|
|
|
password, ok := ConfKey("psql_password").(string) |
|
if !ok { |
|
log.Print("PostgreSQL password not set or not a string") |
|
return sqlite3() |
|
} |
|
|
|
return psql(host, db, user, password, port) |
|
} |
|
|
|
// CreateUser creates a new entry in the authentication database |
|
func CreateUser(name string, verifier, salt []byte) error { |
|
db, err := authDB() |
|
if err != nil { |
|
return err |
|
} |
|
defer db.Close() |
|
|
|
pwd := encodeVerifierAndSalt(salt, verifier) |
|
|
|
_, err = db.Exec(`INSERT INTO auth ( |
|
name, |
|
password |
|
) VALUES ( |
|
$1, |
|
$2 |
|
);`, name, pwd) |
|
return err |
|
} |
|
|
|
// Password returns the SRP tokens of a user |
|
func Password(name string) ([]byte, []byte, error) { |
|
db, err := authDB() |
|
if err != nil { |
|
return nil, nil, err |
|
} |
|
defer db.Close() |
|
|
|
var pwd string |
|
err = db.QueryRow(`SELECT password FROM auth WHERE name = $1;`, name).Scan(&pwd) |
|
if err != nil && !errors.Is(err, sql.ErrNoRows) { |
|
return nil, nil, err |
|
} |
|
|
|
if pwd == "" { |
|
return nil, nil, nil |
|
} |
|
|
|
salt, verifier, err := decodeVerifierAndSalt(pwd) |
|
return verifier, salt, err |
|
} |
|
|
|
// SetPassword changes the SRP tokens of a user |
|
func SetPassword(name string, verifier, salt []byte) error { |
|
db, err := authDB() |
|
if err != nil { |
|
return err |
|
} |
|
defer db.Close() |
|
|
|
pwd := encodeVerifierAndSalt(salt, verifier) |
|
|
|
_, err = db.Exec(`UPDATE auth SET password = $1 WHERE name = $2;`, pwd, name) |
|
return err |
|
} |
|
|
|
func init() { |
|
pwd, err := StorageKey("auth:passphrase") |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
if pwd == "" { |
|
passPhrase = make([]byte, 16) |
|
_, err := rand.Read(passPhrase) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
|
|
// Save the passphrase for future use |
|
// This passphrase should not be changed wihtout deleting |
|
// the auth databases on the minetest servers |
|
err = SetStorageKey("auth:passphrase", string(passPhrase)) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
} else { |
|
passPhrase = []byte(pwd) |
|
} |
|
}
|
|
|