mirror of
https://github.com/cwinfo/matterbridge.git
synced 2025-06-28 17:19:24 +00:00
Add sshchat dependencies in vendor
This commit is contained in:
21
vendor/github.com/shazow/ssh-chat/sshd/LICENSE
generated
vendored
Normal file
21
vendor/github.com/shazow/ssh-chat/sshd/LICENSE
generated
vendored
Normal file
@ -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.
|
72
vendor/github.com/shazow/ssh-chat/sshd/auth.go
generated
vendored
Normal file
72
vendor/github.com/shazow/ssh-chat/sshd/auth.go
generated
vendored
Normal file
@ -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[:])
|
||||
}
|
76
vendor/github.com/shazow/ssh-chat/sshd/client.go
generated
vendored
Normal file
76
vendor/github.com/shazow/ssh-chat/sshd/client.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
34
vendor/github.com/shazow/ssh-chat/sshd/doc.go
generated
vendored
Normal file
34
vendor/github.com/shazow/ssh-chat/sshd/doc.go
generated
vendored
Normal file
@ -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(...)
|
||||
}
|
||||
|
||||
}()
|
||||
}
|
||||
*/
|
22
vendor/github.com/shazow/ssh-chat/sshd/logger.go
generated
vendored
Normal file
22
vendor/github.com/shazow/ssh-chat/sshd/logger.go
generated
vendored
Normal file
@ -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{})
|
||||
}
|
67
vendor/github.com/shazow/ssh-chat/sshd/net.go
generated
vendored
Normal file
67
vendor/github.com/shazow/ssh-chat/sshd/net.go
generated
vendored
Normal file
@ -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)
|
||||
}()
|
||||
}
|
||||
}
|
70
vendor/github.com/shazow/ssh-chat/sshd/pty.go
generated
vendored
Normal file
70
vendor/github.com/shazow/ssh-chat/sshd/pty.go
generated
vendored
Normal file
@ -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
|
||||
}
|
71
vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
generated
vendored
Normal file
71
vendor/github.com/shazow/ssh-chat/sshd/ratelimit.go
generated
vendored
Normal file
@ -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<readCap, else n
|
||||
func (limit *inputLimiter) Count(n int) error {
|
||||
now := time.Now()
|
||||
if now.After(limit.timeRead) {
|
||||
limit.numRead = 0
|
||||
limit.timeRead = now.Add(limit.Frequency)
|
||||
}
|
||||
if n <= limit.readCap {
|
||||
limit.numRead += 1
|
||||
} else {
|
||||
limit.numRead += n
|
||||
}
|
||||
if limit.numRead > limit.Amount {
|
||||
return rateio.ErrRateExceeded
|
||||
}
|
||||
return nil
|
||||
}
|
167
vendor/github.com/shazow/ssh-chat/sshd/terminal.go
generated
vendored
Normal file
167
vendor/github.com/shazow/ssh-chat/sshd/terminal.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user