diff options
| author | Mikhail Osipov <mike.osipov@gmail.com> | 2020-02-23 16:01:23 +0300 |
|---|---|---|
| committer | Mikhail Osipov <mike.osipov@gmail.com> | 2020-02-23 16:01:23 +0300 |
| commit | 4bbdb1b34d9f7e71c0faf6f1943e6bb4ce303af1 (patch) | |
| tree | 99cd0a2becd48025a225399ad167a963f9f5ec90 /pkg/server/module | |
| parent | de868930e2301b68a50bde088dd83dc575b72c54 (diff) | |
make auth (chap)
Diffstat (limited to 'pkg/server/module')
| -rw-r--r-- | pkg/server/module/auth.go | 136 |
1 files changed, 135 insertions, 1 deletions
diff --git a/pkg/server/module/auth.go b/pkg/server/module/auth.go index 05761ed..5dbdccb 100644 --- a/pkg/server/module/auth.go +++ b/pkg/server/module/auth.go @@ -4,24 +4,158 @@ import ( "tunnel/pkg/server/queue" "tunnel/pkg/server/opts" "tunnel/pkg/server/env" + "tunnel/pkg/netstring" + "encoding/hex" + "crypto/rand" + "crypto/md5" + "errors" + "fmt" + "io" ) +const ChallengeLen = 16 + type auth struct { secret string + + challenge struct { + self string + peer string + } + + hash string + + recvChallenge chan struct{} + recvHash chan struct{} + + fail chan struct{} + ok chan struct{} } +var errDupChallenge = errors.New("peer duplicates challenge") +var errAuthFail = errors.New("peer auth fail") + type authModule struct{} +func (a *auth) generateChallenge() error { + b := make([]byte, ChallengeLen) + if _, err := rand.Read(b); err != nil { + return err + } + + c := make([]byte, hex.EncodedLen(len(b))) + hex.Encode(c, b) + + a.challenge.self = string(c) + + return nil +} + +func (a *auth) sendChallenge(q queue.Q) bool { + enc := netstring.NewEncoder(q.Writer()) + enc.Encode(a.challenge.self) + return a.wait(a.recvChallenge) +} + +func (a *auth) getHash(c string) string { + h := md5.New() + io.WriteString(h, a.secret) + io.WriteString(h, c) + + b := make([]byte, hex.EncodedLen(h.Size())) + hex.Encode(b, h.Sum(nil)) + + return string(b) +} + +func (a *auth) sendHash(q queue.Q) bool { + enc := netstring.NewEncoder(q.Writer()) + enc.Encode(a.getHash(a.challenge.peer)) + return a.wait(a.recvHash) +} + +func (a *auth) wait(c chan struct{}) bool { + select { + case <-a.fail: + return false + case <-c: + return true + } +} + func (a *auth) Send(rq, wq queue.Q) error { + if err := a.generateChallenge(); err != nil { + return err + } + + if !a.sendChallenge(wq) { + return nil + } + + if a.challenge.self == a.challenge.peer { + return errDupChallenge + } + + if !a.sendHash(wq) { + return nil + } + + if a.hash != a.getHash(a.challenge.self) { + close(a.fail) + return errAuthFail + } + + close(a.ok) + return queue.Copy(rq, wq) } func (a *auth) Recv(rq, wq queue.Q) error { + dec := netstring.NewDecoder(rq.Reader()) + + if c, err := dec.Decode(); err != nil { + close(a.fail) + return err + } else { + a.challenge.peer = c + close(a.recvChallenge) + } + + if h, err := dec.Decode(); err != nil { + close(a.fail) + return err + } else { + a.hash = h + close(a.recvHash) + } + + if !a.wait(a.ok) { + return nil + } + return queue.Copy(rq, wq) } +func getAuthSecret(env env.Env) string { + if id, ok := env.Find("tunnel"); ok { + k := fmt.Sprintf("tunnel.%s.secret", id) + + if v, ok := env.Find(k); ok { + return v + } + } + + return env.Get("secret") +} + func (m authModule) Open(env env.Env) (Pipe, Pipe) { - a := &auth{env.Get("secret")} + a := &auth{ + secret: getAuthSecret(env), + recvChallenge: make(chan struct{}), + recvHash: make(chan struct{}), + fail: make(chan struct{}), + ok: make(chan struct{}), + } return a.Send, a.Recv } |
