multiserver/auth.go

186 lines
3.8 KiB
Go

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)
}
}