summaryrefslogtreecommitdiff
path: root/pkg/server/hook/auth.go
diff options
context:
space:
mode:
authorMikhail Osipov <mike.osipov@gmail.com>2021-09-22 02:51:33 +0300
committerMikhail Osipov <mike.osipov@gmail.com>2021-09-22 12:45:07 +0300
commitd9b9516ed6a372b1270011c2177681a9713c9d31 (patch)
tree79a3a0c37998d494d4a8a3983fb57af10252b228 /pkg/server/hook/auth.go
parent3c9f376276c4f59bfaf93195441cde402fb1d333 (diff)
auth: via file
Diffstat (limited to 'pkg/server/hook/auth.go')
-rw-r--r--pkg/server/hook/auth.go186
1 files changed, 138 insertions, 48 deletions
diff --git a/pkg/server/hook/auth.go b/pkg/server/hook/auth.go
index 21f900e..0f70d88 100644
--- a/pkg/server/hook/auth.go
+++ b/pkg/server/hook/auth.go
@@ -1,11 +1,14 @@
package hook
import (
+ "bufio"
"crypto/md5"
"crypto/rand"
"errors"
+ "fmt"
"io"
- "sync"
+ "os"
+ "strings"
"time"
"tunnel/pkg/server/env"
@@ -17,62 +20,109 @@ const authTimeout = 5 * time.Second
const saltSize = 16
const hashSize = md5.Size
+var hashNone = strings.Repeat("\x00", hashSize)
+
type authHook struct {
- m sync.Map
+ Passive bool
}
type auth struct {
- h *authHook
-
- secret string
+ file *os.File
+ user string
+ salt string
+ pass string
- salt struct {
- self string
- peer string
+ peer struct {
+ user string
+ salt string
}
hash string
- readSaltDone chan struct{}
- readHashDone chan struct{}
+ passive bool
+
+ stageHello chan struct{}
+ stageHash chan struct{}
ready chan struct{}
- cancel chan struct{}
+ recver chan struct{}
+ sender chan struct{}
tmr *time.Timer
}
-var errDupSalt = errors.New("peer repeats salt")
-var errAuthFail = errors.New("peer auth fail")
+var errAuthFail = errors.New("mismatch auth hash")
+var errAuthUnknown = errors.New("unknown auth peer")
var errTimeout = errors.New("timeout")
-func (a *auth) Init() error {
- b := make([]byte, saltSize)
- if _, err := rand.Read(b); err != nil {
- return err
+func hashsum(args ...string) string {
+ h := md5.New()
+
+ for _, s := range args {
+ io.WriteString(h, s)
}
- a.salt.self = string(b)
+ return string(h.Sum(nil))
+}
- a.h.m.Store(a.salt.self, struct{}{})
+func getpass(f *os.File, salt string, user string) string {
+ f.Seek(0, 0)
- return nil
+ for scanner := bufio.NewScanner(f); scanner.Scan(); {
+ splitted := strings.SplitN(scanner.Text(), "#", 2)
+ tokens := strings.Fields(splitted[0])
+ if len(tokens) > 1 {
+ if salt == "" {
+ if tokens[0] == user {
+ return tokens[1]
+ }
+ } else {
+ if hashsum(salt, tokens[0]) == user {
+ return tokens[1]
+ }
+ }
+ }
+ }
+
+ return ""
}
-func (a *auth) hashSum(c string) string {
- h := md5.New()
+func (a *auth) Init(authfile string, user string) error {
+ file, err := os.Open(authfile)
+ if err != nil {
+ return fmt.Errorf("authfile: %w", err)
+ }
- io.WriteString(h, c)
- io.WriteString(h, a.secret)
+ a.file = file
- return string(h.Sum(nil))
+ if user != "" {
+ pass := getpass(file, "", user)
+ if pass == "" {
+ file.Close()
+ return fmt.Errorf("authfile: no pass for user '%s'", user)
+ }
+ a.user = user
+ a.pass = pass
+ }
+
+ b := make([]byte, saltSize)
+ if _, err := rand.Read(b); err != nil {
+ return err
+ }
+ a.salt = string(b)
+
+ a.tmr = time.NewTimer(authTimeout)
+
+ return nil
}
func (a *auth) sync(c chan struct{}) error {
select {
case <-a.tmr.C:
return errTimeout
- case <-a.cancel:
+ case <-a.recver:
+ return io.EOF
+ case <-a.sender:
return io.EOF
case <-c:
return nil
@@ -82,24 +132,46 @@ func (a *auth) sync(c chan struct{}) error {
func (a *auth) Send(rq, wq queue.Q) error {
w := wq.Writer()
- io.WriteString(w, a.salt.self)
+ io.WriteString(w, a.salt)
+ if a.passive {
+ io.WriteString(w, hashNone)
+ } else {
+ io.WriteString(w, hashsum(a.salt, a.user))
+ }
- if err := a.sync(a.readSaltDone); err != nil {
+ if err := a.sync(a.stageHello); err != nil {
return err
}
- if _, ok := a.h.m.Load(a.salt.peer); ok {
- return errDupSalt
+ if a.passive && a.peer.user == hashNone {
+ close(a.sender)
+ return fmt.Errorf("can not be passive together")
}
- io.WriteString(w, a.hashSum(a.salt.peer))
+ var pass string
- if err := a.sync(a.readHashDone); err != nil {
+ if a.peer.user == hashNone {
+ pass = hashsum(a.pass)
+ } else {
+ pass = getpass(a.file, a.peer.salt, a.peer.user)
+ if pass == "" {
+ close(a.sender)
+ return fmt.Errorf("no pass for peer")
+ }
+
+ if a.passive {
+ a.pass = hashsum(pass)
+ }
+ }
+
+ io.WriteString(w, hashsum(a.peer.salt, pass))
+
+ if err := a.sync(a.stageHash); err != nil {
return err
}
- if a.hash != a.hashSum(a.salt.self) {
- close(a.cancel)
+ if a.hash != hashsum(a.salt, a.pass) {
+ close(a.sender)
return errAuthFail
}
@@ -112,29 +184,32 @@ func (a *auth) read(r io.Reader, n int, s *string) error {
b := make([]byte, n)
if _, err := io.ReadFull(r, b); err != nil {
- close(a.cancel)
+ close(a.recver)
return err
}
*s = string(b)
-
return nil
}
func (a *auth) Recv(rq, wq queue.Q) error {
r := rq.Reader()
- if err := a.read(r, saltSize, &a.salt.peer); err != nil {
+ if err := a.read(r, saltSize, &a.peer.salt); err != nil {
return err
}
- close(a.readSaltDone)
+ if err := a.read(r, hashSize, &a.peer.user); err != nil {
+ return err
+ }
+
+ close(a.stageHello)
if err := a.read(r, hashSize, &a.hash); err != nil {
return err
}
- close(a.readHashDone)
+ close(a.stageHash)
if err := a.sync(a.ready); err != nil {
return err
@@ -146,23 +221,38 @@ func (a *auth) Recv(rq, wq queue.Q) error {
}
func (a *auth) Close() {
- a.h.m.Delete(a.salt.self)
+ a.file.Close()
}
func (h *authHook) New(env env.Env) (interface{}, error) {
+ file := env.Value("authfile")
+
+ if file == "" {
+ return nil, errors.New("no authfile configured")
+ }
+
+ user := ""
+
+ if !h.Passive {
+ user = env.Value("authuser")
+
+ if user == "" {
+ return nil, errors.New("no authuser configured")
+ }
+ }
+
a := &auth{
- h: h,
- secret: env.Value("secret"),
- tmr: time.NewTimer(authTimeout),
+ passive: h.Passive,
- cancel: make(chan struct{}),
ready: make(chan struct{}),
+ recver: make(chan struct{}),
+ sender: make(chan struct{}),
- readSaltDone: make(chan struct{}),
- readHashDone: make(chan struct{}),
+ stageHello: make(chan struct{}),
+ stageHash: make(chan struct{}),
}
- if err := a.Init(); err != nil {
+ if err := a.Init(file, user); err != nil {
return nil, err
}
@@ -170,5 +260,5 @@ func (h *authHook) New(env env.Env) (interface{}, error) {
}
func init() {
- register("auth", "chap authentication out/in", authHook{})
+ register("auth", "chap out/in", authHook{})
}