diff options
author | HimbeerserverDE <himbeerserverde@gmail.com> | 2021-09-13 15:54:57 +0200 |
---|---|---|
committer | HimbeerserverDE <himbeerserverde@gmail.com> | 2021-09-13 15:54:57 +0200 |
commit | 8ddb6cc9198add437426e85fc0b27a5c3eb91349 (patch) | |
tree | 0450c9236aa92bffaa17a5dd86542c0d68b4fa1d | |
parent | c978ee8e278dacaac1664d51e7b46a0e0a433401 (diff) |
Add simple file-based auth system by anon5
-rw-r--r-- | auth_files.go | 193 | ||||
-rw-r--r-- | auth_sqlite3.go | 21 | ||||
-rw-r--r-- | config.go | 11 | ||||
-rw-r--r-- | log.go | 9 | ||||
-rw-r--r-- | plugin.go | 8 | ||||
-rw-r--r-- | proxy.go | 25 | ||||
-rw-r--r-- | run.go | 2 |
7 files changed, 230 insertions, 39 deletions
diff --git a/auth_files.go b/auth_files.go new file mode 100644 index 0000000..22b1e60 --- /dev/null +++ b/auth_files.go @@ -0,0 +1,193 @@ +package proxy + +import ( + "net" + "os" + "time" +) + +type authFiles struct{} + +// Exists reports whether a user is registered. +func (a authFiles) Exists(name string) bool { + os.Mkdir(Path("auth"), 0700) + + _, err := os.Stat(Path("auth/", name)) + return err == nil +} + +// Passwd returns the SRP salt and verifier of a user or an error. +func (a authFiles) Passwd(name string) (salt, verifier []byte, err error) { + os.Mkdir(Path("auth"), 0700) + + salt, err = os.ReadFile(Path("auth/", name, "/salt")) + if err != nil { + return + } + + verifier, err = os.ReadFile(Path("auth/", name, "/verifier")) + if err != nil { + return + } + + a.updateTimestamp(name) + return +} + +// SetPasswd creates a password entry if necessary +// and sets the password of a user. +func (a authFiles) SetPasswd(name string, salt, verifier []byte) error { + os.Mkdir(Path("auth"), 0700) + os.Mkdir(Path("auth/", name), 0700) + + if err := os.WriteFile(Path("auth/", name, "/salt"), salt, 0600); err != nil { + return err + } + + if err := os.WriteFile(Path("auth/", name, "/verifier"), verifier, 0600); err != nil { + return err + } + + a.updateTimestamp(name) + return nil +} + +// Timestamp returns the last time an authentication entry was accessed +// or an error. +func (a authFiles) Timestamp(name string) (time.Time, error) { + os.Mkdir(Path("auth"), 0700) + + info, err := os.Stat(Path("auth/", name, "/timestamp")) + if err != nil { + return time.Time{}, err + } + + return info.ModTime(), nil +} + +// Import deletes all users and adds the passed users. +func (a authFiles) Import(in []user) { + os.Mkdir(Path("auth"), 0700) + + for _, u := range in { + a.SetPasswd(u.name, u.salt, u.verifier) + os.Chtimes(Path("auth/", u.name, "/timestamp"), u.timestamp, u.timestamp) + } +} + +// Export returns data that can be processed by Import +// or an error. +func (a authFiles) Export() ([]user, error) { + dir, err := os.ReadDir(Path("auth")) + if err != nil { + return nil, err + } + + var out []user + for _, f := range dir { + u := user{name: f.Name()} + + u.timestamp, err = a.Timestamp(u.name) + if err != nil { + return nil, err + } + + u.salt, u.verifier, err = a.Passwd(u.name) + if err != nil { + return nil, err + } + + out = append(out, u) + } + + return out, nil +} + +// Ban adds a ban entry for a network address and an associated name. +func (a authFiles) Ban(addr, name string) error { + os.Mkdir(Path("ban"), 0700) + return os.WriteFile(Path("ban/", addr), []byte(name), 0600) +} + +// Unban deletes a ban entry. It accepts both network addresses +// and player names. +func (a authFiles) Unban(id string) error { + os.Mkdir(Path("ban"), 0700) + + if err := os.Remove(Path("ban/", id)); err != nil { + if os.IsNotExist(err) { + dir, err := os.ReadDir(Path("ban")) + if err != nil { + return err + } + + for _, f := range dir { + name, err := os.ReadFile(Path("ban/", f.Name())) + if err != nil { + return err + } + + if string(name) == id { + return os.Remove(Path("ban/", f.Name())) + break + } + } + } + } + + return nil +} + +// Banned reports whether a network address is banned. +func (a authFiles) Banned(addr *net.UDPAddr) bool { + os.Mkdir(Path("ban"), 0700) + + _, err := os.Stat(Path("ban/", addr.IP.String())) + if os.IsNotExist(err) { + return false + } + + return true +} + +// ImportBans deletes all ban entries and adds the passed entries. +func (a authFiles) ImportBans(in []ban) { + os.Mkdir(Path("ban"), 0700) + + for _, b := range in { + a.Ban(b.addr, b.name) + } +} + +// ExportBans returns data that can be processed by ImportBans +// or an error, +func (a authFiles) ExportBans() ([]ban, error) { + os.Mkdir(Path("ban"), 0700) + + dir, err := os.ReadDir(Path("ban")) + if err != nil { + return nil, err + } + + var out []ban + for _, f := range dir { + b := ban{addr: f.Name()} + + name, err := os.ReadFile(Path("ban/", f.Name())) + if err != nil { + return nil, err + } + + b.name = string(name) + out = append(out, b) + } + + return out, nil +} + +func (a authFiles) updateTimestamp(name string) { + os.Mkdir(Path("auth"), 0700) + + t := time.Now().Local() + os.Chtimes(Path("auth/", name, "/timestamp"), t, t) +} diff --git a/auth_sqlite3.go b/auth_sqlite3.go index 5562ca1..f1540c4 100644 --- a/auth_sqlite3.go +++ b/auth_sqlite3.go @@ -4,8 +4,6 @@ import ( "database/sql" "errors" "net" - "os" - "path/filepath" "time" _ "github.com/mattn/go-sqlite3" @@ -73,8 +71,7 @@ func (a authSQLite3) Timestamp(name string) (time.Time, error) { return time.Parse("2006-01-02 15:04:05", tstr) } -// Import deletes all users and and adds the passed -// users. +// Import deletes all users and adds the passed users. func (a authSQLite3) Import(in []user) { if err := a.init(); err != nil { return @@ -114,13 +111,14 @@ func (a authSQLite3) Export() ([]user, error) { var out []user for _, name := range names { - var u user - u.timestamp, err = a.Timestamp(name) + u := user{name: name} + + u.timestamp, err = a.Timestamp(u.name) if err != nil { return nil, err } - u.salt, u.verifier, err = a.Passwd(name) + u.salt, u.verifier, err = a.Passwd(u.name) if err != nil { return nil, err } @@ -224,13 +222,8 @@ func (a authSQLite3) updateTimestamp(name string) { } func (a *authSQLite3) init() error { - executable, err := os.Executable() - if err != nil { - return err - } - - path := filepath.Dir(executable) + "/auth.sqlite" - a.db, err = sql.Open("sqlite3", path) + var err error + a.db, err = sql.Open("sqlite3", Path("auth.sqlite3")) if err != nil { return err } @@ -4,7 +4,6 @@ import ( "encoding/json" "log" "os" - "path/filepath" "sync" ) @@ -12,7 +11,7 @@ const ( defaultCmdPrefix = ">" defaultSendInterval = 0.09 defaultUserLimit = 10 - defaultAuthBackend = "sqlite3" + defaultAuthBackend = "files" defaultTelnetAddr = "[::1]:40010" defaultBindAddr = ":40000" defaultListInterval = 300 @@ -93,13 +92,7 @@ func LoadConfig() error { config.UserGroups = make(map[string]string) config.List.Interval = defaultListInterval - executable, err := os.Executable() - if err != nil { - return err - } - - path := filepath.Dir(executable) + "/config.json" - f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0666) + f, err := os.OpenFile(Path("config.json"), os.O_RDWR|os.O_CREATE, 0666) if err != nil { config = oldConf return err @@ -3,7 +3,6 @@ package proxy import ( "log" "os" - "path/filepath" ) var logWriter *LogWriter @@ -27,13 +26,7 @@ func init() { log.SetPrefix("[proxy] ") log.SetFlags(log.Flags() | log.Lmsgprefix) - executable, err := os.Executable() - if err != nil { - log.Fatal(err) - } - - path := filepath.Dir(executable) + "/latest.log" - f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) + f, err := os.OpenFile(Path("latest.log"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666) if err != nil { log.Fatal(err) } @@ -3,7 +3,6 @@ package proxy import ( "log" "os" - "path/filepath" "plugin" "sync" ) @@ -15,12 +14,7 @@ func loadPlugins() { } func openPlugins() { - executable, err := os.Executable() - if err != nil { - log.Fatal(err) - } - - path := filepath.Dir(executable) + "/plugins" + path := Path("plugins") os.Mkdir(path, 0777) dir, err := os.ReadDir(path) @@ -4,7 +4,14 @@ It also provides an API for plugins. */ package proxy -import "regexp" +import ( + "log" + "os" + "path/filepath" + "regexp" + "strings" + "sync" +) const ( latestSerializeVer = 28 @@ -15,3 +22,19 @@ const ( ) var playerNameChars = regexp.MustCompile("^[a-zA-Z0-9-_]+$") + +var proxyDir string +var proxyDirOnce sync.Once + +func Path(path ...string) string { + proxyDirOnce.Do(func() { + executable, err := os.Executable() + if err != nil { + log.Fatal(err) + } + + proxyDir = filepath.Dir(executable) + }) + + return proxyDir + "/" + strings.Join(path, "") +} @@ -25,6 +25,8 @@ func Run() { switch Conf().AuthBackend { case "sqlite3": setAuthBackend(authSQLite3{}) + case "files": + setAuthBackend(authFiles{}) default: log.Fatal("invalid auth backend") } |