From ed9118b34620f1ecd5f28506328d4ffe1b04793d Mon Sep 17 00:00:00 2001 From: Wim Date: Sun, 3 Dec 2017 01:24:05 +0100 Subject: Add sshchat dependencies in vendor --- vendor/github.com/shazow/ssh-chat/sshd/LICENSE | 21 +++ vendor/github.com/shazow/ssh-chat/sshd/auth.go | 72 +++++++++ vendor/github.com/shazow/ssh-chat/sshd/client.go | 76 ++++++++++ vendor/github.com/shazow/ssh-chat/sshd/doc.go | 34 +++++ vendor/github.com/shazow/ssh-chat/sshd/logger.go | 22 +++ vendor/github.com/shazow/ssh-chat/sshd/net.go | 67 +++++++++ vendor/github.com/shazow/ssh-chat/sshd/pty.go | 70 +++++++++ .../github.com/shazow/ssh-chat/sshd/ratelimit.go | 71 +++++++++ vendor/github.com/shazow/ssh-chat/sshd/terminal.go | 167 +++++++++++++++++++++ 9 files changed, 600 insertions(+) create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/LICENSE create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/auth.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/client.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/doc.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/logger.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/net.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/pty.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go create mode 100644 vendor/github.com/shazow/ssh-chat/sshd/terminal.go (limited to 'vendor/github.com/shazow/ssh-chat/sshd') diff --git a/vendor/github.com/shazow/ssh-chat/sshd/LICENSE b/vendor/github.com/shazow/ssh-chat/sshd/LICENSE new file mode 100644 index 00000000..325b43a6 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Andrey Petrov + +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/shazow/ssh-chat/sshd/auth.go b/vendor/github.com/shazow/ssh-chat/sshd/auth.go new file mode 100644 index 00000000..2fc86fa8 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/auth.go @@ -0,0 +1,72 @@ +package sshd + +import ( + "crypto/sha256" + "encoding/base64" + "errors" + "net" + + "golang.org/x/crypto/ssh" +) + +// Auth is used to authenticate connections based on public keys. +type Auth interface { + // Whether to allow connections without a public key. + AllowAnonymous() bool + // Given address and public key, return if the connection should be permitted. + Check(net.Addr, ssh.PublicKey) (bool, error) +} + +// MakeAuth makes an ssh.ServerConfig which performs authentication against an Auth implementation. +func MakeAuth(auth Auth) *ssh.ServerConfig { + config := ssh.ServerConfig{ + NoClientAuth: false, + // Auth-related things should be constant-time to avoid timing attacks. + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + ok, err := auth.Check(conn.RemoteAddr(), key) + if !ok { + return nil, err + } + perm := &ssh.Permissions{Extensions: map[string]string{ + "pubkey": string(key.Marshal()), + }} + return perm, nil + }, + KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) { + if !auth.AllowAnonymous() { + return nil, errors.New("public key authentication required") + } + _, err := auth.Check(conn.RemoteAddr(), nil) + return nil, err + }, + } + + return &config +} + +// MakeNoAuth makes a simple ssh.ServerConfig which allows all connections. +// Primarily used for testing. +func MakeNoAuth() *ssh.ServerConfig { + config := ssh.ServerConfig{ + NoClientAuth: false, + // Auth-related things should be constant-time to avoid timing attacks. + PublicKeyCallback: func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { + perm := &ssh.Permissions{Extensions: map[string]string{ + "pubkey": string(key.Marshal()), + }} + return perm, nil + }, + KeyboardInteractiveCallback: func(conn ssh.ConnMetadata, challenge ssh.KeyboardInteractiveChallenge) (*ssh.Permissions, error) { + return nil, nil + }, + } + + return &config +} + +// Fingerprint performs a SHA256 BASE64 fingerprint of the PublicKey, similar to OpenSSH. +// See: https://anongit.mindrot.org/openssh.git/commit/?id=56d1c83cdd1ac +func Fingerprint(k ssh.PublicKey) string { + hash := sha256.Sum256(k.Marshal()) + return "SHA256:" + base64.StdEncoding.EncodeToString(hash[:]) +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/client.go b/vendor/github.com/shazow/ssh-chat/sshd/client.go new file mode 100644 index 00000000..004aa473 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/client.go @@ -0,0 +1,76 @@ +package sshd + +import ( + "crypto/rand" + "crypto/rsa" + "io" + + "golang.org/x/crypto/ssh" +) + +// NewRandomSigner generates a random key of a desired bit length. +func NewRandomSigner(bits int) (ssh.Signer, error) { + key, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, err + } + return ssh.NewSignerFromKey(key) +} + +// NewClientConfig creates a barebones ssh.ClientConfig to be used with ssh.Dial. +func NewClientConfig(name string) *ssh.ClientConfig { + return &ssh.ClientConfig{ + User: name, + Auth: []ssh.AuthMethod{ + ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) { + return + }), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + } +} + +// ConnectShell makes a barebones SSH client session, used for testing. +func ConnectShell(host string, name string, handler func(r io.Reader, w io.WriteCloser) error) error { + config := NewClientConfig(name) + conn, err := ssh.Dial("tcp", host, config) + if err != nil { + return err + } + defer conn.Close() + + session, err := conn.NewSession() + if err != nil { + return err + } + defer session.Close() + + in, err := session.StdinPipe() + if err != nil { + return err + } + + out, err := session.StdoutPipe() + if err != nil { + return err + } + + /* + err = session.RequestPty("xterm", 80, 40, ssh.TerminalModes{}) + if err != nil { + return err + } + */ + + err = session.Shell() + if err != nil { + return err + } + + _, err = session.SendRequest("ping", true, nil) + if err != nil { + return err + } + + return handler(out, in) +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/doc.go b/vendor/github.com/shazow/ssh-chat/sshd/doc.go new file mode 100644 index 00000000..21cd9142 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/doc.go @@ -0,0 +1,34 @@ +package sshd + +/* + + signer, err := ssh.ParsePrivateKey(privateKey) + + config := MakeNoAuth() + config.AddHostKey(signer) + + s, err := ListenSSH("0.0.0.0:2022", config) + if err != nil { + // Handle opening socket error + } + defer s.Close() + + terminals := s.ServeTerminal() + + for term := range terminals { + go func() { + defer term.Close() + term.SetPrompt("...") + term.AutoCompleteCallback = nil // ... + + for { + line, err := term.ReadLine() + if err != nil { + break + } + term.Write(...) + } + + }() + } +*/ diff --git a/vendor/github.com/shazow/ssh-chat/sshd/logger.go b/vendor/github.com/shazow/ssh-chat/sshd/logger.go new file mode 100644 index 00000000..9f6998f0 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/logger.go @@ -0,0 +1,22 @@ +package sshd + +import "io" +import stdlog "log" + +var logger *stdlog.Logger + +func SetLogger(w io.Writer) { + flags := stdlog.Flags() + prefix := "[sshd] " + logger = stdlog.New(w, prefix, flags) +} + +type nullWriter struct{} + +func (nullWriter) Write(data []byte) (int, error) { + return len(data), nil +} + +func init() { + SetLogger(nullWriter{}) +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/net.go b/vendor/github.com/shazow/ssh-chat/sshd/net.go new file mode 100644 index 00000000..1b3f4028 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/net.go @@ -0,0 +1,67 @@ +package sshd + +import ( + "net" + + "github.com/shazow/rateio" + "golang.org/x/crypto/ssh" +) + +// Container for the connection and ssh-related configuration +type SSHListener struct { + net.Listener + config *ssh.ServerConfig + + RateLimit func() rateio.Limiter + HandlerFunc func(term *Terminal) +} + +// Make an SSH listener socket +func ListenSSH(laddr string, config *ssh.ServerConfig) (*SSHListener, error) { + socket, err := net.Listen("tcp", laddr) + if err != nil { + return nil, err + } + l := SSHListener{Listener: socket, config: config} + return &l, nil +} + +func (l *SSHListener) handleConn(conn net.Conn) (*Terminal, error) { + if l.RateLimit != nil { + // TODO: Configurable Limiter? + conn = ReadLimitConn(conn, l.RateLimit()) + } + + // Upgrade TCP connection to SSH connection + sshConn, channels, requests, err := ssh.NewServerConn(conn, l.config) + if err != nil { + return nil, err + } + + // FIXME: Disconnect if too many faulty requests? (Avoid DoS.) + go ssh.DiscardRequests(requests) + return NewSession(sshConn, channels) +} + +// Accept incoming connections as terminal requests and yield them +func (l *SSHListener) Serve() { + defer l.Close() + for { + conn, err := l.Accept() + + if err != nil { + logger.Printf("Failed to accept connection: %s", err) + break + } + + // Goroutineify to resume accepting sockets early + go func() { + term, err := l.handleConn(conn) + if err != nil { + logger.Printf("[%s] Failed to handshake: %s", conn.RemoteAddr(), err) + return + } + l.HandlerFunc(term) + }() + } +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/pty.go b/vendor/github.com/shazow/ssh-chat/sshd/pty.go new file mode 100644 index 00000000..4443fd33 --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/pty.go @@ -0,0 +1,70 @@ +package sshd + +import "encoding/binary" + +// Helpers below are borrowed from go.crypto circa 2011: + +// parsePtyRequest parses the payload of the pty-req message and extracts the +// dimensions of the terminal. See RFC 4254, section 6.2. +func parsePtyRequest(s []byte) (width, height int, ok bool) { + _, s, ok = parseString(s) + if !ok { + return + } + width32, s, ok := parseUint32(s) + if !ok { + return + } + height32, _, ok := parseUint32(s) + width = int(width32) + height = int(height32) + if width < 1 { + ok = false + } + if height < 1 { + ok = false + } + return +} + +func parseWinchRequest(s []byte) (width, height int, ok bool) { + width32, _, ok := parseUint32(s) + if !ok { + return + } + height32, _, ok := parseUint32(s) + if !ok { + return + } + + width = int(width32) + height = int(height32) + if width < 1 { + ok = false + } + if height < 1 { + ok = false + } + 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/shazow/ssh-chat/sshd/ratelimit.go b/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go new file mode 100644 index 00000000..b2607e6d --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go @@ -0,0 +1,71 @@ +package sshd + +import ( + "io" + "net" + "time" + + "github.com/shazow/rateio" +) + +type limitedConn struct { + net.Conn + io.Reader // Our rate-limited io.Reader for net.Conn +} + +func (r *limitedConn) Read(p []byte) (n int, err error) { + return r.Reader.Read(p) +} + +// ReadLimitConn returns a net.Conn whose io.Reader interface is rate-limited by limiter. +func ReadLimitConn(conn net.Conn, limiter rateio.Limiter) net.Conn { + return &limitedConn{ + Conn: conn, + Reader: rateio.NewReader(conn, limiter), + } +} + +// Count each read as 1 unless it exceeds some number of bytes. +type inputLimiter struct { + // TODO: Could do all kinds of fancy things here, like be more forgiving of + // connections that have been around for a while. + + Amount int + Frequency time.Duration + + remaining int + readCap int + numRead int + timeRead time.Time +} + +// NewInputLimiter returns a rateio.Limiter with sensible defaults for +// differentiating between humans typing and bots spamming. +func NewInputLimiter() rateio.Limiter { + grace := time.Second * 3 + return &inputLimiter{ + Amount: 2 << 14, // ~16kb, should be plenty for a high typing rate/copypasta/large key handshakes. + Frequency: time.Minute * 1, + readCap: 128, // Allow up to 128 bytes per read (anecdotally, 1 character = 52 bytes over ssh) + numRead: -1024 * 1024, // Start with a 1mb grace + timeRead: time.Now().Add(grace), + } +} + +// Count applies 1 if n limit.Amount { + return rateio.ErrRateExceeded + } + return nil +} diff --git a/vendor/github.com/shazow/ssh-chat/sshd/terminal.go b/vendor/github.com/shazow/ssh-chat/sshd/terminal.go new file mode 100644 index 00000000..977e146e --- /dev/null +++ b/vendor/github.com/shazow/ssh-chat/sshd/terminal.go @@ -0,0 +1,167 @@ +package sshd + +import ( + "errors" + "fmt" + "net" + "sync" + "time" + + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/terminal" +) + +var keepaliveInterval = time.Second * 30 +var keepaliveRequest = "keepalive@ssh-chat" + +var ErrNoSessionChannel = errors.New("no session channel") +var ErrNotSessionChannel = errors.New("terminal requires session channel") + +// Connection is an interface with fields necessary to operate an sshd host. +type Connection interface { + PublicKey() ssh.PublicKey + RemoteAddr() net.Addr + Name() string + ClientVersion() []byte + Close() error +} + +type sshConn struct { + *ssh.ServerConn +} + +func (c sshConn) PublicKey() ssh.PublicKey { + if c.Permissions == nil { + return nil + } + + s, ok := c.Permissions.Extensions["pubkey"] + if !ok { + return nil + } + + key, err := ssh.ParsePublicKey([]byte(s)) + if err != nil { + return nil + } + + return key +} + +func (c sshConn) Name() string { + return c.User() +} + +// Extending ssh/terminal to include a closer interface +type Terminal struct { + terminal.Terminal + Conn Connection + Channel ssh.Channel + + done chan struct{} + closeOnce sync.Once +} + +// Make new terminal from a session channel +func NewTerminal(conn *ssh.ServerConn, ch ssh.NewChannel) (*Terminal, error) { + if ch.ChannelType() != "session" { + return nil, ErrNotSessionChannel + } + channel, requests, err := ch.Accept() + if err != nil { + return nil, err + } + term := Terminal{ + Terminal: *terminal.NewTerminal(channel, "Connecting..."), + Conn: sshConn{conn}, + Channel: channel, + + done: make(chan struct{}), + } + + go term.listen(requests) + + go func() { + // Keep-Alive Ticker + ticker := time.Tick(keepaliveInterval) + for { + select { + case <-ticker: + _, err := channel.SendRequest(keepaliveRequest, true, nil) + if err != nil { + // Connection is gone + logger.Printf("[%s] Keepalive failed, closing terminal: %s", term.Conn.RemoteAddr(), err) + term.Close() + return + } + case <-term.done: + return + } + } + }() + + return &term, nil +} + +// Find session channel and make a Terminal from it +func NewSession(conn *ssh.ServerConn, channels <-chan ssh.NewChannel) (*Terminal, error) { + // Make a terminal from the first session found + for ch := range channels { + if t := ch.ChannelType(); t != "session" { + logger.Printf("[%s] Ignored channel type: %s", conn.RemoteAddr(), t) + ch.Reject(ssh.UnknownChannelType, fmt.Sprintf("unknown channel type: %s", t)) + continue + } + + return NewTerminal(conn, ch) + } + + return nil, ErrNoSessionChannel +} + +// Close terminal and ssh connection +func (t *Terminal) Close() error { + var err error + t.closeOnce.Do(func() { + close(t.done) + t.Channel.Close() + err = t.Conn.Close() + }) + return err +} + +// Negotiate terminal type and settings +func (t *Terminal) listen(requests <-chan *ssh.Request) { + hasShell := false + + for req := range requests { + var width, height int + var ok bool + + switch req.Type { + case "shell": + if !hasShell { + ok = true + hasShell = true + } + case "pty-req": + width, height, ok = parsePtyRequest(req.Payload) + if ok { + // TODO: Hardcode width to 100000? + err := t.SetSize(width, height) + ok = err == nil + } + case "window-change": + width, height, ok = parseWinchRequest(req.Payload) + if ok { + // TODO: Hardcode width to 100000? + err := t.SetSize(width, height) + ok = err == nil + } + } + + if req.WantReply { + req.Reply(ok, nil) + } + } +} -- cgit v1.2.3