diff options
| author | Mikhail Osipov <mike.osipov@gmail.com> | 2020-02-29 00:58:01 +0300 |
|---|---|---|
| committer | Mikhail Osipov <mike.osipov@gmail.com> | 2020-02-29 00:58:01 +0300 |
| commit | c55afd2de177f128fae6e1c52d0c56af17096258 (patch) | |
| tree | 2b06eeabf4db3a6c7ef357fb1569c4e8f72aab68 /pkg/server/hook/auth.go | |
| parent | 11501b56a751d2959480aaeaf2036eff586e5629 (diff) | |
rename module to hook
Diffstat (limited to 'pkg/server/hook/auth.go')
| -rw-r--r-- | pkg/server/hook/auth.go | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/pkg/server/hook/auth.go b/pkg/server/hook/auth.go new file mode 100644 index 0000000..fc19c2a --- /dev/null +++ b/pkg/server/hook/auth.go @@ -0,0 +1,150 @@ +package hook + +import ( + "crypto/md5" + "crypto/rand" + "errors" + "io" + "tunnel/pkg/netstring" + "tunnel/pkg/server/env" + "tunnel/pkg/server/opts" + "tunnel/pkg/server/queue" +) + +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 authHook struct{} + +func (a *auth) generateChallenge() error { + b := make([]byte, ChallengeLen) + if _, err := rand.Read(b); err != nil { + return err + } + + a.challenge.self = string(b) + + return nil +} + +func (a *auth) getHash(c string) string { + h := md5.New() + + io.WriteString(h, a.secret) + io.WriteString(h, c) + + return string(h.Sum(nil)) +} + +func (a *auth) isReady(c chan struct{}) bool { + select { + case <-a.fail: + return false + case <-c: + return true + } +} + +func (a *auth) Send(rq, wq queue.Q) error { + e := netstring.NewEncoder(wq.Writer()) + + if err := a.generateChallenge(); err != nil { + return err + } + + e.Encode(a.challenge.self) + + if !a.isReady(a.recvChallenge) { + return nil + } + + if a.challenge.self == a.challenge.peer { + return errDupChallenge + } + + e.Encode(a.getHash(a.challenge.peer)) + + if !a.isReady(a.recvHash) { + 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) (err error) { + r := rq.Reader() + d := netstring.NewDecoder(r) + + if a.challenge.peer, err = d.Decode(); err != nil { + close(a.fail) + return + } + + close(a.recvChallenge) + + if a.hash, err = d.Decode(); err != nil { + close(a.fail) + return err + } + + close(a.recvHash) + + if !a.isReady(a.ok) { + return nil + } + + return queue.IoCopy(r, wq.Writer()) +} + +func getAuthSecret(env env.Env) string { + if v := env.Eval("@{tunnel.@{tunnel}.secret}"); v != "" { + return v + } + + return env.Get("secret") +} + +func (h authHook) Open(env env.Env) (interface{}, error) { + a := &auth{ + secret: getAuthSecret(env), + recvChallenge: make(chan struct{}), + recvHash: make(chan struct{}), + fail: make(chan struct{}), + ok: make(chan struct{}), + } + return a, nil +} + +func newAuthHook(opts.Opts, env.Env) (hook, error) { + return authHook{}, nil +} + +func init() { + register("auth", newAuthHook) +} |
