diff options
| author | Mikhail Osipov <mike.osipov@gmail.com> | 2021-09-22 02:51:33 +0300 |
|---|---|---|
| committer | Mikhail Osipov <mike.osipov@gmail.com> | 2021-09-22 12:45:07 +0300 |
| commit | d9b9516ed6a372b1270011c2177681a9713c9d31 (patch) | |
| tree | 79a3a0c37998d494d4a8a3983fb57af10252b228 /pkg/server | |
| parent | 3c9f376276c4f59bfaf93195441cde402fb1d333 (diff) | |
auth: via file
Diffstat (limited to 'pkg/server')
| -rw-r--r-- | pkg/server/hook/auth.go | 186 | ||||
| -rw-r--r-- | pkg/server/hook/proxy.go | 2 | ||||
| -rw-r--r-- | pkg/server/socket/socket.go | 4 |
3 files changed, 141 insertions, 51 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{}) } diff --git a/pkg/server/hook/proxy.go b/pkg/server/hook/proxy.go index 0a79056..4e30105 100644 --- a/pkg/server/hook/proxy.go +++ b/pkg/server/hook/proxy.go @@ -11,7 +11,7 @@ import ( "tunnel/pkg/server/queue" ) -var addrPattern = "^([0-9a-zA-Z-.]+|\\[[0-9a-fA-F:]*\\]):[0-9]+$" +var addrPattern = "^(([0-9a-zA-Z-.]+|\\[[0-9a-fA-F:]*\\]):|%)[0-9]+$" var isGoodAddr = regexp.MustCompile(addrPattern).MatchString type proxyHook struct { diff --git a/pkg/server/socket/socket.go b/pkg/server/socket/socket.go index cb76cf7..6768c48 100644 --- a/pkg/server/socket/socket.go +++ b/pkg/server/socket/socket.go @@ -103,8 +103,8 @@ func New(desc string, e env.Env) (S, error) { func parseProtoAddr(proto, addr string) (string, string) { if proto == "tcp" || proto == "udp" { - if strings.HasPrefix(addr, "-:") { - addr = "localhost" + addr[1:] + if strings.HasPrefix(addr, "%") { + addr = "localhost:" + addr[1:] } } |
