From 296e8036f3aba02ca6998f0de1d78cae73a43910 Mon Sep 17 00:00:00 2001 From: Kaleb Elwert Date: Wed, 8 Aug 2018 14:27:07 -0700 Subject: [PATCH 1/4] Switch built-in SSH server to use github.com/gliderlabs/ssh Signed-off-by: Kaleb Elwert --- cmd/serv.go | 4 +- integrations/mysql.ini.tmpl | 1 + integrations/pgsql.ini.tmpl | 1 + integrations/repo_test.go | 2 +- integrations/sqlite.ini | 1 + modules/ssh/ssh.go | 286 ++++++++++++++++++------------------ 6 files changed, 147 insertions(+), 148 deletions(-) diff --git a/cmd/serv.go b/cmd/serv.go index b532b95494e50..e01435c4ec0ac 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -70,7 +70,7 @@ func parseCmd(cmd string) (string, string) { if len(ss) != 2 { return "", "" } - return ss[0], strings.Replace(ss[1], "'/", "'", 1) + return ss[0], ss[1] } var ( @@ -138,7 +138,7 @@ func runServ(c *cli.Context) error { } } - repoPath := strings.ToLower(strings.Trim(args, "'")) + repoPath := strings.ToLower(strings.TrimLeft(strings.Trim(args, "'"), "/")) rr := strings.SplitN(repoPath, "/", 2) if len(rr) != 2 { fail("Invalid repository path", "Invalid repository path: %v", args) diff --git a/integrations/mysql.ini.tmpl b/integrations/mysql.ini.tmpl index e75df81bd6e04..732ff35b65e60 100644 --- a/integrations/mysql.ini.tmpl +++ b/integrations/mysql.ini.tmpl @@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-mysql OFFLINE_MODE = false LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w APP_DATA_PATH = integrations/gitea-integration-mysql/data +BUILTIN_SSH_SERVER_USER = git [mailer] ENABLED = false diff --git a/integrations/pgsql.ini.tmpl b/integrations/pgsql.ini.tmpl index 923608235e608..a9c7265d11925 100644 --- a/integrations/pgsql.ini.tmpl +++ b/integrations/pgsql.ini.tmpl @@ -34,6 +34,7 @@ LFS_CONTENT_PATH = data/lfs-pgsql OFFLINE_MODE = false LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w APP_DATA_PATH = integrations/gitea-integration-pgsql/data +BUILTIN_SSH_SERVER_USER = git [mailer] ENABLED = false diff --git a/integrations/repo_test.go b/integrations/repo_test.go index 165009faa2705..076478eed10c2 100644 --- a/integrations/repo_test.go +++ b/integrations/repo_test.go @@ -73,7 +73,7 @@ func TestViewRepo1CloneLinkAuthorized(t *testing.T) { assert.Equal(t, setting.AppURL+"user2/repo1.git", link) link, exists = htmlDoc.doc.Find("#repo-clone-ssh").Attr("data-link") assert.True(t, exists, "The template has changed") - sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.RunUser, setting.SSH.Domain, setting.SSH.Port) + sshURL := fmt.Sprintf("ssh://%s@%s:%d/user2/repo1.git", setting.SSH.BuiltinServerUser, setting.SSH.Domain, setting.SSH.Port) assert.Equal(t, sshURL, link) } diff --git a/integrations/sqlite.ini b/integrations/sqlite.ini index 857cfe46166f7..cebcd69324bb9 100644 --- a/integrations/sqlite.ini +++ b/integrations/sqlite.ini @@ -30,6 +30,7 @@ LFS_CONTENT_PATH = data/lfs-sqlite OFFLINE_MODE = false LFS_JWT_SECRET = Tv_MjmZuHqpIY6GFl12ebgkRAMt4RlWt0v4EHKSXO0w APP_DATA_PATH = integrations/gitea-integration-sqlite/data +BUILTIN_SSH_SERVER_USER = git [mailer] ENABLED = false diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index aea46daad4c5c..73a63cfd46b18 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -1,4 +1,4 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. @@ -6,165 +6,169 @@ package ssh import ( "io" - "io/ioutil" - "net" "os" "os/exec" "path/filepath" "strings" - - "github.com/Unknwon/com" - "golang.org/x/crypto/ssh" + "sync" + "syscall" + "unicode" "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" + + "github.com/Unknwon/com" + "github.com/gliderlabs/ssh" + gossh "golang.org/x/crypto/ssh" ) -func cleanCommand(cmd string) string { - i := strings.Index(cmd, "git") - if i == -1 { - return cmd +type contextKey string + +const giteaKeyID = contextKey("gitea-key-id") + +func getExitStatusFromError(err error) int { + if err == nil { + return 0 } - return cmd[i:] -} -func handleServerConn(keyID string, chans <-chan ssh.NewChannel) { - for newChan := range chans { - if newChan.ChannelType() != "session" { - newChan.Reject(ssh.UnknownChannelType, "unknown channel type") - continue - } + exitErr, ok := err.(*exec.ExitError) + if !ok { + return 1 + } - ch, reqs, err := newChan.Accept() - if err != nil { - log.Error(3, "Error accepting channel: %v", err) - continue + waitStatus, ok := exitErr.Sys().(syscall.WaitStatus) + if !ok { + // This is a fallback and should at least let us return something useful + // when running on Windows, even if it isn't completely accurate. + if exitErr.Success() { + return 0 } - go func(in <-chan *ssh.Request) { - defer ch.Close() - for req := range in { - payload := cleanCommand(string(req.Payload)) - switch req.Type { - case "env": - args := strings.Split(strings.Replace(payload, "\x00", "", -1), "\v") - if len(args) != 2 { - log.Warn("SSH: Invalid env arguments: '%#v'", args) - continue - } - args[0] = strings.TrimLeft(args[0], "\x04") - _, _, err := com.ExecCmdBytes("env", args[0]+"="+args[1]) - if err != nil { - log.Error(3, "env: %v", err) - return - } - case "exec": - cmdName := strings.TrimLeft(payload, "'()") - log.Trace("SSH: Payload: %v", cmdName) - - args := []string{"serv", "key-" + keyID, "--config=" + setting.CustomConf} - log.Trace("SSH: Arguments: %v", args) - cmd := exec.Command(setting.AppPath, args...) - cmd.Env = append( - os.Environ(), - "SSH_ORIGINAL_COMMAND="+cmdName, - "SKIP_MINWINSVC=1", - ) - - stdout, err := cmd.StdoutPipe() - if err != nil { - log.Error(3, "SSH: StdoutPipe: %v", err) - return - } - stderr, err := cmd.StderrPipe() - if err != nil { - log.Error(3, "SSH: StderrPipe: %v", err) - return - } - input, err := cmd.StdinPipe() - if err != nil { - log.Error(3, "SSH: StdinPipe: %v", err) - return - } - - // FIXME: check timeout - if err = cmd.Start(); err != nil { - log.Error(3, "SSH: Start: %v", err) - return - } - - req.Reply(true, nil) - go io.Copy(input, ch) - io.Copy(ch, stdout) - io.Copy(ch.Stderr(), stderr) - - if err = cmd.Wait(); err != nil { - log.Error(3, "SSH: Wait: %v", err) - return - } - - ch.SendRequest("exit-status", false, []byte{0, 0, 0, 0}) - return - default: - } - } - }(reqs) + return 1 } + + return waitStatus.ExitStatus() } -func listen(config *ssh.ServerConfig, host string, port int) { - listener, err := net.Listen("tcp", host+":"+com.ToStr(port)) +func shellEscape(args []string) string { + var data = make([]string, len(args)) + for k, v := range args { + data[k] = v + + // This is a borderline dirty hack. It's designed to only escape + // strings which *might* need to be escaped. It uses a very + // limited character set so everything that needs to be quoted + // will be but we can ignore some simple cases to make it easier + // to parse in the ssh hook. We allow alpha-numeric characters + // along with ., _, and - + idx := strings.IndexFunc(v, func(r rune) bool { + return !unicode.IsLetter(r) && !unicode.IsDigit(r) && !strings.ContainsRune(".-_", r) + }) + if idx > -1 || v == "" { + data[k] = "'" + strings.Replace(data[k], "'", "'\"'\"'", -1) + "'" + } + } + return strings.Join(data, " ") +} + +func sessionHandler(session ssh.Session) { + keyID := session.Context().Value(giteaKeyID).(int64) + + command := shellEscape(session.Command()) + + log.Trace("SSH: Payload: %v", command) + + args := []string{"serv", "key-" + com.ToStr(keyID), "--config=" + setting.CustomConf} + log.Trace("SSH: Arguments: %v", args) + cmd := exec.Command(setting.AppPath, args...) + cmd.Env = append( + os.Environ(), + "SSH_ORIGINAL_COMMAND="+command, + "SKIP_MINWINSVC=1", + ) + + stdout, err := cmd.StdoutPipe() if err != nil { - log.Fatal(4, "Failed to start SSH server: %v", err) + log.Error(3, "SSH: StdoutPipe: %v", err) + return + } + stderr, err := cmd.StderrPipe() + if err != nil { + log.Error(3, "SSH: StderrPipe: %v", err) + return + } + stdin, err := cmd.StdinPipe() + if err != nil { + log.Error(3, "SSH: StdinPipe: %v", err) + return } - for { - // Once a ServerConfig has been configured, connections can be accepted. - conn, err := listener.Accept() - if err != nil { - log.Error(3, "SSH: Error accepting incoming connection: %v", err) - continue - } - // Before use, a handshake must be performed on the incoming net.Conn. - // It must be handled in a separate goroutine, - // otherwise one user could easily block entire loop. - // For example, user could be asked to trust server key fingerprint and hangs. - go func() { - log.Trace("SSH: Handshaking for %s", conn.RemoteAddr()) - sConn, chans, reqs, err := ssh.NewServerConn(conn, config) - if err != nil { - if err == io.EOF { - log.Warn("SSH: Handshaking was terminated: %v", err) - } else { - log.Error(3, "SSH: Error on handshaking: %v", err) - } - return - } - - log.Trace("SSH: Connection from %s (%s)", sConn.RemoteAddr(), sConn.ClientVersion()) - // The incoming Request channel must be serviced. - go ssh.DiscardRequests(reqs) - go handleServerConn(sConn.Permissions.Extensions["key-id"], chans) - }() + wg := &sync.WaitGroup{} + wg.Add(2) + + if err = cmd.Start(); err != nil { + log.Error(3, "SSH: Start: %v", err) + return + } + + go func() { + defer stdin.Close() + io.Copy(stdin, session) + }() + + go func() { + defer wg.Done() + io.Copy(session, stdout) + }() + + go func() { + defer wg.Done() + io.Copy(session.Stderr(), stderr) + }() + + // Ensure all the output has been written before we wait on the command + // to exit. + wg.Wait() + + // Wait for the command to exit and log any errors we get + err = cmd.Wait() + if err != nil { + log.Error(3, "SSH: Wait: %v", err) + } + + session.Exit(getExitStatusFromError(err)) +} + +func publicKeyHandler(ctx ssh.Context, key ssh.PublicKey) bool { + if ctx.User() != setting.SSH.BuiltinServerUser { + return false } + + pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(gossh.MarshalAuthorizedKey(key)))) + if err != nil { + log.Error(3, "SearchPublicKeyByContent: %v", err) + return false + } + + ctx.SetValue(giteaKeyID, pkey.ID) + + return true } // Listen starts a SSH server listens on given port. func Listen(host string, port int, ciphers []string, keyExchanges []string, macs []string) { - config := &ssh.ServerConfig{ - Config: ssh.Config{ - Ciphers: ciphers, - KeyExchanges: keyExchanges, - MACs: macs, - }, - PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { - pkey, err := models.SearchPublicKeyByContent(strings.TrimSpace(string(ssh.MarshalAuthorizedKey(key)))) - if err != nil { - log.Error(3, "SearchPublicKeyByContent: %v", err) - return nil, err - } - return &ssh.Permissions{Extensions: map[string]string{"key-id": com.ToStr(pkey.ID)}}, nil + // TODO: Handle ciphers, keyExchanges, and macs + + srv := ssh.Server{ + Addr: host + ":" + com.ToStr(port), + PublicKeyHandler: publicKeyHandler, + Handler: sessionHandler, + + // We need to explicitly disable the PtyCallback so text displays + // properly. + PtyCallback: func(ctx ssh.Context, pty ssh.Pty) bool { + return false }, } @@ -180,18 +184,10 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs if err != nil { log.Fatal(4, "Failed to generate private key: %v - %s", err, stderr) } - log.Trace("SSH: New private key is generateed: %s", keyPath) + log.Trace("SSH: New private key is generated: %s", keyPath) } - privateBytes, err := ioutil.ReadFile(keyPath) - if err != nil { - log.Fatal(4, "SSH: Failed to load private key") - } - private, err := ssh.ParsePrivateKey(privateBytes) - if err != nil { - log.Fatal(4, "SSH: Failed to parse private key") - } - config.AddHostKey(private) + srv.SetOption(ssh.HostKeyFile(keyPath)) - go listen(config, host, port) + go srv.ListenAndServe() } From 5d57e580bcc9920127c3706d30d6798d1f299df1 Mon Sep 17 00:00:00 2001 From: Kaleb Elwert Date: Wed, 8 Aug 2018 14:51:22 -0700 Subject: [PATCH 2/4] Add github.com/gliderlabs/ssh to Gopkg.toml Signed-off-by: Kaleb Elwert --- Gopkg.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Gopkg.toml b/Gopkg.toml index 65155cea97e01..cbe4c29fe7701 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -30,6 +30,10 @@ ignored = ["google.golang.org/appengine*"] revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" name = "golang.org/x/net" +[[override]] + name = "github.com/gliderlabs/ssh" + revision = "d3a6756290240931faaf90ec97ddd5d9290623d8" + [[override]] name = "github.com/go-xorm/xorm" #version = "0.6.5" From 5a4eec82db241ea6a57d9e610628eef36becd134 Mon Sep 17 00:00:00 2001 From: Kaleb Elwert Date: Wed, 8 Aug 2018 15:09:29 -0700 Subject: [PATCH 3/4] Add missing vendored packages Signed-off-by: Kaleb Elwert --- Gopkg.lock | 348 ++++++++++++++++++-- vendor/github.com/anmitsu/go-shlex/LICENSE | 20 ++ vendor/github.com/anmitsu/go-shlex/shlex.go | 193 +++++++++++ vendor/github.com/gliderlabs/ssh/LICENSE | 27 ++ vendor/github.com/gliderlabs/ssh/agent.go | 83 +++++ vendor/github.com/gliderlabs/ssh/conn.go | 55 ++++ vendor/github.com/gliderlabs/ssh/context.go | 148 +++++++++ vendor/github.com/gliderlabs/ssh/doc.go | 47 +++ vendor/github.com/gliderlabs/ssh/options.go | 77 +++++ vendor/github.com/gliderlabs/ssh/server.go | 329 ++++++++++++++++++ vendor/github.com/gliderlabs/ssh/session.go | 287 ++++++++++++++++ vendor/github.com/gliderlabs/ssh/ssh.go | 109 ++++++ vendor/github.com/gliderlabs/ssh/tcpip.go | 58 ++++ vendor/github.com/gliderlabs/ssh/util.go | 89 +++++ vendor/github.com/gliderlabs/ssh/wrap.go | 33 ++ 15 files changed, 1884 insertions(+), 19 deletions(-) create mode 100644 vendor/github.com/anmitsu/go-shlex/LICENSE create mode 100644 vendor/github.com/anmitsu/go-shlex/shlex.go create mode 100644 vendor/github.com/gliderlabs/ssh/LICENSE create mode 100644 vendor/github.com/gliderlabs/ssh/agent.go create mode 100644 vendor/github.com/gliderlabs/ssh/conn.go create mode 100644 vendor/github.com/gliderlabs/ssh/context.go create mode 100644 vendor/github.com/gliderlabs/ssh/doc.go create mode 100644 vendor/github.com/gliderlabs/ssh/options.go create mode 100644 vendor/github.com/gliderlabs/ssh/server.go create mode 100644 vendor/github.com/gliderlabs/ssh/session.go create mode 100644 vendor/github.com/gliderlabs/ssh/ssh.go create mode 100644 vendor/github.com/gliderlabs/ssh/tcpip.go create mode 100644 vendor/github.com/gliderlabs/ssh/util.go create mode 100644 vendor/github.com/gliderlabs/ssh/wrap.go diff --git a/Gopkg.lock b/Gopkg.lock index 38573b67187d8..c8563a3e6de6f 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -3,65 +3,94 @@ [[projects]] branch = "master" + digest = "1:42f77a668e3bd06812ef254f334d0d0a62346969fbcd3fa3a613e75067343751" name = "code.gitea.io/git" packages = ["."] + pruneopts = "NUT" revision = "31f4b8e8c805438ac6d8914b38accb1d8aaf695e" [[projects]] branch = "master" + digest = "1:f546da1b2d9e0ac41cec9fc996867108d56b646eb40a0ff04d23719c3cab16c3" name = "code.gitea.io/sdk" packages = ["gitea"] + pruneopts = "NUT" revision = "ec80752c9512cf07fc62ddc42565118183743942" [[projects]] + digest = "1:3fcef06a1a6561955c94af6c7757a6fa37605eb653f0d06ab960e5bb80092195" name = "github.com/PuerkitoBio/goquery" packages = ["."] + pruneopts = "NUT" revision = "ed7d758e9a34ba1f55e8084e0d731448b46921a8" [[projects]] + digest = "1:44121ca443aec4512ceaeec5047d0162b86bfcc26f35eadddda93bc265cb2e39" name = "github.com/RoaringBitmap/roaring" packages = ["."] + pruneopts = "NUT" revision = "1a28a7fa985680f9f4e1644c0a857ec359a444b0" version = "v0.4.7" [[projects]] branch = "master" + digest = "1:93367b6d47a8ccc7d14f9f493ccf103ccf5afb698559ff8e8f1999427ce27ace" name = "github.com/Smerity/govarint" packages = ["."] + pruneopts = "NUT" revision = "7265e41f48f15fd61751e16da866af3c704bb3ab" [[projects]] branch = "master" + digest = "1:d290f4b25abbf574f80f60c8a5603ddada784f13f436b91a9a927bc7ce5a0146" name = "github.com/Unknwon/cae" packages = [ ".", - "zip" + "zip", ] + pruneopts = "NUT" revision = "c6aac99ea2cae2ebaf23f26f76b04fe3fcfc9f8c" [[projects]] branch = "master" + digest = "1:7d23e6e1889b8bb4bbb37a564708fdab4497ce232c3a99d66406c975b642a6ff" name = "github.com/Unknwon/com" packages = ["."] + pruneopts = "NUT" revision = "7677a1d7c1137cd3dd5ba7a076d0c898a1ef4520" [[projects]] branch = "master" + digest = "1:a89bad9aea65873d45195417902ba1e6245114bdf1005d855909a7725b0361d1" name = "github.com/Unknwon/i18n" packages = ["."] + pruneopts = "NUT" revision = "b64d336589669d317928070e70ba0ae558f16633" [[projects]] + digest = "1:d311c4c0cfd13f237665b248a55c909d5b40815c06f28992f35cba3266ef27da" name = "github.com/Unknwon/paginater" packages = ["."] + pruneopts = "NUT" revision = "7748a72e01415173a27d79866b984328e7b0c12b" [[projects]] + digest = "1:fc86904a62ac4bfff8cfbe94f42231ce3d8cea8fe2506d5293eaef468f8eaecf" name = "github.com/andybalholm/cascadia" packages = ["."] + pruneopts = "NUT" revision = "349dd0209470eabd9514242c688c403c0926d266" [[projects]] + branch = "master" + digest = "1:4d3dc52be088499ee1b86a009bf217aa398f3337121584fee4763989862f063b" + name = "github.com/anmitsu/go-shlex" + packages = ["."] + pruneopts = "NUT" + revision = "648efa622239a2f6ff949fed78ee37b48d499ba4" + +[[projects]] + digest = "1:67351095005f164e748a5a21899d1403b03878cb2d40a7b0f742376e6eeda974" name = "github.com/blevesearch/bleve" packages = [ ".", @@ -103,265 +132,361 @@ "search/highlight/highlighter/simple", "search/query", "search/scorer", - "search/searcher" + "search/searcher", ] + pruneopts = "NUT" revision = "ff210fbc6d348ad67aa5754eaea11a463fcddafd" [[projects]] branch = "master" + digest = "1:b165e3f145596f4ff864a34e38e0b63c8e57bbaec2d5a234f234f7554b84c42b" name = "github.com/blevesearch/go-porterstemmer" packages = ["."] + pruneopts = "NUT" revision = "23a2c8e5cf1f380f27722c6d2ae8896431dc7d0e" [[projects]] + digest = "1:a64956a7766e0d081ebff30d8528db107118485d2ee89cab751e6506f5ab55ea" name = "github.com/blevesearch/segment" packages = ["."] + pruneopts = "NUT" revision = "db70c57796cc8c310613541dfade3dce627d09c7" [[projects]] + digest = "1:c7e0968c05659f3973148cd5c5387d6ee960a6ae1b2eaaec0b1d435d806458bb" name = "github.com/boltdb/bolt" packages = ["."] + pruneopts = "NUT" revision = "ccd680d8c1a0179ac3d68f692b01e1a1589cbfc7" source = "github.com/go-gitea/bolt" [[projects]] + digest = "1:7c96cf7bf7f52af67f7a8222185813b9b665f5172ec2ac5f7d49ed96e5fcf3e5" name = "github.com/boombuler/barcode" packages = [ ".", "qr", - "utils" + "utils", ] + pruneopts = "NUT" revision = "fe0f26ff6d26693948ee8189aa064ee8c54141fa" [[projects]] + digest = "1:867be5f6a8d3bf4525390559899af512f48675932e2cc3cbaa2304e9c75fc1ba" name = "github.com/bradfitz/gomemcache" packages = ["memcache"] + pruneopts = "NUT" revision = "fb1f79c6b65acda83063cbc69f6bba1522558bfc" [[projects]] + digest = "1:ce4ec45b1c748a187386bfb3adc6b78f07a3c6cecc689f7bf68dd0d3630c6d58" name = "github.com/chaseadamsio/goorgeous" packages = ["."] + pruneopts = "NUT" revision = "098da33fde5f9220736531b3cb26a2dec86a8367" [[projects]] branch = "master" + digest = "1:82e1ad11d777f7bff9a1fc678a8a534a318f85e5026a8a4d6f4a94a6b0678bb6" name = "github.com/couchbase/vellum" packages = [ ".", "regexp", - "utf8" + "utf8", ] + pruneopts = "NUT" revision = "eb6ae3743b3f300f2136f83ca78c08cc071edbd4" [[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" packages = ["spew"] + pruneopts = "NUT" revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" [[projects]] + digest = "1:08c314ceab5d0a82739bb0846a5d764bed6f1294610e79b651a4c08671830ecb" name = "github.com/denisenkom/go-mssqldb" packages = ["."] + pruneopts = "NUT" revision = "e32ca5036449b7ea12c62ed761ea1ad7fc88a4e2" [[projects]] + digest = "1:01ced7908dbaae42990af9521328922b8948bdcb174c23bba6db572381515716" name = "github.com/dgrijalva/jwt-go" packages = ["."] + pruneopts = "NUT" revision = "9ed569b5d1ac936e6494082958d63a6aa4fff99a" [[projects]] branch = "master" + digest = "1:7e2a28602fd65774c79a780a9c32a7a82d180e5a93bb5a36a6beb8619e24a35b" name = "github.com/edsrzf/mmap-go" packages = ["."] + pruneopts = "NUT" revision = "0bce6a6887123b67a60366d2c9fe2dfb74289d2e" [[projects]] + digest = "1:9b7f3250d2bd2c70fef82ff79a1c43cfacedd114288af3b86da0c31c67756a2f" name = "github.com/elazarl/go-bindata-assetfs" packages = ["."] + pruneopts = "NUT" revision = "57eb5e1fc594ad4b0b1dbea7b286d299e0cb43c2" [[projects]] + digest = "1:8603f74d35c93b37c615a02ba297be2cf2efc9ff6f1ff2b458a903990b568e48" name = "github.com/ethantkoenig/rupture" packages = ["."] + pruneopts = "NUT" revision = "0a76f03a811abcca2e6357329b673e9bb8ef9643" [[projects]] branch = "master" + digest = "1:b29ac3fe6b4601f89b589337c584ab7204a5c72932465e5f4a16772b5560b4a7" name = "github.com/facebookgo/clock" packages = ["."] + pruneopts = "NUT" revision = "600d898af40aa09a7a93ecb9265d87b0504b6f03" [[projects]] + digest = "1:ee3e74e7e3789fc2de1479146477b5cc31f9344dfe2772c01a967a13552c7e60" name = "github.com/facebookgo/grace" packages = [ "gracehttp", - "gracenet" + "gracenet", ] + pruneopts = "NUT" revision = "5729e484473f52048578af1b80d0008c7024089b" [[projects]] branch = "master" + digest = "1:ff3883a000118ba7efd4c41cc4656592e97d3a0860225d5f087a194066685ce4" name = "github.com/facebookgo/httpdown" packages = ["."] + pruneopts = "NUT" revision = "a3b1354551a26449fbe05f5d855937f6e7acbd71" [[projects]] branch = "master" + digest = "1:35a271df55b343e440407daac25d877269e3bb2f60d2031bcc339bcbe6c0f9b9" name = "github.com/facebookgo/stats" packages = ["."] + pruneopts = "NUT" revision = "1b76add642e42c6ffba7211ad7b3939ce654526e" +[[projects]] + digest = "1:e91d7ed26a9fb905bcc11564ca4d893e91dfc593b0b534563540c1cf9e92d470" + name = "github.com/gliderlabs/ssh" + packages = ["."] + pruneopts = "NUT" + revision = "d3a6756290240931faaf90ec97ddd5d9290623d8" + version = "v0.1.1" + [[projects]] branch = "master" + digest = "1:ccee1c7954f7296e324a0832b7a4a0a38ea7e51145375fd837283652366c3653" name = "github.com/glycerine/go-unsnap-stream" packages = ["."] + pruneopts = "NUT" revision = "9f0cb55181dd3a0a4c168d3dbc72d4aca4853126" [[projects]] branch = "master" + digest = "1:1961e20504ac246a7eacff00a56ce75cd60be05ea38e04381663593ba5879f58" name = "github.com/go-macaron/bindata" packages = ["."] + pruneopts = "NUT" revision = "85786f57eee3e5544a9cc24fa2afe425b97a8652" [[projects]] + digest = "1:628de1525ac764bbe390376679e1ee0ea538429c207926eadf66bdeccda1bf89" name = "github.com/go-macaron/binding" packages = ["."] + pruneopts = "NUT" revision = "9440f336b443056c90d7d448a0a55ad8c7599880" [[projects]] branch = "master" + digest = "1:2440f13a7c7b8a7620bf71504c41a5efb6e04a01249d4eba8b9ce00d2575e793" name = "github.com/go-macaron/cache" packages = [ ".", "memcache", - "redis" + "redis", ] + pruneopts = "NUT" revision = "56173531277692bc2925924d51fda1cd0a6b8178" [[projects]] + digest = "1:221be3922b1be8fe9405a73fd030efe0585baaa41fe9828768ac245c6ea53a83" name = "github.com/go-macaron/captcha" packages = ["."] + pruneopts = "NUT" revision = "8aa5919789ab301e865595eb4b1114d6b9847deb" [[projects]] branch = "master" + digest = "1:7ae1d1b3a156d71413b3a35ef6cf2b1fd6f91e6db0b6f1e8a5f69b47a46795d9" name = "github.com/go-macaron/csrf" packages = ["."] + pruneopts = "NUT" revision = "503617c6b37257a55dff6293ec28556506c3a9a8" [[projects]] branch = "master" + digest = "1:6326b27f8e0c8e135c8674ddbc619fae879664ac832e8e6fa6a23ce0d279ed4d" name = "github.com/go-macaron/gzip" packages = ["."] + pruneopts = "NUT" revision = "cad1c6580a07c56f5f6bc52d66002a05985c5854" [[projects]] branch = "master" + digest = "1:d09d3c94e1bd444464c221be961ba4005e119308f1bcd57aa5f49046fb6affd2" name = "github.com/go-macaron/i18n" packages = ["."] + pruneopts = "NUT" revision = "ef57533c3b0fc2d8581deda14937e52f11a203ab" [[projects]] branch = "master" + digest = "1:fb8711b648d1ff03104fc1d9593a13cb1d5120be7ba2b01641c14ccae286a9e3" name = "github.com/go-macaron/inject" packages = ["."] + pruneopts = "NUT" revision = "d8a0b8677191f4380287cfebd08e462217bac7ad" [[projects]] + digest = "1:b327ca585509a889130a8f51f43704a8fe03cb5cd281dbf1bc6405f5a7ea4702" name = "github.com/go-macaron/session" packages = [ ".", - "redis" + "redis", ] + pruneopts = "NUT" revision = "66031fcb37a0fff002a1f028eb0b3a815c78306b" [[projects]] + digest = "1:6fb9cae2a3b4518e048a899b273e23b671802b611ccfcca0b576ecc08bb7b8f5" name = "github.com/go-macaron/toolbox" packages = ["."] + pruneopts = "NUT" revision = "99a42f20e9e88daec5c0d7beb4e7eac134680ab0" [[projects]] + digest = "1:747c1fcb10f8f6734551465ab73c6ed9c551aa6e66250fb6683d1624f554546a" name = "github.com/go-sql-driver/mysql" packages = ["."] + pruneopts = "NUT" revision = "d523deb1b23d913de5bdada721a6071e71283618" [[projects]] + digest = "1:1397763fd29d5667bcfbacde8a37542ee145416b3acb7e1b149c98fef2567930" name = "github.com/go-xorm/builder" packages = ["."] + pruneopts = "NUT" revision = "dc8bf48f58fab2b4da338ffd25191905fd741b8f" version = "v0.3.0" [[projects]] + digest = "1:c910feae32bcc3cbf068c7263424d9f198da931c0cad909179621835e6f87cb8" name = "github.com/go-xorm/core" packages = ["."] + pruneopts = "NUT" revision = "c10e21e7e1cec20e09398f2dfae385e58c8df555" version = "v0.6.0" [[projects]] + digest = "1:22a1ac7f654095f6817076eb975368bab5481e42554d0121ea37e28a86a3f83d" name = "github.com/go-xorm/xorm" packages = ["."] + pruneopts = "NUT" revision = "ad69f7d8f0861a29438154bb0a20b60501298480" [[projects]] branch = "master" + digest = "1:5fdcd55bad5c103674364f4fdb1ad2541186d063300ab24a8f2bb1b31ae07bac" name = "github.com/gogits/chardet" packages = ["."] + pruneopts = "NUT" revision = "2404f777256163ea3eadb273dada5dcb037993c0" [[projects]] + digest = "1:ea030ad2f50b1ced1944914dd80c72e9ebd839fb564c7b8859e7a5d626c61c51" name = "github.com/gogits/cron" packages = ["."] + pruneopts = "NUT" revision = "7f3990acf1833faa5ebd0e86f0a4c72a4b5eba3c" [[projects]] + digest = "1:b64f9be717fdab5f75122dc3868e8ca9d003779b6bc55f64f39a0cddc698bf88" name = "github.com/golang/protobuf" packages = ["proto"] + pruneopts = "NUT" revision = "99511271042a09d1e01baea8781caa5210fec66e" [[projects]] + digest = "1:60e25fc5f5cfd7783f985ca99b4383e848981dddf0be584db7d809be20848e25" name = "github.com/golang/snappy" packages = ["."] + pruneopts = "NUT" revision = "5f1c01d9f64b941dd9582c638279d046eda6ca31" [[projects]] + digest = "1:c01767916c59f084bb7c41a7d5877c0f3099b1595cfa066e84ec6ad6b084dd89" name = "github.com/gorilla/context" packages = ["."] + pruneopts = "NUT" revision = "08b5f424b9271eedf6f9f0ce86cb9396ed337a42" version = "v1.1.1" [[projects]] + digest = "1:9ad9280c98033812aec525d43d4e8137021642dc942bf271b4710f9ac93d0d0b" name = "github.com/gorilla/mux" packages = ["."] + pruneopts = "NUT" revision = "757bef944d0f21880861c2dd9c871ca543023cba" [[projects]] + digest = "1:0faa8131e5822f3420f82426667a29c5c425c3cb7e0fcf0c5d1c718d6e6ba19b" name = "github.com/gorilla/securecookie" packages = ["."] + pruneopts = "NUT" revision = "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983" version = "v1.1.1" [[projects]] + digest = "1:ee5531f1e32c0082a6da68a605bc60284711d99b15d38dfba335a1ec52a58c20" name = "github.com/gorilla/sessions" packages = ["."] + pruneopts = "NUT" revision = "ca9ada44574153444b00d3fd9c8559e4cc95f896" version = "v1.1" [[projects]] + digest = "1:18231f2a4af6633fbcd94baab40320d16e826a7f732094ba3420ab547206949b" name = "github.com/issue9/identicon" packages = ["."] + pruneopts = "NUT" revision = "d36b54562f4cf70c83653e13dc95c220c79ef521" [[projects]] + digest = "1:d2d4f736f81c5312e2b885798f8a33f9819c6217664404d5b9a4088fcd606df4" name = "github.com/jaytaylor/html2text" packages = ["."] + pruneopts = "NUT" revision = "8fb95d837f7d6db1913fecfd7bcc5333e6499596" [[projects]] + digest = "1:6342cf70eaae592f7b8e2552037f2a9d4d16fa321c6e36f09c3bc450add2de19" name = "github.com/kballard/go-shellquote" packages = ["."] + pruneopts = "NUT" revision = "cd60e84ee657ff3dc51de0b4f55dd299a3e136f2" [[projects]] + digest = "1:b32126992771fddadf6a778fe7ab29150665ed78f31ce4eb550a9db3bc0e650c" name = "github.com/keybase/go-crypto" packages = [ "brainpool", @@ -376,55 +501,69 @@ "openpgp/errors", "openpgp/packet", "openpgp/s2k", - "rsa" + "rsa", ] + pruneopts = "NUT" revision = "00ac4db533f63ef97576cbc7b07939ff7daf7329" [[projects]] + digest = "1:c5e37ea5ada80299e6a8e7ebc1a47999c2c8a53eac3a455168adcdcdc78dc93a" name = "github.com/klauspost/compress" packages = [ "flate", - "gzip" + "gzip", ] + pruneopts = "NUT" revision = "8df558b6cb6f9b445f9586446cfe7223e7d8bd6b" version = "v1.1" [[projects]] + digest = "1:6a2949d9a141c72cfaa5b55c65794d9b1179878fccdcae735ab62be27bf9cf34" name = "github.com/klauspost/cpuid" packages = ["."] + pruneopts = "NUT" revision = "09cded8978dc9e80714c4d85b0322337b0a1e5e0" version = "v1.0" [[projects]] + digest = "1:b95da1293525625ef6f07be79d537b9bf2ecd7901efcf9a92193edafbd55b9ef" name = "github.com/klauspost/crc32" packages = ["."] + pruneopts = "NUT" revision = "cb6bfca970f6908083f26f39a79009d608efd5cd" version = "v1.1" [[projects]] + digest = "1:38fc7fe0f78eaecdf1ab39a60ba49df85405597103f38d07f2ac560e7e4c8443" name = "github.com/lafriks/xormstore" packages = [ ".", - "util" + "util", ] + pruneopts = "NUT" revision = "9cab149ea91875cf056211bd6ef82379fce9cb67" version = "v1.0.0" [[projects]] + digest = "1:f1105223bc7ff6a3f3074cf6cccc6c8362ff23db1e7d8505dda01fc4b79b8005" name = "github.com/lib/pq" packages = [ ".", - "oid" + "oid", ] + pruneopts = "NUT" revision = "456514e2defec52e0cd37f90ccf17ec8b28295e2" [[projects]] branch = "master" + digest = "1:6e81c70f78cdc298ea6f19fa9bdba95aacd13a0e0ddb7e118046113997d4dafb" name = "github.com/lunny/dingtalk_webhook" packages = ["."] + pruneopts = "NUT" revision = "e3534c89ef969912856dfa39e56b09e58c5f5daf" [[projects]] + digest = "1:fb22af9d8c1a6166ad299705648db460ba2c28a830f7f6cdd830019d7c3fd96f" name = "github.com/markbates/goth" packages = [ ".", @@ -436,131 +575,175 @@ "providers/gitlab", "providers/gplus", "providers/openidConnect", - "providers/twitter" + "providers/twitter", ] + pruneopts = "NUT" revision = "4933f155d89c3c52ab4ca545c6602cf4a1e87913" version = "1.45.5" [[projects]] + digest = "1:3ef954101983406a71171c4dc816a73e01bb3de608b3dd063627aa67a459f3e3" name = "github.com/mattn/go-sqlite3" packages = ["."] + pruneopts = "NUT" revision = "acfa60124032040b9f5a9406f5a772ee16fe845e" [[projects]] branch = "master" + digest = "1:1ed0f3c066eb9d1c2ff7a864a6fa595c70b9b49049cc46af6a6f7ff0e4655321" name = "github.com/mcuadros/go-version" packages = ["."] + pruneopts = "NUT" revision = "88e56e02bea1c203c99222c365fa52a69996ccac" [[projects]] + digest = "1:f2111ff3f139dacf8b2177e32f296ad20df85d6ec0ee0e2e348f0fcb21186c14" name = "github.com/microcosm-cc/bluemonday" packages = ["."] + pruneopts = "NUT" revision = "f77f16ffc87a6a58814e64ae72d55f9c41374e6d" [[projects]] + digest = "1:c7dc71a7e144df03332152d730f9c5ae22cf1cfd55454cb001ba8ffcb78aa7f0" name = "github.com/mrjones/oauth" packages = ["."] + pruneopts = "NUT" revision = "3f67d9c274355678b2f9844b08d643e2f9213340" [[projects]] branch = "master" + digest = "1:874d20d479aa479b2ed0200ac565e809d8ff849f672a04e7a21536069eba1cdd" name = "github.com/mschoch/smat" packages = ["."] + pruneopts = "NUT" revision = "90eadee771aeab36e8bf796039b8c261bebebe4f" [[projects]] + digest = "1:15bc31ae39f989dda613930c1de860aced7b922502f8d9d861998fc195b7937c" name = "github.com/msteinert/pam" packages = ["."] + pruneopts = "NUT" revision = "02ccfbfaf0cc627aa3aec8ef7ed5cfeec5b43f63" [[projects]] + digest = "1:2be1d891535ce3d6d2a3db9087f07415e909744e9eff1a30f8f0b2519df60ae6" name = "github.com/nfnt/resize" packages = ["."] + pruneopts = "NUT" revision = "891127d8d1b52734debe1b3c3d7e747502b6c366" [[projects]] + digest = "1:44c66ad69563dbe3f8e76d7d6cad21a03626e53f1875b5ab163ded419e01ca7a" name = "github.com/philhofer/fwd" packages = ["."] + pruneopts = "NUT" revision = "bb6d471dc95d4fe11e432687f8b70ff496cf3136" version = "v1.0.0" [[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" name = "github.com/pmezard/go-difflib" packages = ["difflib"] + pruneopts = "NUT" revision = "792786c7400a136282c1664665ae0a8db921c6c2" version = "v1.0.0" [[projects]] + digest = "1:aabb118251a9ee8ddab44c405f2391852a82dcf33358cda25ea5748561411ffc" name = "github.com/pquerna/otp" packages = [ ".", "hotp", - "totp" + "totp", ] + pruneopts = "NUT" revision = "54653902c20e47f3417541d35435cb6d6162e28a" [[projects]] branch = "master" + digest = "1:5be01c22bc1040e2f6ce4755d51a0ac9cef823a9f2004fb1f9896a414ef519e6" name = "github.com/russross/blackfriday" packages = ["."] + pruneopts = "NUT" revision = "11635eb403ff09dbc3a6b5a007ab5ab09151c229" [[projects]] + digest = "1:54f275b550b8a8a20bc91e2067ed740600b69dae9dcf9cdfe905bac1b9cfd1d0" name = "github.com/satori/go.uuid" packages = ["."] + pruneopts = "NUT" revision = "b061729afc07e77a8aa4fad0a2fd840958f1942a" [[projects]] + digest = "1:3e0777ad56c86a93cf22459acca84d5c3c8a86b163362074dcb7b33c01aba82b" name = "github.com/sergi/go-diff" packages = ["diffmatchpatch"] + pruneopts = "NUT" revision = "552b4e9bbdca9e5adafd95ee98c822fdd11b330b" [[projects]] + digest = "1:400359f0b394fb168f4aee9621d42cc005810c6e462009d5fc76055d5e96dcf3" name = "github.com/shurcooL/sanitized_anchor_name" packages = ["."] + pruneopts = "NUT" revision = "1dba4b3954bc059efc3991ec364f9f9a35f597d2" [[projects]] branch = "master" + digest = "1:69177343ca227319b4580441a67d9d889e9ac7fcbfb89fbaa36d3283e6ab0139" name = "github.com/steveyen/gtreap" packages = ["."] + pruneopts = "NUT" revision = "0abe01ef9be25c4aedc174758ec2d917314d6d70" [[projects]] + digest = "1:a852b1ad03ca063d2c57866d9f94dcb1cb2e111415c5902ce0586fc2d207221b" name = "github.com/stretchr/testify" packages = ["assert"] + pruneopts = "NUT" revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" version = "v1.2.1" [[projects]] branch = "master" + digest = "1:3cb6dfe7cdece5716b1c3c3c0b5faf7fce2e83e2758e2baad2e9986d101980b8" name = "github.com/tinylib/msgp" packages = ["msgp"] + pruneopts = "NUT" revision = "c8cf64dff2009d53fa8f8a16df54d1cdfc64c4a7" [[projects]] branch = "master" + digest = "1:aa8ce6454cff67af88a195977ba6489df6c55802e1199cbe24af9febec8c1586" name = "github.com/tstranex/u2f" packages = ["."] + pruneopts = "NUT" revision = "d21a03e0b1d9fc1df59ff54e7a513655c1748b0c" [[projects]] + digest = "1:d9f3ecc1fb41eaf8af777f844a2c0eb9b7ff743224981e5af4ade023210cf6e5" name = "github.com/urfave/cli" packages = ["."] + pruneopts = "NUT" revision = "d86a009f5e13f83df65d0d6cee9a2e3f1445f0da" [[projects]] branch = "master" + digest = "1:1d786f876a94d808e0ee5052e1f65ab284dc7483a7517d76d25baa8099429ed5" name = "github.com/willf/bitset" packages = ["."] + pruneopts = "NUT" revision = "8ce1146b8621c95164efd9c8b1124cfa9b8afb4e" [[projects]] + digest = "1:27d050258a4b19ca3b7a1bf26f4a04c5c66bbf0670b346ee509ebb0ad82257a6" name = "github.com/yohcop/openid-go" packages = ["."] + pruneopts = "NUT" revision = "2c050d2dae5345c417db301f11fda6fbf5ad0f0a" [[projects]] + digest = "1:3d28799e447a7cddae2583cc3c2225ea6a6aa28973dbe7a42feca37e08b268de" name = "golang.org/x/crypto" packages = [ "curve25519", @@ -568,43 +751,53 @@ "ed25519/internal/edwards25519", "md4", "pbkdf2", - "ssh" + "ssh", ] + pruneopts = "NUT" revision = "9f005a07e0d31d45e6656d241bb5c0f2efd4bc94" [[projects]] + digest = "1:47ea747d07fae720d749d06ac5dc5ded0df70c57e328b6549cf2d9c64698757e" name = "golang.org/x/net" packages = [ "context", "html", "html/atom", - "html/charset" + "html/charset", ] + pruneopts = "NUT" revision = "f2499483f923065a842d38eb4c7f1927e6fc6e6d" [[projects]] + digest = "1:8159a9cda4b8810aaaeb0d60e2fa68e2fd86d8af4ec8f5059830839e3c8d93d5" name = "golang.org/x/oauth2" packages = [ ".", - "internal" + "internal", ] + pruneopts = "NUT" revision = "c10ba270aa0bf8b8c1c986e103859c67a9103061" [[projects]] + digest = "1:9f303486d623f840492bfeb48eb906a94e9d3fe638a761639b72ce64bf7bfcc3" name = "golang.org/x/sync" packages = ["syncmap"] + pruneopts = "NUT" revision = "5a06fca2c336a4b2b2fcb45702e8c47621b2aa2c" [[projects]] + digest = "1:47495898c42062ace16d8046915b088e7c3846e77b997ec5a8a00ec552e179d5" name = "golang.org/x/sys" packages = [ "unix", "windows", - "windows/svc" + "windows/svc", ] + pruneopts = "NUT" revision = "a646d33e2ee3172a661fc09bca23bb4889a41bc8" [[projects]] + digest = "1:d2463fd72ee2636c3d9bbdb98fa4996a118f210ae3e5eaf62d281855bc0b4a83" name = "golang.org/x/text" packages = [ "encoding", @@ -626,83 +819,200 @@ "runes", "transform", "unicode/cldr", - "unicode/norm" + "unicode/norm", ] + pruneopts = "NUT" revision = "2bf8f2a19ec09c670e931282edfe6567f6be21c9" [[projects]] branch = "v3" + digest = "1:1244a9b3856f70d5ffb74bbfd780fc9d47f93f2049fa265c6fb602878f507bf8" name = "gopkg.in/alexcesaro/quotedprintable.v3" packages = ["."] + pruneopts = "NUT" revision = "2caba252f4dc53eaf6b553000885530023f54623" [[projects]] + digest = "1:9c577b4736db46066c2a41aef5736316fc5e0c8d1b97e8917f4b6157b530ee53" name = "gopkg.in/asn1-ber.v1" packages = ["."] + pruneopts = "NUT" revision = "4e86f4367175e39f69d9358a5f17b4dda270378d" version = "v1.1" [[projects]] + digest = "1:24bfc2e8bf971485cb5ba0f0e5b08a1b806cca5828134df76b32d1ea50f2ab49" name = "gopkg.in/bufio.v1" packages = ["."] + pruneopts = "NUT" revision = "567b2bfa514e796916c4747494d6ff5132a1dfce" version = "v1" [[projects]] + digest = "1:9146e4250cb4359ac2f7a1ac3766dbcc52b3d8414ab9ac1af195ce6fba6c234f" name = "gopkg.in/editorconfig/editorconfig-core-go.v1" packages = ["."] + pruneopts = "NUT" revision = "a872f05c2e34b37b567401384d202aff11ba06d4" version = "v1.2.0" [[projects]] branch = "v2" + digest = "1:8db3ee13f824cd299f10af1dead41de8bce6b53a704998976e58c5b9274ab4b6" name = "gopkg.in/gomail.v2" packages = ["."] + pruneopts = "NUT" revision = "81ebce5c23dfd25c6c67194b37d3dd3f338c98b1" [[projects]] + digest = "1:cbecb8d0e314f34d1bc65a86cb433e33ad8e32c2cb6ae313a4f2e52806ddd984" name = "gopkg.in/ini.v1" packages = ["."] + pruneopts = "NUT" revision = "7e7da451323b6766da368f8a1e8ec9a88a16b4a0" version = "v1.31.1" [[projects]] + digest = "1:01f4ac37c52bda6f7e1bd73680a99f88733c0408aaa159ecb1ba53a1ade9423c" name = "gopkg.in/ldap.v2" packages = ["."] + pruneopts = "NUT" revision = "d0a5ced67b4dc310b9158d63a2c6f9c5ec13f105" version = "v2.4.1" [[projects]] + digest = "1:cfe1730a152ff033ad7d9c115d22e36b19eec6d5928c06146b9119be45d39dc0" name = "gopkg.in/macaron.v1" packages = ["."] + pruneopts = "NUT" revision = "75f2e9b42e99652f0d82b28ccb73648f44615faa" version = "v1.2.4" [[projects]] + digest = "1:00126f697efdcab42f07c89ac8bf0095fb2328aef6464e070055154088cea859" name = "gopkg.in/redis.v2" packages = ["."] + pruneopts = "NUT" revision = "e6179049628164864e6e84e973cfb56335748dea" version = "v2.3.2" [[projects]] + digest = "1:e2144032dcf8e856fb733151391669dc2d32d79ebb69e3c4151efa605f5e8a01" name = "gopkg.in/testfixtures.v2" packages = ["."] + pruneopts = "NUT" revision = "b9ef14dc461bf934d8df2dfc6f1f456be5664cca" version = "v2.0.0" [[projects]] + digest = "1:ad6f94355d292690137613735965bd3688844880fdab90eccf66321910344942" name = "gopkg.in/yaml.v2" packages = ["."] + pruneopts = "NUT" revision = "a5b47d31c556af34a302ce5d659e6fea44d90de0" [[projects]] + digest = "1:5972c0a5308529a35d87dd2dbba827625df90e6c89b0f496644fc43ea761bcf2" name = "strk.kbt.io/projects/go/libravatar" packages = ["."] + pruneopts = "NUT" revision = "5eed7bff870ae19ef51c5773dbc8f3e9fcbd0982" [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "5ae18d543bbb8186589c003422b333097d67bb5fed8b4c294be70c012ccffc94" + input-imports = [ + "code.gitea.io/git", + "code.gitea.io/sdk/gitea", + "github.com/PuerkitoBio/goquery", + "github.com/Unknwon/cae/zip", + "github.com/Unknwon/com", + "github.com/Unknwon/i18n", + "github.com/Unknwon/paginater", + "github.com/blevesearch/bleve", + "github.com/blevesearch/bleve/analysis/analyzer/custom", + "github.com/blevesearch/bleve/analysis/token/camelcase", + "github.com/blevesearch/bleve/analysis/token/lowercase", + "github.com/blevesearch/bleve/analysis/token/unicodenorm", + "github.com/blevesearch/bleve/analysis/token/unique", + "github.com/blevesearch/bleve/analysis/tokenizer/unicode", + "github.com/blevesearch/bleve/index/upsidedown", + "github.com/blevesearch/bleve/mapping", + "github.com/blevesearch/bleve/search/query", + "github.com/chaseadamsio/goorgeous", + "github.com/denisenkom/go-mssqldb", + "github.com/dgrijalva/jwt-go", + "github.com/ethantkoenig/rupture", + "github.com/facebookgo/grace/gracehttp", + "github.com/gliderlabs/ssh", + "github.com/go-macaron/bindata", + "github.com/go-macaron/binding", + "github.com/go-macaron/cache", + "github.com/go-macaron/cache/memcache", + "github.com/go-macaron/cache/redis", + "github.com/go-macaron/captcha", + "github.com/go-macaron/csrf", + "github.com/go-macaron/gzip", + "github.com/go-macaron/i18n", + "github.com/go-macaron/inject", + "github.com/go-macaron/session", + "github.com/go-macaron/session/redis", + "github.com/go-macaron/toolbox", + "github.com/go-sql-driver/mysql", + "github.com/go-xorm/builder", + "github.com/go-xorm/core", + "github.com/go-xorm/xorm", + "github.com/gogits/chardet", + "github.com/gogits/cron", + "github.com/gorilla/context", + "github.com/issue9/identicon", + "github.com/jaytaylor/html2text", + "github.com/kballard/go-shellquote", + "github.com/keybase/go-crypto/openpgp", + "github.com/keybase/go-crypto/openpgp/armor", + "github.com/keybase/go-crypto/openpgp/packet", + "github.com/lafriks/xormstore", + "github.com/lib/pq", + "github.com/lunny/dingtalk_webhook", + "github.com/markbates/goth", + "github.com/markbates/goth/gothic", + "github.com/markbates/goth/providers/bitbucket", + "github.com/markbates/goth/providers/dropbox", + "github.com/markbates/goth/providers/facebook", + "github.com/markbates/goth/providers/github", + "github.com/markbates/goth/providers/gitlab", + "github.com/markbates/goth/providers/gplus", + "github.com/markbates/goth/providers/openidConnect", + "github.com/markbates/goth/providers/twitter", + "github.com/mattn/go-sqlite3", + "github.com/mcuadros/go-version", + "github.com/microcosm-cc/bluemonday", + "github.com/msteinert/pam", + "github.com/nfnt/resize", + "github.com/pquerna/otp", + "github.com/pquerna/otp/totp", + "github.com/russross/blackfriday", + "github.com/satori/go.uuid", + "github.com/sergi/go-diff/diffmatchpatch", + "github.com/stretchr/testify/assert", + "github.com/tstranex/u2f", + "github.com/urfave/cli", + "github.com/yohcop/openid-go", + "golang.org/x/crypto/pbkdf2", + "golang.org/x/crypto/ssh", + "golang.org/x/net/html", + "golang.org/x/net/html/atom", + "golang.org/x/net/html/charset", + "golang.org/x/sync/syncmap", + "golang.org/x/sys/windows/svc", + "golang.org/x/text/transform", + "gopkg.in/editorconfig/editorconfig-core-go.v1", + "gopkg.in/gomail.v2", + "gopkg.in/ini.v1", + "gopkg.in/ldap.v2", + "gopkg.in/macaron.v1", + "gopkg.in/testfixtures.v2", + "strk.kbt.io/projects/go/libravatar", + ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/vendor/github.com/anmitsu/go-shlex/LICENSE b/vendor/github.com/anmitsu/go-shlex/LICENSE new file mode 100644 index 0000000000000..4a17268ac0d93 --- /dev/null +++ b/vendor/github.com/anmitsu/go-shlex/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) anmitsu + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/anmitsu/go-shlex/shlex.go b/vendor/github.com/anmitsu/go-shlex/shlex.go new file mode 100644 index 0000000000000..e742c38afc38c --- /dev/null +++ b/vendor/github.com/anmitsu/go-shlex/shlex.go @@ -0,0 +1,193 @@ +// Package shlex provides a simple lexical analysis like Unix shell. +package shlex + +import ( + "bufio" + "errors" + "io" + "strings" + "unicode" +) + +var ( + ErrNoClosing = errors.New("No closing quotation") + ErrNoEscaped = errors.New("No escaped character") +) + +// Tokenizer is the interface that classifies a token according to +// words, whitespaces, quotations, escapes and escaped quotations. +type Tokenizer interface { + IsWord(rune) bool + IsWhitespace(rune) bool + IsQuote(rune) bool + IsEscape(rune) bool + IsEscapedQuote(rune) bool +} + +// DefaultTokenizer implements a simple tokenizer like Unix shell. +type DefaultTokenizer struct{} + +func (t *DefaultTokenizer) IsWord(r rune) bool { + return r == '_' || unicode.IsLetter(r) || unicode.IsNumber(r) +} +func (t *DefaultTokenizer) IsQuote(r rune) bool { + switch r { + case '\'', '"': + return true + default: + return false + } +} +func (t *DefaultTokenizer) IsWhitespace(r rune) bool { + return unicode.IsSpace(r) +} +func (t *DefaultTokenizer) IsEscape(r rune) bool { + return r == '\\' +} +func (t *DefaultTokenizer) IsEscapedQuote(r rune) bool { + return r == '"' +} + +// Lexer represents a lexical analyzer. +type Lexer struct { + reader *bufio.Reader + tokenizer Tokenizer + posix bool + whitespacesplit bool +} + +// NewLexer creates a new Lexer reading from io.Reader. This Lexer +// has a DefaultTokenizer according to posix and whitespacesplit +// rules. +func NewLexer(r io.Reader, posix, whitespacesplit bool) *Lexer { + return &Lexer{ + reader: bufio.NewReader(r), + tokenizer: &DefaultTokenizer{}, + posix: posix, + whitespacesplit: whitespacesplit, + } +} + +// NewLexerString creates a new Lexer reading from a string. This +// Lexer has a DefaultTokenizer according to posix and whitespacesplit +// rules. +func NewLexerString(s string, posix, whitespacesplit bool) *Lexer { + return NewLexer(strings.NewReader(s), posix, whitespacesplit) +} + +// Split splits a string according to posix or non-posix rules. +func Split(s string, posix bool) ([]string, error) { + return NewLexerString(s, posix, true).Split() +} + +// SetTokenizer sets a Tokenizer. +func (l *Lexer) SetTokenizer(t Tokenizer) { + l.tokenizer = t +} + +func (l *Lexer) Split() ([]string, error) { + result := make([]string, 0) + for { + token, err := l.readToken() + if token != "" { + result = append(result, token) + } + + if err == io.EOF { + break + } else if err != nil { + return result, err + } + } + return result, nil +} + +func (l *Lexer) readToken() (string, error) { + t := l.tokenizer + token := "" + quoted := false + state := ' ' + escapedstate := ' ' +scanning: + for { + next, _, err := l.reader.ReadRune() + if err != nil { + if t.IsQuote(state) { + return token, ErrNoClosing + } else if t.IsEscape(state) { + return token, ErrNoEscaped + } + return token, err + } + + switch { + case t.IsWhitespace(state): + switch { + case t.IsWhitespace(next): + break scanning + case l.posix && t.IsEscape(next): + escapedstate = 'a' + state = next + case t.IsWord(next): + token += string(next) + state = 'a' + case t.IsQuote(next): + if !l.posix { + token += string(next) + } + state = next + default: + token = string(next) + if l.whitespacesplit { + state = 'a' + } else if token != "" || (l.posix && quoted) { + break scanning + } + } + case t.IsQuote(state): + quoted = true + switch { + case next == state: + if !l.posix { + token += string(next) + break scanning + } else { + state = 'a' + } + case l.posix && t.IsEscape(next) && t.IsEscapedQuote(state): + escapedstate = state + state = next + default: + token += string(next) + } + case t.IsEscape(state): + if t.IsQuote(escapedstate) && next != state && next != escapedstate { + token += string(state) + } + token += string(next) + state = escapedstate + case t.IsWord(state): + switch { + case t.IsWhitespace(next): + if token != "" || (l.posix && quoted) { + break scanning + } + case l.posix && t.IsQuote(next): + state = next + case l.posix && t.IsEscape(next): + escapedstate = 'a' + state = next + case t.IsWord(next) || t.IsQuote(next): + token += string(next) + default: + if l.whitespacesplit { + token += string(next) + } else if token != "" { + l.reader.UnreadRune() + break scanning + } + } + } + } + return token, nil +} diff --git a/vendor/github.com/gliderlabs/ssh/LICENSE b/vendor/github.com/gliderlabs/ssh/LICENSE new file mode 100644 index 0000000000000..4a03f02a28185 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2016 Glider Labs. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Glider Labs nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/gliderlabs/ssh/agent.go b/vendor/github.com/gliderlabs/ssh/agent.go new file mode 100644 index 0000000000000..d8dcb9a0a4a36 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/agent.go @@ -0,0 +1,83 @@ +package ssh + +import ( + "io" + "io/ioutil" + "net" + "path" + "sync" + + gossh "golang.org/x/crypto/ssh" +) + +const ( + agentRequestType = "auth-agent-req@openssh.com" + agentChannelType = "auth-agent@openssh.com" + + agentTempDir = "auth-agent" + agentListenFile = "listener.sock" +) + +// contextKeyAgentRequest is an internal context key for storing if the +// client requested agent forwarding +var contextKeyAgentRequest = &contextKey{"auth-agent-req"} + +// SetAgentRequested sets up the session context so that AgentRequested +// returns true. +func SetAgentRequested(ctx Context) { + ctx.SetValue(contextKeyAgentRequest, true) +} + +// AgentRequested returns true if the client requested agent forwarding. +func AgentRequested(sess Session) bool { + return sess.Context().Value(contextKeyAgentRequest) == true +} + +// NewAgentListener sets up a temporary Unix socket that can be communicated +// to the session environment and used for forwarding connections. +func NewAgentListener() (net.Listener, error) { + dir, err := ioutil.TempDir("", agentTempDir) + if err != nil { + return nil, err + } + l, err := net.Listen("unix", path.Join(dir, agentListenFile)) + if err != nil { + return nil, err + } + return l, nil +} + +// ForwardAgentConnections takes connections from a listener to proxy into the +// session on the OpenSSH channel for agent connections. It blocks and services +// connections until the listener stop accepting. +func ForwardAgentConnections(l net.Listener, s Session) { + sshConn := s.Context().Value(ContextKeyConn).(gossh.Conn) + for { + conn, err := l.Accept() + if err != nil { + return + } + go func(conn net.Conn) { + defer conn.Close() + channel, reqs, err := sshConn.OpenChannel(agentChannelType, nil) + if err != nil { + return + } + defer channel.Close() + go gossh.DiscardRequests(reqs) + var wg sync.WaitGroup + wg.Add(2) + go func() { + io.Copy(conn, channel) + conn.(*net.UnixConn).CloseWrite() + wg.Done() + }() + go func() { + io.Copy(channel, conn) + channel.CloseWrite() + wg.Done() + }() + wg.Wait() + }(conn) + } +} diff --git a/vendor/github.com/gliderlabs/ssh/conn.go b/vendor/github.com/gliderlabs/ssh/conn.go new file mode 100644 index 0000000000000..f8f6057250458 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/conn.go @@ -0,0 +1,55 @@ +package ssh + +import ( + "context" + "net" + "time" +) + +type serverConn struct { + net.Conn + + idleTimeout time.Duration + maxDeadline time.Time + closeCanceler context.CancelFunc +} + +func (c *serverConn) Write(p []byte) (n int, err error) { + c.updateDeadline() + n, err = c.Conn.Write(p) + if _, isNetErr := err.(net.Error); isNetErr && c.closeCanceler != nil { + c.closeCanceler() + } + return +} + +func (c *serverConn) Read(b []byte) (n int, err error) { + c.updateDeadline() + n, err = c.Conn.Read(b) + if _, isNetErr := err.(net.Error); isNetErr && c.closeCanceler != nil { + c.closeCanceler() + } + return +} + +func (c *serverConn) Close() (err error) { + err = c.Conn.Close() + if c.closeCanceler != nil { + c.closeCanceler() + } + return +} + +func (c *serverConn) updateDeadline() { + switch { + case c.idleTimeout > 0: + idleDeadline := time.Now().Add(c.idleTimeout) + if idleDeadline.Unix() < c.maxDeadline.Unix() { + c.Conn.SetDeadline(idleDeadline) + return + } + fallthrough + default: + c.Conn.SetDeadline(c.maxDeadline) + } +} diff --git a/vendor/github.com/gliderlabs/ssh/context.go b/vendor/github.com/gliderlabs/ssh/context.go new file mode 100644 index 0000000000000..008ab5b0d98e2 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/context.go @@ -0,0 +1,148 @@ +package ssh + +import ( + "context" + "encoding/hex" + "net" + + gossh "golang.org/x/crypto/ssh" +) + +// contextKey is a value for use with context.WithValue. It's used as +// a pointer so it fits in an interface{} without allocation. +type contextKey struct { + name string +} + +var ( + // ContextKeyUser is a context key for use with Contexts in this package. + // The associated value will be of type string. + ContextKeyUser = &contextKey{"user"} + + // ContextKeySessionID is a context key for use with Contexts in this package. + // The associated value will be of type string. + ContextKeySessionID = &contextKey{"session-id"} + + // ContextKeyPermissions is a context key for use with Contexts in this package. + // The associated value will be of type *Permissions. + ContextKeyPermissions = &contextKey{"permissions"} + + // ContextKeyClientVersion is a context key for use with Contexts in this package. + // The associated value will be of type string. + ContextKeyClientVersion = &contextKey{"client-version"} + + // ContextKeyServerVersion is a context key for use with Contexts in this package. + // The associated value will be of type string. + ContextKeyServerVersion = &contextKey{"server-version"} + + // ContextKeyLocalAddr is a context key for use with Contexts in this package. + // The associated value will be of type net.Addr. + ContextKeyLocalAddr = &contextKey{"local-addr"} + + // ContextKeyRemoteAddr is a context key for use with Contexts in this package. + // The associated value will be of type net.Addr. + ContextKeyRemoteAddr = &contextKey{"remote-addr"} + + // ContextKeyServer is a context key for use with Contexts in this package. + // The associated value will be of type *Server. + ContextKeyServer = &contextKey{"ssh-server"} + + // ContextKeyConn is a context key for use with Contexts in this package. + // The associated value will be of type gossh.Conn. + ContextKeyConn = &contextKey{"ssh-conn"} + + // ContextKeyPublicKey is a context key for use with Contexts in this package. + // The associated value will be of type PublicKey. + ContextKeyPublicKey = &contextKey{"public-key"} +) + +// Context is a package specific context interface. It exposes connection +// metadata and allows new values to be easily written to it. It's used in +// authentication handlers and callbacks, and its underlying context.Context is +// exposed on Session in the session Handler. +type Context interface { + context.Context + + // User returns the username used when establishing the SSH connection. + User() string + + // SessionID returns the session hash. + SessionID() string + + // ClientVersion returns the version reported by the client. + ClientVersion() string + + // ServerVersion returns the version reported by the server. + ServerVersion() string + + // RemoteAddr returns the remote address for this connection. + RemoteAddr() net.Addr + + // LocalAddr returns the local address for this connection. + LocalAddr() net.Addr + + // Permissions returns the Permissions object used for this connection. + Permissions() *Permissions + + // SetValue allows you to easily write new values into the underlying context. + SetValue(key, value interface{}) +} + +type sshContext struct { + context.Context +} + +func newContext(srv *Server) (*sshContext, context.CancelFunc) { + innerCtx, cancel := context.WithCancel(context.Background()) + ctx := &sshContext{innerCtx} + ctx.SetValue(ContextKeyServer, srv) + perms := &Permissions{&gossh.Permissions{}} + ctx.SetValue(ContextKeyPermissions, perms) + return ctx, cancel +} + +// this is separate from newContext because we will get ConnMetadata +// at different points so it needs to be applied separately +func applyConnMetadata(ctx Context, conn gossh.ConnMetadata) { + if ctx.Value(ContextKeySessionID) != nil { + return + } + ctx.SetValue(ContextKeySessionID, hex.EncodeToString(conn.SessionID())) + ctx.SetValue(ContextKeyClientVersion, string(conn.ClientVersion())) + ctx.SetValue(ContextKeyServerVersion, string(conn.ServerVersion())) + ctx.SetValue(ContextKeyUser, conn.User()) + ctx.SetValue(ContextKeyLocalAddr, conn.LocalAddr()) + ctx.SetValue(ContextKeyRemoteAddr, conn.RemoteAddr()) +} + +func (ctx *sshContext) SetValue(key, value interface{}) { + ctx.Context = context.WithValue(ctx.Context, key, value) +} + +func (ctx *sshContext) User() string { + return ctx.Value(ContextKeyUser).(string) +} + +func (ctx *sshContext) SessionID() string { + return ctx.Value(ContextKeySessionID).(string) +} + +func (ctx *sshContext) ClientVersion() string { + return ctx.Value(ContextKeyClientVersion).(string) +} + +func (ctx *sshContext) ServerVersion() string { + return ctx.Value(ContextKeyServerVersion).(string) +} + +func (ctx *sshContext) RemoteAddr() net.Addr { + return ctx.Value(ContextKeyRemoteAddr).(net.Addr) +} + +func (ctx *sshContext) LocalAddr() net.Addr { + return ctx.Value(ContextKeyLocalAddr).(net.Addr) +} + +func (ctx *sshContext) Permissions() *Permissions { + return ctx.Value(ContextKeyPermissions).(*Permissions) +} diff --git a/vendor/github.com/gliderlabs/ssh/doc.go b/vendor/github.com/gliderlabs/ssh/doc.go new file mode 100644 index 0000000000000..0361fb93bda10 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/doc.go @@ -0,0 +1,47 @@ +/* + +Package ssh wraps the crypto/ssh package with a higher-level API for building +SSH servers. The goal of the API was to make it as simple as using net/http, so +the API is very similar. + +You should be able to build any SSH server using only this package, which wraps +relevant types and some functions from crypto/ssh. However, you still need to +use crypto/ssh for building SSH clients. + +ListenAndServe starts an SSH server with a given address, handler, and options. The +handler is usually nil, which means to use DefaultHandler. Handle sets DefaultHandler: + + ssh.Handle(func(s ssh.Session) { + io.WriteString(s, "Hello world\n") + }) + + log.Fatal(ssh.ListenAndServe(":2222", nil)) + +If you don't specify a host key, it will generate one every time. This is convenient +except you'll have to deal with clients being confused that the host key is different. +It's a better idea to generate or point to an existing key on your system: + + log.Fatal(ssh.ListenAndServe(":2222", nil, ssh.HostKeyFile("/Users/progrium/.ssh/id_rsa"))) + +Although all options have functional option helpers, another way to control the +server's behavior is by creating a custom Server: + + s := &ssh.Server{ + Addr: ":2222", + Handler: sessionHandler, + PublicKeyHandler: authHandler, + } + s.AddHostKey(hostKeySigner) + + log.Fatal(s.ListenAndServe()) + +This package automatically handles basic SSH requests like setting environment +variables, requesting PTY, and changing window size. These requests are +processed, responded to, and any relevant state is updated. This state is then +exposed to you via the Session interface. + +The one big feature missing from the Session abstraction is signals. This was +started, but not completed. Pull Requests welcome! + +*/ +package ssh diff --git a/vendor/github.com/gliderlabs/ssh/options.go b/vendor/github.com/gliderlabs/ssh/options.go new file mode 100644 index 0000000000000..af748985beac8 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/options.go @@ -0,0 +1,77 @@ +package ssh + +import ( + "io/ioutil" + + gossh "golang.org/x/crypto/ssh" +) + +// PasswordAuth returns a functional option that sets PasswordHandler on the server. +func PasswordAuth(fn PasswordHandler) Option { + return func(srv *Server) error { + srv.PasswordHandler = fn + return nil + } +} + +// PublicKeyAuth returns a functional option that sets PublicKeyHandler on the server. +func PublicKeyAuth(fn PublicKeyHandler) Option { + return func(srv *Server) error { + srv.PublicKeyHandler = fn + return nil + } +} + +// HostKeyFile returns a functional option that adds HostSigners to the server +// from a PEM file at filepath. +func HostKeyFile(filepath string) Option { + return func(srv *Server) error { + pemBytes, err := ioutil.ReadFile(filepath) + if err != nil { + return err + } + + signer, err := gossh.ParsePrivateKey(pemBytes) + if err != nil { + return err + } + + srv.AddHostKey(signer) + + return nil + } +} + +// HostKeyPEM returns a functional option that adds HostSigners to the server +// from a PEM file as bytes. +func HostKeyPEM(bytes []byte) Option { + return func(srv *Server) error { + signer, err := gossh.ParsePrivateKey(bytes) + if err != nil { + return err + } + + srv.AddHostKey(signer) + + return nil + } +} + +// NoPty returns a functional option that sets PtyCallback to return false, +// denying PTY requests. +func NoPty() Option { + return func(srv *Server) error { + srv.PtyCallback = func(ctx Context, pty Pty) bool { + return false + } + return nil + } +} + +// WrapConn returns a functional option that sets ConnCallback on the server. +func WrapConn(fn ConnCallback) Option { + return func(srv *Server) error { + srv.ConnCallback = fn + return nil + } +} diff --git a/vendor/github.com/gliderlabs/ssh/server.go b/vendor/github.com/gliderlabs/ssh/server.go new file mode 100644 index 0000000000000..09739e143e5d7 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/server.go @@ -0,0 +1,329 @@ +package ssh + +import ( + "context" + "errors" + "fmt" + "net" + "sync" + "time" + + gossh "golang.org/x/crypto/ssh" +) + +// ErrServerClosed is returned by the Server's Serve, ListenAndServe, +// and ListenAndServeTLS methods after a call to Shutdown or Close. +var ErrServerClosed = errors.New("ssh: Server closed") + +// Server defines parameters for running an SSH server. The zero value for +// Server is a valid configuration. When both PasswordHandler and +// PublicKeyHandler are nil, no client authentication is performed. +type Server struct { + Addr string // TCP address to listen on, ":22" if empty + Handler Handler // handler to invoke, ssh.DefaultHandler if nil + HostSigners []Signer // private keys for the host key, must have at least one + Version string // server version to be sent before the initial handshake + + PasswordHandler PasswordHandler // password authentication handler + PublicKeyHandler PublicKeyHandler // public key authentication handler + PtyCallback PtyCallback // callback for allowing PTY sessions, allows all if nil + ConnCallback ConnCallback // optional callback for wrapping net.Conn before handling + LocalPortForwardingCallback LocalPortForwardingCallback // callback for allowing local port forwarding, denies all if nil + + IdleTimeout time.Duration // connection timeout when no activity, none if empty + MaxTimeout time.Duration // absolute connection timeout, none if empty + + channelHandlers map[string]channelHandler + + listenerWg sync.WaitGroup + mu sync.Mutex + listeners map[net.Listener]struct{} + conns map[*gossh.ServerConn]struct{} + connWg sync.WaitGroup + doneChan chan struct{} +} + +// internal for now +type channelHandler func(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) + +func (srv *Server) ensureHostSigner() error { + if len(srv.HostSigners) == 0 { + signer, err := generateSigner() + if err != nil { + return err + } + srv.HostSigners = append(srv.HostSigners, signer) + } + return nil +} + +func (srv *Server) config(ctx Context) *gossh.ServerConfig { + config := &gossh.ServerConfig{} + for _, signer := range srv.HostSigners { + config.AddHostKey(signer) + } + if srv.PasswordHandler == nil && srv.PublicKeyHandler == nil { + config.NoClientAuth = true + } + if srv.Version != "" { + config.ServerVersion = "SSH-2.0-" + srv.Version + } + if srv.PasswordHandler != nil { + config.PasswordCallback = func(conn gossh.ConnMetadata, password []byte) (*gossh.Permissions, error) { + applyConnMetadata(ctx, conn) + if ok := srv.PasswordHandler(ctx, string(password)); !ok { + return ctx.Permissions().Permissions, fmt.Errorf("permission denied") + } + return ctx.Permissions().Permissions, nil + } + } + if srv.PublicKeyHandler != nil { + config.PublicKeyCallback = func(conn gossh.ConnMetadata, key gossh.PublicKey) (*gossh.Permissions, error) { + applyConnMetadata(ctx, conn) + if ok := srv.PublicKeyHandler(ctx, key); !ok { + return ctx.Permissions().Permissions, fmt.Errorf("permission denied") + } + ctx.SetValue(ContextKeyPublicKey, key) + return ctx.Permissions().Permissions, nil + } + } + return config +} + +// Handle sets the Handler for the server. +func (srv *Server) Handle(fn Handler) { + srv.Handler = fn +} + +// Close immediately closes all active listeners and all active +// connections. +// +// Close returns any error returned from closing the Server's +// underlying Listener(s). +func (srv *Server) Close() error { + srv.mu.Lock() + defer srv.mu.Unlock() + srv.closeDoneChanLocked() + err := srv.closeListenersLocked() + for c := range srv.conns { + c.Close() + delete(srv.conns, c) + } + return err +} + +// Shutdown gracefully shuts down the server without interrupting any +// active connections. Shutdown works by first closing all open +// listeners, and then waiting indefinitely for connections to close. +// If the provided context expires before the shutdown is complete, +// then the context's error is returned. +func (srv *Server) Shutdown(ctx context.Context) error { + srv.mu.Lock() + lnerr := srv.closeListenersLocked() + srv.closeDoneChanLocked() + srv.mu.Unlock() + + finished := make(chan struct{}, 1) + go func() { + srv.listenerWg.Wait() + srv.connWg.Wait() + finished <- struct{}{} + }() + + select { + case <-ctx.Done(): + return ctx.Err() + case <-finished: + return lnerr + } +} + +// Serve accepts incoming connections on the Listener l, creating a new +// connection goroutine for each. The connection goroutines read requests and then +// calls srv.Handler to handle sessions. +// +// Serve always returns a non-nil error. +func (srv *Server) Serve(l net.Listener) error { + defer l.Close() + if err := srv.ensureHostSigner(); err != nil { + return err + } + if srv.Handler == nil { + srv.Handler = DefaultHandler + } + if srv.channelHandlers == nil { + srv.channelHandlers = map[string]channelHandler{ + "session": sessionHandler, + "direct-tcpip": directTcpipHandler, + } + } + var tempDelay time.Duration + + srv.trackListener(l, true) + defer srv.trackListener(l, false) + for { + conn, e := l.Accept() + if e != nil { + select { + case <-srv.getDoneChan(): + return ErrServerClosed + default: + } + if ne, ok := e.(net.Error); ok && ne.Temporary() { + if tempDelay == 0 { + tempDelay = 5 * time.Millisecond + } else { + tempDelay *= 2 + } + if max := 1 * time.Second; tempDelay > max { + tempDelay = max + } + time.Sleep(tempDelay) + continue + } + return e + } + go srv.handleConn(conn) + } +} + +func (srv *Server) handleConn(newConn net.Conn) { + if srv.ConnCallback != nil { + cbConn := srv.ConnCallback(newConn) + if cbConn == nil { + newConn.Close() + return + } + newConn = cbConn + } + ctx, cancel := newContext(srv) + conn := &serverConn{ + Conn: newConn, + idleTimeout: srv.IdleTimeout, + closeCanceler: cancel, + } + if srv.MaxTimeout > 0 { + conn.maxDeadline = time.Now().Add(srv.MaxTimeout) + } + defer conn.Close() + sshConn, chans, reqs, err := gossh.NewServerConn(conn, srv.config(ctx)) + if err != nil { + // TODO: trigger event callback + return + } + + srv.trackConn(sshConn, true) + defer srv.trackConn(sshConn, false) + + ctx.SetValue(ContextKeyConn, sshConn) + applyConnMetadata(ctx, sshConn) + go gossh.DiscardRequests(reqs) + for ch := range chans { + handler, found := srv.channelHandlers[ch.ChannelType()] + if !found { + ch.Reject(gossh.UnknownChannelType, "unsupported channel type") + continue + } + go handler(srv, sshConn, ch, ctx) + } +} + +// ListenAndServe listens on the TCP network address srv.Addr and then calls +// Serve to handle incoming connections. If srv.Addr is blank, ":22" is used. +// ListenAndServe always returns a non-nil error. +func (srv *Server) ListenAndServe() error { + addr := srv.Addr + if addr == "" { + addr = ":22" + } + ln, err := net.Listen("tcp", addr) + if err != nil { + return err + } + return srv.Serve(ln) +} + +// AddHostKey adds a private key as a host key. If an existing host key exists +// with the same algorithm, it is overwritten. Each server config must have at +// least one host key. +func (srv *Server) AddHostKey(key Signer) { + // these are later added via AddHostKey on ServerConfig, which performs the + // check for one of every algorithm. + srv.HostSigners = append(srv.HostSigners, key) +} + +// SetOption runs a functional option against the server. +func (srv *Server) SetOption(option Option) error { + return option(srv) +} + +func (srv *Server) getDoneChan() <-chan struct{} { + srv.mu.Lock() + defer srv.mu.Unlock() + return srv.getDoneChanLocked() +} + +func (srv *Server) getDoneChanLocked() chan struct{} { + if srv.doneChan == nil { + srv.doneChan = make(chan struct{}) + } + return srv.doneChan +} + +func (srv *Server) closeDoneChanLocked() { + ch := srv.getDoneChanLocked() + select { + case <-ch: + // Already closed. Don't close again. + default: + // Safe to close here. We're the only closer, guarded + // by srv.mu. + close(ch) + } +} + +func (srv *Server) closeListenersLocked() error { + var err error + for ln := range srv.listeners { + if cerr := ln.Close(); cerr != nil && err == nil { + err = cerr + } + delete(srv.listeners, ln) + } + return err +} + +func (srv *Server) trackListener(ln net.Listener, add bool) { + srv.mu.Lock() + defer srv.mu.Unlock() + if srv.listeners == nil { + srv.listeners = make(map[net.Listener]struct{}) + } + if add { + // If the *Server is being reused after a previous + // Close or Shutdown, reset its doneChan: + if len(srv.listeners) == 0 && len(srv.conns) == 0 { + srv.doneChan = nil + } + srv.listeners[ln] = struct{}{} + srv.listenerWg.Add(1) + } else { + delete(srv.listeners, ln) + srv.listenerWg.Done() + } +} + +func (srv *Server) trackConn(c *gossh.ServerConn, add bool) { + srv.mu.Lock() + defer srv.mu.Unlock() + if srv.conns == nil { + srv.conns = make(map[*gossh.ServerConn]struct{}) + } + if add { + srv.conns[c] = struct{}{} + srv.connWg.Add(1) + } else { + delete(srv.conns, c) + srv.connWg.Done() + } +} diff --git a/vendor/github.com/gliderlabs/ssh/session.go b/vendor/github.com/gliderlabs/ssh/session.go new file mode 100644 index 0000000000000..b7454771289e8 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/session.go @@ -0,0 +1,287 @@ +package ssh + +import ( + "bytes" + "context" + "errors" + "fmt" + "net" + "sync" + + "github.com/anmitsu/go-shlex" + gossh "golang.org/x/crypto/ssh" +) + +// Session provides access to information about an SSH session and methods +// to read and write to the SSH channel with an embedded Channel interface from +// cypto/ssh. +// +// When Command() returns an empty slice, the user requested a shell. Otherwise +// the user is performing an exec with those command arguments. +// +// TODO: Signals +type Session interface { + gossh.Channel + + // User returns the username used when establishing the SSH connection. + User() string + + // RemoteAddr returns the net.Addr of the client side of the connection. + RemoteAddr() net.Addr + + // LocalAddr returns the net.Addr of the server side of the connection. + LocalAddr() net.Addr + + // Environ returns a copy of strings representing the environment set by the + // user for this session, in the form "key=value". + Environ() []string + + // Exit sends an exit status and then closes the session. + Exit(code int) error + + // Command returns a shell parsed slice of arguments that were provided by the + // user. Shell parsing splits the command string according to POSIX shell rules, + // which considers quoting not just whitespace. + Command() []string + + // PublicKey returns the PublicKey used to authenticate. If a public key was not + // used it will return nil. + PublicKey() PublicKey + + // Context returns the connection's context. The returned context is always + // non-nil and holds the same data as the Context passed into auth + // handlers and callbacks. + // + // The context is canceled when the client's connection closes or I/O + // operation fails. + Context() context.Context + + // Permissions returns a copy of the Permissions object that was available for + // setup in the auth handlers via the Context. + Permissions() Permissions + + // Pty returns PTY information, a channel of window size changes, and a boolean + // of whether or not a PTY was accepted for this session. + Pty() (Pty, <-chan Window, bool) + + // Signals registers a channel to receive signals sent from the client. The + // channel must handle signal sends or it will block the SSH request loop. + // Registering nil will unregister the channel from signal sends. During the + // time no channel is registered signals are buffered up to a reasonable amount. + // If there are buffered signals when a channel is registered, they will be + // sent in order on the channel immediately after registering. + Signals(c chan<- Signal) +} + +// maxSigBufSize is how many signals will be buffered +// when there is no signal channel specified +const maxSigBufSize = 128 + +func sessionHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) { + ch, reqs, err := newChan.Accept() + if err != nil { + // TODO: trigger event callback + return + } + sess := &session{ + Channel: ch, + conn: conn, + handler: srv.Handler, + ptyCb: srv.PtyCallback, + ctx: ctx, + } + sess.handleRequests(reqs) +} + +type session struct { + sync.Mutex + gossh.Channel + conn *gossh.ServerConn + handler Handler + handled bool + exited bool + pty *Pty + winch chan Window + env []string + ptyCb PtyCallback + cmd []string + ctx Context + sigCh chan<- Signal + sigBuf []Signal +} + +func (sess *session) Write(p []byte) (n int, err error) { + if sess.pty != nil { + m := len(p) + // normalize \n to \r\n when pty is accepted. + // this is a hardcoded shortcut since we don't support terminal modes. + p = bytes.Replace(p, []byte{'\n'}, []byte{'\r', '\n'}, -1) + p = bytes.Replace(p, []byte{'\r', '\r', '\n'}, []byte{'\r', '\n'}, -1) + n, err = sess.Channel.Write(p) + if n > m { + n = m + } + return + } + return sess.Channel.Write(p) +} + +func (sess *session) PublicKey() PublicKey { + sessionkey := sess.ctx.Value(ContextKeyPublicKey) + if sessionkey == nil { + return nil + } + return sessionkey.(PublicKey) +} + +func (sess *session) Permissions() Permissions { + // use context permissions because its properly + // wrapped and easier to dereference + perms := sess.ctx.Value(ContextKeyPermissions).(*Permissions) + return *perms +} + +func (sess *session) Context() context.Context { + return sess.ctx +} + +func (sess *session) Exit(code int) error { + sess.Lock() + defer sess.Unlock() + if sess.exited { + return errors.New("Session.Exit called multiple times") + } + sess.exited = true + + status := struct{ Status uint32 }{uint32(code)} + _, err := sess.SendRequest("exit-status", false, gossh.Marshal(&status)) + if err != nil { + return err + } + return sess.Close() +} + +func (sess *session) User() string { + return sess.conn.User() +} + +func (sess *session) RemoteAddr() net.Addr { + return sess.conn.RemoteAddr() +} + +func (sess *session) LocalAddr() net.Addr { + return sess.conn.LocalAddr() +} + +func (sess *session) Environ() []string { + return append([]string(nil), sess.env...) +} + +func (sess *session) Command() []string { + return append([]string(nil), sess.cmd...) +} + +func (sess *session) Pty() (Pty, <-chan Window, bool) { + if sess.pty != nil { + return *sess.pty, sess.winch, true + } + return Pty{}, sess.winch, false +} + +func (sess *session) Signals(c chan<- Signal) { + sess.Lock() + defer sess.Unlock() + sess.sigCh = c + if len(sess.sigBuf) > 0 { + go func() { + for _, sig := range sess.sigBuf { + sess.sigCh <- sig + } + }() + } +} + +func (sess *session) handleRequests(reqs <-chan *gossh.Request) { + for req := range reqs { + switch req.Type { + case "shell", "exec": + if sess.handled { + req.Reply(false, nil) + continue + } + sess.handled = true + req.Reply(true, nil) + + var payload = struct{ Value string }{} + gossh.Unmarshal(req.Payload, &payload) + sess.cmd, _ = shlex.Split(payload.Value, true) + go func() { + sess.handler(sess) + sess.Exit(0) + }() + case "env": + if sess.handled { + req.Reply(false, nil) + continue + } + var kv struct{ Key, Value string } + gossh.Unmarshal(req.Payload, &kv) + sess.env = append(sess.env, fmt.Sprintf("%s=%s", kv.Key, kv.Value)) + req.Reply(true, nil) + case "signal": + var payload struct{ Signal string } + gossh.Unmarshal(req.Payload, &payload) + sess.Lock() + if sess.sigCh != nil { + sess.sigCh <- Signal(payload.Signal) + } else { + if len(sess.sigBuf) < maxSigBufSize { + sess.sigBuf = append(sess.sigBuf, Signal(payload.Signal)) + } + } + sess.Unlock() + case "pty-req": + if sess.handled || sess.pty != nil { + req.Reply(false, nil) + continue + } + ptyReq, ok := parsePtyRequest(req.Payload) + if !ok { + req.Reply(false, nil) + continue + } + if sess.ptyCb != nil { + ok := sess.ptyCb(sess.ctx, ptyReq) + if !ok { + req.Reply(false, nil) + continue + } + } + sess.pty = &ptyReq + sess.winch = make(chan Window, 1) + sess.winch <- ptyReq.Window + defer func() { + // when reqs is closed + close(sess.winch) + }() + req.Reply(ok, nil) + case "window-change": + if sess.pty == nil { + req.Reply(false, nil) + continue + } + win, ok := parseWinchRequest(req.Payload) + if ok { + sess.pty.Window = win + sess.winch <- win + } + req.Reply(ok, nil) + case agentRequestType: + // TODO: option/callback to allow agent forwarding + SetAgentRequested(sess.ctx) + req.Reply(true, nil) + default: + // TODO: debug log + } + } +} diff --git a/vendor/github.com/gliderlabs/ssh/ssh.go b/vendor/github.com/gliderlabs/ssh/ssh.go new file mode 100644 index 0000000000000..017377536ac49 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/ssh.go @@ -0,0 +1,109 @@ +package ssh + +import ( + "crypto/subtle" + "net" +) + +type Signal string + +// POSIX signals as listed in RFC 4254 Section 6.10. +const ( + SIGABRT Signal = "ABRT" + SIGALRM Signal = "ALRM" + SIGFPE Signal = "FPE" + SIGHUP Signal = "HUP" + SIGILL Signal = "ILL" + SIGINT Signal = "INT" + SIGKILL Signal = "KILL" + SIGPIPE Signal = "PIPE" + SIGQUIT Signal = "QUIT" + SIGSEGV Signal = "SEGV" + SIGTERM Signal = "TERM" + SIGUSR1 Signal = "USR1" + SIGUSR2 Signal = "USR2" +) + +// DefaultHandler is the default Handler used by Serve. +var DefaultHandler Handler + +// Option is a functional option handler for Server. +type Option func(*Server) error + +// Handler is a callback for handling established SSH sessions. +type Handler func(Session) + +// PublicKeyHandler is a callback for performing public key authentication. +type PublicKeyHandler func(ctx Context, key PublicKey) bool + +// PasswordHandler is a callback for performing password authentication. +type PasswordHandler func(ctx Context, password string) bool + +// PtyCallback is a hook for allowing PTY sessions. +type PtyCallback func(ctx Context, pty Pty) bool + +// ConnCallback is a hook for new connections before handling. +// It allows wrapping for timeouts and limiting by returning +// the net.Conn that will be used as the underlying connection. +type ConnCallback func(conn net.Conn) net.Conn + +// LocalPortForwardingCallback is a hook for allowing port forwarding +type LocalPortForwardingCallback func(ctx Context, destinationHost string, destinationPort uint32) bool + +// Window represents the size of a PTY window. +type Window struct { + Width int + Height int +} + +// Pty represents a PTY request and configuration. +type Pty struct { + Term string + Window Window + // HELP WANTED: terminal modes! +} + +// Serve accepts incoming SSH connections on the listener l, creating a new +// connection goroutine for each. The connection goroutines read requests and +// then calls handler to handle sessions. Handler is typically nil, in which +// case the DefaultHandler is used. +func Serve(l net.Listener, handler Handler, options ...Option) error { + srv := &Server{Handler: handler} + for _, option := range options { + if err := srv.SetOption(option); err != nil { + return err + } + } + return srv.Serve(l) +} + +// ListenAndServe listens on the TCP network address addr and then calls Serve +// with handler to handle sessions on incoming connections. Handler is typically +// nil, in which case the DefaultHandler is used. +func ListenAndServe(addr string, handler Handler, options ...Option) error { + srv := &Server{Addr: addr, Handler: handler} + for _, option := range options { + if err := srv.SetOption(option); err != nil { + return err + } + } + return srv.ListenAndServe() +} + +// Handle registers the handler as the DefaultHandler. +func Handle(handler Handler) { + DefaultHandler = handler +} + +// KeysEqual is constant time compare of the keys to avoid timing attacks. +func KeysEqual(ak, bk PublicKey) bool { + + //avoid panic if one of the keys is nil, return false instead + if ak == nil || bk == nil { + return false + } + + a := ak.Marshal() + b := bk.Marshal() + return (len(a) == len(b) && subtle.ConstantTimeCompare(a, b) == 1) +} diff --git a/vendor/github.com/gliderlabs/ssh/tcpip.go b/vendor/github.com/gliderlabs/ssh/tcpip.go new file mode 100644 index 0000000000000..a7a077eb60252 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/tcpip.go @@ -0,0 +1,58 @@ +package ssh + +import ( + "fmt" + "io" + "net" + + gossh "golang.org/x/crypto/ssh" +) + +// direct-tcpip data struct as specified in RFC4254, Section 7.2 +type forwardData struct { + DestinationHost string + DestinationPort uint32 + + OriginatorHost string + OriginatorPort uint32 +} + +func directTcpipHandler(srv *Server, conn *gossh.ServerConn, newChan gossh.NewChannel, ctx Context) { + d := forwardData{} + if err := gossh.Unmarshal(newChan.ExtraData(), &d); err != nil { + newChan.Reject(gossh.ConnectionFailed, "error parsing forward data: "+err.Error()) + return + } + + if srv.LocalPortForwardingCallback == nil || !srv.LocalPortForwardingCallback(ctx, d.DestinationHost, d.DestinationPort) { + newChan.Reject(gossh.Prohibited, "port forwarding is disabled") + return + } + + dest := fmt.Sprintf("%s:%d", d.DestinationHost, d.DestinationPort) + + var dialer net.Dialer + dconn, err := dialer.DialContext(ctx, "tcp", dest) + if err != nil { + newChan.Reject(gossh.ConnectionFailed, err.Error()) + return + } + + ch, reqs, err := newChan.Accept() + if err != nil { + dconn.Close() + return + } + go gossh.DiscardRequests(reqs) + + go func() { + defer ch.Close() + defer dconn.Close() + io.Copy(ch, dconn) + }() + go func() { + defer ch.Close() + defer dconn.Close() + io.Copy(dconn, ch) + }() +} diff --git a/vendor/github.com/gliderlabs/ssh/util.go b/vendor/github.com/gliderlabs/ssh/util.go new file mode 100644 index 0000000000000..c1fbc39958b7d --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/util.go @@ -0,0 +1,89 @@ +package ssh + +import ( + "crypto/rand" + "crypto/rsa" + "encoding/binary" + + "golang.org/x/crypto/ssh" +) + +func generateSigner() (ssh.Signer, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + return ssh.NewSignerFromKey(key) +} + +func parsePtyRequest(s []byte) (pty Pty, ok bool) { + term, s, ok := parseString(s) + if !ok { + return + } + width32, s, ok := parseUint32(s) + if width32 < 1 { + ok = false + } + if !ok { + return + } + height32, _, ok := parseUint32(s) + if height32 < 1 { + ok = false + } + if !ok { + return + } + pty = Pty{ + Term: term, + Window: Window{ + Width: int(width32), + Height: int(height32), + }, + } + return +} + +func parseWinchRequest(s []byte) (win Window, ok bool) { + width32, s, ok := parseUint32(s) + if width32 < 1 { + ok = false + } + if !ok { + return + } + height32, _, ok := parseUint32(s) + if height32 < 1 { + ok = false + } + if !ok { + return + } + win = Window{ + Width: int(width32), + Height: int(height32), + } + return +} + +func parseString(in []byte) (out string, rest []byte, ok bool) { + if len(in) < 4 { + return + } + length := binary.BigEndian.Uint32(in) + if uint32(len(in)) < 4+length { + return + } + out = string(in[4 : 4+length]) + rest = in[4+length:] + ok = true + return +} + +func parseUint32(in []byte) (uint32, []byte, bool) { + if len(in) < 4 { + return 0, nil, false + } + return binary.BigEndian.Uint32(in), in[4:], true +} diff --git a/vendor/github.com/gliderlabs/ssh/wrap.go b/vendor/github.com/gliderlabs/ssh/wrap.go new file mode 100644 index 0000000000000..d1f2b161e6932 --- /dev/null +++ b/vendor/github.com/gliderlabs/ssh/wrap.go @@ -0,0 +1,33 @@ +package ssh + +import gossh "golang.org/x/crypto/ssh" + +// PublicKey is an abstraction of different types of public keys. +type PublicKey interface { + gossh.PublicKey +} + +// The Permissions type holds fine-grained permissions that are specific to a +// user or a specific authentication method for a user. Permissions, except for +// "source-address", must be enforced in the server application layer, after +// successful authentication. +type Permissions struct { + *gossh.Permissions +} + +// A Signer can create signatures that verify against a public key. +type Signer interface { + gossh.Signer +} + +// ParseAuthorizedKey parses a public key from an authorized_keys file used in +// OpenSSH according to the sshd(8) manual page. +func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) { + return gossh.ParseAuthorizedKey(in) +} + +// ParsePublicKey parses an SSH public key formatted for use in +// the SSH wire protocol according to RFC 4253, section 6.6. +func ParsePublicKey(in []byte) (out PublicKey, err error) { + return gossh.ParsePublicKey(in) +} From 179f2ad723581bbdbd471fd287652a86368bda9f Mon Sep 17 00:00:00 2001 From: techknowlogick Date: Mon, 11 Feb 2019 22:35:48 -0500 Subject: [PATCH 4/4] Update ssh.go --- modules/ssh/ssh.go | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index d0eadf45a7c35..55088f911b8eb 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -1,4 +1,3 @@ -// Copyright 2014 The Gogs Authors. All rights reserved. // Copyright 2017 The Gitea Authors. All rights reserved. // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file.