glide up
This commit is contained in:
13
vendor/golang.org/x/crypto/ssh/agent/client_test.go
generated
vendored
13
vendor/golang.org/x/crypto/ssh/agent/client_test.go
generated
vendored
@@ -180,9 +180,12 @@ func TestCert(t *testing.T) {
|
||||
// therefore is buffered (net.Pipe deadlocks if both sides start with
|
||||
// a write.)
|
||||
func netPipe() (net.Conn, net.Conn, error) {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
listener, err = net.Listen("tcp", "[::1]:0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
defer listener.Close()
|
||||
c1, err := net.Dial("tcp", listener.Addr().String())
|
||||
@@ -200,6 +203,9 @@ func netPipe() (net.Conn, net.Conn, error) {
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
a, b, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
@@ -208,9 +214,6 @@ func TestAuth(t *testing.T) {
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
agent, _, cleanup := startAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
|
||||
t.Errorf("Add: %v", err)
|
||||
}
|
||||
|
24
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
24
vendor/golang.org/x/crypto/ssh/certs.go
generated
vendored
@@ -251,10 +251,18 @@ type CertChecker struct {
|
||||
// for user certificates.
|
||||
SupportedCriticalOptions []string
|
||||
|
||||
// IsAuthority should return true if the key is recognized as
|
||||
// an authority. This allows for certificates to be signed by other
|
||||
// certificates.
|
||||
IsAuthority func(auth PublicKey) bool
|
||||
// IsUserAuthority should return true if the key is recognized as an
|
||||
// authority for the given user certificate. This allows for
|
||||
// certificates to be signed by other certificates. This must be set
|
||||
// if this CertChecker will be checking user certificates.
|
||||
IsUserAuthority func(auth PublicKey) bool
|
||||
|
||||
// IsHostAuthority should report whether the key is recognized as
|
||||
// an authority for this host. This allows for certificates to be
|
||||
// signed by other keys, and for those other keys to only be valid
|
||||
// signers for particular hostnames. This must be set if this
|
||||
// CertChecker will be checking host certificates.
|
||||
IsHostAuthority func(auth PublicKey, address string) bool
|
||||
|
||||
// Clock is used for verifying time stamps. If nil, time.Now
|
||||
// is used.
|
||||
@@ -356,7 +364,13 @@ func (c *CertChecker) CheckCert(principal string, cert *Certificate) error {
|
||||
}
|
||||
}
|
||||
|
||||
if !c.IsAuthority(cert.SignatureKey) {
|
||||
// if this is a host cert, principal is the remote hostname as passed
|
||||
// to CheckHostCert.
|
||||
if cert.CertType == HostCert && !c.IsHostAuthority(cert.SignatureKey, principal) {
|
||||
return fmt.Errorf("ssh: no authorities for hostname: %v", principal)
|
||||
}
|
||||
|
||||
if cert.CertType == UserCert && !c.IsUserAuthority(cert.SignatureKey) {
|
||||
return fmt.Errorf("ssh: certificate signed by unrecognized authority")
|
||||
}
|
||||
|
||||
|
12
vendor/golang.org/x/crypto/ssh/certs_test.go
generated
vendored
12
vendor/golang.org/x/crypto/ssh/certs_test.go
generated
vendored
@@ -104,7 +104,7 @@ func TestValidateCert(t *testing.T) {
|
||||
t.Fatalf("got %v (%T), want *Certificate", key, key)
|
||||
}
|
||||
checker := CertChecker{}
|
||||
checker.IsAuthority = func(k PublicKey) bool {
|
||||
checker.IsUserAuthority = func(k PublicKey) bool {
|
||||
return bytes.Equal(k.Marshal(), validCert.SignatureKey.Marshal())
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func TestValidateCertTime(t *testing.T) {
|
||||
checker := CertChecker{
|
||||
Clock: func() time.Time { return time.Unix(ts, 0) },
|
||||
}
|
||||
checker.IsAuthority = func(k PublicKey) bool {
|
||||
checker.IsUserAuthority = func(k PublicKey) bool {
|
||||
return bytes.Equal(k.Marshal(),
|
||||
testPublicKeys["ecdsa"].Marshal())
|
||||
}
|
||||
@@ -160,7 +160,7 @@ func TestValidateCertTime(t *testing.T) {
|
||||
|
||||
func TestHostKeyCert(t *testing.T) {
|
||||
cert := &Certificate{
|
||||
ValidPrincipals: []string{"hostname", "hostname.domain"},
|
||||
ValidPrincipals: []string{"hostname", "hostname.domain", "otherhost"},
|
||||
Key: testPublicKeys["rsa"],
|
||||
ValidBefore: CertTimeInfinity,
|
||||
CertType: HostCert,
|
||||
@@ -168,8 +168,8 @@ func TestHostKeyCert(t *testing.T) {
|
||||
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||
|
||||
checker := &CertChecker{
|
||||
IsAuthority: func(p PublicKey) bool {
|
||||
return bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
|
||||
IsHostAuthority: func(p PublicKey, h string) bool {
|
||||
return h == "hostname" && bytes.Equal(testPublicKeys["ecdsa"].Marshal(), p.Marshal())
|
||||
},
|
||||
}
|
||||
|
||||
@@ -178,7 +178,7 @@ func TestHostKeyCert(t *testing.T) {
|
||||
t.Errorf("NewCertSigner: %v", err)
|
||||
}
|
||||
|
||||
for _, name := range []string{"hostname", "otherhost"} {
|
||||
for _, name := range []string{"hostname", "otherhost", "lasthost"} {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
|
3
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
3
vendor/golang.org/x/crypto/ssh/client.go
generated
vendored
@@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
// Client implements a traditional SSH client that supports shells,
|
||||
// subprocesses, port forwarding and tunneled dialing.
|
||||
// subprocesses, TCP port/streamlocal forwarding and tunneled dialing.
|
||||
type Client struct {
|
||||
Conn
|
||||
|
||||
@@ -60,6 +60,7 @@ func NewClient(c Conn, chans <-chan NewChannel, reqs <-chan *Request) *Client {
|
||||
conn.forwards.closeAll()
|
||||
}()
|
||||
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-tcpip"))
|
||||
go conn.forwards.handleChannels(conn.HandleChannelOpen("forwarded-streamlocal@openssh.com"))
|
||||
return conn
|
||||
}
|
||||
|
||||
|
49
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
49
vendor/golang.org/x/crypto/ssh/client_auth.go
generated
vendored
@@ -179,31 +179,26 @@ func (cb publicKeyCallback) method() string {
|
||||
}
|
||||
|
||||
func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand io.Reader) (bool, []string, error) {
|
||||
// Authentication is performed in two stages. The first stage sends an
|
||||
// enquiry to test if each key is acceptable to the remote. The second
|
||||
// stage attempts to authenticate with the valid keys obtained in the
|
||||
// first stage.
|
||||
// Authentication is performed by sending an enquiry to test if a key is
|
||||
// acceptable to the remote. If the key is acceptable, the client will
|
||||
// attempt to authenticate with the valid key. If not the client will repeat
|
||||
// the process with the remaining keys.
|
||||
|
||||
signers, err := cb()
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
var validKeys []Signer
|
||||
for _, signer := range signers {
|
||||
if ok, err := validateKey(signer.PublicKey(), user, c); ok {
|
||||
validKeys = append(validKeys, signer)
|
||||
} else {
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// methods that may continue if this auth is not successful.
|
||||
var methods []string
|
||||
for _, signer := range validKeys {
|
||||
pub := signer.PublicKey()
|
||||
for _, signer := range signers {
|
||||
ok, err := validateKey(signer.PublicKey(), user, c)
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
pub := signer.PublicKey()
|
||||
pubKey := pub.Marshal()
|
||||
sign, err := signer.Sign(rand, buildDataSignedForAuth(session, userAuthRequestMsg{
|
||||
User: user,
|
||||
@@ -236,13 +231,29 @@ func (cb publicKeyCallback) auth(session []byte, user string, c packetConn, rand
|
||||
if err != nil {
|
||||
return false, nil, err
|
||||
}
|
||||
if success {
|
||||
|
||||
// If authentication succeeds or the list of available methods does not
|
||||
// contain the "publickey" method, do not attempt to authenticate with any
|
||||
// other keys. According to RFC 4252 Section 7, the latter can occur when
|
||||
// additional authentication methods are required.
|
||||
if success || !containsMethod(methods, cb.method()) {
|
||||
return success, methods, err
|
||||
}
|
||||
}
|
||||
|
||||
return false, methods, nil
|
||||
}
|
||||
|
||||
func containsMethod(methods []string, method string) bool {
|
||||
for _, m := range methods {
|
||||
if m == method {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// validateKey validates the key provided is acceptable to the server.
|
||||
func validateKey(key PublicKey, user string, c packetConn) (bool, error) {
|
||||
pubKey := key.Marshal()
|
||||
|
101
vendor/golang.org/x/crypto/ssh/client_auth_test.go
generated
vendored
101
vendor/golang.org/x/crypto/ssh/client_auth_test.go
generated
vendored
@@ -38,7 +38,7 @@ func tryAuth(t *testing.T, config *ClientConfig) error {
|
||||
defer c2.Close()
|
||||
|
||||
certChecker := CertChecker{
|
||||
IsAuthority: func(k PublicKey) bool {
|
||||
IsUserAuthority: func(k PublicKey) bool {
|
||||
return bytes.Equal(k.Marshal(), testPublicKeys["ecdsa"].Marshal())
|
||||
},
|
||||
UserKeyFallback: func(conn ConnMetadata, key PublicKey) (*Permissions, error) {
|
||||
@@ -76,8 +76,6 @@ func tryAuth(t *testing.T, config *ClientConfig) error {
|
||||
}
|
||||
return nil, errors.New("keyboard-interactive failed")
|
||||
},
|
||||
AuthLogCallback: func(conn ConnMetadata, method string, err error) {
|
||||
},
|
||||
}
|
||||
serverConfig.AddHostKey(testSigners["rsa"])
|
||||
|
||||
@@ -482,3 +480,100 @@ func TestClientAuthNone(t *testing.T) {
|
||||
t.Fatalf("server: got %q, want %q", serverConn.User(), user)
|
||||
}
|
||||
}
|
||||
|
||||
// Test if authentication attempts are limited on server when MaxAuthTries is set
|
||||
func TestClientAuthMaxAuthTries(t *testing.T) {
|
||||
user := "testuser"
|
||||
|
||||
serverConfig := &ServerConfig{
|
||||
MaxAuthTries: 2,
|
||||
PasswordCallback: func(conn ConnMetadata, pass []byte) (*Permissions, error) {
|
||||
if conn.User() == "testuser" && string(pass) == "right" {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, errors.New("password auth failed")
|
||||
},
|
||||
}
|
||||
serverConfig.AddHostKey(testSigners["rsa"])
|
||||
|
||||
expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{
|
||||
Reason: 2,
|
||||
Message: "too many authentication failures",
|
||||
})
|
||||
|
||||
for tries := 2; tries < 4; tries++ {
|
||||
n := tries
|
||||
clientConfig := &ClientConfig{
|
||||
User: user,
|
||||
Auth: []AuthMethod{
|
||||
RetryableAuthMethod(PasswordCallback(func() (string, error) {
|
||||
n--
|
||||
if n == 0 {
|
||||
return "right", nil
|
||||
} else {
|
||||
return "wrong", nil
|
||||
}
|
||||
}), tries),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go newServer(c1, serverConfig)
|
||||
_, _, _, err = NewClientConn(c2, "", clientConfig)
|
||||
if tries > 2 {
|
||||
if err == nil {
|
||||
t.Fatalf("client: got no error, want %s", expectedErr)
|
||||
} else if err.Error() != expectedErr.Error() {
|
||||
t.Fatalf("client: got %s, want %s", err, expectedErr)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Fatalf("client: got %s, want no error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test if authentication attempts are correctly limited on server
|
||||
// when more public keys are provided then MaxAuthTries
|
||||
func TestClientAuthMaxAuthTriesPublicKey(t *testing.T) {
|
||||
signers := []Signer{}
|
||||
for i := 0; i < 6; i++ {
|
||||
signers = append(signers, testSigners["dsa"])
|
||||
}
|
||||
|
||||
validConfig := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(append([]Signer{testSigners["rsa"]}, signers...)...),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
if err := tryAuth(t, validConfig); err != nil {
|
||||
t.Fatalf("unable to dial remote side: %s", err)
|
||||
}
|
||||
|
||||
expectedErr := fmt.Errorf("ssh: handshake failed: %v", &disconnectMsg{
|
||||
Reason: 2,
|
||||
Message: "too many authentication failures",
|
||||
})
|
||||
invalidConfig := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
PublicKeys(append(signers, testSigners["rsa"])...),
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
if err := tryAuth(t, invalidConfig); err == nil {
|
||||
t.Fatalf("client: got no error, want %s", expectedErr)
|
||||
} else if err.Error() != expectedErr.Error() {
|
||||
t.Fatalf("client: got %s, want %s", err, expectedErr)
|
||||
}
|
||||
}
|
||||
|
2
vendor/golang.org/x/crypto/ssh/client_test.go
generated
vendored
2
vendor/golang.org/x/crypto/ssh/client_test.go
generated
vendored
@@ -72,7 +72,7 @@ func TestHostKeyCheck(t *testing.T) {
|
||||
_, _, _, err = NewClientConn(c2, "", &clientConf)
|
||||
if err != nil {
|
||||
if tt.wantError == "" || !strings.Contains(err.Error(), tt.wantError) {
|
||||
t.Errorf("%s: got error %q, missing %q", err.Error(), tt.wantError)
|
||||
t.Errorf("%s: got error %q, missing %q", tt.name, err.Error(), tt.wantError)
|
||||
}
|
||||
} else if tt.wantError != "" {
|
||||
t.Errorf("%s: succeeded, but want error string %q", tt.name, tt.wantError)
|
||||
|
12
vendor/golang.org/x/crypto/ssh/common.go
generated
vendored
12
vendor/golang.org/x/crypto/ssh/common.go
generated
vendored
@@ -9,6 +9,7 @@ import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
|
||||
_ "crypto/sha1"
|
||||
@@ -186,7 +187,7 @@ type Config struct {
|
||||
|
||||
// The maximum number of bytes sent or received after which a
|
||||
// new key is negotiated. It must be at least 256. If
|
||||
// unspecified, 1 gigabyte is used.
|
||||
// unspecified, a size suitable for the chosen cipher is used.
|
||||
RekeyThreshold uint64
|
||||
|
||||
// The allowed key exchanges algorithms. If unspecified then a
|
||||
@@ -230,11 +231,12 @@ func (c *Config) SetDefaults() {
|
||||
}
|
||||
|
||||
if c.RekeyThreshold == 0 {
|
||||
// RFC 4253, section 9 suggests rekeying after 1G.
|
||||
c.RekeyThreshold = 1 << 30
|
||||
}
|
||||
if c.RekeyThreshold < minRekeyThreshold {
|
||||
// cipher specific default
|
||||
} else if c.RekeyThreshold < minRekeyThreshold {
|
||||
c.RekeyThreshold = minRekeyThreshold
|
||||
} else if c.RekeyThreshold >= math.MaxInt64 {
|
||||
// Avoid weirdness if somebody uses -1 as a threshold.
|
||||
c.RekeyThreshold = math.MaxInt64
|
||||
}
|
||||
}
|
||||
|
||||
|
39
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
39
vendor/golang.org/x/crypto/ssh/handshake.go
generated
vendored
@@ -107,6 +107,8 @@ func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion,
|
||||
|
||||
config: config,
|
||||
}
|
||||
t.resetReadThresholds()
|
||||
t.resetWriteThresholds()
|
||||
|
||||
// We always start with a mandatory key exchange.
|
||||
t.requestKex <- struct{}{}
|
||||
@@ -237,6 +239,17 @@ func (t *handshakeTransport) requestKeyExchange() {
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) resetWriteThresholds() {
|
||||
t.writePacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.writeBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
|
||||
} else {
|
||||
t.writeBytesLeft = 1 << 30
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) kexLoop() {
|
||||
|
||||
write:
|
||||
@@ -285,12 +298,8 @@ write:
|
||||
t.writeError = err
|
||||
t.sentInitPacket = nil
|
||||
t.sentInitMsg = nil
|
||||
t.writePacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.writeBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.writeBytesLeft = t.algorithms.w.rekeyBytes()
|
||||
}
|
||||
|
||||
t.resetWriteThresholds()
|
||||
|
||||
// we have completed the key exchange. Since the
|
||||
// reader is still blocked, it is safe to clear out
|
||||
@@ -344,6 +353,17 @@ write:
|
||||
// key exchange itself.
|
||||
const packetRekeyThreshold = (1 << 31)
|
||||
|
||||
func (t *handshakeTransport) resetReadThresholds() {
|
||||
t.readPacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.readBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else if t.algorithms != nil {
|
||||
t.readBytesLeft = t.algorithms.r.rekeyBytes()
|
||||
} else {
|
||||
t.readBytesLeft = 1 << 30
|
||||
}
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
||||
p, err := t.conn.readPacket()
|
||||
if err != nil {
|
||||
@@ -391,12 +411,7 @@ func (t *handshakeTransport) readOnePacket(first bool) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
t.readPacketsLeft = packetRekeyThreshold
|
||||
if t.config.RekeyThreshold > 0 {
|
||||
t.readBytesLeft = int64(t.config.RekeyThreshold)
|
||||
} else {
|
||||
t.readBytesLeft = t.algorithms.r.rekeyBytes()
|
||||
}
|
||||
t.resetReadThresholds()
|
||||
|
||||
// By default, a key exchange is hidden from higher layers by
|
||||
// translating it into msgIgnore.
|
||||
|
35
vendor/golang.org/x/crypto/ssh/handshake_test.go
generated
vendored
35
vendor/golang.org/x/crypto/ssh/handshake_test.go
generated
vendored
@@ -40,9 +40,12 @@ func (t *testChecker) Check(dialAddr string, addr net.Addr, key PublicKey) error
|
||||
// therefore is buffered (net.Pipe deadlocks if both sides start with
|
||||
// a write.)
|
||||
func netPipe() (net.Conn, net.Conn, error) {
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
listener, err = net.Listen("tcp", "[::1]:0")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
defer listener.Close()
|
||||
c1, err := net.Dial("tcp", listener.Addr().String())
|
||||
@@ -526,3 +529,31 @@ func TestDisconnect(t *testing.T) {
|
||||
t.Errorf("readPacket 3 succeeded")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandshakeRekeyDefault(t *testing.T) {
|
||||
clientConf := &ClientConfig{
|
||||
Config: Config{
|
||||
Ciphers: []string{"aes128-ctr"},
|
||||
},
|
||||
HostKeyCallback: InsecureIgnoreHostKey(),
|
||||
}
|
||||
trC, trS, err := handshakePair(clientConf, "addr", false)
|
||||
if err != nil {
|
||||
t.Fatalf("handshakePair: %v", err)
|
||||
}
|
||||
defer trC.Close()
|
||||
defer trS.Close()
|
||||
|
||||
trC.writePacket([]byte{msgRequestSuccess, 0, 0})
|
||||
trC.Close()
|
||||
|
||||
rgb := (1024 + trC.readBytesLeft) >> 30
|
||||
wgb := (1024 + trC.writeBytesLeft) >> 30
|
||||
|
||||
if rgb != 64 {
|
||||
t.Errorf("got rekey after %dG read, want 64G", rgb)
|
||||
}
|
||||
if wgb != 64 {
|
||||
t.Errorf("got rekey after %dG write, want 64G", wgb)
|
||||
}
|
||||
}
|
||||
|
94
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
94
vendor/golang.org/x/crypto/ssh/keys.go
generated
vendored
@@ -824,7 +824,7 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
||||
|
||||
// Implemented based on the documentation at
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||
func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
func parseOpenSSHPrivateKey(key []byte) (crypto.PrivateKey, error) {
|
||||
magic := append([]byte("openssh-key-v1"), 0)
|
||||
if !bytes.Equal(magic, key[0:len(magic)]) {
|
||||
return nil, errors.New("ssh: invalid openssh private key format")
|
||||
@@ -844,14 +844,15 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if w.KdfName != "none" || w.CipherName != "none" {
|
||||
return nil, errors.New("ssh: cannot decode encrypted private keys")
|
||||
}
|
||||
|
||||
pk1 := struct {
|
||||
Check1 uint32
|
||||
Check2 uint32
|
||||
Keytype string
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
Rest []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
|
||||
@@ -862,24 +863,75 @@ func parseOpenSSHPrivateKey(key []byte) (*ed25519.PrivateKey, error) {
|
||||
return nil, errors.New("ssh: checkint mismatch")
|
||||
}
|
||||
|
||||
// we only handle ed25519 keys currently
|
||||
if pk1.Keytype != KeyAlgoED25519 {
|
||||
// we only handle ed25519 and rsa keys currently
|
||||
switch pk1.Keytype {
|
||||
case KeyAlgoRSA:
|
||||
// https://github.com/openssh/openssh-portable/blob/master/sshkey.c#L2760-L2773
|
||||
key := struct {
|
||||
N *big.Int
|
||||
E *big.Int
|
||||
D *big.Int
|
||||
Iqmp *big.Int
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for i, b := range key.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
pk := &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
N: key.N,
|
||||
E: int(key.E.Int64()),
|
||||
},
|
||||
D: key.D,
|
||||
Primes: []*big.Int{key.P, key.Q},
|
||||
}
|
||||
|
||||
if err := pk.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk.Precompute()
|
||||
|
||||
return pk, nil
|
||||
case KeyAlgoED25519:
|
||||
key := struct {
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(pk1.Rest, &key); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(key.Priv) != ed25519.PrivateKeySize {
|
||||
return nil, errors.New("ssh: private key unexpected length")
|
||||
}
|
||||
|
||||
for i, b := range key.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
||||
copy(pk, key.Priv)
|
||||
return &pk, nil
|
||||
default:
|
||||
return nil, errors.New("ssh: unhandled key type")
|
||||
}
|
||||
|
||||
for i, b := range pk1.Pad {
|
||||
if int(b) != i+1 {
|
||||
return nil, errors.New("ssh: padding not as expected")
|
||||
}
|
||||
}
|
||||
|
||||
if len(pk1.Priv) != ed25519.PrivateKeySize {
|
||||
return nil, errors.New("ssh: private key unexpected length")
|
||||
}
|
||||
|
||||
pk := ed25519.PrivateKey(make([]byte, ed25519.PrivateKeySize))
|
||||
copy(pk, pk1.Priv)
|
||||
return &pk, nil
|
||||
}
|
||||
|
||||
// FingerprintLegacyMD5 returns the user presentation of the key's
|
||||
|
546
vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go
generated
vendored
Normal file
546
vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts.go
generated
vendored
Normal file
@@ -0,0 +1,546 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package knownhosts implements a parser for the OpenSSH
|
||||
// known_hosts host key database.
|
||||
package knownhosts
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha1"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// See the sshd manpage
|
||||
// (http://man.openbsd.org/sshd#SSH_KNOWN_HOSTS_FILE_FORMAT) for
|
||||
// background.
|
||||
|
||||
type addr struct{ host, port string }
|
||||
|
||||
func (a *addr) String() string {
|
||||
h := a.host
|
||||
if strings.Contains(h, ":") {
|
||||
h = "[" + h + "]"
|
||||
}
|
||||
return h + ":" + a.port
|
||||
}
|
||||
|
||||
type matcher interface {
|
||||
match([]addr) bool
|
||||
}
|
||||
|
||||
type hostPattern struct {
|
||||
negate bool
|
||||
addr addr
|
||||
}
|
||||
|
||||
func (p *hostPattern) String() string {
|
||||
n := ""
|
||||
if p.negate {
|
||||
n = "!"
|
||||
}
|
||||
|
||||
return n + p.addr.String()
|
||||
}
|
||||
|
||||
type hostPatterns []hostPattern
|
||||
|
||||
func (ps hostPatterns) match(addrs []addr) bool {
|
||||
matched := false
|
||||
for _, p := range ps {
|
||||
for _, a := range addrs {
|
||||
m := p.match(a)
|
||||
if !m {
|
||||
continue
|
||||
}
|
||||
if p.negate {
|
||||
return false
|
||||
}
|
||||
matched = true
|
||||
}
|
||||
}
|
||||
return matched
|
||||
}
|
||||
|
||||
// See
|
||||
// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/addrmatch.c
|
||||
// The matching of * has no regard for separators, unlike filesystem globs
|
||||
func wildcardMatch(pat []byte, str []byte) bool {
|
||||
for {
|
||||
if len(pat) == 0 {
|
||||
return len(str) == 0
|
||||
}
|
||||
if len(str) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
if pat[0] == '*' {
|
||||
if len(pat) == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
for j := range str {
|
||||
if wildcardMatch(pat[1:], str[j:]) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if pat[0] == '?' || pat[0] == str[0] {
|
||||
pat = pat[1:]
|
||||
str = str[1:]
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *hostPattern) match(a addr) bool {
|
||||
return wildcardMatch([]byte(l.addr.host), []byte(a.host)) && l.addr.port == a.port
|
||||
}
|
||||
|
||||
type keyDBLine struct {
|
||||
cert bool
|
||||
matcher matcher
|
||||
knownKey KnownKey
|
||||
}
|
||||
|
||||
func serialize(k ssh.PublicKey) string {
|
||||
return k.Type() + " " + base64.StdEncoding.EncodeToString(k.Marshal())
|
||||
}
|
||||
|
||||
func (l *keyDBLine) match(addrs []addr) bool {
|
||||
return l.matcher.match(addrs)
|
||||
}
|
||||
|
||||
type hostKeyDB struct {
|
||||
// Serialized version of revoked keys
|
||||
revoked map[string]*KnownKey
|
||||
lines []keyDBLine
|
||||
}
|
||||
|
||||
func newHostKeyDB() *hostKeyDB {
|
||||
db := &hostKeyDB{
|
||||
revoked: make(map[string]*KnownKey),
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func keyEq(a, b ssh.PublicKey) bool {
|
||||
return bytes.Equal(a.Marshal(), b.Marshal())
|
||||
}
|
||||
|
||||
// IsAuthorityForHost can be used as a callback in ssh.CertChecker
|
||||
func (db *hostKeyDB) IsHostAuthority(remote ssh.PublicKey, address string) bool {
|
||||
h, p, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
a := addr{host: h, port: p}
|
||||
|
||||
for _, l := range db.lines {
|
||||
if l.cert && keyEq(l.knownKey.Key, remote) && l.match([]addr{a}) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRevoked can be used as a callback in ssh.CertChecker
|
||||
func (db *hostKeyDB) IsRevoked(key *ssh.Certificate) bool {
|
||||
_, ok := db.revoked[string(key.Marshal())]
|
||||
return ok
|
||||
}
|
||||
|
||||
const markerCert = "@cert-authority"
|
||||
const markerRevoked = "@revoked"
|
||||
|
||||
func nextWord(line []byte) (string, []byte) {
|
||||
i := bytes.IndexAny(line, "\t ")
|
||||
if i == -1 {
|
||||
return string(line), nil
|
||||
}
|
||||
|
||||
return string(line[:i]), bytes.TrimSpace(line[i:])
|
||||
}
|
||||
|
||||
func parseLine(line []byte) (marker, host string, key ssh.PublicKey, err error) {
|
||||
if w, next := nextWord(line); w == markerCert || w == markerRevoked {
|
||||
marker = w
|
||||
line = next
|
||||
}
|
||||
|
||||
host, line = nextWord(line)
|
||||
if len(line) == 0 {
|
||||
return "", "", nil, errors.New("knownhosts: missing host pattern")
|
||||
}
|
||||
|
||||
// ignore the keytype as it's in the key blob anyway.
|
||||
_, line = nextWord(line)
|
||||
if len(line) == 0 {
|
||||
return "", "", nil, errors.New("knownhosts: missing key type pattern")
|
||||
}
|
||||
|
||||
keyBlob, _ := nextWord(line)
|
||||
|
||||
keyBytes, err := base64.StdEncoding.DecodeString(keyBlob)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
key, err = ssh.ParsePublicKey(keyBytes)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
return marker, host, key, nil
|
||||
}
|
||||
|
||||
func (db *hostKeyDB) parseLine(line []byte, filename string, linenum int) error {
|
||||
marker, pattern, key, err := parseLine(line)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if marker == markerRevoked {
|
||||
db.revoked[string(key.Marshal())] = &KnownKey{
|
||||
Key: key,
|
||||
Filename: filename,
|
||||
Line: linenum,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
entry := keyDBLine{
|
||||
cert: marker == markerCert,
|
||||
knownKey: KnownKey{
|
||||
Filename: filename,
|
||||
Line: linenum,
|
||||
Key: key,
|
||||
},
|
||||
}
|
||||
|
||||
if pattern[0] == '|' {
|
||||
entry.matcher, err = newHashedHost(pattern)
|
||||
} else {
|
||||
entry.matcher, err = newHostnameMatcher(pattern)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
db.lines = append(db.lines, entry)
|
||||
return nil
|
||||
}
|
||||
|
||||
func newHostnameMatcher(pattern string) (matcher, error) {
|
||||
var hps hostPatterns
|
||||
for _, p := range strings.Split(pattern, ",") {
|
||||
if len(p) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
var a addr
|
||||
var negate bool
|
||||
if p[0] == '!' {
|
||||
negate = true
|
||||
p = p[1:]
|
||||
}
|
||||
|
||||
if len(p) == 0 {
|
||||
return nil, errors.New("knownhosts: negation without following hostname")
|
||||
}
|
||||
|
||||
var err error
|
||||
if p[0] == '[' {
|
||||
a.host, a.port, err = net.SplitHostPort(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
a.host, a.port, err = net.SplitHostPort(p)
|
||||
if err != nil {
|
||||
a.host = p
|
||||
a.port = "22"
|
||||
}
|
||||
}
|
||||
hps = append(hps, hostPattern{
|
||||
negate: negate,
|
||||
addr: a,
|
||||
})
|
||||
}
|
||||
return hps, nil
|
||||
}
|
||||
|
||||
// KnownKey represents a key declared in a known_hosts file.
|
||||
type KnownKey struct {
|
||||
Key ssh.PublicKey
|
||||
Filename string
|
||||
Line int
|
||||
}
|
||||
|
||||
func (k *KnownKey) String() string {
|
||||
return fmt.Sprintf("%s:%d: %s", k.Filename, k.Line, serialize(k.Key))
|
||||
}
|
||||
|
||||
// KeyError is returned if we did not find the key in the host key
|
||||
// database, or there was a mismatch. Typically, in batch
|
||||
// applications, this should be interpreted as failure. Interactive
|
||||
// applications can offer an interactive prompt to the user.
|
||||
type KeyError struct {
|
||||
// Want holds the accepted host keys. For each key algorithm,
|
||||
// there can be one hostkey. If Want is empty, the host is
|
||||
// unknown. If Want is non-empty, there was a mismatch, which
|
||||
// can signify a MITM attack.
|
||||
Want []KnownKey
|
||||
}
|
||||
|
||||
func (u *KeyError) Error() string {
|
||||
if len(u.Want) == 0 {
|
||||
return "knownhosts: key is unknown"
|
||||
}
|
||||
return "knownhosts: key mismatch"
|
||||
}
|
||||
|
||||
// RevokedError is returned if we found a key that was revoked.
|
||||
type RevokedError struct {
|
||||
Revoked KnownKey
|
||||
}
|
||||
|
||||
func (r *RevokedError) Error() string {
|
||||
return "knownhosts: key is revoked"
|
||||
}
|
||||
|
||||
// check checks a key against the host database. This should not be
|
||||
// used for verifying certificates.
|
||||
func (db *hostKeyDB) check(address string, remote net.Addr, remoteKey ssh.PublicKey) error {
|
||||
if revoked := db.revoked[string(remoteKey.Marshal())]; revoked != nil {
|
||||
return &RevokedError{Revoked: *revoked}
|
||||
}
|
||||
|
||||
host, port, err := net.SplitHostPort(remote.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", remote, err)
|
||||
}
|
||||
|
||||
addrs := []addr{
|
||||
{host, port},
|
||||
}
|
||||
|
||||
if address != "" {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("knownhosts: SplitHostPort(%s): %v", address, err)
|
||||
}
|
||||
|
||||
addrs = append(addrs, addr{host, port})
|
||||
}
|
||||
|
||||
return db.checkAddrs(addrs, remoteKey)
|
||||
}
|
||||
|
||||
// checkAddrs checks if we can find the given public key for any of
|
||||
// the given addresses. If we only find an entry for the IP address,
|
||||
// or only the hostname, then this still succeeds.
|
||||
func (db *hostKeyDB) checkAddrs(addrs []addr, remoteKey ssh.PublicKey) error {
|
||||
// TODO(hanwen): are these the right semantics? What if there
|
||||
// is just a key for the IP address, but not for the
|
||||
// hostname?
|
||||
|
||||
// Algorithm => key.
|
||||
knownKeys := map[string]KnownKey{}
|
||||
for _, l := range db.lines {
|
||||
if l.match(addrs) {
|
||||
typ := l.knownKey.Key.Type()
|
||||
if _, ok := knownKeys[typ]; !ok {
|
||||
knownKeys[typ] = l.knownKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keyErr := &KeyError{}
|
||||
for _, v := range knownKeys {
|
||||
keyErr.Want = append(keyErr.Want, v)
|
||||
}
|
||||
|
||||
// Unknown remote host.
|
||||
if len(knownKeys) == 0 {
|
||||
return keyErr
|
||||
}
|
||||
|
||||
// If the remote host starts using a different, unknown key type, we
|
||||
// also interpret that as a mismatch.
|
||||
if known, ok := knownKeys[remoteKey.Type()]; !ok || !keyEq(known.Key, remoteKey) {
|
||||
return keyErr
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// The Read function parses file contents.
|
||||
func (db *hostKeyDB) Read(r io.Reader, filename string) error {
|
||||
scanner := bufio.NewScanner(r)
|
||||
|
||||
lineNum := 0
|
||||
for scanner.Scan() {
|
||||
lineNum++
|
||||
line := scanner.Bytes()
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == 0 || line[0] == '#' {
|
||||
continue
|
||||
}
|
||||
|
||||
if err := db.parseLine(line, filename, lineNum); err != nil {
|
||||
return fmt.Errorf("knownhosts: %s:%d: %v", filename, lineNum, err)
|
||||
}
|
||||
}
|
||||
return scanner.Err()
|
||||
}
|
||||
|
||||
// New creates a host key callback from the given OpenSSH host key
|
||||
// files. The returned callback is for use in
|
||||
// ssh.ClientConfig.HostKeyCallback. Hashed hostnames are not supported.
|
||||
func New(files ...string) (ssh.HostKeyCallback, error) {
|
||||
db := newHostKeyDB()
|
||||
for _, fn := range files {
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
if err := db.Read(f, fn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var certChecker ssh.CertChecker
|
||||
certChecker.IsHostAuthority = db.IsHostAuthority
|
||||
certChecker.IsRevoked = db.IsRevoked
|
||||
certChecker.HostKeyFallback = db.check
|
||||
|
||||
return certChecker.CheckHostKey, nil
|
||||
}
|
||||
|
||||
// Normalize normalizes an address into the form used in known_hosts
|
||||
func Normalize(address string) string {
|
||||
host, port, err := net.SplitHostPort(address)
|
||||
if err != nil {
|
||||
host = address
|
||||
port = "22"
|
||||
}
|
||||
entry := host
|
||||
if port != "22" {
|
||||
entry = "[" + entry + "]:" + port
|
||||
} else if strings.Contains(host, ":") && !strings.HasPrefix(host, "[") {
|
||||
entry = "[" + entry + "]"
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// Line returns a line to add append to the known_hosts files.
|
||||
func Line(addresses []string, key ssh.PublicKey) string {
|
||||
var trimmed []string
|
||||
for _, a := range addresses {
|
||||
trimmed = append(trimmed, Normalize(a))
|
||||
}
|
||||
|
||||
return strings.Join(trimmed, ",") + " " + serialize(key)
|
||||
}
|
||||
|
||||
// HashHostname hashes the given hostname. The hostname is not
|
||||
// normalized before hashing.
|
||||
func HashHostname(hostname string) string {
|
||||
// TODO(hanwen): check if we can safely normalize this always.
|
||||
salt := make([]byte, sha1.Size)
|
||||
|
||||
_, err := rand.Read(salt)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("crypto/rand failure %v", err))
|
||||
}
|
||||
|
||||
hash := hashHost(hostname, salt)
|
||||
return encodeHash(sha1HashType, salt, hash)
|
||||
}
|
||||
|
||||
func decodeHash(encoded string) (hashType string, salt, hash []byte, err error) {
|
||||
if len(encoded) == 0 || encoded[0] != '|' {
|
||||
err = errors.New("knownhosts: hashed host must start with '|'")
|
||||
return
|
||||
}
|
||||
components := strings.Split(encoded, "|")
|
||||
if len(components) != 4 {
|
||||
err = fmt.Errorf("knownhosts: got %d components, want 3", len(components))
|
||||
return
|
||||
}
|
||||
|
||||
hashType = components[1]
|
||||
if salt, err = base64.StdEncoding.DecodeString(components[2]); err != nil {
|
||||
return
|
||||
}
|
||||
if hash, err = base64.StdEncoding.DecodeString(components[3]); err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func encodeHash(typ string, salt []byte, hash []byte) string {
|
||||
return strings.Join([]string{"",
|
||||
typ,
|
||||
base64.StdEncoding.EncodeToString(salt),
|
||||
base64.StdEncoding.EncodeToString(hash),
|
||||
}, "|")
|
||||
}
|
||||
|
||||
// See https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
|
||||
func hashHost(hostname string, salt []byte) []byte {
|
||||
mac := hmac.New(sha1.New, salt)
|
||||
mac.Write([]byte(hostname))
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
type hashedHost struct {
|
||||
salt []byte
|
||||
hash []byte
|
||||
}
|
||||
|
||||
const sha1HashType = "1"
|
||||
|
||||
func newHashedHost(encoded string) (*hashedHost, error) {
|
||||
typ, salt, hash, err := decodeHash(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// The type field seems for future algorithm agility, but it's
|
||||
// actually hardcoded in openssh currently, see
|
||||
// https://android.googlesource.com/platform/external/openssh/+/ab28f5495c85297e7a597c1ba62e996416da7c7e/hostfile.c#120
|
||||
if typ != sha1HashType {
|
||||
return nil, fmt.Errorf("knownhosts: got hash type %s, must be '1'", typ)
|
||||
}
|
||||
|
||||
return &hashedHost{salt: salt, hash: hash}, nil
|
||||
}
|
||||
|
||||
func (h *hashedHost) match(addrs []addr) bool {
|
||||
for _, a := range addrs {
|
||||
if bytes.Equal(hashHost(Normalize(a.String()), h.salt), h.hash) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
329
vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go
generated
vendored
Normal file
329
vendor/golang.org/x/crypto/ssh/knownhosts/knownhosts_test.go
generated
vendored
Normal file
@@ -0,0 +1,329 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package knownhosts
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
const edKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGBAarftlLeoyf+v+nVchEZII/vna2PCV8FaX4vsF5BX"
|
||||
const alternateEdKeyStr = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIXffBYeYL+WVzVru8npl5JHt2cjlr4ornFTWzoij9sx"
|
||||
const ecKeyStr = "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBNLCu01+wpXe3xB5olXCN4SqU2rQu0qjSRKJO4Bg+JRCPU+ENcgdA5srTU8xYDz/GEa4dzK5ldPw4J/gZgSXCMs="
|
||||
|
||||
var ecKey, alternateEdKey, edKey ssh.PublicKey
|
||||
var testAddr = &net.TCPAddr{
|
||||
IP: net.IP{198, 41, 30, 196},
|
||||
Port: 22,
|
||||
}
|
||||
|
||||
var testAddr6 = &net.TCPAddr{
|
||||
IP: net.IP{198, 41, 30, 196,
|
||||
1, 2, 3, 4,
|
||||
1, 2, 3, 4,
|
||||
1, 2, 3, 4,
|
||||
},
|
||||
Port: 22,
|
||||
}
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
ecKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(ecKeyStr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
edKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(edKeyStr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
alternateEdKey, _, _, _, err = ssh.ParseAuthorizedKey([]byte(alternateEdKeyStr))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testDB(t *testing.T, s string) *hostKeyDB {
|
||||
db := newHostKeyDB()
|
||||
if err := db.Read(bytes.NewBufferString(s), "testdb"); err != nil {
|
||||
t.Fatalf("Read: %v", err)
|
||||
}
|
||||
|
||||
return db
|
||||
}
|
||||
|
||||
func TestRevoked(t *testing.T) {
|
||||
db := testDB(t, "\n\n@revoked * "+edKeyStr+"\n")
|
||||
want := &RevokedError{
|
||||
Revoked: KnownKey{
|
||||
Key: edKey,
|
||||
Filename: "testdb",
|
||||
Line: 3,
|
||||
},
|
||||
}
|
||||
if err := db.check("", &net.TCPAddr{
|
||||
Port: 42,
|
||||
}, edKey); err == nil {
|
||||
t.Fatal("no error for revoked key")
|
||||
} else if !reflect.DeepEqual(want, err) {
|
||||
t.Fatalf("got %#v, want %#v", want, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostAuthority(t *testing.T) {
|
||||
for _, m := range []struct {
|
||||
authorityFor string
|
||||
address string
|
||||
|
||||
good bool
|
||||
}{
|
||||
{authorityFor: "localhost", address: "localhost:22", good: true},
|
||||
{authorityFor: "localhost", address: "localhost", good: false},
|
||||
{authorityFor: "localhost", address: "localhost:1234", good: false},
|
||||
{authorityFor: "[localhost]:1234", address: "localhost:1234", good: true},
|
||||
{authorityFor: "[localhost]:1234", address: "localhost:22", good: false},
|
||||
{authorityFor: "[localhost]:1234", address: "localhost", good: false},
|
||||
} {
|
||||
db := testDB(t, `@cert-authority `+m.authorityFor+` `+edKeyStr)
|
||||
if ok := db.IsHostAuthority(db.lines[0].knownKey.Key, m.address); ok != m.good {
|
||||
t.Errorf("IsHostAuthority: authority %s, address %s, wanted good = %v, got good = %v",
|
||||
m.authorityFor, m.address, m.good, ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBracket(t *testing.T) {
|
||||
db := testDB(t, `[git.eclipse.org]:29418,[198.41.30.196]:29418 `+edKeyStr)
|
||||
|
||||
if err := db.check("git.eclipse.org:29418", &net.TCPAddr{
|
||||
IP: net.IP{198, 41, 30, 196},
|
||||
Port: 29418,
|
||||
}, edKey); err != nil {
|
||||
t.Errorf("got error %v, want none", err)
|
||||
}
|
||||
|
||||
if err := db.check("git.eclipse.org:29419", &net.TCPAddr{
|
||||
Port: 42,
|
||||
}, edKey); err == nil {
|
||||
t.Fatalf("no error for unknown address")
|
||||
} else if ke, ok := err.(*KeyError); !ok {
|
||||
t.Fatalf("got type %T, want *KeyError", err)
|
||||
} else if len(ke.Want) > 0 {
|
||||
t.Fatalf("got Want %v, want []", ke.Want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewKeyType(t *testing.T) {
|
||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check("", testAddr, ecKey); err == nil {
|
||||
t.Fatalf("no error for unknown address")
|
||||
} else if ke, ok := err.(*KeyError); !ok {
|
||||
t.Fatalf("got type %T, want *KeyError", err)
|
||||
} else if len(ke.Want) == 0 {
|
||||
t.Fatalf("got empty KeyError.Want")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSameKeyType(t *testing.T) {
|
||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check("", testAddr, alternateEdKey); err == nil {
|
||||
t.Fatalf("no error for unknown address")
|
||||
} else if ke, ok := err.(*KeyError); !ok {
|
||||
t.Fatalf("got type %T, want *KeyError", err)
|
||||
} else if len(ke.Want) == 0 {
|
||||
t.Fatalf("got empty KeyError.Want")
|
||||
} else if got, want := ke.Want[0].Key.Marshal(), edKey.Marshal(); !bytes.Equal(got, want) {
|
||||
t.Fatalf("got key %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPAddress(t *testing.T) {
|
||||
str := fmt.Sprintf("%s %s", testAddr, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check("", testAddr, edKey); err != nil {
|
||||
t.Errorf("got error %q, want none", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIPv6Address(t *testing.T) {
|
||||
str := fmt.Sprintf("%s %s", testAddr6, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
|
||||
if err := db.check("", testAddr6, edKey); err != nil {
|
||||
t.Errorf("got error %q, want none", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
str := fmt.Sprintf("#comment\n\nserver.org,%s %s\notherhost %s", testAddr, edKeyStr, ecKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check("server.org:22", testAddr, edKey); err != nil {
|
||||
t.Errorf("got error %q, want none", err)
|
||||
}
|
||||
|
||||
want := KnownKey{
|
||||
Key: edKey,
|
||||
Filename: "testdb",
|
||||
Line: 3,
|
||||
}
|
||||
if err := db.check("server.org:22", testAddr, ecKey); err == nil {
|
||||
t.Errorf("succeeded, want KeyError")
|
||||
} else if ke, ok := err.(*KeyError); !ok {
|
||||
t.Errorf("got %T, want *KeyError", err)
|
||||
} else if len(ke.Want) != 1 {
|
||||
t.Errorf("got %v, want 1 entry", ke)
|
||||
} else if !reflect.DeepEqual(ke.Want[0], want) {
|
||||
t.Errorf("got %v, want %v", ke.Want[0], want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNegate(t *testing.T) {
|
||||
str := fmt.Sprintf("%s,!server.org %s", testAddr, edKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check("server.org:22", testAddr, ecKey); err == nil {
|
||||
t.Errorf("succeeded")
|
||||
} else if ke, ok := err.(*KeyError); !ok {
|
||||
t.Errorf("got error type %T, want *KeyError", err)
|
||||
} else if len(ke.Want) != 0 {
|
||||
t.Errorf("got expected keys %d (first of type %s), want []", len(ke.Want), ke.Want[0].Key.Type())
|
||||
}
|
||||
}
|
||||
|
||||
func TestWildcard(t *testing.T) {
|
||||
str := fmt.Sprintf("server*.domain %s", edKeyStr)
|
||||
db := testDB(t, str)
|
||||
|
||||
want := &KeyError{
|
||||
Want: []KnownKey{{
|
||||
Filename: "testdb",
|
||||
Line: 1,
|
||||
Key: edKey,
|
||||
}},
|
||||
}
|
||||
|
||||
got := db.check("server.domain:22", &net.TCPAddr{}, ecKey)
|
||||
if !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLine(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"server.org": "server.org " + edKeyStr,
|
||||
"server.org:22": "server.org " + edKeyStr,
|
||||
"server.org:23": "[server.org]:23 " + edKeyStr,
|
||||
"[c629:1ec4:102:304:102:304:102:304]:22": "[c629:1ec4:102:304:102:304:102:304] " + edKeyStr,
|
||||
"[c629:1ec4:102:304:102:304:102:304]:23": "[c629:1ec4:102:304:102:304:102:304]:23 " + edKeyStr,
|
||||
} {
|
||||
if got := Line([]string{in}, edKey); got != want {
|
||||
t.Errorf("Line(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestWildcardMatch(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
pat, str string
|
||||
want bool
|
||||
}{
|
||||
{"a?b", "abb", true},
|
||||
{"ab", "abc", false},
|
||||
{"abc", "ab", false},
|
||||
{"a*b", "axxxb", true},
|
||||
{"a*b", "axbxb", true},
|
||||
{"a*b", "axbxbc", false},
|
||||
{"a*?", "axbxc", true},
|
||||
{"a*b*", "axxbxxxxxx", true},
|
||||
{"a*b*c", "axxbxxxxxxc", true},
|
||||
{"a*b*?", "axxbxxxxxxc", true},
|
||||
{"a*b*z", "axxbxxbxxxz", true},
|
||||
{"a*b*z", "axxbxxzxxxz", true},
|
||||
{"a*b*z", "axxbxxzxxx", false},
|
||||
} {
|
||||
got := wildcardMatch([]byte(c.pat), []byte(c.str))
|
||||
if got != c.want {
|
||||
t.Errorf("wildcardMatch(%q, %q) = %v, want %v", c.pat, c.str, got, c.want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(hanwen): test coverage for certificates.
|
||||
|
||||
const testHostname = "hostname"
|
||||
|
||||
// generated with keygen -H -f
|
||||
const encodedTestHostnameHash = "|1|IHXZvQMvTcZTUU29+2vXFgx8Frs=|UGccIWfRVDwilMBnA3WJoRAC75Y="
|
||||
|
||||
func TestHostHash(t *testing.T) {
|
||||
testHostHash(t, testHostname, encodedTestHostnameHash)
|
||||
}
|
||||
|
||||
func TestHashList(t *testing.T) {
|
||||
encoded := HashHostname(testHostname)
|
||||
testHostHash(t, testHostname, encoded)
|
||||
}
|
||||
|
||||
func testHostHash(t *testing.T, hostname, encoded string) {
|
||||
typ, salt, hash, err := decodeHash(encoded)
|
||||
if err != nil {
|
||||
t.Fatalf("decodeHash: %v", err)
|
||||
}
|
||||
|
||||
if got := encodeHash(typ, salt, hash); got != encoded {
|
||||
t.Errorf("got encoding %s want %s", got, encoded)
|
||||
}
|
||||
|
||||
if typ != sha1HashType {
|
||||
t.Fatalf("got hash type %q, want %q", typ, sha1HashType)
|
||||
}
|
||||
|
||||
got := hashHost(hostname, salt)
|
||||
if !bytes.Equal(got, hash) {
|
||||
t.Errorf("got hash %x want %x", got, hash)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalize(t *testing.T) {
|
||||
for in, want := range map[string]string{
|
||||
"127.0.0.1:22": "127.0.0.1",
|
||||
"[127.0.0.1]:22": "127.0.0.1",
|
||||
"[127.0.0.1]:23": "[127.0.0.1]:23",
|
||||
"127.0.0.1:23": "[127.0.0.1]:23",
|
||||
"[a.b.c]:22": "a.b.c",
|
||||
"[abcd:abcd:abcd:abcd]": "[abcd:abcd:abcd:abcd]",
|
||||
"[abcd:abcd:abcd:abcd]:22": "[abcd:abcd:abcd:abcd]",
|
||||
"[abcd:abcd:abcd:abcd]:23": "[abcd:abcd:abcd:abcd]:23",
|
||||
} {
|
||||
got := Normalize(in)
|
||||
if got != want {
|
||||
t.Errorf("Normalize(%q) = %q, want %q", in, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashedHostkeyCheck(t *testing.T) {
|
||||
str := fmt.Sprintf("%s %s", HashHostname(testHostname), edKeyStr)
|
||||
db := testDB(t, str)
|
||||
if err := db.check(testHostname+":22", testAddr, edKey); err != nil {
|
||||
t.Errorf("check(%s): %v", testHostname, err)
|
||||
}
|
||||
want := &KeyError{
|
||||
Want: []KnownKey{{
|
||||
Filename: "testdb",
|
||||
Line: 1,
|
||||
Key: edKey,
|
||||
}},
|
||||
}
|
||||
if got := db.check(testHostname+":22", testAddr, alternateEdKey); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("got error %v, want %v", got, want)
|
||||
}
|
||||
}
|
33
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
33
vendor/golang.org/x/crypto/ssh/server.go
generated
vendored
@@ -45,6 +45,12 @@ type ServerConfig struct {
|
||||
// authenticating.
|
||||
NoClientAuth bool
|
||||
|
||||
// MaxAuthTries specifies the maximum number of authentication attempts
|
||||
// permitted per connection. If set to a negative number, the number of
|
||||
// attempts are unlimited. If set to zero, the number of attempts are limited
|
||||
// to 6.
|
||||
MaxAuthTries int
|
||||
|
||||
// PasswordCallback, if non-nil, is called when a user
|
||||
// attempts to authenticate using a password.
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
|
||||
@@ -143,6 +149,10 @@ type ServerConn struct {
|
||||
func NewServerConn(c net.Conn, config *ServerConfig) (*ServerConn, <-chan NewChannel, <-chan *Request, error) {
|
||||
fullConf := *config
|
||||
fullConf.SetDefaults()
|
||||
if fullConf.MaxAuthTries == 0 {
|
||||
fullConf.MaxAuthTries = 6
|
||||
}
|
||||
|
||||
s := &connection{
|
||||
sshConn: sshConn{conn: c},
|
||||
}
|
||||
@@ -267,8 +277,23 @@ func (s *connection) serverAuthenticate(config *ServerConfig) (*Permissions, err
|
||||
var cache pubKeyCache
|
||||
var perms *Permissions
|
||||
|
||||
authFailures := 0
|
||||
|
||||
userAuthLoop:
|
||||
for {
|
||||
if authFailures >= config.MaxAuthTries && config.MaxAuthTries > 0 {
|
||||
discMsg := &disconnectMsg{
|
||||
Reason: 2,
|
||||
Message: "too many authentication failures",
|
||||
}
|
||||
|
||||
if err := s.transport.writePacket(Marshal(discMsg)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return nil, discMsg
|
||||
}
|
||||
|
||||
var userAuthReq userAuthRequestMsg
|
||||
if packet, err := s.transport.readPacket(); err != nil {
|
||||
return nil, err
|
||||
@@ -289,6 +314,11 @@ userAuthLoop:
|
||||
if config.NoClientAuth {
|
||||
authErr = nil
|
||||
}
|
||||
|
||||
// allow initial attempt of 'none' without penalty
|
||||
if authFailures == 0 {
|
||||
authFailures--
|
||||
}
|
||||
case "password":
|
||||
if config.PasswordCallback == nil {
|
||||
authErr = errors.New("ssh: password auth not configured")
|
||||
@@ -360,6 +390,7 @@ userAuthLoop:
|
||||
if isQuery {
|
||||
// The client can query if the given public key
|
||||
// would be okay.
|
||||
|
||||
if len(payload) > 0 {
|
||||
return nil, parseError(msgUserAuthRequest)
|
||||
}
|
||||
@@ -409,6 +440,8 @@ userAuthLoop:
|
||||
break userAuthLoop
|
||||
}
|
||||
|
||||
authFailures++
|
||||
|
||||
var failureMsg userAuthFailureMsg
|
||||
if config.PasswordCallback != nil {
|
||||
failureMsg.Methods = append(failureMsg.Methods, "password")
|
||||
|
115
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
Normal file
115
vendor/golang.org/x/crypto/ssh/streamlocal.go
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// streamLocalChannelOpenDirectMsg is a struct used for SSH_MSG_CHANNEL_OPEN message
|
||||
// with "direct-streamlocal@openssh.com" string.
|
||||
//
|
||||
// See openssh-portable/PROTOCOL, section 2.4. connection: Unix domain socket forwarding
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL#L235
|
||||
type streamLocalChannelOpenDirectMsg struct {
|
||||
socketPath string
|
||||
reserved0 string
|
||||
reserved1 uint32
|
||||
}
|
||||
|
||||
// forwardedStreamLocalPayload is a struct used for SSH_MSG_CHANNEL_OPEN message
|
||||
// with "forwarded-streamlocal@openssh.com" string.
|
||||
type forwardedStreamLocalPayload struct {
|
||||
SocketPath string
|
||||
Reserved0 string
|
||||
}
|
||||
|
||||
// streamLocalChannelForwardMsg is a struct used for SSH2_MSG_GLOBAL_REQUEST message
|
||||
// with "streamlocal-forward@openssh.com"/"cancel-streamlocal-forward@openssh.com" string.
|
||||
type streamLocalChannelForwardMsg struct {
|
||||
socketPath string
|
||||
}
|
||||
|
||||
// ListenUnix is similar to ListenTCP but uses a Unix domain socket.
|
||||
func (c *Client) ListenUnix(socketPath string) (net.Listener, error) {
|
||||
m := streamLocalChannelForwardMsg{
|
||||
socketPath,
|
||||
}
|
||||
// send message
|
||||
ok, _, err := c.SendRequest("streamlocal-forward@openssh.com", true, Marshal(&m))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !ok {
|
||||
return nil, errors.New("ssh: streamlocal-forward@openssh.com request denied by peer")
|
||||
}
|
||||
ch := c.forwards.add(&net.UnixAddr{Name: socketPath, Net: "unix"})
|
||||
|
||||
return &unixListener{socketPath, c, ch}, nil
|
||||
}
|
||||
|
||||
func (c *Client) dialStreamLocal(socketPath string) (Channel, error) {
|
||||
msg := streamLocalChannelOpenDirectMsg{
|
||||
socketPath: socketPath,
|
||||
}
|
||||
ch, in, err := c.OpenChannel("direct-streamlocal@openssh.com", Marshal(&msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(in)
|
||||
return ch, err
|
||||
}
|
||||
|
||||
type unixListener struct {
|
||||
socketPath string
|
||||
|
||||
conn *Client
|
||||
in <-chan forward
|
||||
}
|
||||
|
||||
// Accept waits for and returns the next connection to the listener.
|
||||
func (l *unixListener) Accept() (net.Conn, error) {
|
||||
s, ok := <-l.in
|
||||
if !ok {
|
||||
return nil, io.EOF
|
||||
}
|
||||
ch, incoming, err := s.newCh.Accept()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(incoming)
|
||||
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: &net.UnixAddr{
|
||||
Name: l.socketPath,
|
||||
Net: "unix",
|
||||
},
|
||||
raddr: &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close closes the listener.
|
||||
func (l *unixListener) Close() error {
|
||||
// this also closes the listener.
|
||||
l.conn.forwards.remove(&net.UnixAddr{Name: l.socketPath, Net: "unix"})
|
||||
m := streamLocalChannelForwardMsg{
|
||||
l.socketPath,
|
||||
}
|
||||
ok, _, err := l.conn.SendRequest("cancel-streamlocal-forward@openssh.com", true, Marshal(&m))
|
||||
if err == nil && !ok {
|
||||
err = errors.New("ssh: cancel-streamlocal-forward@openssh.com failed")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Addr returns the listener's network address.
|
||||
func (l *unixListener) Addr() net.Addr {
|
||||
return &net.UnixAddr{
|
||||
Name: l.socketPath,
|
||||
Net: "unix",
|
||||
}
|
||||
}
|
196
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
196
vendor/golang.org/x/crypto/ssh/tcpip.go
generated
vendored
@@ -20,12 +20,20 @@ import (
|
||||
// addr. Incoming connections will be available by calling Accept on
|
||||
// the returned net.Listener. The listener must be serviced, or the
|
||||
// SSH connection may hang.
|
||||
// N must be "tcp", "tcp4", "tcp6", or "unix".
|
||||
func (c *Client) Listen(n, addr string) (net.Listener, error) {
|
||||
laddr, err := net.ResolveTCPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
switch n {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
laddr, err := net.ResolveTCPAddr(n, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.ListenTCP(laddr)
|
||||
case "unix":
|
||||
return c.ListenUnix(addr)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
|
||||
}
|
||||
return c.ListenTCP(laddr)
|
||||
}
|
||||
|
||||
// Automatic port allocation is broken with OpenSSH before 6.0. See
|
||||
@@ -116,7 +124,7 @@ func (c *Client) ListenTCP(laddr *net.TCPAddr) (net.Listener, error) {
|
||||
}
|
||||
|
||||
// Register this forward, using the port number we obtained.
|
||||
ch := c.forwards.add(*laddr)
|
||||
ch := c.forwards.add(laddr)
|
||||
|
||||
return &tcpListener{laddr, c, ch}, nil
|
||||
}
|
||||
@@ -131,7 +139,7 @@ type forwardList struct {
|
||||
// forwardEntry represents an established mapping of a laddr on a
|
||||
// remote ssh server to a channel connected to a tcpListener.
|
||||
type forwardEntry struct {
|
||||
laddr net.TCPAddr
|
||||
laddr net.Addr
|
||||
c chan forward
|
||||
}
|
||||
|
||||
@@ -139,16 +147,16 @@ type forwardEntry struct {
|
||||
// arguments to add/remove/lookup should be address as specified in
|
||||
// the original forward-request.
|
||||
type forward struct {
|
||||
newCh NewChannel // the ssh client channel underlying this forward
|
||||
raddr *net.TCPAddr // the raddr of the incoming connection
|
||||
newCh NewChannel // the ssh client channel underlying this forward
|
||||
raddr net.Addr // the raddr of the incoming connection
|
||||
}
|
||||
|
||||
func (l *forwardList) add(addr net.TCPAddr) chan forward {
|
||||
func (l *forwardList) add(addr net.Addr) chan forward {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
f := forwardEntry{
|
||||
addr,
|
||||
make(chan forward, 1),
|
||||
laddr: addr,
|
||||
c: make(chan forward, 1),
|
||||
}
|
||||
l.entries = append(l.entries, f)
|
||||
return f.c
|
||||
@@ -176,44 +184,69 @@ func parseTCPAddr(addr string, port uint32) (*net.TCPAddr, error) {
|
||||
|
||||
func (l *forwardList) handleChannels(in <-chan NewChannel) {
|
||||
for ch := range in {
|
||||
var payload forwardedTCPPayload
|
||||
if err := Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
var (
|
||||
laddr net.Addr
|
||||
raddr net.Addr
|
||||
err error
|
||||
)
|
||||
switch channelType := ch.ChannelType(); channelType {
|
||||
case "forwarded-tcpip":
|
||||
var payload forwardedTCPPayload
|
||||
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-tcpip payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
// RFC 4254 section 7.2 specifies that incoming
|
||||
// addresses should list the address, in string
|
||||
// format. It is implied that this should be an IP
|
||||
// address, as it would be impossible to connect to it
|
||||
// otherwise.
|
||||
laddr, err := parseTCPAddr(payload.Addr, payload.Port)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
raddr, err := parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
// RFC 4254 section 7.2 specifies that incoming
|
||||
// addresses should list the address, in string
|
||||
// format. It is implied that this should be an IP
|
||||
// address, as it would be impossible to connect to it
|
||||
// otherwise.
|
||||
laddr, err = parseTCPAddr(payload.Addr, payload.Port)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
raddr, err = parseTCPAddr(payload.OriginAddr, payload.OriginPort)
|
||||
if err != nil {
|
||||
ch.Reject(ConnectionFailed, err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
if ok := l.forward(*laddr, *raddr, ch); !ok {
|
||||
case "forwarded-streamlocal@openssh.com":
|
||||
var payload forwardedStreamLocalPayload
|
||||
if err = Unmarshal(ch.ExtraData(), &payload); err != nil {
|
||||
ch.Reject(ConnectionFailed, "could not parse forwarded-streamlocal@openssh.com payload: "+err.Error())
|
||||
continue
|
||||
}
|
||||
laddr = &net.UnixAddr{
|
||||
Name: payload.SocketPath,
|
||||
Net: "unix",
|
||||
}
|
||||
raddr = &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("ssh: unknown channel type %s", channelType))
|
||||
}
|
||||
if ok := l.forward(laddr, raddr, ch); !ok {
|
||||
// Section 7.2, implementations MUST reject spurious incoming
|
||||
// connections.
|
||||
ch.Reject(Prohibited, "no forward for address")
|
||||
continue
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// remove removes the forward entry, and the channel feeding its
|
||||
// listener.
|
||||
func (l *forwardList) remove(addr net.TCPAddr) {
|
||||
func (l *forwardList) remove(addr net.Addr) {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
for i, f := range l.entries {
|
||||
if addr.IP.Equal(f.laddr.IP) && addr.Port == f.laddr.Port {
|
||||
if addr.Network() == f.laddr.Network() && addr.String() == f.laddr.String() {
|
||||
l.entries = append(l.entries[:i], l.entries[i+1:]...)
|
||||
close(f.c)
|
||||
return
|
||||
@@ -231,12 +264,12 @@ func (l *forwardList) closeAll() {
|
||||
l.entries = nil
|
||||
}
|
||||
|
||||
func (l *forwardList) forward(laddr, raddr net.TCPAddr, ch NewChannel) bool {
|
||||
func (l *forwardList) forward(laddr, raddr net.Addr, ch NewChannel) bool {
|
||||
l.Lock()
|
||||
defer l.Unlock()
|
||||
for _, f := range l.entries {
|
||||
if laddr.IP.Equal(f.laddr.IP) && laddr.Port == f.laddr.Port {
|
||||
f.c <- forward{ch, &raddr}
|
||||
if laddr.Network() == f.laddr.Network() && laddr.String() == f.laddr.String() {
|
||||
f.c <- forward{newCh: ch, raddr: raddr}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -262,7 +295,7 @@ func (l *tcpListener) Accept() (net.Conn, error) {
|
||||
}
|
||||
go DiscardRequests(incoming)
|
||||
|
||||
return &tcpChanConn{
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: l.laddr,
|
||||
raddr: s.raddr,
|
||||
@@ -277,7 +310,7 @@ func (l *tcpListener) Close() error {
|
||||
}
|
||||
|
||||
// this also closes the listener.
|
||||
l.conn.forwards.remove(*l.laddr)
|
||||
l.conn.forwards.remove(l.laddr)
|
||||
ok, _, err := l.conn.SendRequest("cancel-tcpip-forward", true, Marshal(&m))
|
||||
if err == nil && !ok {
|
||||
err = errors.New("ssh: cancel-tcpip-forward failed")
|
||||
@@ -293,29 +326,52 @@ func (l *tcpListener) Addr() net.Addr {
|
||||
// Dial initiates a connection to the addr from the remote host.
|
||||
// The resulting connection has a zero LocalAddr() and RemoteAddr().
|
||||
func (c *Client) Dial(n, addr string) (net.Conn, error) {
|
||||
// Parse the address into host and numeric port.
|
||||
host, portString, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var ch Channel
|
||||
switch n {
|
||||
case "tcp", "tcp4", "tcp6":
|
||||
// Parse the address into host and numeric port.
|
||||
host, portString, err := net.SplitHostPort(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
port, err := strconv.ParseUint(portString, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ch, err = c.dial(net.IPv4zero.String(), 0, host, int(port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use a zero address for local and remote address.
|
||||
zeroAddr := &net.TCPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: zeroAddr,
|
||||
raddr: zeroAddr,
|
||||
}, nil
|
||||
case "unix":
|
||||
var err error
|
||||
ch, err = c.dialStreamLocal(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: &net.UnixAddr{
|
||||
Name: "@",
|
||||
Net: "unix",
|
||||
},
|
||||
raddr: &net.UnixAddr{
|
||||
Name: addr,
|
||||
Net: "unix",
|
||||
},
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported protocol: %s", n)
|
||||
}
|
||||
port, err := strconv.ParseUint(portString, 10, 16)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Use a zero address for local and remote address.
|
||||
zeroAddr := &net.TCPAddr{
|
||||
IP: net.IPv4zero,
|
||||
Port: 0,
|
||||
}
|
||||
ch, err := c.dial(net.IPv4zero.String(), 0, host, int(port))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpChanConn{
|
||||
Channel: ch,
|
||||
laddr: zeroAddr,
|
||||
raddr: zeroAddr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DialTCP connects to the remote address raddr on the network net,
|
||||
@@ -332,7 +388,7 @@ func (c *Client) DialTCP(n string, laddr, raddr *net.TCPAddr) (net.Conn, error)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tcpChanConn{
|
||||
return &chanConn{
|
||||
Channel: ch,
|
||||
laddr: laddr,
|
||||
raddr: raddr,
|
||||
@@ -366,26 +422,26 @@ type tcpChan struct {
|
||||
Channel // the backing channel
|
||||
}
|
||||
|
||||
// tcpChanConn fulfills the net.Conn interface without
|
||||
// chanConn fulfills the net.Conn interface without
|
||||
// the tcpChan having to hold laddr or raddr directly.
|
||||
type tcpChanConn struct {
|
||||
type chanConn struct {
|
||||
Channel
|
||||
laddr, raddr net.Addr
|
||||
}
|
||||
|
||||
// LocalAddr returns the local network address.
|
||||
func (t *tcpChanConn) LocalAddr() net.Addr {
|
||||
func (t *chanConn) LocalAddr() net.Addr {
|
||||
return t.laddr
|
||||
}
|
||||
|
||||
// RemoteAddr returns the remote network address.
|
||||
func (t *tcpChanConn) RemoteAddr() net.Addr {
|
||||
func (t *chanConn) RemoteAddr() net.Addr {
|
||||
return t.raddr
|
||||
}
|
||||
|
||||
// SetDeadline sets the read and write deadlines associated
|
||||
// with the connection.
|
||||
func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetDeadline(deadline time.Time) error {
|
||||
if err := t.SetReadDeadline(deadline); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -396,12 +452,14 @@ func (t *tcpChanConn) SetDeadline(deadline time.Time) error {
|
||||
// A zero value for t means Read will not time out.
|
||||
// After the deadline, the error from Read will implement net.Error
|
||||
// with Timeout() == true.
|
||||
func (t *tcpChanConn) SetReadDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetReadDeadline(deadline time.Time) error {
|
||||
// for compatibility with previous version,
|
||||
// the error message contains "tcpChan"
|
||||
return errors.New("ssh: tcpChan: deadline not supported")
|
||||
}
|
||||
|
||||
// SetWriteDeadline exists to satisfy the net.Conn interface
|
||||
// but is not implemented by this type. It always returns an error.
|
||||
func (t *tcpChanConn) SetWriteDeadline(deadline time.Time) error {
|
||||
func (t *chanConn) SetWriteDeadline(deadline time.Time) error {
|
||||
return errors.New("ssh: tcpChan: deadline not supported")
|
||||
}
|
||||
|
63
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
63
vendor/golang.org/x/crypto/ssh/terminal/util_solaris.go
generated
vendored
@@ -14,14 +14,12 @@ import (
|
||||
|
||||
// State contains the state of a terminal.
|
||||
type State struct {
|
||||
termios syscall.Termios
|
||||
state *unix.Termios
|
||||
}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||
var termio unix.Termio
|
||||
err := unix.IoctlSetTermio(fd, unix.TCGETA, &termio)
|
||||
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
@@ -71,3 +69,60 @@ func ReadPassword(fd int) ([]byte, error) {
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// MakeRaw puts the terminal connected to the given file descriptor into raw
|
||||
// mode and returns the previous state of the terminal so that it can be
|
||||
// restored.
|
||||
// see http://cr.illumos.org/~webrev/andy_js/1060/
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
oldTermios := *oldTermiosPtr
|
||||
|
||||
newTermios := oldTermios
|
||||
newTermios.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||
newTermios.Oflag &^= syscall.OPOST
|
||||
newTermios.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newTermios.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newTermios.Cflag |= syscall.CS8
|
||||
newTermios.Cc[unix.VMIN] = 1
|
||||
newTermios.Cc[unix.VTIME] = 0
|
||||
|
||||
if err := unix.IoctlSetTermios(fd, unix.TCSETS, &newTermios); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, oldState *State) error {
|
||||
return unix.IoctlSetTermios(fd, unix.TCSETS, oldState.state)
|
||||
}
|
||||
|
||||
// GetState returns the current state of a terminal which may be useful to
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
oldTermiosPtr, err := unix.IoctlGetTermios(fd, unix.TCGETS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &State{
|
||||
state: oldTermiosPtr,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
|
128
vendor/golang.org/x/crypto/ssh/test/dial_unix_test.go
generated
vendored
Normal file
128
vendor/golang.org/x/crypto/ssh/test/dial_unix_test.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package test
|
||||
|
||||
// direct-tcpip and direct-streamlocal functional tests
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type dialTester interface {
|
||||
TestServerConn(t *testing.T, c net.Conn)
|
||||
TestClientConn(t *testing.T, c net.Conn)
|
||||
}
|
||||
|
||||
func testDial(t *testing.T, n, listenAddr string, x dialTester) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
sshConn := server.Dial(clientConfig())
|
||||
defer sshConn.Close()
|
||||
|
||||
l, err := net.Listen(n, listenAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("Listen: %v", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
testData := fmt.Sprintf("hello from %s, %s", n, listenAddr)
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
x.TestServerConn(t, c)
|
||||
|
||||
io.WriteString(c, testData)
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := sshConn.Dial(n, l.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("Dial: %v", err)
|
||||
}
|
||||
x.TestClientConn(t, conn)
|
||||
defer conn.Close()
|
||||
b, err := ioutil.ReadAll(conn)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAll: %v", err)
|
||||
}
|
||||
t.Logf("got %q", string(b))
|
||||
if string(b) != testData {
|
||||
t.Fatalf("expected %q, got %q", testData, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
type tcpDialTester struct {
|
||||
listenAddr string
|
||||
}
|
||||
|
||||
func (x *tcpDialTester) TestServerConn(t *testing.T, c net.Conn) {
|
||||
host := strings.Split(x.listenAddr, ":")[0]
|
||||
prefix := host + ":"
|
||||
if !strings.HasPrefix(c.LocalAddr().String(), prefix) {
|
||||
t.Fatalf("expected to start with %q, got %q", prefix, c.LocalAddr().String())
|
||||
}
|
||||
if !strings.HasPrefix(c.RemoteAddr().String(), prefix) {
|
||||
t.Fatalf("expected to start with %q, got %q", prefix, c.RemoteAddr().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (x *tcpDialTester) TestClientConn(t *testing.T, c net.Conn) {
|
||||
// we use zero addresses. see *Client.Dial.
|
||||
if c.LocalAddr().String() != "0.0.0.0:0" {
|
||||
t.Fatalf("expected \"0.0.0.0:0\", got %q", c.LocalAddr().String())
|
||||
}
|
||||
if c.RemoteAddr().String() != "0.0.0.0:0" {
|
||||
t.Fatalf("expected \"0.0.0.0:0\", got %q", c.RemoteAddr().String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialTCP(t *testing.T) {
|
||||
x := &tcpDialTester{
|
||||
listenAddr: "127.0.0.1:0",
|
||||
}
|
||||
testDial(t, "tcp", x.listenAddr, x)
|
||||
}
|
||||
|
||||
type unixDialTester struct {
|
||||
listenAddr string
|
||||
}
|
||||
|
||||
func (x *unixDialTester) TestServerConn(t *testing.T, c net.Conn) {
|
||||
if c.LocalAddr().String() != x.listenAddr {
|
||||
t.Fatalf("expected %q, got %q", x.listenAddr, c.LocalAddr().String())
|
||||
}
|
||||
if c.RemoteAddr().String() != "@" {
|
||||
t.Fatalf("expected \"@\", got %q", c.RemoteAddr().String())
|
||||
}
|
||||
}
|
||||
|
||||
func (x *unixDialTester) TestClientConn(t *testing.T, c net.Conn) {
|
||||
if c.RemoteAddr().String() != x.listenAddr {
|
||||
t.Fatalf("expected %q, got %q", x.listenAddr, c.RemoteAddr().String())
|
||||
}
|
||||
if c.LocalAddr().String() != "@" {
|
||||
t.Fatalf("expected \"@\", got %q", c.LocalAddr().String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestDialUnix(t *testing.T) {
|
||||
addr, cleanup := newTempSocket(t)
|
||||
defer cleanup()
|
||||
x := &unixDialTester{
|
||||
listenAddr: addr,
|
||||
}
|
||||
testDial(t, "unix", x.listenAddr, x)
|
||||
}
|
62
vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go
generated
vendored
62
vendor/golang.org/x/crypto/ssh/test/forward_unix_test.go
generated
vendored
@@ -16,13 +16,17 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPortForward(t *testing.T) {
|
||||
type closeWriter interface {
|
||||
CloseWrite() error
|
||||
}
|
||||
|
||||
func testPortForward(t *testing.T, n, listenAddr string) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conn := server.Dial(clientConfig())
|
||||
defer conn.Close()
|
||||
|
||||
sshListener, err := conn.Listen("tcp", "localhost:0")
|
||||
sshListener, err := conn.Listen(n, listenAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -41,14 +45,14 @@ func TestPortForward(t *testing.T) {
|
||||
}()
|
||||
|
||||
forwardedAddr := sshListener.Addr().String()
|
||||
tcpConn, err := net.Dial("tcp", forwardedAddr)
|
||||
netConn, err := net.Dial(n, forwardedAddr)
|
||||
if err != nil {
|
||||
t.Fatalf("TCP dial failed: %v", err)
|
||||
t.Fatalf("net dial failed: %v", err)
|
||||
}
|
||||
|
||||
readChan := make(chan []byte)
|
||||
go func() {
|
||||
data, _ := ioutil.ReadAll(tcpConn)
|
||||
data, _ := ioutil.ReadAll(netConn)
|
||||
readChan <- data
|
||||
}()
|
||||
|
||||
@@ -62,14 +66,14 @@ func TestPortForward(t *testing.T) {
|
||||
for len(sent) < 1000*1000 {
|
||||
// Send random sized chunks
|
||||
m := rand.Intn(len(data))
|
||||
n, err := tcpConn.Write(data[:m])
|
||||
n, err := netConn.Write(data[:m])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
sent = append(sent, data[:n]...)
|
||||
}
|
||||
if err := tcpConn.(*net.TCPConn).CloseWrite(); err != nil {
|
||||
t.Errorf("tcpConn.CloseWrite: %v", err)
|
||||
if err := netConn.(closeWriter).CloseWrite(); err != nil {
|
||||
t.Errorf("netConn.CloseWrite: %v", err)
|
||||
}
|
||||
|
||||
read := <-readChan
|
||||
@@ -86,19 +90,29 @@ func TestPortForward(t *testing.T) {
|
||||
}
|
||||
|
||||
// Check that the forward disappeared.
|
||||
tcpConn, err = net.Dial("tcp", forwardedAddr)
|
||||
netConn, err = net.Dial(n, forwardedAddr)
|
||||
if err == nil {
|
||||
tcpConn.Close()
|
||||
netConn.Close()
|
||||
t.Errorf("still listening to %s after closing", forwardedAddr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcceptClose(t *testing.T) {
|
||||
func TestPortForwardTCP(t *testing.T) {
|
||||
testPortForward(t, "tcp", "localhost:0")
|
||||
}
|
||||
|
||||
func TestPortForwardUnix(t *testing.T) {
|
||||
addr, cleanup := newTempSocket(t)
|
||||
defer cleanup()
|
||||
testPortForward(t, "unix", addr)
|
||||
}
|
||||
|
||||
func testAcceptClose(t *testing.T, n, listenAddr string) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conn := server.Dial(clientConfig())
|
||||
|
||||
sshListener, err := conn.Listen("tcp", "localhost:0")
|
||||
sshListener, err := conn.Listen(n, listenAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -124,13 +138,23 @@ func TestAcceptClose(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAcceptCloseTCP(t *testing.T) {
|
||||
testAcceptClose(t, "tcp", "localhost:0")
|
||||
}
|
||||
|
||||
func TestAcceptCloseUnix(t *testing.T) {
|
||||
addr, cleanup := newTempSocket(t)
|
||||
defer cleanup()
|
||||
testAcceptClose(t, "unix", addr)
|
||||
}
|
||||
|
||||
// Check that listeners exit if the underlying client transport dies.
|
||||
func TestPortForwardConnectionClose(t *testing.T) {
|
||||
func testPortForwardConnectionClose(t *testing.T, n, listenAddr string) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conn := server.Dial(clientConfig())
|
||||
|
||||
sshListener, err := conn.Listen("tcp", "localhost:0")
|
||||
sshListener, err := conn.Listen(n, listenAddr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -158,3 +182,13 @@ func TestPortForwardConnectionClose(t *testing.T) {
|
||||
t.Logf("quit as expected (error %v)", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPortForwardConnectionCloseTCP(t *testing.T) {
|
||||
testPortForwardConnectionClose(t, "tcp", "localhost:0")
|
||||
}
|
||||
|
||||
func TestPortForwardConnectionCloseUnix(t *testing.T) {
|
||||
addr, cleanup := newTempSocket(t)
|
||||
defer cleanup()
|
||||
testPortForwardConnectionClose(t, "unix", addr)
|
||||
}
|
||||
|
46
vendor/golang.org/x/crypto/ssh/test/tcpip_test.go
generated
vendored
46
vendor/golang.org/x/crypto/ssh/test/tcpip_test.go
generated
vendored
@@ -1,46 +0,0 @@
|
||||
// Copyright 2012 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package test
|
||||
|
||||
// direct-tcpip functional tests
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDial(t *testing.T) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
sshConn := server.Dial(clientConfig())
|
||||
defer sshConn.Close()
|
||||
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
t.Fatalf("Listen: %v", err)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
c, err := l.Accept()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
io.WriteString(c, c.RemoteAddr().String())
|
||||
c.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
conn, err := sshConn.Dial("tcp", l.Addr().String())
|
||||
if err != nil {
|
||||
t.Fatalf("Dial: %v", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
}
|
10
vendor/golang.org/x/crypto/ssh/test/test_unix_test.go
generated
vendored
10
vendor/golang.org/x/crypto/ssh/test/test_unix_test.go
generated
vendored
@@ -266,3 +266,13 @@ func newServer(t *testing.T) *server {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newTempSocket(t *testing.T) (string, func()) {
|
||||
dir, err := ioutil.TempDir("", "socket")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
deferFunc := func() { os.RemoveAll(dir) }
|
||||
addr := filepath.Join(dir, "sock")
|
||||
return addr, deferFunc
|
||||
}
|
||||
|
16
vendor/golang.org/x/crypto/ssh/testdata/keys.go
generated
vendored
16
vendor/golang.org/x/crypto/ssh/testdata/keys.go
generated
vendored
@@ -48,6 +48,22 @@ AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb
|
||||
HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
`),
|
||||
"rsa-openssh-format": []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
|
||||
NhAAAAAwEAAQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5l
|
||||
oEuW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lz
|
||||
a+yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAIQWL0H31i9B98AAAAH
|
||||
c3NoLXJzYQAAAIEAwa48yfWFi3uIdqzuf9X7C2Zxfea/Iaaw0zIwHudpF8U92WVIiC5loE
|
||||
uW1+OaVi3UWfIEjWMV1tHGysrHOwtwc34BPCJqJknUQO/KtDTBTJ4Pryhw1bWPC999Lza+
|
||||
yrCTdNQYBzoROXKExZgPFh9pTMi5wqpHDuOQ2qZFIEI3lT0AAAADAQABAAAAgCThyTGsT4
|
||||
IARDxVMhWl6eiB2ZrgFgWSeJm/NOqtppWgOebsIqPMMg4UVuVFsl422/lE3RkPhVkjGXgE
|
||||
pWvZAdCnmLmApK8wK12vF334lZhZT7t3Z9EzJps88PWEHo7kguf285HcnUM7FlFeissJdk
|
||||
kXly34y7/3X/a6Tclm+iABAAAAQE0xR/KxZ39slwfMv64Rz7WKk1PPskaryI29aHE3mKHk
|
||||
pY2QA+P3QlrKxT/VWUMjHUbNNdYfJm48xu0SGNMRdKMAAABBAORh2NP/06JUV3J9W/2Hju
|
||||
X1ViJuqqcQnJPVzpgSL826EC2xwOECTqoY8uvFpUdD7CtpksIxNVqRIhuNOlz0lqEAAABB
|
||||
ANkaHTTaPojClO0dKJ/Zjs7pWOCGliebBYprQ/Y4r9QLBkC/XaWMS26gFIrjgC7D2Rv+rZ
|
||||
wSD0v0RcmkITP1ZR0AAAAYcHF1ZXJuYUBMdWNreUh5ZHJvLmxvY2FsAQID
|
||||
-----END OPENSSH PRIVATE KEY-----`),
|
||||
"user": []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49
|
||||
AwEHoUQDQgAEYcO2xNKiRUYOLEHM7VYAp57HNyKbOdYtHD83Z4hzNPVC4tM5mdGD
|
||||
|
Reference in New Issue
Block a user