summaryrefslogtreecommitdiff
path: root/pkg/server/hook/auth.go
diff options
context:
space:
mode:
authorMikhail Osipov <mike.osipov@gmail.com>2020-02-29 00:58:01 +0300
committerMikhail Osipov <mike.osipov@gmail.com>2020-02-29 00:58:01 +0300
commitc55afd2de177f128fae6e1c52d0c56af17096258 (patch)
tree2b06eeabf4db3a6c7ef357fb1569c4e8f72aab68 /pkg/server/hook/auth.go
parent11501b56a751d2959480aaeaf2036eff586e5629 (diff)
rename module to hook
Diffstat (limited to 'pkg/server/hook/auth.go')
-rw-r--r--pkg/server/hook/auth.go150
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)
+}