forked from TrueCloudLab/restic
Update golang.org/x/crypto/ssh
This commit is contained in:
parent
35ba817128
commit
799cc37c22
46 changed files with 2294 additions and 504 deletions
4
vendor/manifest
vendored
4
vendor/manifest
vendored
|
@ -73,8 +73,8 @@
|
|||
{
|
||||
"importpath": "golang.org/x/crypto/ssh",
|
||||
"repository": "https://go.googlesource.com/crypto",
|
||||
"revision": "cc04154d65fb9296747569b107cfd05380b1ea3e",
|
||||
"branch": "HEAD",
|
||||
"revision": "81372b2fc2f10bef2a7f338da115c315a56b2726",
|
||||
"branch": "master",
|
||||
"path": "/ssh"
|
||||
},
|
||||
{
|
||||
|
|
276
vendor/src/golang.org/x/crypto/ssh/agent/client.go
vendored
276
vendor/src/golang.org/x/crypto/ssh/agent/client.go
vendored
|
@ -2,12 +2,13 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
/*
|
||||
Package agent implements a client to an ssh-agent daemon.
|
||||
|
||||
References:
|
||||
[PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
|
||||
*/
|
||||
// Package agent implements the ssh-agent protocol, and provides both
|
||||
// a client and a server. The client can talk to a standard ssh-agent
|
||||
// that uses UNIX sockets, and one could implement an alternative
|
||||
// ssh-agent process using the sample server.
|
||||
//
|
||||
// References:
|
||||
// [PROTOCOL.agent]: http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
|
||||
package agent // import "golang.org/x/crypto/ssh/agent"
|
||||
|
||||
import (
|
||||
|
@ -24,6 +25,7 @@ import (
|
|||
"math/big"
|
||||
"sync"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
@ -36,9 +38,8 @@ type Agent interface {
|
|||
// in [PROTOCOL.agent] section 2.6.2.
|
||||
Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
|
||||
|
||||
// Insert adds a private key to the agent. If a certificate
|
||||
// is given, that certificate is added as public key.
|
||||
Add(s interface{}, cert *ssh.Certificate, comment string) error
|
||||
// Add adds a private key to the agent.
|
||||
Add(key AddedKey) error
|
||||
|
||||
// Remove removes all identities with the given public key.
|
||||
Remove(key ssh.PublicKey) error
|
||||
|
@ -56,9 +57,28 @@ type Agent interface {
|
|||
Signers() ([]ssh.Signer, error)
|
||||
}
|
||||
|
||||
// AddedKey describes an SSH key to be added to an Agent.
|
||||
type AddedKey struct {
|
||||
// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
|
||||
// *ecdsa.PrivateKey, which will be inserted into the agent.
|
||||
PrivateKey interface{}
|
||||
// Certificate, if not nil, is communicated to the agent and will be
|
||||
// stored with the key.
|
||||
Certificate *ssh.Certificate
|
||||
// Comment is an optional, free-form string.
|
||||
Comment string
|
||||
// LifetimeSecs, if not zero, is the number of seconds that the
|
||||
// agent will store the key for.
|
||||
LifetimeSecs uint32
|
||||
// ConfirmBeforeUse, if true, requests that the agent confirm with the
|
||||
// user before each use of this key.
|
||||
ConfirmBeforeUse bool
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 3.
|
||||
const (
|
||||
agentRequestV1Identities = 1
|
||||
agentRequestV1Identities = 1
|
||||
agentRemoveAllV1Identities = 9
|
||||
|
||||
// 3.2 Requests from client to agent for protocol 2 key operations
|
||||
agentAddIdentity = 17
|
||||
|
@ -165,10 +185,13 @@ func (k *Key) Marshal() []byte {
|
|||
return k.Blob
|
||||
}
|
||||
|
||||
// Verify satisfies the ssh.PublicKey interface, but is not
|
||||
// implemented for agent keys.
|
||||
// Verify satisfies the ssh.PublicKey interface.
|
||||
func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
|
||||
return errors.New("agent: agent key does not know how to verify")
|
||||
pubKey, err := ssh.ParsePublicKey(k.Blob)
|
||||
if err != nil {
|
||||
return fmt.Errorf("agent: bad public key: %v", err)
|
||||
}
|
||||
return pubKey.Verify(data, sig)
|
||||
}
|
||||
|
||||
type wireKey struct {
|
||||
|
@ -358,6 +381,8 @@ func unmarshal(packet []byte) (interface{}, error) {
|
|||
msg = new(identitiesAnswerAgentMsg)
|
||||
case agentSignResponse:
|
||||
msg = new(signResponseAgentMsg)
|
||||
case agentV1IdentitiesAnswer:
|
||||
msg = new(agentV1IdentityMsg)
|
||||
default:
|
||||
return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
|
||||
}
|
||||
|
@ -368,36 +393,47 @@ func unmarshal(packet []byte) (interface{}, error) {
|
|||
}
|
||||
|
||||
type rsaKeyMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
N *big.Int
|
||||
E *big.Int
|
||||
D *big.Int
|
||||
Iqmp *big.Int // IQMP = Inverse Q Mod P
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
N *big.Int
|
||||
E *big.Int
|
||||
D *big.Int
|
||||
Iqmp *big.Int // IQMP = Inverse Q Mod P
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
type dsaKeyMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
G *big.Int
|
||||
Y *big.Int
|
||||
X *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
G *big.Int
|
||||
Y *big.Int
|
||||
X *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
type ecdsaKeyMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
Curve string
|
||||
KeyBytes []byte
|
||||
D *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
Curve string
|
||||
KeyBytes []byte
|
||||
D *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
type ed25519KeyMsg struct {
|
||||
Type string `sshtype:"17|25"`
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Insert adds a private key to the agent.
|
||||
func (c *client) insertKey(s interface{}, comment string) error {
|
||||
func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
|
||||
var req []byte
|
||||
switch k := s.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
|
@ -406,37 +442,54 @@ func (c *client) insertKey(s interface{}, comment string) error {
|
|||
}
|
||||
k.Precompute()
|
||||
req = ssh.Marshal(rsaKeyMsg{
|
||||
Type: ssh.KeyAlgoRSA,
|
||||
N: k.N,
|
||||
E: big.NewInt(int64(k.E)),
|
||||
D: k.D,
|
||||
Iqmp: k.Precomputed.Qinv,
|
||||
P: k.Primes[0],
|
||||
Q: k.Primes[1],
|
||||
Comments: comment,
|
||||
Type: ssh.KeyAlgoRSA,
|
||||
N: k.N,
|
||||
E: big.NewInt(int64(k.E)),
|
||||
D: k.D,
|
||||
Iqmp: k.Precomputed.Qinv,
|
||||
P: k.Primes[0],
|
||||
Q: k.Primes[1],
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *dsa.PrivateKey:
|
||||
req = ssh.Marshal(dsaKeyMsg{
|
||||
Type: ssh.KeyAlgoDSA,
|
||||
P: k.P,
|
||||
Q: k.Q,
|
||||
G: k.G,
|
||||
Y: k.Y,
|
||||
X: k.X,
|
||||
Comments: comment,
|
||||
Type: ssh.KeyAlgoDSA,
|
||||
P: k.P,
|
||||
Q: k.Q,
|
||||
G: k.G,
|
||||
Y: k.Y,
|
||||
X: k.X,
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *ecdsa.PrivateKey:
|
||||
nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
|
||||
req = ssh.Marshal(ecdsaKeyMsg{
|
||||
Type: "ecdsa-sha2-" + nistID,
|
||||
Curve: nistID,
|
||||
KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
|
||||
D: k.D,
|
||||
Comments: comment,
|
||||
Type: "ecdsa-sha2-" + nistID,
|
||||
Curve: nistID,
|
||||
KeyBytes: elliptic.Marshal(k.Curve, k.X, k.Y),
|
||||
D: k.D,
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *ed25519.PrivateKey:
|
||||
req = ssh.Marshal(ed25519KeyMsg{
|
||||
Type: ssh.KeyAlgoED25519,
|
||||
Pub: []byte(*k)[32:],
|
||||
Priv: []byte(*k),
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("agent: unsupported key type %T", s)
|
||||
}
|
||||
|
||||
// if constraints are present then the message type needs to be changed.
|
||||
if len(constraints) != 0 {
|
||||
req[0] = agentAddIdConstrained
|
||||
}
|
||||
|
||||
resp, err := c.call(req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -448,40 +501,66 @@ func (c *client) insertKey(s interface{}, comment string) error {
|
|||
}
|
||||
|
||||
type rsaCertMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
CertBytes []byte
|
||||
D *big.Int
|
||||
Iqmp *big.Int // IQMP = Inverse Q Mod P
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
CertBytes []byte
|
||||
D *big.Int
|
||||
Iqmp *big.Int // IQMP = Inverse Q Mod P
|
||||
P *big.Int
|
||||
Q *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
type dsaCertMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
CertBytes []byte
|
||||
X *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
CertBytes []byte
|
||||
X *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
type ecdsaCertMsg struct {
|
||||
Type string `sshtype:"17"`
|
||||
CertBytes []byte
|
||||
D *big.Int
|
||||
Comments string
|
||||
Type string `sshtype:"17|25"`
|
||||
CertBytes []byte
|
||||
D *big.Int
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Insert adds a private key to the agent. If a certificate is given,
|
||||
type ed25519CertMsg struct {
|
||||
Type string `sshtype:"17|25"`
|
||||
CertBytes []byte
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comments string
|
||||
Constraints []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Add adds a private key to the agent. If a certificate is given,
|
||||
// that certificate is added instead as public key.
|
||||
func (c *client) Add(s interface{}, cert *ssh.Certificate, comment string) error {
|
||||
if cert == nil {
|
||||
return c.insertKey(s, comment)
|
||||
func (c *client) Add(key AddedKey) error {
|
||||
var constraints []byte
|
||||
|
||||
if secs := key.LifetimeSecs; secs != 0 {
|
||||
constraints = append(constraints, agentConstrainLifetime)
|
||||
|
||||
var secsBytes [4]byte
|
||||
binary.BigEndian.PutUint32(secsBytes[:], secs)
|
||||
constraints = append(constraints, secsBytes[:]...)
|
||||
}
|
||||
|
||||
if key.ConfirmBeforeUse {
|
||||
constraints = append(constraints, agentConstrainConfirm)
|
||||
}
|
||||
|
||||
if cert := key.Certificate; cert == nil {
|
||||
return c.insertKey(key.PrivateKey, key.Comment, constraints)
|
||||
} else {
|
||||
return c.insertCert(s, cert, comment)
|
||||
return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string) error {
|
||||
func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
|
||||
var req []byte
|
||||
switch k := s.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
|
@ -490,32 +569,49 @@ func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string
|
|||
}
|
||||
k.Precompute()
|
||||
req = ssh.Marshal(rsaCertMsg{
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
D: k.D,
|
||||
Iqmp: k.Precomputed.Qinv,
|
||||
P: k.Primes[0],
|
||||
Q: k.Primes[1],
|
||||
Comments: comment,
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
D: k.D,
|
||||
Iqmp: k.Precomputed.Qinv,
|
||||
P: k.Primes[0],
|
||||
Q: k.Primes[1],
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *dsa.PrivateKey:
|
||||
req = ssh.Marshal(dsaCertMsg{
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
X: k.X,
|
||||
Comments: comment,
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
X: k.X,
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *ecdsa.PrivateKey:
|
||||
req = ssh.Marshal(ecdsaCertMsg{
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
D: k.D,
|
||||
Comments: comment,
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
D: k.D,
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
case *ed25519.PrivateKey:
|
||||
req = ssh.Marshal(ed25519CertMsg{
|
||||
Type: cert.Type(),
|
||||
CertBytes: cert.Marshal(),
|
||||
Pub: []byte(*k)[32:],
|
||||
Priv: []byte(*k),
|
||||
Comments: comment,
|
||||
Constraints: constraints,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("agent: unsupported key type %T", s)
|
||||
}
|
||||
|
||||
// if constraints are present then the message type needs to be changed.
|
||||
if len(constraints) != 0 {
|
||||
req[0] = agentAddIdConstrained
|
||||
}
|
||||
|
||||
signer, err := ssh.NewSignerFromKey(s)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"path/filepath"
|
||||
"strconv"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
@ -78,14 +79,14 @@ func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
|||
}
|
||||
}
|
||||
|
||||
func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate) {
|
||||
func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
testAgentInterface(t, agent, key, cert)
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate) {
|
||||
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
signer, err := ssh.NewSignerFromKey(key)
|
||||
if err != nil {
|
||||
t.Fatalf("NewSignerFromKey(%T): %v", key, err)
|
||||
|
@ -100,10 +101,15 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
|
|||
// Attempt to insert the key, with certificate if specified.
|
||||
var pubKey ssh.PublicKey
|
||||
if cert != nil {
|
||||
err = agent.Add(key, cert, "comment")
|
||||
err = agent.Add(AddedKey{
|
||||
PrivateKey: key,
|
||||
Certificate: cert,
|
||||
Comment: "comment",
|
||||
LifetimeSecs: lifetimeSecs,
|
||||
})
|
||||
pubKey = cert
|
||||
} else {
|
||||
err = agent.Add(key, nil, "comment")
|
||||
err = agent.Add(AddedKey{PrivateKey: key, Comment: "comment", LifetimeSecs: lifetimeSecs})
|
||||
pubKey = signer.PublicKey()
|
||||
}
|
||||
if err != nil {
|
||||
|
@ -134,8 +140,8 @@ func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Ce
|
|||
}
|
||||
|
||||
func TestAgent(t *testing.T) {
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
|
||||
testAgent(t, testPrivateKeys[keyType], nil)
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa", "ed25519"} {
|
||||
testAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -147,7 +153,11 @@ func TestCert(t *testing.T) {
|
|||
}
|
||||
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||
|
||||
testAgent(t, testPrivateKeys["rsa"], cert)
|
||||
testAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
}
|
||||
|
||||
func TestConstraints(t *testing.T) {
|
||||
testAgent(t, testPrivateKeys["rsa"], nil, 3600 /* lifetime in seconds */)
|
||||
}
|
||||
|
||||
// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
|
||||
|
@ -185,7 +195,7 @@ func TestAuth(t *testing.T) {
|
|||
agent, _, cleanup := startAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
if err := agent.Add(testPrivateKeys["rsa"], nil, "comment"); err != nil {
|
||||
if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment"}); err != nil {
|
||||
t.Errorf("Add: %v", err)
|
||||
}
|
||||
|
||||
|
@ -223,10 +233,10 @@ func TestLockClient(t *testing.T) {
|
|||
}
|
||||
|
||||
func testLockAgent(agent Agent, t *testing.T) {
|
||||
if err := agent.Add(testPrivateKeys["rsa"], nil, "comment 1"); err != nil {
|
||||
if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["rsa"], Comment: "comment 1"}); err != nil {
|
||||
t.Errorf("Add: %v", err)
|
||||
}
|
||||
if err := agent.Add(testPrivateKeys["dsa"], nil, "comment dsa"); err != nil {
|
||||
if err := agent.Add(AddedKey{PrivateKey: testPrivateKeys["dsa"], Comment: "comment dsa"}); err != nil {
|
||||
t.Errorf("Add: %v", err)
|
||||
}
|
||||
if keys, err := agent.List(); err != nil {
|
||||
|
@ -276,3 +286,42 @@ func testLockAgent(agent Agent, t *testing.T) {
|
|||
t.Errorf("Want 1 keys, got %v", keys)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentLifetime(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
|
||||
// Add private keys to the agent.
|
||||
err := agent.Add(AddedKey{
|
||||
PrivateKey: testPrivateKeys[keyType],
|
||||
Comment: "comment",
|
||||
LifetimeSecs: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
// Add certs to the agent.
|
||||
cert := &ssh.Certificate{
|
||||
Key: testPublicKeys[keyType],
|
||||
ValidBefore: ssh.CertTimeInfinity,
|
||||
CertType: ssh.UserCert,
|
||||
}
|
||||
cert.SignCert(rand.Reader, testSigners[keyType])
|
||||
err = agent.Add(AddedKey{
|
||||
PrivateKey: testPrivateKeys[keyType],
|
||||
Certificate: cert,
|
||||
Comment: "comment",
|
||||
LifetimeSecs: 1,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("add: %v", err)
|
||||
}
|
||||
}
|
||||
time.Sleep(1100 * time.Millisecond)
|
||||
if keys, err := agent.List(); err != nil {
|
||||
t.Errorf("List: %v", err)
|
||||
} else if len(keys) != 0 {
|
||||
t.Errorf("Want 0 keys, got %v", len(keys))
|
||||
}
|
||||
}
|
||||
|
|
40
vendor/src/golang.org/x/crypto/ssh/agent/example_test.go
vendored
Normal file
40
vendor/src/golang.org/x/crypto/ssh/agent/example_test.go
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
// Copyright 2016 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 agent_test
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"net"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
"golang.org/x/crypto/ssh/agent"
|
||||
)
|
||||
|
||||
func ExampleClientAgent() {
|
||||
// ssh-agent has a UNIX socket under $SSH_AUTH_SOCK
|
||||
socket := os.Getenv("SSH_AUTH_SOCK")
|
||||
conn, err := net.Dial("unix", socket)
|
||||
if err != nil {
|
||||
log.Fatalf("net.Dial: %v", err)
|
||||
}
|
||||
agentClient := agent.NewClient(conn)
|
||||
config := &ssh.ClientConfig{
|
||||
User: "username",
|
||||
Auth: []ssh.AuthMethod{
|
||||
// Use a callback rather than PublicKeys
|
||||
// so we only consult the agent once the remote server
|
||||
// wants it.
|
||||
ssh.PublicKeysCallback(agentClient.Signers),
|
||||
},
|
||||
}
|
||||
|
||||
sshc, err := ssh.Dial("tcp", "localhost:22", config)
|
||||
if err != nil {
|
||||
log.Fatalf("Dial: %v", err)
|
||||
}
|
||||
// .. use sshc
|
||||
sshc.Close()
|
||||
}
|
|
@ -62,7 +62,7 @@ func (r *keyring) Remove(key ssh.PublicKey) error {
|
|||
if bytes.Equal(r.keys[i].signer.PublicKey().Marshal(), want) {
|
||||
found = true
|
||||
r.keys[i] = r.keys[len(r.keys)-1]
|
||||
r.keys = r.keys[len(r.keys)-1:]
|
||||
r.keys = r.keys[:len(r.keys)-1]
|
||||
continue
|
||||
} else {
|
||||
i++
|
||||
|
@ -125,27 +125,28 @@ func (r *keyring) List() ([]*Key, error) {
|
|||
}
|
||||
|
||||
// Insert adds a private key to the keyring. If a certificate
|
||||
// is given, that certificate is added as public key.
|
||||
func (r *keyring) Add(priv interface{}, cert *ssh.Certificate, comment string) error {
|
||||
// is given, that certificate is added as public key. Note that
|
||||
// any constraints given are ignored.
|
||||
func (r *keyring) Add(key AddedKey) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if r.locked {
|
||||
return errLocked
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(priv)
|
||||
signer, err := ssh.NewSignerFromKey(key.PrivateKey)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cert != nil {
|
||||
if cert := key.Certificate; cert != nil {
|
||||
signer, err = ssh.NewCertSigner(cert, signer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r.keys = append(r.keys, privKey{signer, comment})
|
||||
r.keys = append(r.keys, privKey{signer, key.Comment})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
78
vendor/src/golang.org/x/crypto/ssh/agent/keyring_test.go
vendored
Normal file
78
vendor/src/golang.org/x/crypto/ssh/agent/keyring_test.go
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2015 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 agent
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func addTestKey(t *testing.T, a Agent, keyName string) {
|
||||
err := a.Add(AddedKey{
|
||||
PrivateKey: testPrivateKeys[keyName],
|
||||
Comment: keyName,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to add key %q: %v", keyName, err)
|
||||
}
|
||||
}
|
||||
|
||||
func removeTestKey(t *testing.T, a Agent, keyName string) {
|
||||
err := a.Remove(testPublicKeys[keyName])
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove key %q: %v", keyName, err)
|
||||
}
|
||||
}
|
||||
|
||||
func validateListedKeys(t *testing.T, a Agent, expectedKeys []string) {
|
||||
listedKeys, err := a.List()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to list keys: %v", err)
|
||||
return
|
||||
}
|
||||
actualKeys := make(map[string]bool)
|
||||
for _, key := range listedKeys {
|
||||
actualKeys[key.Comment] = true
|
||||
}
|
||||
|
||||
matchedKeys := make(map[string]bool)
|
||||
for _, expectedKey := range expectedKeys {
|
||||
if !actualKeys[expectedKey] {
|
||||
t.Fatalf("expected key %q, but was not found", expectedKey)
|
||||
} else {
|
||||
matchedKeys[expectedKey] = true
|
||||
}
|
||||
}
|
||||
|
||||
for actualKey := range actualKeys {
|
||||
if !matchedKeys[actualKey] {
|
||||
t.Fatalf("key %q was found, but was not expected", actualKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyringAddingAndRemoving(t *testing.T) {
|
||||
keyNames := []string{"dsa", "ecdsa", "rsa", "user"}
|
||||
|
||||
// add all test private keys
|
||||
k := NewKeyring()
|
||||
for _, keyName := range keyNames {
|
||||
addTestKey(t, k, keyName)
|
||||
}
|
||||
validateListedKeys(t, k, keyNames)
|
||||
|
||||
// remove a key in the middle
|
||||
keyToRemove := keyNames[1]
|
||||
keyNames = append(keyNames[:1], keyNames[2:]...)
|
||||
|
||||
removeTestKey(t, k, keyToRemove)
|
||||
validateListedKeys(t, k, keyNames)
|
||||
|
||||
// remove all keys
|
||||
err := k.RemoveAll()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to remove all keys: %v", err)
|
||||
}
|
||||
validateListedKeys(t, k, []string{})
|
||||
}
|
280
vendor/src/golang.org/x/crypto/ssh/agent/server.go
vendored
280
vendor/src/golang.org/x/crypto/ssh/agent/server.go
vendored
|
@ -5,13 +5,18 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"crypto/dsa"
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rsa"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
|
@ -49,6 +54,9 @@ func marshalKey(k *Key) []byte {
|
|||
return ssh.Marshal(&record)
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 2.5.1.
|
||||
const agentV1IdentitiesAnswer = 2
|
||||
|
||||
type agentV1IdentityMsg struct {
|
||||
Numkeys uint32 `sshtype:"2"`
|
||||
}
|
||||
|
@ -69,6 +77,10 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
|||
switch data[0] {
|
||||
case agentRequestV1Identities:
|
||||
return &agentV1IdentityMsg{0}, nil
|
||||
|
||||
case agentRemoveAllV1Identities:
|
||||
return nil, nil
|
||||
|
||||
case agentRemoveIdentity:
|
||||
var req agentRemoveIdentityMsg
|
||||
if err := ssh.Unmarshal(data, &req); err != nil {
|
||||
|
@ -121,6 +133,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil
|
||||
|
||||
case agentRequestIdentities:
|
||||
keys, err := s.agent.List()
|
||||
if err != nil {
|
||||
|
@ -134,42 +147,271 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
|||
rep.Keys = append(rep.Keys, marshalKey(k)...)
|
||||
}
|
||||
return rep, nil
|
||||
case agentAddIdentity:
|
||||
|
||||
case agentAddIdConstrained, agentAddIdentity:
|
||||
return nil, s.insertIdentity(data)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown opcode %d", data[0])
|
||||
}
|
||||
|
||||
func parseRSAKey(req []byte) (*AddedKey, error) {
|
||||
var k rsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if k.E.BitLen() > 30 {
|
||||
return nil, errors.New("agent: RSA public exponent too large")
|
||||
}
|
||||
priv := &rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
E: int(k.E.Int64()),
|
||||
N: k.N,
|
||||
},
|
||||
D: k.D,
|
||||
Primes: []*big.Int{k.P, k.Q},
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseEd25519Key(req []byte) (*AddedKey, error) {
|
||||
var k ed25519KeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv := ed25519.PrivateKey(k.Priv)
|
||||
return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseDSAKey(req []byte) (*AddedKey, error) {
|
||||
var k dsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv := &dsa.PrivateKey{
|
||||
PublicKey: dsa.PublicKey{
|
||||
Parameters: dsa.Parameters{
|
||||
P: k.P,
|
||||
Q: k.Q,
|
||||
G: k.G,
|
||||
},
|
||||
Y: k.Y,
|
||||
},
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
|
||||
priv = &ecdsa.PrivateKey{
|
||||
D: privScalar,
|
||||
}
|
||||
|
||||
switch curveName {
|
||||
case "nistp256":
|
||||
priv.Curve = elliptic.P256()
|
||||
case "nistp384":
|
||||
priv.Curve = elliptic.P384()
|
||||
case "nistp521":
|
||||
priv.Curve = elliptic.P521()
|
||||
default:
|
||||
return nil, fmt.Errorf("agent: unknown curve %q", curveName)
|
||||
}
|
||||
|
||||
priv.X, priv.Y = elliptic.Unmarshal(priv.Curve, keyBytes)
|
||||
if priv.X == nil || priv.Y == nil {
|
||||
return nil, errors.New("agent: point not on curve")
|
||||
}
|
||||
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
func parseEd25519Cert(req []byte) (*AddedKey, error) {
|
||||
var k ed25519CertMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
priv := ed25519.PrivateKey(k.Priv)
|
||||
cert, ok := pubKey.(*ssh.Certificate)
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad ED25519 certificate")
|
||||
}
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseECDSAKey(req []byte) (*AddedKey, error) {
|
||||
var k ecdsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv, err := unmarshalECDSA(k.Curve, k.KeyBytes, k.D)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseRSACert(req []byte) (*AddedKey, error) {
|
||||
var k rsaCertMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cert, ok := pubKey.(*ssh.Certificate)
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad RSA certificate")
|
||||
}
|
||||
|
||||
// An RSA publickey as marshaled by rsaPublicKey.Marshal() in keys.go
|
||||
var rsaPub struct {
|
||||
Name string
|
||||
E *big.Int
|
||||
N *big.Int
|
||||
}
|
||||
if err := ssh.Unmarshal(cert.Key.Marshal(), &rsaPub); err != nil {
|
||||
return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
|
||||
}
|
||||
|
||||
if rsaPub.E.BitLen() > 30 {
|
||||
return nil, errors.New("agent: RSA public exponent too large")
|
||||
}
|
||||
|
||||
priv := rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
E: int(rsaPub.E.Int64()),
|
||||
N: rsaPub.N,
|
||||
},
|
||||
D: k.D,
|
||||
Primes: []*big.Int{k.Q, k.P},
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseDSACert(req []byte) (*AddedKey, error) {
|
||||
var k dsaCertMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, ok := pubKey.(*ssh.Certificate)
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad DSA certificate")
|
||||
}
|
||||
|
||||
// A DSA publickey as marshaled by dsaPublicKey.Marshal() in keys.go
|
||||
var w struct {
|
||||
Name string
|
||||
P, Q, G, Y *big.Int
|
||||
}
|
||||
if err := ssh.Unmarshal(cert.Key.Marshal(), &w); err != nil {
|
||||
return nil, fmt.Errorf("agent: Unmarshal failed to parse public key: %v", err)
|
||||
}
|
||||
|
||||
priv := &dsa.PrivateKey{
|
||||
PublicKey: dsa.PublicKey{
|
||||
Parameters: dsa.Parameters{
|
||||
P: w.P,
|
||||
Q: w.Q,
|
||||
G: w.G,
|
||||
},
|
||||
Y: w.Y,
|
||||
},
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func parseECDSACert(req []byte) (*AddedKey, error) {
|
||||
var k ecdsaCertMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pubKey, err := ssh.ParsePublicKey(k.CertBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cert, ok := pubKey.(*ssh.Certificate)
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad ECDSA certificate")
|
||||
}
|
||||
|
||||
// An ECDSA publickey as marshaled by ecdsaPublicKey.Marshal() in keys.go
|
||||
var ecdsaPub struct {
|
||||
Name string
|
||||
ID string
|
||||
Key []byte
|
||||
}
|
||||
if err := ssh.Unmarshal(cert.Key.Marshal(), &ecdsaPub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priv, err := unmarshalECDSA(ecdsaPub.ID, ecdsaPub.Key, k.D)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
}
|
||||
|
||||
func (s *server) insertIdentity(req []byte) error {
|
||||
var record struct {
|
||||
Type string `sshtype:"17"`
|
||||
Type string `sshtype:"17|25"`
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
if err := ssh.Unmarshal(req, &record); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var addedKey *AddedKey
|
||||
var err error
|
||||
|
||||
switch record.Type {
|
||||
case ssh.KeyAlgoRSA:
|
||||
var k rsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
priv := rsa.PrivateKey{
|
||||
PublicKey: rsa.PublicKey{
|
||||
E: int(k.E.Int64()),
|
||||
N: k.N,
|
||||
},
|
||||
D: k.D,
|
||||
Primes: []*big.Int{k.P, k.Q},
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return s.agent.Add(&priv, nil, k.Comments)
|
||||
addedKey, err = parseRSAKey(req)
|
||||
case ssh.KeyAlgoDSA:
|
||||
addedKey, err = parseDSAKey(req)
|
||||
case ssh.KeyAlgoECDSA256, ssh.KeyAlgoECDSA384, ssh.KeyAlgoECDSA521:
|
||||
addedKey, err = parseECDSAKey(req)
|
||||
case ssh.KeyAlgoED25519:
|
||||
addedKey, err = parseEd25519Key(req)
|
||||
case ssh.CertAlgoRSAv01:
|
||||
addedKey, err = parseRSACert(req)
|
||||
case ssh.CertAlgoDSAv01:
|
||||
addedKey, err = parseDSACert(req)
|
||||
case ssh.CertAlgoECDSA256v01, ssh.CertAlgoECDSA384v01, ssh.CertAlgoECDSA521v01:
|
||||
addedKey, err = parseECDSACert(req)
|
||||
case ssh.CertAlgoED25519v01:
|
||||
addedKey, err = parseEd25519Cert(req)
|
||||
default:
|
||||
return fmt.Errorf("agent: not implemented: %q", record.Type)
|
||||
}
|
||||
return fmt.Errorf("not implemented: %s", record.Type)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.agent.Add(*addedKey)
|
||||
}
|
||||
|
||||
// ServeAgent serves the agent protocol on the given connection. It
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
@ -21,7 +24,7 @@ func TestServer(t *testing.T) {
|
|||
|
||||
go ServeAgent(NewKeyring(), c2)
|
||||
|
||||
testAgentInterface(t, client, testPrivateKeys["rsa"], nil)
|
||||
testAgentInterface(t, client, testPrivateKeys["rsa"], nil, 0)
|
||||
}
|
||||
|
||||
func TestLockServer(t *testing.T) {
|
||||
|
@ -72,6 +75,133 @@ func TestSetupForwardAgent(t *testing.T) {
|
|||
go ssh.DiscardRequests(reqs)
|
||||
|
||||
agentClient := NewClient(ch)
|
||||
testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil)
|
||||
testAgentInterface(t, agentClient, testPrivateKeys["rsa"], nil, 0)
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func TestV1ProtocolMessages(t *testing.T) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
c := NewClient(c1)
|
||||
|
||||
go ServeAgent(NewKeyring(), c2)
|
||||
|
||||
testV1ProtocolMessages(t, c.(*client))
|
||||
}
|
||||
|
||||
func testV1ProtocolMessages(t *testing.T, c *client) {
|
||||
reply, err := c.call([]byte{agentRequestV1Identities})
|
||||
if err != nil {
|
||||
t.Fatalf("v1 request all failed: %v", err)
|
||||
}
|
||||
if msg, ok := reply.(*agentV1IdentityMsg); !ok || msg.Numkeys != 0 {
|
||||
t.Fatalf("invalid request all response: %#v", reply)
|
||||
}
|
||||
|
||||
reply, err = c.call([]byte{agentRemoveAllV1Identities})
|
||||
if err != nil {
|
||||
t.Fatalf("v1 remove all failed: %v", err)
|
||||
}
|
||||
if _, ok := reply.(*successAgentMsg); !ok {
|
||||
t.Fatalf("invalid remove all response: %#v", reply)
|
||||
}
|
||||
}
|
||||
|
||||
func verifyKey(sshAgent Agent) error {
|
||||
keys, err := sshAgent.List()
|
||||
if err != nil {
|
||||
return fmt.Errorf("listing keys: %v", err)
|
||||
}
|
||||
|
||||
if len(keys) != 1 {
|
||||
return fmt.Errorf("bad number of keys found. expected 1, got %d", len(keys))
|
||||
}
|
||||
|
||||
buf := make([]byte, 128)
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
return fmt.Errorf("rand: %v", err)
|
||||
}
|
||||
|
||||
sig, err := sshAgent.Sign(keys[0], buf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("sign: %v", err)
|
||||
}
|
||||
|
||||
if err := keys[0].Verify(buf, sig); err != nil {
|
||||
return fmt.Errorf("verify: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func addKeyToAgent(key crypto.PrivateKey) error {
|
||||
sshAgent := NewKeyring()
|
||||
if err := sshAgent.Add(AddedKey{PrivateKey: key}); err != nil {
|
||||
return fmt.Errorf("add: %v", err)
|
||||
}
|
||||
return verifyKey(sshAgent)
|
||||
}
|
||||
|
||||
func TestKeyTypes(t *testing.T) {
|
||||
for k, v := range testPrivateKeys {
|
||||
if err := addKeyToAgent(v); err != nil {
|
||||
t.Errorf("error adding key type %s, %v", k, err)
|
||||
}
|
||||
if err := addCertToAgentSock(v, nil); err != nil {
|
||||
t.Errorf("error adding key type %s, %v", k, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addCertToAgentSock(key crypto.PrivateKey, cert *ssh.Certificate) error {
|
||||
a, b, err := netPipe()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
agentServer := NewKeyring()
|
||||
go ServeAgent(agentServer, a)
|
||||
|
||||
agentClient := NewClient(b)
|
||||
if err := agentClient.Add(AddedKey{PrivateKey: key, Certificate: cert}); err != nil {
|
||||
return fmt.Errorf("add: %v", err)
|
||||
}
|
||||
return verifyKey(agentClient)
|
||||
}
|
||||
|
||||
func addCertToAgent(key crypto.PrivateKey, cert *ssh.Certificate) error {
|
||||
sshAgent := NewKeyring()
|
||||
if err := sshAgent.Add(AddedKey{PrivateKey: key, Certificate: cert}); err != nil {
|
||||
return fmt.Errorf("add: %v", err)
|
||||
}
|
||||
return verifyKey(sshAgent)
|
||||
}
|
||||
|
||||
func TestCertTypes(t *testing.T) {
|
||||
for keyType, key := range testPublicKeys {
|
||||
cert := &ssh.Certificate{
|
||||
ValidPrincipals: []string{"gopher1"},
|
||||
ValidAfter: 0,
|
||||
ValidBefore: ssh.CertTimeInfinity,
|
||||
Key: key,
|
||||
Serial: 1,
|
||||
CertType: ssh.UserCert,
|
||||
SignatureKey: testPublicKeys["rsa"],
|
||||
Permissions: ssh.Permissions{
|
||||
CriticalOptions: map[string]string{},
|
||||
Extensions: map[string]string{},
|
||||
},
|
||||
}
|
||||
if err := cert.SignCert(rand.Reader, testSigners["rsa"]); err != nil {
|
||||
t.Fatalf("signcert: %v", err)
|
||||
}
|
||||
if err := addCertToAgent(testPrivateKeys[keyType], cert); err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if err := addCertToAgentSock(testPrivateKeys[keyType], cert); err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places:
|
||||
// IMPLEMENTATION NOTE: To avoid a package loop, this file is in three places:
|
||||
// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three
|
||||
// instances.
|
||||
|
||||
|
|
4
vendor/src/golang.org/x/crypto/ssh/certs.go
vendored
4
vendor/src/golang.org/x/crypto/ssh/certs.go
vendored
|
@ -22,6 +22,7 @@ const (
|
|||
CertAlgoECDSA256v01 = "ecdsa-sha2-nistp256-cert-v01@openssh.com"
|
||||
CertAlgoECDSA384v01 = "ecdsa-sha2-nistp384-cert-v01@openssh.com"
|
||||
CertAlgoECDSA521v01 = "ecdsa-sha2-nistp521-cert-v01@openssh.com"
|
||||
CertAlgoED25519v01 = "ssh-ed25519-cert-v01@openssh.com"
|
||||
)
|
||||
|
||||
// Certificate types distinguish between host and user
|
||||
|
@ -401,6 +402,7 @@ var certAlgoNames = map[string]string{
|
|||
KeyAlgoECDSA256: CertAlgoECDSA256v01,
|
||||
KeyAlgoECDSA384: CertAlgoECDSA384v01,
|
||||
KeyAlgoECDSA521: CertAlgoECDSA521v01,
|
||||
KeyAlgoED25519: CertAlgoED25519v01,
|
||||
}
|
||||
|
||||
// certToPrivAlgo returns the underlying algorithm for a certificate algorithm.
|
||||
|
@ -459,7 +461,7 @@ func (c *Certificate) Marshal() []byte {
|
|||
func (c *Certificate) Type() string {
|
||||
algo, ok := certAlgoNames[c.Key.Type()]
|
||||
if !ok {
|
||||
panic("unknown cert key type")
|
||||
panic("unknown cert key type " + c.Key.Type())
|
||||
}
|
||||
return algo
|
||||
}
|
||||
|
|
11
vendor/src/golang.org/x/crypto/ssh/certs_test.go
vendored
11
vendor/src/golang.org/x/crypto/ssh/certs_test.go
vendored
|
@ -186,15 +186,15 @@ func TestHostKeyCert(t *testing.T) {
|
|||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
conf := ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
conf.AddHostKey(certSigner)
|
||||
_, _, _, err := NewServerConn(c1, &conf)
|
||||
if err != nil {
|
||||
t.Fatalf("NewServerConn: %v", err)
|
||||
}
|
||||
errc <- err
|
||||
}()
|
||||
|
||||
config := &ClientConfig{
|
||||
|
@ -207,5 +207,10 @@ func TestHostKeyCert(t *testing.T) {
|
|||
if (err == nil) != succeed {
|
||||
t.Fatalf("NewClientConn(%q): %v", name, err)
|
||||
}
|
||||
|
||||
err = <-errc
|
||||
if (err == nil) != succeed {
|
||||
t.Fatalf("NewServerConn(%q): %v", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,8 @@ type Channel interface {
|
|||
// boolean, otherwise the return value will be false. Channel
|
||||
// requests are out-of-band messages so they may be sent even
|
||||
// if the data stream is closed or blocked by flow control.
|
||||
// If the channel is closed before a reply is returned, io.EOF
|
||||
// is returned.
|
||||
SendRequest(name string, wantReply bool, payload []byte) (bool, error)
|
||||
|
||||
// Stderr returns an io.ReadWriter that writes to this channel
|
||||
|
@ -217,7 +219,7 @@ func (c *channel) writePacket(packet []byte) error {
|
|||
|
||||
func (c *channel) sendMessage(msg interface{}) error {
|
||||
if debugMux {
|
||||
log.Printf("send %d: %#v", c.mux.chanList.offset, msg)
|
||||
log.Printf("send(%d): %#v", c.mux.chanList.offset, msg)
|
||||
}
|
||||
|
||||
p := Marshal(msg)
|
||||
|
@ -371,7 +373,7 @@ func (c *channel) close() {
|
|||
close(c.msg)
|
||||
close(c.incomingRequests)
|
||||
c.writeMu.Lock()
|
||||
// This is not necesary for a normal channel teardown, but if
|
||||
// This is not necessary for a normal channel teardown, but if
|
||||
// there was another error, it is.
|
||||
c.sentClose = true
|
||||
c.writeMu.Unlock()
|
||||
|
|
48
vendor/src/golang.org/x/crypto/ssh/cipher.go
vendored
48
vendor/src/golang.org/x/crypto/ssh/cipher.go
vendored
|
@ -7,6 +7,7 @@ package ssh
|
|||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/des"
|
||||
"crypto/rc4"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
|
@ -115,9 +116,15 @@ var cipherModes = map[string]*streamCipherMode{
|
|||
// should invest a cleaner way to do this.
|
||||
gcmCipherID: {16, 12, 0, nil},
|
||||
|
||||
// insecure cipher, see http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
|
||||
// uncomment below to enable it.
|
||||
// aes128cbcID: {16, aes.BlockSize, 0, nil},
|
||||
// CBC mode is insecure and so is not included in the default config.
|
||||
// (See http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf). If absolutely
|
||||
// needed, it's possible to specify a custom Config to enable it.
|
||||
// You should expect that an active attacker can recover plaintext if
|
||||
// you do.
|
||||
aes128cbcID: {16, aes.BlockSize, 0, nil},
|
||||
|
||||
// 3des-cbc is insecure and is disabled by default.
|
||||
tripledescbcID: {24, des.BlockSize, 0, nil},
|
||||
}
|
||||
|
||||
// prefixLen is the length of the packet prefix that contains the packet length
|
||||
|
@ -365,12 +372,7 @@ type cbcCipher struct {
|
|||
oracleCamouflage uint32
|
||||
}
|
||||
|
||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func newCBCCipher(c cipher.Block, iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
cbc := &cbcCipher{
|
||||
mac: macModes[algs.MAC].new(macKey),
|
||||
decrypter: cipher.NewCBCDecrypter(c, iv),
|
||||
|
@ -384,6 +386,34 @@ func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCi
|
|||
return cbc, nil
|
||||
}
|
||||
|
||||
func newAESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func newTripleDESCBCCipher(iv, key, macKey []byte, algs directionAlgorithms) (packetCipher, error) {
|
||||
c, err := des.NewTripleDESCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc, err := newCBCCipher(c, iv, key, macKey, algs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cbc, nil
|
||||
}
|
||||
|
||||
func maxUInt32(a, b int) uint32 {
|
||||
if a > b {
|
||||
return uint32(a)
|
||||
|
|
|
@ -21,7 +21,7 @@ func TestDefaultCiphersExist(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestPacketCiphers(t *testing.T) {
|
||||
// Still test aes128cbc cipher althought it's commented out.
|
||||
// Still test aes128cbc cipher although it's commented out.
|
||||
cipherModes[aes128cbcID] = &streamCipherMode{16, aes.BlockSize, 0, nil}
|
||||
defer delete(cipherModes, aes128cbcID)
|
||||
|
||||
|
|
23
vendor/src/golang.org/x/crypto/ssh/client.go
vendored
23
vendor/src/golang.org/x/crypto/ssh/client.go
vendored
|
@ -9,6 +9,7 @@ import (
|
|||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Client implements a traditional SSH client that supports shells,
|
||||
|
@ -96,16 +97,10 @@ func (c *connection) clientHandshake(dialAddress string, config *ClientConfig) e
|
|||
c.transport = newClientTransport(
|
||||
newTransport(c.sshConn.conn, config.Rand, true /* is client */),
|
||||
c.clientVersion, c.serverVersion, config, dialAddress, c.sshConn.RemoteAddr())
|
||||
if err := c.transport.requestKeyChange(); err != nil {
|
||||
if err := c.transport.requestInitialKeyChange(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if packet, err := c.transport.readPacket(); err != nil {
|
||||
return err
|
||||
} else if packet[0] != msgNewKeys {
|
||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||
}
|
||||
|
||||
// We just did the key change, so the session ID is established.
|
||||
c.sessionID = c.transport.getSessionID()
|
||||
|
||||
|
@ -169,7 +164,7 @@ func (c *Client) handleChannelOpens(in <-chan NewChannel) {
|
|||
// to incoming channels and requests, use net.Dial with NewClientConn
|
||||
// instead.
|
||||
func Dial(network, addr string, config *ClientConfig) (*Client, error) {
|
||||
conn, err := net.Dial(network, addr)
|
||||
conn, err := net.DialTimeout(network, addr, config.Timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -203,4 +198,16 @@ type ClientConfig struct {
|
|||
// ClientVersion contains the version identification string that will
|
||||
// be used for the connection. If empty, a reasonable default is used.
|
||||
ClientVersion string
|
||||
|
||||
// HostKeyAlgorithms lists the key types that the client will
|
||||
// accept from the server as host key, in order of
|
||||
// preference. If empty, a reasonable default is used. Any
|
||||
// string returned from PublicKey.Type method may be used, or
|
||||
// any of the CertAlgoXxxx and KeyAlgoXxxx constants.
|
||||
HostKeyAlgorithms []string
|
||||
|
||||
// Timeout is the maximum amount of time for the TCP connection to establish.
|
||||
//
|
||||
// A Timeout of zero means no timeout.
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
|
|
@ -321,8 +321,6 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
|||
return false, msg.Methods, nil
|
||||
case msgUserAuthSuccess:
|
||||
return true, nil, nil
|
||||
case msgDisconnect:
|
||||
return false, nil, io.EOF
|
||||
default:
|
||||
return false, nil, unexpectedMessageError(msgUserAuthSuccess, packet[0])
|
||||
}
|
||||
|
@ -439,3 +437,37 @@ func (cb KeyboardInteractiveChallenge) auth(session []byte, user string, c packe
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type retryableAuthMethod struct {
|
||||
authMethod AuthMethod
|
||||
maxTries int
|
||||
}
|
||||
|
||||
func (r *retryableAuthMethod) auth(session []byte, user string, c packetConn, rand io.Reader) (ok bool, methods []string, err error) {
|
||||
for i := 0; r.maxTries <= 0 || i < r.maxTries; i++ {
|
||||
ok, methods, err = r.authMethod.auth(session, user, c, rand)
|
||||
if ok || err != nil { // either success or error terminate
|
||||
return ok, methods, err
|
||||
}
|
||||
}
|
||||
return ok, methods, err
|
||||
}
|
||||
|
||||
func (r *retryableAuthMethod) method() string {
|
||||
return r.authMethod.method()
|
||||
}
|
||||
|
||||
// RetryableAuthMethod is a decorator for other auth methods enabling them to
|
||||
// be retried up to maxTries before considering that AuthMethod itself failed.
|
||||
// If maxTries is <= 0, will retry indefinitely
|
||||
//
|
||||
// This is useful for interactive clients using challenge/response type
|
||||
// authentication (e.g. Keyboard-Interactive, Password, etc) where the user
|
||||
// could mistype their response resulting in the server issuing a
|
||||
// SSH_MSG_USERAUTH_FAILURE (rfc4252 #8 [password] and rfc4256 #3.4
|
||||
// [keyboard-interactive]); Without this decorator, the non-retryable
|
||||
// AuthMethod would be removed from future consideration, and never tried again
|
||||
// (and so the user would never be able to retry their entry).
|
||||
func RetryableAuthMethod(auth AuthMethod, maxTries int) AuthMethod {
|
||||
return &retryableAuthMethod{authMethod: auth, maxTries: maxTries}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -243,6 +244,9 @@ func TestClientUnsupportedCipher(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestClientUnsupportedKex(t *testing.T) {
|
||||
if os.Getenv("GO_BUILDER_NAME") != "" {
|
||||
t.Skip("skipping known-flaky test on the Go build dashboard; see golang.org/issue/15198")
|
||||
}
|
||||
config := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
|
@ -252,8 +256,8 @@ func TestClientUnsupportedKex(t *testing.T) {
|
|||
KeyExchanges: []string{"diffie-hellman-group-exchange-sha256"}, // not currently supported
|
||||
},
|
||||
}
|
||||
if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "no common algorithms") {
|
||||
t.Errorf("got %v, expected 'no common algorithms'", err)
|
||||
if err := tryAuth(t, config); err == nil || !strings.Contains(err.Error(), "common algorithm") {
|
||||
t.Errorf("got %v, expected 'common algorithm'", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -296,7 +300,7 @@ func TestClientLoginCert(t *testing.T) {
|
|||
t.Log("sign with wrong key")
|
||||
cert.SignCert(rand.Reader, testSigners["dsa"])
|
||||
if err := tryAuth(t, clientConfig); err == nil {
|
||||
t.Errorf("cert login passed with non-authoritive key")
|
||||
t.Errorf("cert login passed with non-authoritative key")
|
||||
}
|
||||
|
||||
t.Log("host cert")
|
||||
|
@ -391,3 +395,78 @@ func TestPermissionsPassing(t *testing.T) {
|
|||
func TestNoPermissionsPassing(t *testing.T) {
|
||||
testPermissionsPassing(false, t)
|
||||
}
|
||||
|
||||
func TestRetryableAuth(t *testing.T) {
|
||||
n := 0
|
||||
passwords := []string{"WRONG1", "WRONG2"}
|
||||
|
||||
config := &ClientConfig{
|
||||
User: "testuser",
|
||||
Auth: []AuthMethod{
|
||||
RetryableAuthMethod(PasswordCallback(func() (string, error) {
|
||||
p := passwords[n]
|
||||
n++
|
||||
return p, nil
|
||||
}), 2),
|
||||
PublicKeys(testSigners["rsa"]),
|
||||
},
|
||||
}
|
||||
|
||||
if err := tryAuth(t, config); err != nil {
|
||||
t.Fatalf("unable to dial remote side: %s", err)
|
||||
}
|
||||
if n != 2 {
|
||||
t.Fatalf("Did not try all passwords")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRetryableAuthMethod(t *testing.T) {
|
||||
user := "testuser"
|
||||
NumberOfPrompts := 3
|
||||
|
||||
// Normally this would be a callback that prompts the user to answer the
|
||||
// provided questions
|
||||
Cb := func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
|
||||
return []string{"answer1", "answer2"}, nil
|
||||
}
|
||||
|
||||
config := &ClientConfig{
|
||||
User: user,
|
||||
Auth: []AuthMethod{
|
||||
RetryableAuthMethod(KeyboardInteractiveChallenge(Cb), NumberOfPrompts),
|
||||
},
|
||||
}
|
||||
|
||||
if err := tryAuth(t, config); err != nil {
|
||||
t.Fatalf("unable to dial remote side: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test if username is received on server side when NoClientAuth is used
|
||||
func TestClientAuthNone(t *testing.T) {
|
||||
user := "testuser"
|
||||
serverConfig := &ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
serverConfig.AddHostKey(testSigners["rsa"])
|
||||
|
||||
clientConfig := &ClientConfig{
|
||||
User: user,
|
||||
}
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go NewClientConn(c2, "", clientConfig)
|
||||
serverConn, err := newServer(c1, serverConfig)
|
||||
if err != nil {
|
||||
t.Fatal("newServer: %v", err)
|
||||
}
|
||||
if serverConn.User() != user {
|
||||
t.Fatalf("server: got %q, want %q", serverConn.User(), user)
|
||||
}
|
||||
}
|
||||
|
|
67
vendor/src/golang.org/x/crypto/ssh/common.go
vendored
67
vendor/src/golang.org/x/crypto/ssh/common.go
vendored
|
@ -33,6 +33,7 @@ var supportedCiphers = []string{
|
|||
// supportedKexAlgos specifies the supported key-exchange algorithms in
|
||||
// preference order.
|
||||
var supportedKexAlgos = []string{
|
||||
kexAlgoCurve25519SHA256,
|
||||
// P384 and P521 are not constant-time yet, but since we don't
|
||||
// reuse ephemeral keys, using them for ECDH should be OK.
|
||||
kexAlgoECDH256, kexAlgoECDH384, kexAlgoECDH521,
|
||||
|
@ -43,10 +44,12 @@ var supportedKexAlgos = []string{
|
|||
// of authenticating servers) in preference order.
|
||||
var supportedHostKeyAlgos = []string{
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01,
|
||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01,
|
||||
CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01,
|
||||
|
||||
KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
KeyAlgoRSA, KeyAlgoDSA,
|
||||
|
||||
KeyAlgoED25519,
|
||||
}
|
||||
|
||||
// supportedMACs specifies a default set of MAC algorithms in preference order.
|
||||
|
@ -84,27 +87,15 @@ func parseError(tag uint8) error {
|
|||
return fmt.Errorf("ssh: parse error in message type %d", tag)
|
||||
}
|
||||
|
||||
func findCommonAlgorithm(clientAlgos []string, serverAlgos []string) (commonAlgo string, ok bool) {
|
||||
for _, clientAlgo := range clientAlgos {
|
||||
for _, serverAlgo := range serverAlgos {
|
||||
if clientAlgo == serverAlgo {
|
||||
return clientAlgo, true
|
||||
func findCommon(what string, client []string, server []string) (common string, err error) {
|
||||
for _, c := range client {
|
||||
for _, s := range server {
|
||||
if c == s {
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func findCommonCipher(clientCiphers []string, serverCiphers []string) (commonCipher string, ok bool) {
|
||||
for _, clientCipher := range clientCiphers {
|
||||
for _, serverCipher := range serverCiphers {
|
||||
// reject the cipher if we have no cipherModes definition
|
||||
if clientCipher == serverCipher && cipherModes[clientCipher] != nil {
|
||||
return clientCipher, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return "", fmt.Errorf("ssh: no common algorithm for %s; client offered: %v, server offered: %v", what, client, server)
|
||||
}
|
||||
|
||||
type directionAlgorithms struct {
|
||||
|
@ -120,50 +111,50 @@ type algorithms struct {
|
|||
r directionAlgorithms
|
||||
}
|
||||
|
||||
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms) {
|
||||
var ok bool
|
||||
func findAgreedAlgorithms(clientKexInit, serverKexInit *kexInitMsg) (algs *algorithms, err error) {
|
||||
result := &algorithms{}
|
||||
result.kex, ok = findCommonAlgorithm(clientKexInit.KexAlgos, serverKexInit.KexAlgos)
|
||||
if !ok {
|
||||
|
||||
result.kex, err = findCommon("key exchange", clientKexInit.KexAlgos, serverKexInit.KexAlgos)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.hostKey, ok = findCommonAlgorithm(clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
|
||||
if !ok {
|
||||
result.hostKey, err = findCommon("host key", clientKexInit.ServerHostKeyAlgos, serverKexInit.ServerHostKeyAlgos)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.w.Cipher, ok = findCommonCipher(clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
|
||||
if !ok {
|
||||
result.w.Cipher, err = findCommon("client to server cipher", clientKexInit.CiphersClientServer, serverKexInit.CiphersClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.Cipher, ok = findCommonCipher(clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
|
||||
if !ok {
|
||||
result.r.Cipher, err = findCommon("server to client cipher", clientKexInit.CiphersServerClient, serverKexInit.CiphersServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.w.MAC, ok = findCommonAlgorithm(clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
|
||||
if !ok {
|
||||
result.w.MAC, err = findCommon("client to server MAC", clientKexInit.MACsClientServer, serverKexInit.MACsClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.MAC, ok = findCommonAlgorithm(clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
|
||||
if !ok {
|
||||
result.r.MAC, err = findCommon("server to client MAC", clientKexInit.MACsServerClient, serverKexInit.MACsServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.w.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
|
||||
if !ok {
|
||||
result.w.Compression, err = findCommon("client to server compression", clientKexInit.CompressionClientServer, serverKexInit.CompressionClientServer)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result.r.Compression, ok = findCommonAlgorithm(clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
|
||||
if !ok {
|
||||
result.r.Compression, err = findCommon("server to client compression", clientKexInit.CompressionServerClient, serverKexInit.CompressionServerClient)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return result
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// If rekeythreshold is too small, we can't make any progress sending
|
||||
|
|
|
@ -23,7 +23,6 @@ func (e *OpenChannelError) Error() string {
|
|||
// ConnMetadata holds metadata for the connection.
|
||||
type ConnMetadata interface {
|
||||
// User returns the user ID for this connection.
|
||||
// It is empty if no authentication is used.
|
||||
User() string
|
||||
|
||||
// SessionID returns the sesson hash, also denoted by H.
|
||||
|
@ -33,7 +32,7 @@ type ConnMetadata interface {
|
|||
// into the session ID.
|
||||
ClientVersion() []byte
|
||||
|
||||
// ServerVersion returns the client's version string as hashed
|
||||
// ServerVersion returns the server's version string as hashed
|
||||
// into the session ID.
|
||||
ServerVersion() []byte
|
||||
|
||||
|
|
|
@ -113,8 +113,7 @@ func ExampleNewServerConn() {
|
|||
}
|
||||
|
||||
func ExampleDial() {
|
||||
// An SSH client is represented with a ClientConn. Currently only
|
||||
// the "password" authentication method is supported.
|
||||
// An SSH client is represented with a ClientConn.
|
||||
//
|
||||
// To authenticate with the remote server you must pass at least one
|
||||
// implementation of AuthMethod via the Auth field in ClientConfig.
|
||||
|
@ -147,6 +146,39 @@ func ExampleDial() {
|
|||
fmt.Println(b.String())
|
||||
}
|
||||
|
||||
func ExamplePublicKeys() {
|
||||
// A public key may be used to authenticate against the remote
|
||||
// server by using an unencrypted PEM-encoded private key file.
|
||||
//
|
||||
// If you have an encrypted private key, the crypto/x509 package
|
||||
// can be used to decrypt it.
|
||||
key, err := ioutil.ReadFile("/home/user/.ssh/id_rsa")
|
||||
if err != nil {
|
||||
log.Fatalf("unable to read private key: %v", err)
|
||||
}
|
||||
|
||||
// Create the Signer for this private key.
|
||||
signer, err := ssh.ParsePrivateKey(key)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to parse private key: %v", err)
|
||||
}
|
||||
|
||||
config := &ssh.ClientConfig{
|
||||
User: "user",
|
||||
Auth: []ssh.AuthMethod{
|
||||
// Use the PublicKeys method for remote authentication.
|
||||
ssh.PublicKeys(signer),
|
||||
},
|
||||
}
|
||||
|
||||
// Connect to the remote server and perform the SSH handshake.
|
||||
client, err := ssh.Dial("tcp", "host.com:22", config)
|
||||
if err != nil {
|
||||
log.Fatalf("unable to connect: %v", err)
|
||||
}
|
||||
defer client.Close()
|
||||
}
|
||||
|
||||
func ExampleClient_Listen() {
|
||||
config := &ssh.ClientConfig{
|
||||
User: "username",
|
||||
|
|
152
vendor/src/golang.org/x/crypto/ssh/handshake.go
vendored
152
vendor/src/golang.org/x/crypto/ssh/handshake.go
vendored
|
@ -29,25 +29,6 @@ type keyingTransport interface {
|
|||
// direction will be effected if a msgNewKeys message is sent
|
||||
// or received.
|
||||
prepareKeyChange(*algorithms, *kexResult) error
|
||||
|
||||
// getSessionID returns the session ID. prepareKeyChange must
|
||||
// have been called once.
|
||||
getSessionID() []byte
|
||||
}
|
||||
|
||||
// rekeyingTransport is the interface of handshakeTransport that we
|
||||
// (internally) expose to ClientConn and ServerConn.
|
||||
type rekeyingTransport interface {
|
||||
packetConn
|
||||
|
||||
// requestKeyChange asks the remote side to change keys. All
|
||||
// writes are blocked until the key change succeeds, which is
|
||||
// signaled by reading a msgNewKeys.
|
||||
requestKeyChange() error
|
||||
|
||||
// getSessionID returns the session ID. This is only valid
|
||||
// after the first key change has completed.
|
||||
getSessionID() []byte
|
||||
}
|
||||
|
||||
// handshakeTransport implements rekeying on top of a keyingTransport
|
||||
|
@ -59,7 +40,14 @@ type handshakeTransport struct {
|
|||
serverVersion []byte
|
||||
clientVersion []byte
|
||||
|
||||
hostKeys []Signer // If hostKeys are given, we are the server.
|
||||
// hostKeys is non-empty if we are the server. In that case,
|
||||
// it contains all host keys that can be used to sign the
|
||||
// connection.
|
||||
hostKeys []Signer
|
||||
|
||||
// hostKeyAlgorithms is non-empty if we are the client. In that case,
|
||||
// we accept these key types from the server as host key.
|
||||
hostKeyAlgorithms []string
|
||||
|
||||
// On read error, incoming is closed, and readError is set.
|
||||
incoming chan []byte
|
||||
|
@ -79,6 +67,9 @@ type handshakeTransport struct {
|
|||
sentInitMsg *kexInitMsg
|
||||
writtenSinceKex uint64
|
||||
writeError error
|
||||
|
||||
// The session ID or nil if first kex did not complete yet.
|
||||
sessionID []byte
|
||||
}
|
||||
|
||||
func newHandshakeTransport(conn keyingTransport, config *Config, clientVersion, serverVersion []byte) *handshakeTransport {
|
||||
|
@ -98,6 +89,11 @@ func newClientTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
|||
t.dialAddress = dialAddr
|
||||
t.remoteAddr = addr
|
||||
t.hostKeyCallback = config.HostKeyCallback
|
||||
if config.HostKeyAlgorithms != nil {
|
||||
t.hostKeyAlgorithms = config.HostKeyAlgorithms
|
||||
} else {
|
||||
t.hostKeyAlgorithms = supportedHostKeyAlgos
|
||||
}
|
||||
go t.readLoop()
|
||||
return t
|
||||
}
|
||||
|
@ -110,7 +106,7 @@ func newServerTransport(conn keyingTransport, clientVersion, serverVersion []byt
|
|||
}
|
||||
|
||||
func (t *handshakeTransport) getSessionID() []byte {
|
||||
return t.conn.getSessionID()
|
||||
return t.sessionID
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) id() string {
|
||||
|
@ -141,6 +137,14 @@ func (t *handshakeTransport) readLoop() {
|
|||
}
|
||||
t.incoming <- p
|
||||
}
|
||||
|
||||
// If we can't read, declare the writing part dead too.
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if t.writeError == nil {
|
||||
t.writeError = t.readError
|
||||
}
|
||||
t.cond.Broadcast()
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
||||
|
@ -157,15 +161,22 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
|||
|
||||
t.readSinceKex += uint64(len(p))
|
||||
if debugHandshake {
|
||||
msg, err := decode(p)
|
||||
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
|
||||
if p[0] == msgChannelData || p[0] == msgChannelExtendedData {
|
||||
log.Printf("%s got data (packet %d bytes)", t.id(), len(p))
|
||||
} else {
|
||||
msg, err := decode(p)
|
||||
log.Printf("%s got %T %v (%v)", t.id(), msg, msg, err)
|
||||
}
|
||||
}
|
||||
if p[0] != msgKexInit {
|
||||
return p, nil
|
||||
}
|
||||
err = t.enterKeyExchange(p)
|
||||
|
||||
t.mu.Lock()
|
||||
|
||||
firstKex := t.sessionID == nil
|
||||
|
||||
err = t.enterKeyExchangeLocked(p)
|
||||
if err != nil {
|
||||
// drop connection
|
||||
t.conn.Close()
|
||||
|
@ -173,7 +184,7 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
|||
}
|
||||
|
||||
if debugHandshake {
|
||||
log.Printf("%s exited key exchange, err %v", t.id(), err)
|
||||
log.Printf("%s exited key exchange (first %v), err %v", t.id(), firstKex, err)
|
||||
}
|
||||
|
||||
// Unblock writers.
|
||||
|
@ -188,28 +199,69 @@ func (t *handshakeTransport) readOnePacket() ([]byte, error) {
|
|||
}
|
||||
|
||||
t.readSinceKex = 0
|
||||
return []byte{msgNewKeys}, nil
|
||||
|
||||
// By default, a key exchange is hidden from higher layers by
|
||||
// translating it into msgIgnore.
|
||||
successPacket := []byte{msgIgnore}
|
||||
if firstKex {
|
||||
// sendKexInit() for the first kex waits for
|
||||
// msgNewKeys so the authentication process is
|
||||
// guaranteed to happen over an encrypted transport.
|
||||
successPacket = []byte{msgNewKeys}
|
||||
}
|
||||
|
||||
return successPacket, nil
|
||||
}
|
||||
|
||||
// keyChangeCategory describes whether a key exchange is the first on a
|
||||
// connection, or a subsequent one.
|
||||
type keyChangeCategory bool
|
||||
|
||||
const (
|
||||
firstKeyExchange keyChangeCategory = true
|
||||
subsequentKeyExchange keyChangeCategory = false
|
||||
)
|
||||
|
||||
// sendKexInit sends a key change message, and returns the message
|
||||
// that was sent. After initiating the key change, all writes will be
|
||||
// blocked until the change is done, and a failed key change will
|
||||
// close the underlying transport. This function is safe for
|
||||
// concurrent use by multiple goroutines.
|
||||
func (t *handshakeTransport) sendKexInit() (*kexInitMsg, []byte, error) {
|
||||
func (t *handshakeTransport) sendKexInit(isFirst keyChangeCategory) error {
|
||||
var err error
|
||||
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
return t.sendKexInitLocked()
|
||||
// If this is the initial key change, but we already have a sessionID,
|
||||
// then do nothing because the key exchange has already completed
|
||||
// asynchronously.
|
||||
if !isFirst || t.sessionID == nil {
|
||||
_, _, err = t.sendKexInitLocked(isFirst)
|
||||
}
|
||||
t.mu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if isFirst {
|
||||
if packet, err := t.readPacket(); err != nil {
|
||||
return err
|
||||
} else if packet[0] != msgNewKeys {
|
||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) requestInitialKeyChange() error {
|
||||
return t.sendKexInit(firstKeyExchange)
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) requestKeyChange() error {
|
||||
_, _, err := t.sendKexInit()
|
||||
return err
|
||||
return t.sendKexInit(subsequentKeyExchange)
|
||||
}
|
||||
|
||||
// sendKexInitLocked sends a key change message. t.mu must be locked
|
||||
// while this happens.
|
||||
func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
||||
func (t *handshakeTransport) sendKexInitLocked(isFirst keyChangeCategory) (*kexInitMsg, []byte, error) {
|
||||
// kexInits may be sent either in response to the other side,
|
||||
// or because our side wants to initiate a key change, so we
|
||||
// may have already sent a kexInit. In that case, don't send a
|
||||
|
@ -217,6 +269,7 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
|||
if t.sentInitMsg != nil {
|
||||
return t.sentInitMsg, t.sentInitPacket, nil
|
||||
}
|
||||
|
||||
msg := &kexInitMsg{
|
||||
KexAlgos: t.config.KeyExchanges,
|
||||
CiphersClientServer: t.config.Ciphers,
|
||||
|
@ -234,7 +287,7 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
|||
msg.ServerHostKeyAlgos, k.PublicKey().Type())
|
||||
}
|
||||
} else {
|
||||
msg.ServerHostKeyAlgos = supportedHostKeyAlgos
|
||||
msg.ServerHostKeyAlgos = t.hostKeyAlgorithms
|
||||
}
|
||||
packet := Marshal(msg)
|
||||
|
||||
|
@ -253,10 +306,12 @@ func (t *handshakeTransport) sendKexInitLocked() (*kexInitMsg, []byte, error) {
|
|||
|
||||
func (t *handshakeTransport) writePacket(p []byte) error {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
||||
if t.writtenSinceKex > t.config.RekeyThreshold {
|
||||
t.sendKexInitLocked()
|
||||
t.sendKexInitLocked(subsequentKeyExchange)
|
||||
}
|
||||
for t.sentInitMsg != nil {
|
||||
for t.sentInitMsg != nil && t.writeError == nil {
|
||||
t.cond.Wait()
|
||||
}
|
||||
if t.writeError != nil {
|
||||
|
@ -264,29 +319,26 @@ func (t *handshakeTransport) writePacket(p []byte) error {
|
|||
}
|
||||
t.writtenSinceKex += uint64(len(p))
|
||||
|
||||
var err error
|
||||
switch p[0] {
|
||||
case msgKexInit:
|
||||
err = errors.New("ssh: only handshakeTransport can send kexInit")
|
||||
return errors.New("ssh: only handshakeTransport can send kexInit")
|
||||
case msgNewKeys:
|
||||
err = errors.New("ssh: only handshakeTransport can send newKeys")
|
||||
return errors.New("ssh: only handshakeTransport can send newKeys")
|
||||
default:
|
||||
err = t.conn.writePacket(p)
|
||||
return t.conn.writePacket(p)
|
||||
}
|
||||
t.mu.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *handshakeTransport) Close() error {
|
||||
return t.conn.Close()
|
||||
}
|
||||
|
||||
// enterKeyExchange runs the key exchange.
|
||||
func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
||||
// enterKeyExchange runs the key exchange. t.mu must be held while running this.
|
||||
func (t *handshakeTransport) enterKeyExchangeLocked(otherInitPacket []byte) error {
|
||||
if debugHandshake {
|
||||
log.Printf("%s entered key exchange", t.id())
|
||||
}
|
||||
myInit, myInitPacket, err := t.sendKexInit()
|
||||
myInit, myInitPacket, err := t.sendKexInitLocked(subsequentKeyExchange)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -313,9 +365,9 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
magics.serverKexInit = otherInitPacket
|
||||
}
|
||||
|
||||
algs := findAgreedAlgorithms(clientInit, serverInit)
|
||||
if algs == nil {
|
||||
return errors.New("ssh: no common algorithms")
|
||||
algs, err := findAgreedAlgorithms(clientInit, serverInit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We don't send FirstKexFollows, but we handle receiving it.
|
||||
|
@ -343,6 +395,11 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if t.sessionID == nil {
|
||||
t.sessionID = result.H
|
||||
}
|
||||
result.SessionID = t.sessionID
|
||||
|
||||
t.conn.prepareKeyChange(algs, result)
|
||||
if err = t.conn.writePacket([]byte{msgNewKeys}); err != nil {
|
||||
return err
|
||||
|
@ -352,6 +409,7 @@ func (t *handshakeTransport) enterKeyExchange(otherInitPacket []byte) error {
|
|||
} else if packet[0] != msgNewKeys {
|
||||
return unexpectedMessageError(msgNewKeys, packet[0])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
201
vendor/src/golang.org/x/crypto/ssh/handshake_test.go
vendored
201
vendor/src/golang.org/x/crypto/ssh/handshake_test.go
vendored
|
@ -7,8 +7,13 @@ package ssh
|
|||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -68,6 +73,7 @@ func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTran
|
|||
|
||||
serverConf := &ServerConfig{}
|
||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
serverConf.SetDefaults()
|
||||
server = newServerTransport(trS, v, v, serverConf)
|
||||
|
||||
|
@ -75,6 +81,9 @@ func handshakePair(clientConf *ClientConfig, addr string) (client *handshakeTran
|
|||
}
|
||||
|
||||
func TestHandshakeBasic(t *testing.T) {
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("see golang.org/issue/7237")
|
||||
}
|
||||
checker := &testChecker{}
|
||||
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr")
|
||||
if err != nil {
|
||||
|
@ -95,7 +104,7 @@ func TestHandshakeBasic(t *testing.T) {
|
|||
}
|
||||
if i == 5 {
|
||||
// halfway through, we request a key change.
|
||||
_, _, err := trC.sendKexInit()
|
||||
err := trC.sendKexInit(subsequentKeyExchange)
|
||||
if err != nil {
|
||||
t.Fatalf("sendKexInit: %v", err)
|
||||
}
|
||||
|
@ -152,7 +161,7 @@ func TestHandshakeError(t *testing.T) {
|
|||
}
|
||||
|
||||
// Now request a key change.
|
||||
_, _, err = trC.sendKexInit()
|
||||
err = trC.sendKexInit(subsequentKeyExchange)
|
||||
if err != nil {
|
||||
t.Errorf("sendKexInit: %v", err)
|
||||
}
|
||||
|
@ -175,6 +184,28 @@ func TestHandshakeError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestForceFirstKex(t *testing.T) {
|
||||
checker := &testChecker{}
|
||||
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr")
|
||||
if err != nil {
|
||||
t.Fatalf("handshakePair: %v", err)
|
||||
}
|
||||
|
||||
defer trC.Close()
|
||||
defer trS.Close()
|
||||
|
||||
trC.writePacket(Marshal(&serviceRequestMsg{serviceUserAuth}))
|
||||
|
||||
// We setup the initial key exchange, but the remote side
|
||||
// tries to send serviceRequestMsg in cleartext, which is
|
||||
// disallowed.
|
||||
|
||||
err = trS.sendKexInit(firstKeyExchange)
|
||||
if err == nil {
|
||||
t.Errorf("server first kex init should reject unexpected packet")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandshakeTwice(t *testing.T) {
|
||||
checker := &testChecker{}
|
||||
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr")
|
||||
|
@ -185,18 +216,25 @@ func TestHandshakeTwice(t *testing.T) {
|
|||
defer trC.Close()
|
||||
defer trS.Close()
|
||||
|
||||
// Both sides should ask for the first key exchange first.
|
||||
err = trS.sendKexInit(firstKeyExchange)
|
||||
if err != nil {
|
||||
t.Errorf("server sendKexInit: %v", err)
|
||||
}
|
||||
|
||||
err = trC.sendKexInit(firstKeyExchange)
|
||||
if err != nil {
|
||||
t.Errorf("client sendKexInit: %v", err)
|
||||
}
|
||||
|
||||
sent := 0
|
||||
// send a packet
|
||||
packet := make([]byte, 5)
|
||||
packet[0] = msgRequestSuccess
|
||||
if err := trC.writePacket(packet); err != nil {
|
||||
t.Errorf("writePacket: %v", err)
|
||||
}
|
||||
|
||||
// Now request a key change.
|
||||
_, _, err = trC.sendKexInit()
|
||||
if err != nil {
|
||||
t.Errorf("sendKexInit: %v", err)
|
||||
}
|
||||
sent++
|
||||
|
||||
// Send another packet. Use a fresh one, since writePacket destroys.
|
||||
packet = make([]byte, 5)
|
||||
|
@ -204,9 +242,10 @@ func TestHandshakeTwice(t *testing.T) {
|
|||
if err := trC.writePacket(packet); err != nil {
|
||||
t.Errorf("writePacket: %v", err)
|
||||
}
|
||||
sent++
|
||||
|
||||
// 2nd key change.
|
||||
_, _, err = trC.sendKexInit()
|
||||
err = trC.sendKexInit(subsequentKeyExchange)
|
||||
if err != nil {
|
||||
t.Errorf("sendKexInit: %v", err)
|
||||
}
|
||||
|
@ -216,17 +255,15 @@ func TestHandshakeTwice(t *testing.T) {
|
|||
if err := trC.writePacket(packet); err != nil {
|
||||
t.Errorf("writePacket: %v", err)
|
||||
}
|
||||
sent++
|
||||
|
||||
packet = make([]byte, 5)
|
||||
packet[0] = msgRequestSuccess
|
||||
for i := 0; i < 5; i++ {
|
||||
for i := 0; i < sent; i++ {
|
||||
msg, err := trS.readPacket()
|
||||
if err != nil {
|
||||
t.Fatalf("server closed too soon: %v", err)
|
||||
}
|
||||
if msg[0] == msgNewKeys {
|
||||
continue
|
||||
}
|
||||
|
||||
if bytes.Compare(msg, packet) != 0 {
|
||||
t.Errorf("packet %d: got %q want %q", i, msg, packet)
|
||||
|
@ -309,3 +346,141 @@ func TestHandshakeAutoRekeyRead(t *testing.T) {
|
|||
|
||||
<-sync.called
|
||||
}
|
||||
|
||||
// errorKeyingTransport generates errors after a given number of
|
||||
// read/write operations.
|
||||
type errorKeyingTransport struct {
|
||||
packetConn
|
||||
readLeft, writeLeft int
|
||||
}
|
||||
|
||||
func (n *errorKeyingTransport) prepareKeyChange(*algorithms, *kexResult) error {
|
||||
return nil
|
||||
}
|
||||
func (n *errorKeyingTransport) getSessionID() []byte {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *errorKeyingTransport) writePacket(packet []byte) error {
|
||||
if n.writeLeft == 0 {
|
||||
n.Close()
|
||||
return errors.New("barf")
|
||||
}
|
||||
|
||||
n.writeLeft--
|
||||
return n.packetConn.writePacket(packet)
|
||||
}
|
||||
|
||||
func (n *errorKeyingTransport) readPacket() ([]byte, error) {
|
||||
if n.readLeft == 0 {
|
||||
n.Close()
|
||||
return nil, errors.New("barf")
|
||||
}
|
||||
|
||||
n.readLeft--
|
||||
return n.packetConn.readPacket()
|
||||
}
|
||||
|
||||
func TestHandshakeErrorHandlingRead(t *testing.T) {
|
||||
for i := 0; i < 20; i++ {
|
||||
testHandshakeErrorHandlingN(t, i, -1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandshakeErrorHandlingWrite(t *testing.T) {
|
||||
for i := 0; i < 20; i++ {
|
||||
testHandshakeErrorHandlingN(t, -1, i)
|
||||
}
|
||||
}
|
||||
|
||||
// testHandshakeErrorHandlingN runs handshakes, injecting errors. If
|
||||
// handshakeTransport deadlocks, the go runtime will detect it and
|
||||
// panic.
|
||||
func testHandshakeErrorHandlingN(t *testing.T, readLimit, writeLimit int) {
|
||||
msg := Marshal(&serviceRequestMsg{strings.Repeat("x", int(minRekeyThreshold)/4)})
|
||||
|
||||
a, b := memPipe()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
key := testSigners["ecdsa"]
|
||||
serverConf := Config{RekeyThreshold: minRekeyThreshold}
|
||||
serverConf.SetDefaults()
|
||||
serverConn := newHandshakeTransport(&errorKeyingTransport{a, readLimit, writeLimit}, &serverConf, []byte{'a'}, []byte{'b'})
|
||||
serverConn.hostKeys = []Signer{key}
|
||||
go serverConn.readLoop()
|
||||
|
||||
clientConf := Config{RekeyThreshold: 10 * minRekeyThreshold}
|
||||
clientConf.SetDefaults()
|
||||
clientConn := newHandshakeTransport(&errorKeyingTransport{b, -1, -1}, &clientConf, []byte{'a'}, []byte{'b'})
|
||||
clientConn.hostKeyAlgorithms = []string{key.PublicKey().Type()}
|
||||
go clientConn.readLoop()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(4)
|
||||
|
||||
for _, hs := range []packetConn{serverConn, clientConn} {
|
||||
go func(c packetConn) {
|
||||
for {
|
||||
err := c.writePacket(msg)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(hs)
|
||||
go func(c packetConn) {
|
||||
for {
|
||||
_, err := c.readPacket()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
wg.Done()
|
||||
}(hs)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestDisconnect(t *testing.T) {
|
||||
if runtime.GOOS == "plan9" {
|
||||
t.Skip("see golang.org/issue/7237")
|
||||
}
|
||||
checker := &testChecker{}
|
||||
trC, trS, err := handshakePair(&ClientConfig{HostKeyCallback: checker.Check}, "addr")
|
||||
if err != nil {
|
||||
t.Fatalf("handshakePair: %v", err)
|
||||
}
|
||||
|
||||
defer trC.Close()
|
||||
defer trS.Close()
|
||||
|
||||
trC.writePacket([]byte{msgRequestSuccess, 0, 0})
|
||||
errMsg := &disconnectMsg{
|
||||
Reason: 42,
|
||||
Message: "such is life",
|
||||
}
|
||||
trC.writePacket(Marshal(errMsg))
|
||||
trC.writePacket([]byte{msgRequestSuccess, 0, 0})
|
||||
|
||||
packet, err := trS.readPacket()
|
||||
if err != nil {
|
||||
t.Fatalf("readPacket 1: %v", err)
|
||||
}
|
||||
if packet[0] != msgRequestSuccess {
|
||||
t.Errorf("got packet %v, want packet type %d", packet, msgRequestSuccess)
|
||||
}
|
||||
|
||||
_, err = trS.readPacket()
|
||||
if err == nil {
|
||||
t.Errorf("readPacket 2 succeeded")
|
||||
} else if !reflect.DeepEqual(err, errMsg) {
|
||||
t.Errorf("got error %#v, want %#v", err, errMsg)
|
||||
}
|
||||
|
||||
_, err = trS.readPacket()
|
||||
if err == nil {
|
||||
t.Errorf("readPacket 3 succeeded")
|
||||
}
|
||||
}
|
||||
|
|
152
vendor/src/golang.org/x/crypto/ssh/kex.go
vendored
152
vendor/src/golang.org/x/crypto/ssh/kex.go
vendored
|
@ -9,17 +9,21 @@ import (
|
|||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"io"
|
||||
"math/big"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
const (
|
||||
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
||||
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||
kexAlgoDH1SHA1 = "diffie-hellman-group1-sha1"
|
||||
kexAlgoDH14SHA1 = "diffie-hellman-group14-sha1"
|
||||
kexAlgoECDH256 = "ecdh-sha2-nistp256"
|
||||
kexAlgoECDH384 = "ecdh-sha2-nistp384"
|
||||
kexAlgoECDH521 = "ecdh-sha2-nistp521"
|
||||
kexAlgoCurve25519SHA256 = "curve25519-sha256@libssh.org"
|
||||
)
|
||||
|
||||
// kexResult captures the outcome of a key exchange.
|
||||
|
@ -42,7 +46,7 @@ type kexResult struct {
|
|||
Hash crypto.Hash
|
||||
|
||||
// The session ID, which is the first H computed. This is used
|
||||
// to signal data inside transport.
|
||||
// to derive key material inside the transport.
|
||||
SessionID []byte
|
||||
}
|
||||
|
||||
|
@ -383,4 +387,140 @@ func init() {
|
|||
kexAlgoMap[kexAlgoECDH521] = &ecdh{elliptic.P521()}
|
||||
kexAlgoMap[kexAlgoECDH384] = &ecdh{elliptic.P384()}
|
||||
kexAlgoMap[kexAlgoECDH256] = &ecdh{elliptic.P256()}
|
||||
kexAlgoMap[kexAlgoCurve25519SHA256] = &curve25519sha256{}
|
||||
}
|
||||
|
||||
// curve25519sha256 implements the curve25519-sha256@libssh.org key
|
||||
// agreement protocol, as described in
|
||||
// https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt
|
||||
type curve25519sha256 struct{}
|
||||
|
||||
type curve25519KeyPair struct {
|
||||
priv [32]byte
|
||||
pub [32]byte
|
||||
}
|
||||
|
||||
func (kp *curve25519KeyPair) generate(rand io.Reader) error {
|
||||
if _, err := io.ReadFull(rand, kp.priv[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
curve25519.ScalarBaseMult(&kp.pub, &kp.priv)
|
||||
return nil
|
||||
}
|
||||
|
||||
// curve25519Zeros is just an array of 32 zero bytes so that we have something
|
||||
// convenient to compare against in order to reject curve25519 points with the
|
||||
// wrong order.
|
||||
var curve25519Zeros [32]byte
|
||||
|
||||
func (kex *curve25519sha256) Client(c packetConn, rand io.Reader, magics *handshakeMagics) (*kexResult, error) {
|
||||
var kp curve25519KeyPair
|
||||
if err := kp.generate(rand); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := c.writePacket(Marshal(&kexECDHInitMsg{kp.pub[:]})); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var reply kexECDHReplyMsg
|
||||
if err = Unmarshal(packet, &reply); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(reply.EphemeralPubKey) != 32 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
|
||||
}
|
||||
|
||||
var servPub, secret [32]byte
|
||||
copy(servPub[:], reply.EphemeralPubKey)
|
||||
curve25519.ScalarMult(&secret, &kp.priv, &servPub)
|
||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
||||
}
|
||||
|
||||
h := crypto.SHA256.New()
|
||||
magics.write(h)
|
||||
writeString(h, reply.HostKey)
|
||||
writeString(h, kp.pub[:])
|
||||
writeString(h, reply.EphemeralPubKey)
|
||||
|
||||
kInt := new(big.Int).SetBytes(secret[:])
|
||||
K := make([]byte, intLength(kInt))
|
||||
marshalInt(K, kInt)
|
||||
h.Write(K)
|
||||
|
||||
return &kexResult{
|
||||
H: h.Sum(nil),
|
||||
K: K,
|
||||
HostKey: reply.HostKey,
|
||||
Signature: reply.Signature,
|
||||
Hash: crypto.SHA256,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (kex *curve25519sha256) Server(c packetConn, rand io.Reader, magics *handshakeMagics, priv Signer) (result *kexResult, err error) {
|
||||
packet, err := c.readPacket()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
var kexInit kexECDHInitMsg
|
||||
if err = Unmarshal(packet, &kexInit); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(kexInit.ClientPubKey) != 32 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong length")
|
||||
}
|
||||
|
||||
var kp curve25519KeyPair
|
||||
if err := kp.generate(rand); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var clientPub, secret [32]byte
|
||||
copy(clientPub[:], kexInit.ClientPubKey)
|
||||
curve25519.ScalarMult(&secret, &kp.priv, &clientPub)
|
||||
if subtle.ConstantTimeCompare(secret[:], curve25519Zeros[:]) == 1 {
|
||||
return nil, errors.New("ssh: peer's curve25519 public value has wrong order")
|
||||
}
|
||||
|
||||
hostKeyBytes := priv.PublicKey().Marshal()
|
||||
|
||||
h := crypto.SHA256.New()
|
||||
magics.write(h)
|
||||
writeString(h, hostKeyBytes)
|
||||
writeString(h, kexInit.ClientPubKey)
|
||||
writeString(h, kp.pub[:])
|
||||
|
||||
kInt := new(big.Int).SetBytes(secret[:])
|
||||
K := make([]byte, intLength(kInt))
|
||||
marshalInt(K, kInt)
|
||||
h.Write(K)
|
||||
|
||||
H := h.Sum(nil)
|
||||
|
||||
sig, err := signAndMarshal(priv, rand, H)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reply := kexECDHReplyMsg{
|
||||
EphemeralPubKey: kp.pub[:],
|
||||
HostKey: hostKeyBytes,
|
||||
Signature: sig,
|
||||
}
|
||||
if err := c.writePacket(Marshal(&reply)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &kexResult{
|
||||
H: H,
|
||||
K: K,
|
||||
HostKey: hostKeyBytes,
|
||||
Signature: sig,
|
||||
Hash: crypto.SHA256,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -26,10 +26,12 @@ func TestKexes(t *testing.T) {
|
|||
var magics handshakeMagics
|
||||
go func() {
|
||||
r, e := kex.Client(a, rand.Reader, &magics)
|
||||
a.Close()
|
||||
c <- kexResultErr{r, e}
|
||||
}()
|
||||
go func() {
|
||||
r, e := kex.Server(b, rand.Reader, &magics, testSigners["ecdsa"])
|
||||
b.Close()
|
||||
s <- kexResultErr{r, e}
|
||||
}()
|
||||
|
||||
|
|
384
vendor/src/golang.org/x/crypto/ssh/keys.go
vendored
384
vendor/src/golang.org/x/crypto/ssh/keys.go
vendored
|
@ -19,6 +19,9 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
)
|
||||
|
||||
// These constants represent the algorithm names for key types supported by this
|
||||
|
@ -29,6 +32,7 @@ const (
|
|||
KeyAlgoECDSA256 = "ecdsa-sha2-nistp256"
|
||||
KeyAlgoECDSA384 = "ecdsa-sha2-nistp384"
|
||||
KeyAlgoECDSA521 = "ecdsa-sha2-nistp521"
|
||||
KeyAlgoED25519 = "ssh-ed25519"
|
||||
)
|
||||
|
||||
// parsePubKey parses a public key of the given algorithm.
|
||||
|
@ -41,14 +45,16 @@ func parsePubKey(in []byte, algo string) (pubKey PublicKey, rest []byte, err err
|
|||
return parseDSA(in)
|
||||
case KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521:
|
||||
return parseECDSA(in)
|
||||
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||
case KeyAlgoED25519:
|
||||
return parseED25519(in)
|
||||
case CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01, CertAlgoED25519v01:
|
||||
cert, err := parseCert(in, certToPrivAlgo(algo))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return cert, nil, nil
|
||||
}
|
||||
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", err)
|
||||
return nil, nil, fmt.Errorf("ssh: unknown key algorithm: %v", algo)
|
||||
}
|
||||
|
||||
// parseAuthorizedKey parses a public key in OpenSSH authorized_keys format
|
||||
|
@ -77,6 +83,79 @@ func parseAuthorizedKey(in []byte) (out PublicKey, comment string, err error) {
|
|||
return out, comment, nil
|
||||
}
|
||||
|
||||
// ParseKnownHosts parses an entry in the format of the known_hosts file.
|
||||
//
|
||||
// The known_hosts format is documented in the sshd(8) manual page. This
|
||||
// function will parse a single entry from in. On successful return, marker
|
||||
// will contain the optional marker value (i.e. "cert-authority" or "revoked")
|
||||
// or else be empty, hosts will contain the hosts that this entry matches,
|
||||
// pubKey will contain the public key and comment will contain any trailing
|
||||
// comment at the end of the line. See the sshd(8) manual page for the various
|
||||
// forms that a host string can take.
|
||||
//
|
||||
// The unparsed remainder of the input will be returned in rest. This function
|
||||
// can be called repeatedly to parse multiple entries.
|
||||
//
|
||||
// If no entries were found in the input then err will be io.EOF. Otherwise a
|
||||
// non-nil err value indicates a parse error.
|
||||
func ParseKnownHosts(in []byte) (marker string, hosts []string, pubKey PublicKey, comment string, rest []byte, err error) {
|
||||
for len(in) > 0 {
|
||||
end := bytes.IndexByte(in, '\n')
|
||||
if end != -1 {
|
||||
rest = in[end+1:]
|
||||
in = in[:end]
|
||||
} else {
|
||||
rest = nil
|
||||
}
|
||||
|
||||
end = bytes.IndexByte(in, '\r')
|
||||
if end != -1 {
|
||||
in = in[:end]
|
||||
}
|
||||
|
||||
in = bytes.TrimSpace(in)
|
||||
if len(in) == 0 || in[0] == '#' {
|
||||
in = rest
|
||||
continue
|
||||
}
|
||||
|
||||
i := bytes.IndexAny(in, " \t")
|
||||
if i == -1 {
|
||||
in = rest
|
||||
continue
|
||||
}
|
||||
|
||||
// Strip out the beginning of the known_host key.
|
||||
// This is either an optional marker or a (set of) hostname(s).
|
||||
keyFields := bytes.Fields(in)
|
||||
if len(keyFields) < 3 || len(keyFields) > 5 {
|
||||
return "", nil, nil, "", nil, errors.New("ssh: invalid entry in known_hosts data")
|
||||
}
|
||||
|
||||
// keyFields[0] is either "@cert-authority", "@revoked" or a comma separated
|
||||
// list of hosts
|
||||
marker := ""
|
||||
if keyFields[0][0] == '@' {
|
||||
marker = string(keyFields[0][1:])
|
||||
keyFields = keyFields[1:]
|
||||
}
|
||||
|
||||
hosts := string(keyFields[0])
|
||||
// keyFields[1] contains the key type (e.g. “ssh-rsa”).
|
||||
// However, that information is duplicated inside the
|
||||
// base64-encoded key and so is ignored here.
|
||||
|
||||
key := bytes.Join(keyFields[2:], []byte(" "))
|
||||
if pubKey, comment, err = parseAuthorizedKey(key); err != nil {
|
||||
return "", nil, nil, "", nil, err
|
||||
}
|
||||
|
||||
return marker, strings.Split(hosts, ","), pubKey, comment, rest, nil
|
||||
}
|
||||
|
||||
return "", nil, nil, "", nil, io.EOF
|
||||
}
|
||||
|
||||
// ParseAuthorizedKeys parses a public key from an authorized_keys
|
||||
// file used in OpenSSH according to the sshd(8) manual page.
|
||||
func ParseAuthorizedKey(in []byte) (out PublicKey, comment string, options []string, rest []byte, err error) {
|
||||
|
@ -245,6 +324,8 @@ func parseRSA(in []byte) (out PublicKey, rest []byte, err error) {
|
|||
|
||||
func (r *rsaPublicKey) Marshal() []byte {
|
||||
e := new(big.Int).SetInt64(int64(r.E))
|
||||
// RSA publickey struct layout should match the struct used by
|
||||
// parseRSACert in the x/crypto/ssh/agent package.
|
||||
wirekey := struct {
|
||||
Name string
|
||||
E *big.Int
|
||||
|
@ -267,28 +348,6 @@ func (r *rsaPublicKey) Verify(data []byte, sig *Signature) error {
|
|||
return rsa.VerifyPKCS1v15((*rsa.PublicKey)(r), crypto.SHA1, digest, sig.Blob)
|
||||
}
|
||||
|
||||
type rsaPrivateKey struct {
|
||||
*rsa.PrivateKey
|
||||
}
|
||||
|
||||
func (r *rsaPrivateKey) PublicKey() PublicKey {
|
||||
return (*rsaPublicKey)(&r.PrivateKey.PublicKey)
|
||||
}
|
||||
|
||||
func (r *rsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
h := crypto.SHA1.New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
blob, err := rsa.SignPKCS1v15(rand, r.PrivateKey, crypto.SHA1, digest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Signature{
|
||||
Format: r.PublicKey().Type(),
|
||||
Blob: blob,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type dsaPublicKey dsa.PublicKey
|
||||
|
||||
func (r *dsaPublicKey) Type() string {
|
||||
|
@ -317,6 +376,8 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
|||
}
|
||||
|
||||
func (k *dsaPublicKey) Marshal() []byte {
|
||||
// DSA publickey struct layout should match the struct used by
|
||||
// parseDSACert in the x/crypto/ssh/agent package.
|
||||
w := struct {
|
||||
Name string
|
||||
P, Q, G, Y *big.Int
|
||||
|
@ -403,6 +464,51 @@ func (key *ecdsaPublicKey) nistID() string {
|
|||
panic("ssh: unsupported ecdsa key size")
|
||||
}
|
||||
|
||||
type ed25519PublicKey ed25519.PublicKey
|
||||
|
||||
func (key ed25519PublicKey) Type() string {
|
||||
return KeyAlgoED25519
|
||||
}
|
||||
|
||||
func parseED25519(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
var w struct {
|
||||
KeyBytes []byte
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
if err := Unmarshal(in, &w); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key := ed25519.PublicKey(w.KeyBytes)
|
||||
|
||||
return (ed25519PublicKey)(key), w.Rest, nil
|
||||
}
|
||||
|
||||
func (key ed25519PublicKey) Marshal() []byte {
|
||||
w := struct {
|
||||
Name string
|
||||
KeyBytes []byte
|
||||
}{
|
||||
KeyAlgoED25519,
|
||||
[]byte(key),
|
||||
}
|
||||
return Marshal(&w)
|
||||
}
|
||||
|
||||
func (key ed25519PublicKey) Verify(b []byte, sig *Signature) error {
|
||||
if sig.Format != key.Type() {
|
||||
return fmt.Errorf("ssh: signature type %s for key type %s", sig.Format, key.Type())
|
||||
}
|
||||
|
||||
edKey := (ed25519.PublicKey)(key)
|
||||
if ok := ed25519.Verify(edKey, b, sig.Blob); !ok {
|
||||
return errors.New("ssh: signature did not verify")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func supportedEllipticCurve(curve elliptic.Curve) bool {
|
||||
return curve == elliptic.P256() || curve == elliptic.P384() || curve == elliptic.P521()
|
||||
}
|
||||
|
@ -422,14 +528,19 @@ func ecHash(curve elliptic.Curve) crypto.Hash {
|
|||
|
||||
// parseECDSA parses an ECDSA key according to RFC 5656, section 3.1.
|
||||
func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
identifier, in, ok := parseString(in)
|
||||
if !ok {
|
||||
return nil, nil, errShortRead
|
||||
var w struct {
|
||||
Curve string
|
||||
KeyBytes []byte
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
if err := Unmarshal(in, &w); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key := new(ecdsa.PublicKey)
|
||||
|
||||
switch string(identifier) {
|
||||
switch w.Curve {
|
||||
case "nistp256":
|
||||
key.Curve = elliptic.P256()
|
||||
case "nistp384":
|
||||
|
@ -440,21 +551,18 @@ func parseECDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
|||
return nil, nil, errors.New("ssh: unsupported curve")
|
||||
}
|
||||
|
||||
var keyBytes []byte
|
||||
if keyBytes, in, ok = parseString(in); !ok {
|
||||
return nil, nil, errShortRead
|
||||
}
|
||||
|
||||
key.X, key.Y = elliptic.Unmarshal(key.Curve, keyBytes)
|
||||
key.X, key.Y = elliptic.Unmarshal(key.Curve, w.KeyBytes)
|
||||
if key.X == nil || key.Y == nil {
|
||||
return nil, nil, errors.New("ssh: invalid curve point")
|
||||
}
|
||||
return (*ecdsaPublicKey)(key), in, nil
|
||||
return (*ecdsaPublicKey)(key), w.Rest, nil
|
||||
}
|
||||
|
||||
func (key *ecdsaPublicKey) Marshal() []byte {
|
||||
// See RFC 5656, section 3.1.
|
||||
keyBytes := elliptic.Marshal(key.Curve, key.X, key.Y)
|
||||
// ECDSA publickey struct layout should match the struct used by
|
||||
// parseECDSACert in the x/crypto/ssh/agent package.
|
||||
w := struct {
|
||||
Name string
|
||||
ID string
|
||||
|
@ -496,72 +604,120 @@ func (key *ecdsaPublicKey) Verify(data []byte, sig *Signature) error {
|
|||
return errors.New("ssh: signature did not verify")
|
||||
}
|
||||
|
||||
type ecdsaPrivateKey struct {
|
||||
*ecdsa.PrivateKey
|
||||
// NewSignerFromKey takes an *rsa.PrivateKey, *dsa.PrivateKey,
|
||||
// *ecdsa.PrivateKey or any other crypto.Signer and returns a corresponding
|
||||
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
||||
func NewSignerFromKey(key interface{}) (Signer, error) {
|
||||
switch key := key.(type) {
|
||||
case crypto.Signer:
|
||||
return NewSignerFromSigner(key)
|
||||
case *dsa.PrivateKey:
|
||||
return &dsaPrivateKey{key}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) PublicKey() PublicKey {
|
||||
return (*ecdsaPublicKey)(&k.PrivateKey.PublicKey)
|
||||
type wrappedSigner struct {
|
||||
signer crypto.Signer
|
||||
pubKey PublicKey
|
||||
}
|
||||
|
||||
func (k *ecdsaPrivateKey) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
h := ecHash(k.PrivateKey.PublicKey.Curve).New()
|
||||
h.Write(data)
|
||||
digest := h.Sum(nil)
|
||||
r, s, err := ecdsa.Sign(rand, k.PrivateKey, digest)
|
||||
// NewSignerFromSigner takes any crypto.Signer implementation and
|
||||
// returns a corresponding Signer interface. This can be used, for
|
||||
// example, with keys kept in hardware modules.
|
||||
func NewSignerFromSigner(signer crypto.Signer) (Signer, error) {
|
||||
pubKey, err := NewPublicKey(signer.Public())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := make([]byte, intLength(r)+intLength(s))
|
||||
rest := marshalInt(sig, r)
|
||||
marshalInt(rest, s)
|
||||
return &wrappedSigner{signer, pubKey}, nil
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) PublicKey() PublicKey {
|
||||
return s.pubKey
|
||||
}
|
||||
|
||||
func (s *wrappedSigner) Sign(rand io.Reader, data []byte) (*Signature, error) {
|
||||
var hashFunc crypto.Hash
|
||||
|
||||
switch key := s.pubKey.(type) {
|
||||
case *rsaPublicKey, *dsaPublicKey:
|
||||
hashFunc = crypto.SHA1
|
||||
case *ecdsaPublicKey:
|
||||
hashFunc = ecHash(key.Curve)
|
||||
case ed25519PublicKey:
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
|
||||
var digest []byte
|
||||
if hashFunc != 0 {
|
||||
h := hashFunc.New()
|
||||
h.Write(data)
|
||||
digest = h.Sum(nil)
|
||||
} else {
|
||||
digest = data
|
||||
}
|
||||
|
||||
signature, err := s.signer.Sign(rand, digest, hashFunc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// crypto.Signer.Sign is expected to return an ASN.1-encoded signature
|
||||
// for ECDSA and DSA, but that's not the encoding expected by SSH, so
|
||||
// re-encode.
|
||||
switch s.pubKey.(type) {
|
||||
case *ecdsaPublicKey, *dsaPublicKey:
|
||||
type asn1Signature struct {
|
||||
R, S *big.Int
|
||||
}
|
||||
asn1Sig := new(asn1Signature)
|
||||
_, err := asn1.Unmarshal(signature, asn1Sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch s.pubKey.(type) {
|
||||
case *ecdsaPublicKey:
|
||||
signature = Marshal(asn1Sig)
|
||||
|
||||
case *dsaPublicKey:
|
||||
signature = make([]byte, 40)
|
||||
r := asn1Sig.R.Bytes()
|
||||
s := asn1Sig.S.Bytes()
|
||||
copy(signature[20-len(r):20], r)
|
||||
copy(signature[40-len(s):40], s)
|
||||
}
|
||||
}
|
||||
|
||||
return &Signature{
|
||||
Format: k.PublicKey().Type(),
|
||||
Blob: sig,
|
||||
Format: s.pubKey.Type(),
|
||||
Blob: signature,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewSignerFromKey takes a pointer to rsa, dsa or ecdsa PrivateKey
|
||||
// returns a corresponding Signer instance. EC keys should use P256,
|
||||
// P384 or P521.
|
||||
func NewSignerFromKey(k interface{}) (Signer, error) {
|
||||
var sshKey Signer
|
||||
switch t := k.(type) {
|
||||
case *rsa.PrivateKey:
|
||||
sshKey = &rsaPrivateKey{t}
|
||||
case *dsa.PrivateKey:
|
||||
sshKey = &dsaPrivateKey{t}
|
||||
case *ecdsa.PrivateKey:
|
||||
if !supportedEllipticCurve(t.Curve) {
|
||||
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
|
||||
}
|
||||
|
||||
sshKey = &ecdsaPrivateKey{t}
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
|
||||
}
|
||||
return sshKey, nil
|
||||
}
|
||||
|
||||
// NewPublicKey takes a pointer to rsa, dsa or ecdsa PublicKey
|
||||
// and returns a corresponding ssh PublicKey instance. EC keys should use P256, P384 or P521.
|
||||
func NewPublicKey(k interface{}) (PublicKey, error) {
|
||||
var sshKey PublicKey
|
||||
switch t := k.(type) {
|
||||
// NewPublicKey takes an *rsa.PublicKey, *dsa.PublicKey, *ecdsa.PublicKey,
|
||||
// ed25519.PublicKey, or any other crypto.Signer and returns a corresponding
|
||||
// Signer instance. ECDSA keys must use P-256, P-384 or P-521.
|
||||
func NewPublicKey(key interface{}) (PublicKey, error) {
|
||||
switch key := key.(type) {
|
||||
case *rsa.PublicKey:
|
||||
sshKey = (*rsaPublicKey)(t)
|
||||
return (*rsaPublicKey)(key), nil
|
||||
case *ecdsa.PublicKey:
|
||||
if !supportedEllipticCurve(t.Curve) {
|
||||
return nil, errors.New("ssh: only P256, P384 and P521 EC keys are supported.")
|
||||
if !supportedEllipticCurve(key.Curve) {
|
||||
return nil, errors.New("ssh: only P-256, P-384 and P-521 EC keys are supported.")
|
||||
}
|
||||
sshKey = (*ecdsaPublicKey)(t)
|
||||
return (*ecdsaPublicKey)(key), nil
|
||||
case *dsa.PublicKey:
|
||||
sshKey = (*dsaPublicKey)(t)
|
||||
return (*dsaPublicKey)(key), nil
|
||||
case ed25519.PublicKey:
|
||||
return (ed25519PublicKey)(key), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", k)
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
return sshKey, nil
|
||||
}
|
||||
|
||||
// ParsePrivateKey returns a Signer from a PEM encoded private key. It supports
|
||||
|
@ -590,6 +746,8 @@ func ParseRawPrivateKey(pemBytes []byte) (interface{}, error) {
|
|||
return x509.ParseECPrivateKey(block.Bytes)
|
||||
case "DSA PRIVATE KEY":
|
||||
return ParseDSAPrivateKey(block.Bytes)
|
||||
case "OPENSSH PRIVATE KEY":
|
||||
return parseOpenSSHPrivateKey(block.Bytes)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %q", block.Type)
|
||||
}
|
||||
|
@ -626,3 +784,63 @@ func ParseDSAPrivateKey(der []byte) (*dsa.PrivateKey, error) {
|
|||
X: k.Pub,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Implemented based on the documentation at
|
||||
// https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key
|
||||
func parseOpenSSHPrivateKey(key []byte) (*ed25519.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")
|
||||
}
|
||||
remaining := key[len(magic):]
|
||||
|
||||
var w struct {
|
||||
CipherName string
|
||||
KdfName string
|
||||
KdfOpts string
|
||||
NumKeys uint32
|
||||
PubKey []byte
|
||||
PrivKeyBlock []byte
|
||||
}
|
||||
|
||||
if err := Unmarshal(remaining, &w); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pk1 := struct {
|
||||
Check1 uint32
|
||||
Check2 uint32
|
||||
Keytype string
|
||||
Pub []byte
|
||||
Priv []byte
|
||||
Comment string
|
||||
Pad []byte `ssh:"rest"`
|
||||
}{}
|
||||
|
||||
if err := Unmarshal(w.PrivKeyBlock, &pk1); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if pk1.Check1 != pk1.Check2 {
|
||||
return nil, errors.New("ssh: checkint mismatch")
|
||||
}
|
||||
|
||||
// we only handle ed25519 keys currently
|
||||
if pk1.Keytype != KeyAlgoED25519 {
|
||||
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
|
||||
}
|
||||
|
|
142
vendor/src/golang.org/x/crypto/ssh/keys_test.go
vendored
142
vendor/src/golang.org/x/crypto/ssh/keys_test.go
vendored
|
@ -17,6 +17,7 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ed25519"
|
||||
"golang.org/x/crypto/ssh/testdata"
|
||||
)
|
||||
|
||||
|
@ -28,6 +29,8 @@ func rawKey(pub PublicKey) interface{} {
|
|||
return (*dsa.PublicKey)(k)
|
||||
case *ecdsaPublicKey:
|
||||
return (*ecdsa.PublicKey)(k)
|
||||
case ed25519PublicKey:
|
||||
return (ed25519.PublicKey)(k)
|
||||
case *Certificate:
|
||||
return k
|
||||
}
|
||||
|
@ -57,12 +60,12 @@ func TestUnsupportedCurves(t *testing.T) {
|
|||
t.Fatalf("GenerateKey: %v", err)
|
||||
}
|
||||
|
||||
if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P256") {
|
||||
t.Fatalf("NewPrivateKey should not succeed with P224, got: %v", err)
|
||||
if _, err = NewSignerFromKey(raw); err == nil || !strings.Contains(err.Error(), "only P-256") {
|
||||
t.Fatalf("NewPrivateKey should not succeed with P-224, got: %v", err)
|
||||
}
|
||||
|
||||
if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P256") {
|
||||
t.Fatalf("NewPublicKey should not succeed with P224, got: %v", err)
|
||||
if _, err = NewPublicKey(&raw.PublicKey); err == nil || !strings.Contains(err.Error(), "only P-256") {
|
||||
t.Fatalf("NewPublicKey should not succeed with P-224, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -304,3 +307,134 @@ func TestInvalidEntry(t *testing.T) {
|
|||
t.Errorf("got valid entry for %q", authInvalid)
|
||||
}
|
||||
}
|
||||
|
||||
var knownHostsParseTests = []struct {
|
||||
input string
|
||||
err string
|
||||
|
||||
marker string
|
||||
comment string
|
||||
hosts []string
|
||||
rest string
|
||||
} {
|
||||
{
|
||||
"",
|
||||
"EOF",
|
||||
|
||||
"", "", nil, "",
|
||||
},
|
||||
{
|
||||
"# Just a comment",
|
||||
"EOF",
|
||||
|
||||
"", "", nil, "",
|
||||
},
|
||||
{
|
||||
" \t ",
|
||||
"EOF",
|
||||
|
||||
"", "", nil, "",
|
||||
},
|
||||
{
|
||||
"localhost ssh-rsa {RSAPUB}",
|
||||
"",
|
||||
|
||||
"", "", []string{"localhost"}, "",
|
||||
},
|
||||
{
|
||||
"localhost\tssh-rsa {RSAPUB}",
|
||||
"",
|
||||
|
||||
"", "", []string{"localhost"}, "",
|
||||
},
|
||||
{
|
||||
"localhost\tssh-rsa {RSAPUB}\tcomment comment",
|
||||
"",
|
||||
|
||||
"", "comment comment", []string{"localhost"}, "",
|
||||
},
|
||||
{
|
||||
"localhost\tssh-rsa {RSAPUB}\tcomment comment\n",
|
||||
"",
|
||||
|
||||
"", "comment comment", []string{"localhost"}, "",
|
||||
},
|
||||
{
|
||||
"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\n",
|
||||
"",
|
||||
|
||||
"", "comment comment", []string{"localhost"}, "",
|
||||
},
|
||||
{
|
||||
"localhost\tssh-rsa {RSAPUB}\tcomment comment\r\nnext line",
|
||||
"",
|
||||
|
||||
"", "comment comment", []string{"localhost"}, "next line",
|
||||
},
|
||||
{
|
||||
"localhost,[host2:123]\tssh-rsa {RSAPUB}\tcomment comment",
|
||||
"",
|
||||
|
||||
"", "comment comment", []string{"localhost","[host2:123]"}, "",
|
||||
},
|
||||
{
|
||||
"@marker \tlocalhost,[host2:123]\tssh-rsa {RSAPUB}",
|
||||
"",
|
||||
|
||||
"marker", "", []string{"localhost","[host2:123]"}, "",
|
||||
},
|
||||
{
|
||||
"@marker \tlocalhost,[host2:123]\tssh-rsa aabbccdd",
|
||||
"short read",
|
||||
|
||||
"", "", nil, "",
|
||||
},
|
||||
}
|
||||
|
||||
func TestKnownHostsParsing(t *testing.T) {
|
||||
rsaPub, rsaPubSerialized := getTestKey()
|
||||
|
||||
for i, test := range knownHostsParseTests {
|
||||
var expectedKey PublicKey
|
||||
const rsaKeyToken = "{RSAPUB}"
|
||||
|
||||
input := test.input
|
||||
if strings.Contains(input, rsaKeyToken) {
|
||||
expectedKey = rsaPub
|
||||
input = strings.Replace(test.input, rsaKeyToken, rsaPubSerialized, -1)
|
||||
}
|
||||
|
||||
marker, hosts, pubKey, comment, rest, err := ParseKnownHosts([]byte(input))
|
||||
if err != nil {
|
||||
if len(test.err) == 0 {
|
||||
t.Errorf("#%d: unexpectedly failed with %q", i, err)
|
||||
} else if !strings.Contains(err.Error(), test.err) {
|
||||
t.Errorf("#%d: expected error containing %q, but got %q", i, test.err, err)
|
||||
}
|
||||
continue
|
||||
} else if len(test.err) != 0 {
|
||||
t.Errorf("#%d: succeeded but expected error including %q", i, test.err)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(expectedKey, pubKey) {
|
||||
t.Errorf("#%d: expected key %#v, but got %#v", i, expectedKey, pubKey)
|
||||
}
|
||||
|
||||
if marker != test.marker {
|
||||
t.Errorf("#%d: expected marker %q, but got %q", i, test.marker, marker)
|
||||
}
|
||||
|
||||
if comment != test.comment {
|
||||
t.Errorf("#%d: expected comment %q, but got %q", i, test.comment, comment)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(test.hosts, hosts) {
|
||||
t.Errorf("#%d: expected hosts %#v, but got %#v", i, test.hosts, hosts)
|
||||
}
|
||||
|
||||
if rest := string(rest); rest != test.rest {
|
||||
t.Errorf("#%d: expected remaining input to be %q, but got %q", i, test.rest, rest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ func memPipe() (a, b packetConn) {
|
|||
return &t1, &t2
|
||||
}
|
||||
|
||||
func TestmemPipe(t *testing.T) {
|
||||
func TestMemPipe(t *testing.T) {
|
||||
a, b := memPipe()
|
||||
if err := a.writePacket([]byte{42}); err != nil {
|
||||
t.Fatalf("writePacket: %v", err)
|
||||
|
|
77
vendor/src/golang.org/x/crypto/ssh/messages.go
vendored
77
vendor/src/golang.org/x/crypto/ssh/messages.go
vendored
|
@ -13,6 +13,7 @@ import (
|
|||
"math/big"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// These are SSH message type numbers. They are scattered around several
|
||||
|
@ -47,7 +48,7 @@ type disconnectMsg struct {
|
|||
}
|
||||
|
||||
func (d *disconnectMsg) Error() string {
|
||||
return fmt.Sprintf("ssh: disconnect reason %d: %s", d.Reason, d.Message)
|
||||
return fmt.Sprintf("ssh: disconnect, reason %d: %s", d.Reason, d.Message)
|
||||
}
|
||||
|
||||
// See RFC 4253, section 7.1.
|
||||
|
@ -124,6 +125,10 @@ type userAuthRequestMsg struct {
|
|||
Payload []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Used for debug printouts of packets.
|
||||
type userAuthSuccessMsg struct {
|
||||
}
|
||||
|
||||
// See RFC 4252, section 5.1
|
||||
const msgUserAuthFailure = 51
|
||||
|
||||
|
@ -158,6 +163,13 @@ type channelOpenMsg struct {
|
|||
const msgChannelExtendedData = 95
|
||||
const msgChannelData = 94
|
||||
|
||||
// Used for debug print outs of packets.
|
||||
type channelDataMsg struct {
|
||||
PeersId uint32 `sshtype:"94"`
|
||||
Length uint32
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// See RFC 4254, section 5.1.
|
||||
const msgChannelOpenConfirm = 91
|
||||
|
||||
|
@ -255,17 +267,19 @@ type userAuthPubKeyOkMsg struct {
|
|||
PubKey []byte
|
||||
}
|
||||
|
||||
// typeTag returns the type byte for the given type. The type should
|
||||
// be struct.
|
||||
func typeTag(structType reflect.Type) byte {
|
||||
var tag byte
|
||||
var tagStr string
|
||||
tagStr = structType.Field(0).Tag.Get("sshtype")
|
||||
i, err := strconv.Atoi(tagStr)
|
||||
if err == nil {
|
||||
tag = byte(i)
|
||||
// typeTags returns the possible type bytes for the given reflect.Type, which
|
||||
// should be a struct. The possible values are separated by a '|' character.
|
||||
func typeTags(structType reflect.Type) (tags []byte) {
|
||||
tagStr := structType.Field(0).Tag.Get("sshtype")
|
||||
|
||||
for _, tag := range strings.Split(tagStr, "|") {
|
||||
i, err := strconv.Atoi(tag)
|
||||
if err == nil {
|
||||
tags = append(tags, byte(i))
|
||||
}
|
||||
}
|
||||
return tag
|
||||
|
||||
return tags
|
||||
}
|
||||
|
||||
func fieldError(t reflect.Type, field int, problem string) error {
|
||||
|
@ -279,19 +293,34 @@ var errShortRead = errors.New("ssh: short read")
|
|||
|
||||
// Unmarshal parses data in SSH wire format into a structure. The out
|
||||
// argument should be a pointer to struct. If the first member of the
|
||||
// struct has the "sshtype" tag set to a number in decimal, the packet
|
||||
// must start that number. In case of error, Unmarshal returns a
|
||||
// ParseError or UnexpectedMessageError.
|
||||
// struct has the "sshtype" tag set to a '|'-separated set of numbers
|
||||
// in decimal, the packet must start with one of those numbers. In
|
||||
// case of error, Unmarshal returns a ParseError or
|
||||
// UnexpectedMessageError.
|
||||
func Unmarshal(data []byte, out interface{}) error {
|
||||
v := reflect.ValueOf(out).Elem()
|
||||
structType := v.Type()
|
||||
expectedType := typeTag(structType)
|
||||
expectedTypes := typeTags(structType)
|
||||
|
||||
var expectedType byte
|
||||
if len(expectedTypes) > 0 {
|
||||
expectedType = expectedTypes[0]
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return parseError(expectedType)
|
||||
}
|
||||
if expectedType > 0 {
|
||||
if data[0] != expectedType {
|
||||
return unexpectedMessageError(expectedType, data[0])
|
||||
|
||||
if len(expectedTypes) > 0 {
|
||||
goodType := false
|
||||
for _, e := range expectedTypes {
|
||||
if e > 0 && data[0] == e {
|
||||
goodType = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !goodType {
|
||||
return fmt.Errorf("ssh: unexpected message type %d (expected one of %v)", data[0], expectedTypes)
|
||||
}
|
||||
data = data[1:]
|
||||
}
|
||||
|
@ -375,7 +404,7 @@ func Unmarshal(data []byte, out interface{}) error {
|
|||
return fieldError(structType, i, "pointer to unsupported type")
|
||||
}
|
||||
default:
|
||||
return fieldError(structType, i, "unsupported type")
|
||||
return fieldError(structType, i, fmt.Sprintf("unsupported type: %v", t))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -398,9 +427,9 @@ func Marshal(msg interface{}) []byte {
|
|||
|
||||
func marshalStruct(out []byte, msg interface{}) []byte {
|
||||
v := reflect.Indirect(reflect.ValueOf(msg))
|
||||
msgType := typeTag(v.Type())
|
||||
if msgType > 0 {
|
||||
out = append(out, msgType)
|
||||
msgTypes := typeTags(v.Type())
|
||||
if len(msgTypes) > 0 {
|
||||
out = append(out, msgTypes[0])
|
||||
}
|
||||
|
||||
for i, n := 0, v.NumField(); i < n; i++ {
|
||||
|
@ -687,6 +716,8 @@ func decode(packet []byte) (interface{}, error) {
|
|||
msg = new(kexDHReplyMsg)
|
||||
case msgUserAuthRequest:
|
||||
msg = new(userAuthRequestMsg)
|
||||
case msgUserAuthSuccess:
|
||||
return new(userAuthSuccessMsg), nil
|
||||
case msgUserAuthFailure:
|
||||
msg = new(userAuthFailureMsg)
|
||||
case msgUserAuthPubKeyOk:
|
||||
|
@ -699,6 +730,8 @@ func decode(packet []byte) (interface{}, error) {
|
|||
msg = new(globalRequestFailureMsg)
|
||||
case msgChannelOpen:
|
||||
msg = new(channelOpenMsg)
|
||||
case msgChannelData:
|
||||
msg = new(channelDataMsg)
|
||||
case msgChannelOpenConfirm:
|
||||
msg = new(channelOpenConfirmMsg)
|
||||
case msgChannelOpenFailure:
|
||||
|
|
|
@ -172,6 +172,40 @@ func TestUnmarshalShortKexInitPacket(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMarshalMultiTag(t *testing.T) {
|
||||
var res struct {
|
||||
A uint32 `sshtype:"1|2"`
|
||||
}
|
||||
|
||||
good1 := struct {
|
||||
A uint32 `sshtype:"1"`
|
||||
}{
|
||||
1,
|
||||
}
|
||||
good2 := struct {
|
||||
A uint32 `sshtype:"2"`
|
||||
}{
|
||||
1,
|
||||
}
|
||||
|
||||
if e := Unmarshal(Marshal(good1), &res); e != nil {
|
||||
t.Errorf("error unmarshaling multipart tag: %v", e)
|
||||
}
|
||||
|
||||
if e := Unmarshal(Marshal(good2), &res); e != nil {
|
||||
t.Errorf("error unmarshaling multipart tag: %v", e)
|
||||
}
|
||||
|
||||
bad1 := struct {
|
||||
A uint32 `sshtype:"3"`
|
||||
}{
|
||||
1,
|
||||
}
|
||||
if e := Unmarshal(Marshal(bad1), &res); e == nil {
|
||||
t.Errorf("bad struct unmarshaled without error")
|
||||
}
|
||||
}
|
||||
|
||||
func randomBytes(out []byte, rand *rand.Rand) {
|
||||
for i := 0; i < len(out); i++ {
|
||||
out[i] = byte(rand.Int31())
|
||||
|
|
32
vendor/src/golang.org/x/crypto/ssh/mux.go
vendored
32
vendor/src/golang.org/x/crypto/ssh/mux.go
vendored
|
@ -131,6 +131,9 @@ func newMux(p packetConn) *mux {
|
|||
|
||||
func (m *mux) sendMessage(msg interface{}) error {
|
||||
p := Marshal(msg)
|
||||
if debugMux {
|
||||
log.Printf("send global(%d): %#v", m.chanList.offset, msg)
|
||||
}
|
||||
return m.conn.writePacket(p)
|
||||
}
|
||||
|
||||
|
@ -175,18 +178,6 @@ func (m *mux) ackRequest(ok bool, data []byte) error {
|
|||
return m.sendMessage(globalRequestFailureMsg{Data: data})
|
||||
}
|
||||
|
||||
// TODO(hanwen): Disconnect is a transport layer message. We should
|
||||
// probably send and receive Disconnect somewhere in the transport
|
||||
// code.
|
||||
|
||||
// Disconnect sends a disconnect message.
|
||||
func (m *mux) Disconnect(reason uint32, message string) error {
|
||||
return m.sendMessage(disconnectMsg{
|
||||
Reason: reason,
|
||||
Message: message,
|
||||
})
|
||||
}
|
||||
|
||||
func (m *mux) Close() error {
|
||||
return m.conn.Close()
|
||||
}
|
||||
|
@ -236,11 +227,6 @@ func (m *mux) onePacket() error {
|
|||
}
|
||||
|
||||
switch packet[0] {
|
||||
case msgNewKeys:
|
||||
// Ignore notification of key change.
|
||||
return nil
|
||||
case msgDisconnect:
|
||||
return m.handleDisconnect(packet)
|
||||
case msgChannelOpen:
|
||||
return m.handleChannelOpen(packet)
|
||||
case msgGlobalRequest, msgRequestSuccess, msgRequestFailure:
|
||||
|
@ -260,18 +246,6 @@ func (m *mux) onePacket() error {
|
|||
return ch.handlePacket(packet)
|
||||
}
|
||||
|
||||
func (m *mux) handleDisconnect(packet []byte) error {
|
||||
var d disconnectMsg
|
||||
if err := Unmarshal(packet, &d); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if debugMux {
|
||||
log.Printf("caught disconnect: %v", d)
|
||||
}
|
||||
return &d
|
||||
}
|
||||
|
||||
func (m *mux) handleGlobalPacket(packet []byte) error {
|
||||
msg, err := decode(packet)
|
||||
if err != nil {
|
||||
|
|
23
vendor/src/golang.org/x/crypto/ssh/mux_test.go
vendored
23
vendor/src/golang.org/x/crypto/ssh/mux_test.go
vendored
|
@ -331,7 +331,6 @@ func TestMuxGlobalRequest(t *testing.T) {
|
|||
ok, data, err)
|
||||
}
|
||||
|
||||
clientMux.Disconnect(0, "")
|
||||
if !seen {
|
||||
t.Errorf("never saw 'peek' request")
|
||||
}
|
||||
|
@ -378,28 +377,6 @@ func TestMuxChannelRequestUnblock(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestMuxDisconnect(t *testing.T) {
|
||||
a, b := muxPair()
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
go func() {
|
||||
for r := range b.incomingRequests {
|
||||
r.Reply(true, nil)
|
||||
}
|
||||
}()
|
||||
|
||||
a.Disconnect(42, "whatever")
|
||||
ok, _, err := a.SendRequest("hello", true, nil)
|
||||
if ok || err == nil {
|
||||
t.Errorf("got reply after disconnecting")
|
||||
}
|
||||
err = b.Wait()
|
||||
if d, ok := err.(*disconnectMsg); !ok || d.Reason != 42 {
|
||||
t.Errorf("got %#v, want disconnectMsg{Reason:42}", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMuxCloseChannel(t *testing.T) {
|
||||
r, w, mux := channelPair(t)
|
||||
defer mux.Close()
|
||||
|
|
17
vendor/src/golang.org/x/crypto/ssh/server.go
vendored
17
vendor/src/golang.org/x/crypto/ssh/server.go
vendored
|
@ -66,9 +66,11 @@ type ServerConfig struct {
|
|||
// attempts.
|
||||
AuthLogCallback func(conn ConnMetadata, method string, err error)
|
||||
|
||||
// ServerVersion is the version identification string to
|
||||
// announce in the public handshake.
|
||||
// ServerVersion is the version identification string to announce in
|
||||
// the public handshake.
|
||||
// If empty, a reasonable default is used.
|
||||
// Note that RFC 4253 section 4.2 requires that this string start with
|
||||
// "SSH-2.0-".
|
||||
ServerVersion string
|
||||
}
|
||||
|
||||
|
@ -186,16 +188,10 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
|||
tr := newTransport(s.sshConn.conn, config.Rand, false /* not client */)
|
||||
s.transport = newServerTransport(tr, s.clientVersion, s.serverVersion, config)
|
||||
|
||||
if err := s.transport.requestKeyChange(); err != nil {
|
||||
if err := s.transport.requestInitialKeyChange(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if packet, err := s.transport.readPacket(); err != nil {
|
||||
return nil, err
|
||||
} else if packet[0] != msgNewKeys {
|
||||
return nil, unexpectedMessageError(msgNewKeys, packet[0])
|
||||
}
|
||||
|
||||
// We just did the key change, so the session ID is established.
|
||||
s.sessionID = s.transport.getSessionID()
|
||||
|
||||
|
@ -228,7 +224,7 @@ func (s *connection) serverHandshake(config *ServerConfig) (*Permissions, error)
|
|||
|
||||
func isAcceptableAlgo(algo string) bool {
|
||||
switch algo {
|
||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521,
|
||||
case KeyAlgoRSA, KeyAlgoDSA, KeyAlgoECDSA256, KeyAlgoECDSA384, KeyAlgoECDSA521, KeyAlgoED25519,
|
||||
CertAlgoRSAv01, CertAlgoDSAv01, CertAlgoECDSA256v01, CertAlgoECDSA384v01, CertAlgoECDSA521v01:
|
||||
return true
|
||||
}
|
||||
|
@ -288,7 +284,6 @@ userAuthLoop:
|
|||
switch userAuthReq.Method {
|
||||
case "none":
|
||||
if config.NoClientAuth {
|
||||
s.user = ""
|
||||
authErr = nil
|
||||
}
|
||||
case "password":
|
||||
|
|
44
vendor/src/golang.org/x/crypto/ssh/session.go
vendored
44
vendor/src/golang.org/x/crypto/ssh/session.go
vendored
|
@ -9,6 +9,7 @@ package ssh
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
@ -281,9 +282,10 @@ func (s *Session) Start(cmd string) error {
|
|||
// copying stdin, stdout, and stderr, and exits with a zero exit
|
||||
// status.
|
||||
//
|
||||
// If the command fails to run or doesn't complete successfully, the
|
||||
// error is of type *ExitError. Other error types may be
|
||||
// returned for I/O problems.
|
||||
// If the remote server does not send an exit status, an error of type
|
||||
// *ExitMissingError is returned. If the command completes
|
||||
// unsuccessfully or is interrupted by a signal, the error is of type
|
||||
// *ExitError. Other error types may be returned for I/O problems.
|
||||
func (s *Session) Run(cmd string) error {
|
||||
err := s.Start(cmd)
|
||||
if err != nil {
|
||||
|
@ -339,7 +341,7 @@ func (s *Session) Shell() error {
|
|||
|
||||
ok, err := s.ch.SendRequest("shell", true, nil)
|
||||
if err == nil && !ok {
|
||||
return fmt.Errorf("ssh: cound not start shell")
|
||||
return errors.New("ssh: could not start shell")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -370,9 +372,10 @@ func (s *Session) start() error {
|
|||
// copying stdin, stdout, and stderr, and exits with a zero exit
|
||||
// status.
|
||||
//
|
||||
// If the command fails to run or doesn't complete successfully, the
|
||||
// error is of type *ExitError. Other error types may be
|
||||
// returned for I/O problems.
|
||||
// If the remote server does not send an exit status, an error of type
|
||||
// *ExitMissingError is returned. If the command completes
|
||||
// unsuccessfully or is interrupted by a signal, the error is of type
|
||||
// *ExitError. Other error types may be returned for I/O problems.
|
||||
func (s *Session) Wait() error {
|
||||
if !s.started {
|
||||
return errors.New("ssh: session not started")
|
||||
|
@ -400,8 +403,7 @@ func (s *Session) wait(reqs <-chan *Request) error {
|
|||
for msg := range reqs {
|
||||
switch msg.Type {
|
||||
case "exit-status":
|
||||
d := msg.Payload
|
||||
wm.status = int(d[0])<<24 | int(d[1])<<16 | int(d[2])<<8 | int(d[3])
|
||||
wm.status = int(binary.BigEndian.Uint32(msg.Payload))
|
||||
case "exit-signal":
|
||||
var sigval struct {
|
||||
Signal string
|
||||
|
@ -431,16 +433,29 @@ func (s *Session) wait(reqs <-chan *Request) error {
|
|||
if wm.status == -1 {
|
||||
// exit-status was never sent from server
|
||||
if wm.signal == "" {
|
||||
return errors.New("wait: remote command exited without exit status or exit signal")
|
||||
// signal was not sent either. RFC 4254
|
||||
// section 6.10 recommends against this
|
||||
// behavior, but it is allowed, so we let
|
||||
// clients handle it.
|
||||
return &ExitMissingError{}
|
||||
}
|
||||
wm.status = 128
|
||||
if _, ok := signals[Signal(wm.signal)]; ok {
|
||||
wm.status += signals[Signal(wm.signal)]
|
||||
}
|
||||
}
|
||||
|
||||
return &ExitError{wm}
|
||||
}
|
||||
|
||||
// ExitMissingError is returned if a session is torn down cleanly, but
|
||||
// the server sends no confirmation of the exit status.
|
||||
type ExitMissingError struct{}
|
||||
|
||||
func (e *ExitMissingError) Error() string {
|
||||
return "wait: remote command exited without exit status or exit signal"
|
||||
}
|
||||
|
||||
func (s *Session) stdin() {
|
||||
if s.stdinpipe {
|
||||
return
|
||||
|
@ -601,5 +616,12 @@ func (w Waitmsg) Lang() string {
|
|||
}
|
||||
|
||||
func (w Waitmsg) String() string {
|
||||
return fmt.Sprintf("Process exited with: %v. Reason was: %v (%v)", w.status, w.msg, w.signal)
|
||||
str := fmt.Sprintf("Process exited with status %v", w.status)
|
||||
if w.signal != "" {
|
||||
str += fmt.Sprintf(" from signal %v", w.signal)
|
||||
}
|
||||
if w.msg != "" {
|
||||
str += fmt.Sprintf(". Reason was: %v", w.msg)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -297,7 +297,6 @@ func TestUnknownExitSignal(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Test WaitMsg is not returned if the channel closes abruptly.
|
||||
func TestExitWithoutStatusOrSignal(t *testing.T) {
|
||||
conn := dial(exitWithoutSignalOrStatus, t)
|
||||
defer conn.Close()
|
||||
|
@ -313,11 +312,8 @@ func TestExitWithoutStatusOrSignal(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatalf("expected command to fail but it didn't")
|
||||
}
|
||||
_, ok := err.(*ExitError)
|
||||
if ok {
|
||||
// you can't actually test for errors.errorString
|
||||
// because it's not exported.
|
||||
t.Fatalf("expected *errorString but got %T", err)
|
||||
if _, ok := err.(*ExitMissingError); !ok {
|
||||
t.Fatalf("got %T want *ExitMissingError", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,3 +714,57 @@ func TestInvalidServerConfiguration(t *testing.T) {
|
|||
t.Fatalf("NewServerConn attempted to Read() from Conn while configuration is missing authentication method")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHostKeyAlgorithms(t *testing.T) {
|
||||
serverConf := &ServerConfig{
|
||||
NoClientAuth: true,
|
||||
}
|
||||
serverConf.AddHostKey(testSigners["rsa"])
|
||||
serverConf.AddHostKey(testSigners["ecdsa"])
|
||||
|
||||
connect := func(clientConf *ClientConfig, want string) {
|
||||
var alg string
|
||||
clientConf.HostKeyCallback = func(h string, a net.Addr, key PublicKey) error {
|
||||
alg = key.Type()
|
||||
return nil
|
||||
}
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||
if err != nil {
|
||||
t.Fatalf("NewClientConn: %v", err)
|
||||
}
|
||||
if alg != want {
|
||||
t.Errorf("selected key algorithm %s, want %s", alg, want)
|
||||
}
|
||||
}
|
||||
|
||||
// By default, we get the preferred algorithm, which is ECDSA 256.
|
||||
|
||||
clientConf := &ClientConfig{}
|
||||
connect(clientConf, KeyAlgoECDSA256)
|
||||
|
||||
// Client asks for RSA explicitly.
|
||||
clientConf.HostKeyAlgorithms = []string{KeyAlgoRSA}
|
||||
connect(clientConf, KeyAlgoRSA)
|
||||
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
go NewServerConn(c1, serverConf)
|
||||
clientConf.HostKeyAlgorithms = []string{"nonexistent-hostkey-algo"}
|
||||
_, _, _, err = NewClientConn(c2, "", clientConf)
|
||||
if err == nil {
|
||||
t.Fatal("succeeded connecting with unknown hostkey algorithm")
|
||||
}
|
||||
}
|
||||
|
|
3
vendor/src/golang.org/x/crypto/ssh/tcpip.go
vendored
3
vendor/src/golang.org/x/crypto/ssh/tcpip.go
vendored
|
@ -355,6 +355,9 @@ func (c *Client) dial(laddr string, lport int, raddr string, rport int) (Channel
|
|||
lport: uint32(lport),
|
||||
}
|
||||
ch, in, err := c.OpenChannel("direct-tcpip", Marshal(&msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go DiscardRequests(in)
|
||||
return ch, err
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ package terminal
|
|||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -267,3 +268,24 @@ func TestTerminalSetSize(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMakeRawState(t *testing.T) {
|
||||
fd := int(os.Stdout.Fd())
|
||||
if !IsTerminal(fd) {
|
||||
t.Skip("stdout is not a terminal; skipping test")
|
||||
}
|
||||
|
||||
st, err := GetState(fd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get terminal state from GetState: %s", err)
|
||||
}
|
||||
defer Restore(fd, st)
|
||||
raw, err := MakeRaw(fd)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to get terminal state from MakeRaw: %s", err)
|
||||
}
|
||||
|
||||
if *st != *raw {
|
||||
t.Errorf("states do not match; was %v, expected %v", raw, st)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,13 @@ func MakeRaw(fd int) (*State, error) {
|
|||
}
|
||||
|
||||
newState := oldState.termios
|
||||
newState.Iflag &^= syscall.ISTRIP | syscall.INLCR | syscall.ICRNL | syscall.IGNCR | syscall.IXON | syscall.IXOFF
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ICANON | syscall.ISIG
|
||||
// This attempts to replicate the behaviour documented for cfmakeraw in
|
||||
// the termios(3) manpage.
|
||||
newState.Iflag &^= syscall.IGNBRK | syscall.BRKINT | syscall.PARMRK | syscall.ISTRIP | syscall.INLCR | syscall.IGNCR | syscall.ICRNL | syscall.IXON
|
||||
newState.Oflag &^= syscall.OPOST
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newState.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newState.Cflag |= syscall.CS8
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
|
58
vendor/src/golang.org/x/crypto/ssh/terminal/util_plan9.go
vendored
Normal file
58
vendor/src/golang.org/x/crypto/ssh/terminal/util_plan9.go
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 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 terminal provides support functions for dealing with terminals, as
|
||||
// commonly found on UNIX systems.
|
||||
//
|
||||
// Putting a terminal into raw mode is the most common requirement:
|
||||
//
|
||||
// oldState, err := terminal.MakeRaw(0)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer terminal.Restore(0, oldState)
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type State struct{}
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// MakeRaw put 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.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
return nil, fmt.Errorf("terminal: MakeRaw not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return nil, fmt.Errorf("terminal: GetState not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
return fmt.Errorf("terminal: Restore not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
return 0, 0, fmt.Errorf("terminal: GetSize not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
// is commonly used for inputting passwords and other sensitive data. The slice
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
return nil, fmt.Errorf("terminal: ReadPassword not implemented on %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
|
@ -87,8 +87,8 @@ func MakeRaw(fd int) (*State, error) {
|
|||
if e != 0 {
|
||||
return nil, error(e)
|
||||
}
|
||||
st &^= (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
|
||||
raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
}
|
||||
|
|
|
@ -21,7 +21,16 @@ func TestAgentForward(t *testing.T) {
|
|||
defer conn.Close()
|
||||
|
||||
keyring := agent.NewKeyring()
|
||||
keyring.Add(testPrivateKeys["dsa"], nil, "")
|
||||
if err := keyring.Add(agent.AddedKey{PrivateKey: testPrivateKeys["dsa"]}); err != nil {
|
||||
t.Fatalf("Error adding key: %s", err)
|
||||
}
|
||||
if err := keyring.Add(agent.AddedKey{
|
||||
PrivateKey: testPrivateKeys["dsa"],
|
||||
ConfirmBeforeUse: true,
|
||||
LifetimeSecs: 3600,
|
||||
}); err != nil {
|
||||
t.Fatalf("Error adding key with constraints: %s", err)
|
||||
}
|
||||
pub := testPublicKeys["dsa"]
|
||||
|
||||
sess, err := conn.NewSession()
|
||||
|
|
|
@ -280,16 +280,16 @@ func TestCiphers(t *testing.T) {
|
|||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
cipherOrder := config.Ciphers
|
||||
// This cipher will not be tested when commented out in cipher.go it will
|
||||
// These ciphers will not be tested when commented out in cipher.go it will
|
||||
// fallback to the next available as per line 292.
|
||||
cipherOrder = append(cipherOrder, "aes128-cbc")
|
||||
cipherOrder = append(cipherOrder, "aes128-cbc", "3des-cbc")
|
||||
|
||||
for _, ciph := range cipherOrder {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conf := clientConfig()
|
||||
conf.Ciphers = []string{ciph}
|
||||
// Don't fail if sshd doesnt have the cipher.
|
||||
// Don't fail if sshd doesn't have the cipher.
|
||||
conf.Ciphers = append(conf.Ciphers, cipherOrder...)
|
||||
conn, err := server.TryDial(conf)
|
||||
if err == nil {
|
||||
|
@ -310,7 +310,7 @@ func TestMACs(t *testing.T) {
|
|||
defer server.Shutdown()
|
||||
conf := clientConfig()
|
||||
conf.MACs = []string{mac}
|
||||
// Don't fail if sshd doesnt have the MAC.
|
||||
// Don't fail if sshd doesn't have the MAC.
|
||||
conf.MACs = append(conf.MACs, macOrder...)
|
||||
if conn, err := server.TryDial(conf); err == nil {
|
||||
conn.Close()
|
||||
|
@ -319,3 +319,47 @@ func TestMACs(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeyExchanges(t *testing.T) {
|
||||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
kexOrder := config.KeyExchanges
|
||||
for _, kex := range kexOrder {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conf := clientConfig()
|
||||
// Don't fail if sshd doesn't have the kex.
|
||||
conf.KeyExchanges = append([]string{kex}, kexOrder...)
|
||||
conn, err := server.TryDial(conf)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
} else {
|
||||
t.Errorf("failed for kex %q", kex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestClientAuthAlgorithms(t *testing.T) {
|
||||
for _, key := range []string{
|
||||
"rsa",
|
||||
"dsa",
|
||||
"ecdsa",
|
||||
"ed25519",
|
||||
} {
|
||||
server := newServer(t)
|
||||
conf := clientConfig()
|
||||
conf.SetDefaults()
|
||||
conf.Auth = []ssh.AuthMethod{
|
||||
ssh.PublicKeys(testSigners[key]),
|
||||
}
|
||||
|
||||
conn, err := server.TryDial(conf)
|
||||
if err == nil {
|
||||
conn.Close()
|
||||
} else {
|
||||
t.Errorf("failed for key %q", key)
|
||||
}
|
||||
|
||||
server.Shutdown()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,11 +41,12 @@ PermitRootLogin no
|
|||
StrictModes no
|
||||
RSAAuthentication yes
|
||||
PubkeyAuthentication yes
|
||||
AuthorizedKeysFile {{.Dir}}/id_user.pub
|
||||
AuthorizedKeysFile {{.Dir}}/authorized_keys
|
||||
TrustedUserCAKeys {{.Dir}}/id_ecdsa.pub
|
||||
IgnoreRhosts yes
|
||||
RhostsRSAAuthentication no
|
||||
HostbasedAuthentication no
|
||||
PubkeyAcceptedKeyTypes=*
|
||||
`
|
||||
|
||||
var configTmpl = template.Must(template.New("").Parse(sshd_config))
|
||||
|
@ -249,6 +250,12 @@ func newServer(t *testing.T) *server {
|
|||
writeFile(filepath.Join(dir, filename+".pub"), ssh.MarshalAuthorizedKey(testPublicKeys[k]))
|
||||
}
|
||||
|
||||
var authkeys bytes.Buffer
|
||||
for k, _ := range testdata.PEMBytes {
|
||||
authkeys.Write(ssh.MarshalAuthorizedKey(testPublicKeys[k]))
|
||||
}
|
||||
writeFile(filepath.Join(dir, "authorized_keys"), authkeys.Bytes())
|
||||
|
||||
return &server{
|
||||
t: t,
|
||||
configfile: f.Name(),
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places:
|
||||
// IMPLEMENTATION NOTE: To avoid a package loop, this file is in three places:
|
||||
// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three
|
||||
// instances.
|
||||
|
||||
|
|
|
@ -25,14 +25,28 @@ AwEHoUQDQgAEi9Hdw6KvZcWxfg2IDhA7UkpDtzzt6ZqJXSsFdLd+Kx4S3Sx4cVO+
|
|||
-----END EC PRIVATE KEY-----
|
||||
`),
|
||||
"rsa": []byte(`-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIBOwIBAAJBALdGZxkXDAjsYk10ihwU6Id2KeILz1TAJuoq4tOgDWxEEGeTrcld
|
||||
r/ZwVaFzjWzxaf6zQIJbfaSEAhqD5yo72+sCAwEAAQJBAK8PEVU23Wj8mV0QjwcJ
|
||||
tZ4GcTUYQL7cF4+ezTCE9a1NrGnCP2RuQkHEKxuTVrxXt+6OF15/1/fuXnxKjmJC
|
||||
nxkCIQDaXvPPBi0c7vAxGwNY9726x01/dNbHCE0CBtcotobxpwIhANbbQbh3JHVW
|
||||
2haQh4fAG5mhesZKAGcxTyv4mQ7uMSQdAiAj+4dzMpJWdSzQ+qGHlHMIBvVHLkqB
|
||||
y2VdEyF7DPCZewIhAI7GOI/6LDIFOvtPo6Bj2nNmyQ1HU6k/LRtNIXi4c9NJAiAr
|
||||
rrxx26itVhJmcvoUhOjwuzSlP2bE5VHAvkGB352YBg==
|
||||
MIICXAIBAAKBgQC8A6FGHDiWCSREAXCq6yBfNVr0xCVG2CzvktFNRpue+RXrGs/2
|
||||
a6ySEJQb3IYquw7HlJgu6fg3WIWhOmHCjfpG0PrL4CRwbqQ2LaPPXhJErWYejcD8
|
||||
Di00cF3677+G10KMZk9RXbmHtuBFZT98wxg8j+ZsBMqGM1+7yrWUvynswQIDAQAB
|
||||
AoGAJMCk5vqfSRzyXOTXLGIYCuR4Kj6pdsbNSeuuRGfYBeR1F2c/XdFAg7D/8s5R
|
||||
38p/Ih52/Ty5S8BfJtwtvgVY9ecf/JlU/rl/QzhG8/8KC0NG7KsyXklbQ7gJT8UT
|
||||
Ojmw5QpMk+rKv17ipDVkQQmPaj+gJXYNAHqImke5mm/K/h0CQQDciPmviQ+DOhOq
|
||||
2ZBqUfH8oXHgFmp7/6pXw80DpMIxgV3CwkxxIVx6a8lVH9bT/AFySJ6vXq4zTuV9
|
||||
6QmZcZzDAkEA2j/UXJPIs1fQ8z/6sONOkU/BjtoePFIWJlRxdN35cZjXnBraX5UR
|
||||
fFHkePv4YwqmXNqrBOvSu+w2WdSDci+IKwJAcsPRc/jWmsrJW1q3Ha0hSf/WG/Bu
|
||||
X7MPuXaKpP/DkzGoUmb8ks7yqj6XWnYkPNLjCc8izU5vRwIiyWBRf4mxMwJBAILa
|
||||
NDvRS0rjwt6lJGv7zPZoqDc65VfrK2aNyHx2PgFyzwrEOtuF57bu7pnvEIxpLTeM
|
||||
z26i6XVMeYXAWZMTloMCQBbpGgEERQpeUknLBqUHhg/wXF6+lFA+vEGnkY+Dwab2
|
||||
KCXFGd+SQ5GdUcEMe9isUH6DYj/6/yCDoFrXXmpQb+M=
|
||||
-----END RSA PRIVATE KEY-----
|
||||
`),
|
||||
"ed25519": []byte(`-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8DvvwAAAJhAFfkOQBX5
|
||||
DgAAAAtzc2gtZWQyNTUxOQAAACA+3f7hS7g5UWwXOGVTrMfhmxyrjqz7Sxxbx7I1j8Dvvw
|
||||
AAAEAaYmXltfW6nhRo3iWGglRB48lYq0z0Q3I3KyrdutEr6j7d/uFLuDlRbBc4ZVOsx+Gb
|
||||
HKuOrPtLHFvHsjWPwO+/AAAAE2dhcnRvbm1AZ2FydG9ubS14cHMBAg==
|
||||
-----END OPENSSH PRIVATE KEY-----
|
||||
`),
|
||||
"user": []byte(`-----BEGIN EC PRIVATE KEY-----
|
||||
MHcCAQEEILYCAeq8f7V4vSSypRw7pxy8yz3V5W4qg8kSC3zJhqpQoAoGCCqGSM49
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// IMPLEMENTOR NOTE: To avoid a package loop, this file is in three places:
|
||||
// IMPLEMENTATION NOTE: To avoid a package loop, this file is in three places:
|
||||
// ssh/, ssh/agent, and ssh/test/. It should be kept in sync across all three
|
||||
// instances.
|
||||
|
||||
|
|
55
vendor/src/golang.org/x/crypto/ssh/transport.go
vendored
55
vendor/src/golang.org/x/crypto/ssh/transport.go
vendored
|
@ -11,8 +11,9 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
gcmCipherID = "aes128-gcm@openssh.com"
|
||||
aes128cbcID = "aes128-cbc"
|
||||
gcmCipherID = "aes128-gcm@openssh.com"
|
||||
aes128cbcID = "aes128-cbc"
|
||||
tripledescbcID = "3des-cbc"
|
||||
)
|
||||
|
||||
// packetConn represents a transport that implements packet based
|
||||
|
@ -39,19 +40,6 @@ type transport struct {
|
|||
rand io.Reader
|
||||
|
||||
io.Closer
|
||||
|
||||
// Initial H used for the session ID. Once assigned this does
|
||||
// not change, even during subsequent key exchanges.
|
||||
sessionID []byte
|
||||
}
|
||||
|
||||
// getSessionID returns the ID of the SSH connection. The return value
|
||||
// should not be modified.
|
||||
func (t *transport) getSessionID() []byte {
|
||||
if t.sessionID == nil {
|
||||
panic("session ID not set yet")
|
||||
}
|
||||
return t.sessionID
|
||||
}
|
||||
|
||||
// packetCipher represents a combination of SSH encryption/MAC
|
||||
|
@ -81,12 +69,6 @@ type connectionState struct {
|
|||
// both directions are triggered by reading and writing a msgNewKey packet
|
||||
// respectively.
|
||||
func (t *transport) prepareKeyChange(algs *algorithms, kexResult *kexResult) error {
|
||||
if t.sessionID == nil {
|
||||
t.sessionID = kexResult.H
|
||||
}
|
||||
|
||||
kexResult.SessionID = t.sessionID
|
||||
|
||||
if ciph, err := newPacketCipher(t.reader.dir, algs.r, kexResult); err != nil {
|
||||
return err
|
||||
} else {
|
||||
|
@ -114,12 +96,27 @@ func (s *connectionState) readPacket(r *bufio.Reader) ([]byte, error) {
|
|||
err = errors.New("ssh: zero length packet")
|
||||
}
|
||||
|
||||
if len(packet) > 0 && packet[0] == msgNewKeys {
|
||||
select {
|
||||
case cipher := <-s.pendingKeyChange:
|
||||
s.packetCipher = cipher
|
||||
default:
|
||||
return nil, errors.New("ssh: got bogus newkeys message.")
|
||||
if len(packet) > 0 {
|
||||
switch packet[0] {
|
||||
case msgNewKeys:
|
||||
select {
|
||||
case cipher := <-s.pendingKeyChange:
|
||||
s.packetCipher = cipher
|
||||
default:
|
||||
return nil, errors.New("ssh: got bogus newkeys message.")
|
||||
}
|
||||
|
||||
case msgDisconnect:
|
||||
// Transform a disconnect message into an
|
||||
// error. Since this is lowest level at which
|
||||
// we interpret message types, doing it here
|
||||
// ensures that we don't have to handle it
|
||||
// elsewhere.
|
||||
var msg disconnectMsg
|
||||
if err := Unmarshal(packet, &msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, &msg
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -223,6 +220,10 @@ func newPacketCipher(d direction, algs directionAlgorithms, kex *kexResult) (pac
|
|||
return newAESCBCCipher(iv, key, macKey, algs)
|
||||
}
|
||||
|
||||
if algs.Cipher == tripledescbcID {
|
||||
return newTripleDESCBCCipher(iv, key, macKey, algs)
|
||||
}
|
||||
|
||||
c := &streamPacketCipher{
|
||||
mac: macModes[algs.MAC].new(macKey),
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue