mirror of
https://github.com/eiblog/eiblog.git
synced 2026-03-01 00:34:58 +08:00
使用github的七牛SDK,配置名称Kodo->Qiniu
This commit is contained in:
+31
-7
@@ -57,6 +57,17 @@ type Agent interface {
|
||||
Signers() ([]ssh.Signer, error)
|
||||
}
|
||||
|
||||
// ConstraintExtension describes an optional constraint defined by users.
|
||||
type ConstraintExtension struct {
|
||||
// ExtensionName consist of a UTF-8 string suffixed by the
|
||||
// implementation domain following the naming scheme defined
|
||||
// in Section 4.2 of [RFC4251], e.g. "foo@example.com".
|
||||
ExtensionName string
|
||||
// ExtensionDetails contains the actual content of the extended
|
||||
// constraint.
|
||||
ExtensionDetails []byte
|
||||
}
|
||||
|
||||
// AddedKey describes an SSH key to be added to an Agent.
|
||||
type AddedKey struct {
|
||||
// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
|
||||
@@ -73,6 +84,9 @@ type AddedKey struct {
|
||||
// ConfirmBeforeUse, if true, requests that the agent confirm with the
|
||||
// user before each use of this key.
|
||||
ConfirmBeforeUse bool
|
||||
// ConstraintExtensions are the experimental or private-use constraints
|
||||
// defined by users.
|
||||
ConstraintExtensions []ConstraintExtension
|
||||
}
|
||||
|
||||
// See [PROTOCOL.agent], section 3.
|
||||
@@ -94,8 +108,9 @@ const (
|
||||
agentAddSmartcardKeyConstrained = 26
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
agentConstrainLifetime = 1
|
||||
agentConstrainConfirm = 2
|
||||
agentConstrainExtension = 3
|
||||
)
|
||||
|
||||
// maxAgentResponseBytes is the maximum agent reply size that is accepted. This
|
||||
@@ -151,6 +166,19 @@ type publicKey struct {
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// 3.7 Key constraint identifiers
|
||||
type constrainLifetimeAgentMsg struct {
|
||||
LifetimeSecs uint32 `sshtype:"1"`
|
||||
}
|
||||
|
||||
type constrainExtensionAgentMsg struct {
|
||||
ExtensionName string `sshtype:"3"`
|
||||
ExtensionDetails []byte
|
||||
|
||||
// Rest is a field used for parsing, not part of message
|
||||
Rest []byte `ssh:"rest"`
|
||||
}
|
||||
|
||||
// Key represents a protocol 2 public key as defined in
|
||||
// [PROTOCOL.agent], section 2.5.2.
|
||||
type Key struct {
|
||||
@@ -542,11 +570,7 @@ 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[:]...)
|
||||
constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...)
|
||||
}
|
||||
|
||||
if key.ConfirmBeforeUse {
|
||||
|
||||
+47
-16
@@ -19,8 +19,8 @@ import (
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// startAgent executes ssh-agent, and returns a Agent interface to it.
|
||||
func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
// startOpenSSHAgent executes ssh-agent, and returns an Agent interface to it.
|
||||
func startOpenSSHAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
if testing.Short() {
|
||||
// ssh-agent is not always available, and the key
|
||||
// types supported vary by platform.
|
||||
@@ -79,16 +79,32 @@ func startAgent(t *testing.T) (client Agent, socket string, cleanup func()) {
|
||||
}
|
||||
}
|
||||
|
||||
func testAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
// startKeyringAgent uses Keyring to simulate a ssh-agent Server and returns a client.
|
||||
func startKeyringAgent(t *testing.T) (client Agent, cleanup func()) {
|
||||
c1, c2, err := netPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("netPipe: %v", err)
|
||||
}
|
||||
go ServeAgent(NewKeyring(), c2)
|
||||
|
||||
return NewClient(c1), func() {
|
||||
c1.Close()
|
||||
c2.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func testOpenSSHAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testKeyring(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
a := NewKeyring()
|
||||
testAgentInterface(t, a, key, cert, lifetimeSecs)
|
||||
func testKeyringAgent(t *testing.T, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
testAgentInterface(t, agent, key, cert, lifetimeSecs)
|
||||
}
|
||||
|
||||
func testAgentInterface(t *testing.T, agent Agent, key interface{}, cert *ssh.Certificate, lifetimeSecs uint32) {
|
||||
@@ -159,8 +175,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", "ed25519"} {
|
||||
testAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
testKeyring(t, testPrivateKeys[keyType], nil, 1)
|
||||
testOpenSSHAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
testKeyringAgent(t, testPrivateKeys[keyType], nil, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,8 +188,8 @@ func TestCert(t *testing.T) {
|
||||
}
|
||||
cert.SignCert(rand.Reader, testSigners["ecdsa"])
|
||||
|
||||
testAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
testKeyring(t, testPrivateKeys["rsa"], cert, 1)
|
||||
testOpenSSHAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
testKeyringAgent(t, testPrivateKeys["rsa"], cert, 0)
|
||||
}
|
||||
|
||||
// netPipe is analogous to net.Pipe, but it uses a real net.Conn, and
|
||||
@@ -203,7 +219,7 @@ func netPipe() (net.Conn, net.Conn, error) {
|
||||
}
|
||||
|
||||
func TestAuth(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
a, b, err := netPipe()
|
||||
@@ -247,8 +263,14 @@ func TestAuth(t *testing.T) {
|
||||
conn.Close()
|
||||
}
|
||||
|
||||
func TestLockClient(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
func TestLockOpenSSHAgent(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
testLockAgent(agent, t)
|
||||
}
|
||||
|
||||
func TestLockKeyringAgent(t *testing.T) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
testLockAgent(agent, t)
|
||||
}
|
||||
@@ -308,10 +330,19 @@ func testLockAgent(agent Agent, t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAgentLifetime(t *testing.T) {
|
||||
agent, _, cleanup := startAgent(t)
|
||||
func testOpenSSHAgentLifetime(t *testing.T) {
|
||||
agent, _, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
testAgentLifetime(t, agent)
|
||||
}
|
||||
|
||||
func testKeyringAgentLifetime(t *testing.T) {
|
||||
agent, cleanup := startKeyringAgent(t)
|
||||
defer cleanup()
|
||||
testAgentLifetime(t, agent)
|
||||
}
|
||||
|
||||
func testAgentLifetime(t *testing.T, agent Agent) {
|
||||
for _, keyType := range []string{"rsa", "dsa", "ecdsa"} {
|
||||
// Add private keys to the agent.
|
||||
err := agent.Add(AddedKey{
|
||||
|
||||
+81
-9
@@ -106,7 +106,7 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
||||
return nil, s.agent.Lock(req.Passphrase)
|
||||
|
||||
case agentUnlock:
|
||||
var req agentLockMsg
|
||||
var req agentUnlockMsg
|
||||
if err := ssh.Unmarshal(data, &req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -155,6 +155,44 @@ func (s *server) processRequest(data []byte) (interface{}, error) {
|
||||
return nil, fmt.Errorf("unknown opcode %d", data[0])
|
||||
}
|
||||
|
||||
func parseConstraints(constraints []byte) (lifetimeSecs uint32, confirmBeforeUse bool, extensions []ConstraintExtension, err error) {
|
||||
for len(constraints) != 0 {
|
||||
switch constraints[0] {
|
||||
case agentConstrainLifetime:
|
||||
lifetimeSecs = binary.BigEndian.Uint32(constraints[1:5])
|
||||
constraints = constraints[5:]
|
||||
case agentConstrainConfirm:
|
||||
confirmBeforeUse = true
|
||||
constraints = constraints[1:]
|
||||
case agentConstrainExtension:
|
||||
var msg constrainExtensionAgentMsg
|
||||
if err = ssh.Unmarshal(constraints, &msg); err != nil {
|
||||
return 0, false, nil, err
|
||||
}
|
||||
extensions = append(extensions, ConstraintExtension{
|
||||
ExtensionName: msg.ExtensionName,
|
||||
ExtensionDetails: msg.ExtensionDetails,
|
||||
})
|
||||
constraints = msg.Rest
|
||||
default:
|
||||
return 0, false, nil, fmt.Errorf("unknown constraint type: %d", constraints[0])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setConstraints(key *AddedKey, constraintBytes []byte) error {
|
||||
lifetimeSecs, confirmBeforeUse, constraintExtensions, err := parseConstraints(constraintBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
key.LifetimeSecs = lifetimeSecs
|
||||
key.ConfirmBeforeUse = confirmBeforeUse
|
||||
key.ConstraintExtensions = constraintExtensions
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseRSAKey(req []byte) (*AddedKey, error) {
|
||||
var k rsaKeyMsg
|
||||
if err := ssh.Unmarshal(req, &k); err != nil {
|
||||
@@ -173,7 +211,11 @@ func parseRSAKey(req []byte) (*AddedKey, error) {
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseEd25519Key(req []byte) (*AddedKey, error) {
|
||||
@@ -182,7 +224,12 @@ func parseEd25519Key(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
priv := ed25519.PrivateKey(k.Priv)
|
||||
return &AddedKey{PrivateKey: &priv, Comment: k.Comments}, nil
|
||||
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseDSAKey(req []byte) (*AddedKey, error) {
|
||||
@@ -202,7 +249,11 @@ func parseDSAKey(req []byte) (*AddedKey, error) {
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func unmarshalECDSA(curveName string, keyBytes []byte, privScalar *big.Int) (priv *ecdsa.PrivateKey, err error) {
|
||||
@@ -243,7 +294,12 @@ func parseEd25519Cert(req []byte) (*AddedKey, error) {
|
||||
if !ok {
|
||||
return nil, errors.New("agent: bad ED25519 certificate")
|
||||
}
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseECDSAKey(req []byte) (*AddedKey, error) {
|
||||
@@ -257,7 +313,11 @@ func parseECDSAKey(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseRSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -300,7 +360,11 @@ func parseRSACert(req []byte) (*AddedKey, error) {
|
||||
}
|
||||
priv.Precompute()
|
||||
|
||||
return &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: &priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseDSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -338,7 +402,11 @@ func parseDSACert(req []byte) (*AddedKey, error) {
|
||||
X: k.X,
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func parseECDSACert(req []byte) (*AddedKey, error) {
|
||||
@@ -371,7 +439,11 @@ func parseECDSACert(req []byte) (*AddedKey, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}, nil
|
||||
addedKey := &AddedKey{PrivateKey: priv, Certificate: cert, Comment: k.Comments}
|
||||
if err := setConstraints(addedKey, k.Constraints); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addedKey, nil
|
||||
}
|
||||
|
||||
func (s *server) insertIdentity(req []byte) error {
|
||||
|
||||
+51
-1
@@ -8,6 +8,9 @@ import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
pseudorand "math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@@ -40,7 +43,7 @@ func TestSetupForwardAgent(t *testing.T) {
|
||||
defer a.Close()
|
||||
defer b.Close()
|
||||
|
||||
_, socket, cleanup := startAgent(t)
|
||||
_, socket, cleanup := startOpenSSHAgent(t)
|
||||
defer cleanup()
|
||||
|
||||
serverConf := ssh.ServerConfig{
|
||||
@@ -207,3 +210,50 @@ func TestCertTypes(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseConstraints(t *testing.T) {
|
||||
// Test LifetimeSecs
|
||||
var msg = constrainLifetimeAgentMsg{pseudorand.Uint32()}
|
||||
lifetimeSecs, _, _, err := parseConstraints(ssh.Marshal(msg))
|
||||
if err != nil {
|
||||
t.Fatalf("parseConstraints: %v", err)
|
||||
}
|
||||
if lifetimeSecs != msg.LifetimeSecs {
|
||||
t.Errorf("got lifetime %v, want %v", lifetimeSecs, msg.LifetimeSecs)
|
||||
}
|
||||
|
||||
// Test ConfirmBeforeUse
|
||||
_, confirmBeforeUse, _, err := parseConstraints([]byte{agentConstrainConfirm})
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if !confirmBeforeUse {
|
||||
t.Error("got comfirmBeforeUse == false")
|
||||
}
|
||||
|
||||
// Test ConstraintExtensions
|
||||
var data []byte
|
||||
var expect []ConstraintExtension
|
||||
for i := 0; i < 10; i++ {
|
||||
var ext = ConstraintExtension{
|
||||
ExtensionName: fmt.Sprintf("name%d", i),
|
||||
ExtensionDetails: []byte(fmt.Sprintf("details: %d", i)),
|
||||
}
|
||||
expect = append(expect, ext)
|
||||
data = append(data, agentConstrainExtension)
|
||||
data = append(data, ssh.Marshal(ext)...)
|
||||
}
|
||||
_, _, extensions, err := parseConstraints(data)
|
||||
if err != nil {
|
||||
t.Fatalf("%v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expect, extensions) {
|
||||
t.Errorf("got extension %v, want %v", extensions, expect)
|
||||
}
|
||||
|
||||
// Test Unknown Constraint
|
||||
_, _, _, err = parseConstraints([]byte{128})
|
||||
if err == nil || !strings.Contains(err.Error(), "unknown constraint") {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-3
@@ -51,13 +51,12 @@ func (b *buffer) write(buf []byte) {
|
||||
}
|
||||
|
||||
// eof closes the buffer. Reads from the buffer once all
|
||||
// the data has been consumed will receive os.EOF.
|
||||
func (b *buffer) eof() error {
|
||||
// the data has been consumed will receive io.EOF.
|
||||
func (b *buffer) eof() {
|
||||
b.Cond.L.Lock()
|
||||
b.closed = true
|
||||
b.Cond.Signal()
|
||||
b.Cond.L.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Read reads data from the internal buffer in buf. Reads will block
|
||||
|
||||
+1
-1
@@ -349,7 +349,7 @@ func handleAuthResponse(c packetConn) (bool, []string, error) {
|
||||
// both CLI and GUI environments.
|
||||
type KeyboardInteractiveChallenge func(user, instruction string, questions []string, echos []bool) (answers []string, err error)
|
||||
|
||||
// KeyboardInteractive returns a AuthMethod using a prompt/response
|
||||
// KeyboardInteractive returns an AuthMethod using a prompt/response
|
||||
// sequence controlled by the server.
|
||||
func KeyboardInteractive(challenge KeyboardInteractiveChallenge) AuthMethod {
|
||||
return challenge
|
||||
|
||||
+34
-9
@@ -367,6 +367,17 @@ func (r *dsaPublicKey) Type() string {
|
||||
return "ssh-dss"
|
||||
}
|
||||
|
||||
func checkDSAParams(param *dsa.Parameters) error {
|
||||
// SSH specifies FIPS 186-2, which only provided a single size
|
||||
// (1024 bits) DSA key. FIPS 186-3 allows for larger key
|
||||
// sizes, which would confuse SSH.
|
||||
if l := param.P.BitLen(); l != 1024 {
|
||||
return fmt.Errorf("ssh: unsupported DSA key size %d", l)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseDSA parses an DSA key according to RFC 4253, section 6.6.
|
||||
func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
var w struct {
|
||||
@@ -377,13 +388,18 @@ func parseDSA(in []byte) (out PublicKey, rest []byte, err error) {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
param := dsa.Parameters{
|
||||
P: w.P,
|
||||
Q: w.Q,
|
||||
G: w.G,
|
||||
}
|
||||
if err := checkDSAParams(¶m); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
key := &dsaPublicKey{
|
||||
Parameters: dsa.Parameters{
|
||||
P: w.P,
|
||||
Q: w.Q,
|
||||
G: w.G,
|
||||
},
|
||||
Y: w.Y,
|
||||
Parameters: param,
|
||||
Y: w.Y,
|
||||
}
|
||||
return key, w.Rest, nil
|
||||
}
|
||||
@@ -630,19 +646,28 @@ func (k *ecdsaPublicKey) CryptoPublicKey() crypto.PublicKey {
|
||||
}
|
||||
|
||||
// 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.
|
||||
// *ecdsa.PrivateKey or any other crypto.Signer and returns a
|
||||
// corresponding Signer instance. ECDSA keys must use P-256, P-384 or
|
||||
// P-521. DSA keys must use parameter size L1024N160.
|
||||
func NewSignerFromKey(key interface{}) (Signer, error) {
|
||||
switch key := key.(type) {
|
||||
case crypto.Signer:
|
||||
return NewSignerFromSigner(key)
|
||||
case *dsa.PrivateKey:
|
||||
return &dsaPrivateKey{key}, nil
|
||||
return newDSAPrivateKey(key)
|
||||
default:
|
||||
return nil, fmt.Errorf("ssh: unsupported key type %T", key)
|
||||
}
|
||||
}
|
||||
|
||||
func newDSAPrivateKey(key *dsa.PrivateKey) (Signer, error) {
|
||||
if err := checkDSAParams(&key.PublicKey.Parameters); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dsaPrivateKey{key}, nil
|
||||
}
|
||||
|
||||
type wrappedSigner struct {
|
||||
signer crypto.Signer
|
||||
pubKey PublicKey
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ type ServerConfig struct {
|
||||
PasswordCallback func(conn ConnMetadata, password []byte) (*Permissions, error)
|
||||
|
||||
// PublicKeyCallback, if non-nil, is called when a client
|
||||
// offers a public key for authentication. It must return true
|
||||
// offers a public key for authentication. It must return a nil error
|
||||
// if the given public key can be used to authenticate the
|
||||
// given user. For example, see CertChecker.Authenticate. A
|
||||
// call to this function does not guarantee that the key
|
||||
|
||||
+20
@@ -231,6 +231,26 @@ func (s *Session) RequestSubsystem(subsystem string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// RFC 4254 Section 6.7.
|
||||
type ptyWindowChangeMsg struct {
|
||||
Columns uint32
|
||||
Rows uint32
|
||||
Width uint32
|
||||
Height uint32
|
||||
}
|
||||
|
||||
// WindowChange informs the remote host about a terminal window dimension change to h rows and w columns.
|
||||
func (s *Session) WindowChange(h, w int) error {
|
||||
req := ptyWindowChangeMsg{
|
||||
Columns: uint32(w),
|
||||
Rows: uint32(h),
|
||||
Width: uint32(w * 8),
|
||||
Height: uint32(h * 8),
|
||||
}
|
||||
_, err := s.ch.SendRequest("window-change", false, Marshal(&req))
|
||||
return err
|
||||
}
|
||||
|
||||
// RFC 4254 Section 6.9.
|
||||
type signalMsg struct {
|
||||
Signal string
|
||||
|
||||
+4
@@ -19,6 +19,8 @@ package terminal // import "golang.org/x/crypto/ssh/terminal"
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// State contains the state of a terminal.
|
||||
@@ -50,6 +52,8 @@ func MakeRaw(fd int) (*State, error) {
|
||||
newState.Lflag &^= syscall.ECHO | syscall.ECHONL | syscall.ICANON | syscall.ISIG | syscall.IEXTEN
|
||||
newState.Cflag &^= syscall.CSIZE | syscall.PARENB
|
||||
newState.Cflag |= syscall.CS8
|
||||
newState.Cc[unix.VMIN] = 1
|
||||
newState.Cc[unix.VTIME] = 0
|
||||
if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlWriteTermios, uintptr(unsafe.Pointer(&newState)), 0, 0, 0); err != 0 {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+3
-3
@@ -6,7 +6,7 @@
|
||||
|
||||
package terminal
|
||||
|
||||
import "syscall"
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
const ioctlWriteTermios = syscall.TIOCSETA
|
||||
const ioctlReadTermios = unix.TIOCGETA
|
||||
const ioctlWriteTermios = unix.TIOCSETA
|
||||
|
||||
+4
-5
@@ -4,8 +4,7 @@
|
||||
|
||||
package terminal
|
||||
|
||||
// These constants are declared here, rather than importing
|
||||
// them from the syscall package as some syscall packages, even
|
||||
// on linux, for example gccgo, do not declare them.
|
||||
const ioctlReadTermios = 0x5401 // syscall.TCGETS
|
||||
const ioctlWriteTermios = 0x5402 // syscall.TCSETS
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
const ioctlReadTermios = unix.TCGETS
|
||||
const ioctlWriteTermios = unix.TCSETS
|
||||
|
||||
+23
-76
@@ -17,53 +17,7 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
enableLineInput = 2
|
||||
enableEchoInput = 4
|
||||
enableProcessedInput = 1
|
||||
enableWindowInput = 8
|
||||
enableMouseInput = 16
|
||||
enableInsertMode = 32
|
||||
enableQuickEditMode = 64
|
||||
enableExtendedFlags = 128
|
||||
enableAutoPosition = 256
|
||||
enableProcessedOutput = 1
|
||||
enableWrapAtEolOutput = 2
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
var (
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
)
|
||||
|
||||
type (
|
||||
short int16
|
||||
word uint16
|
||||
|
||||
coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
@@ -73,8 +27,8 @@ type State struct {
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd int) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
err := windows.GetConsoleMode(windows.Handle(fd), &st)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// MakeRaw put the terminal connected to the given file descriptor into raw
|
||||
@@ -82,14 +36,12 @@ func IsTerminal(fd int) bool {
|
||||
// restored.
|
||||
func MakeRaw(fd int) (*State, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (enableEchoInput | enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(raw), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
@@ -98,9 +50,8 @@ func MakeRaw(fd int) (*State, error) {
|
||||
// restore the terminal after a signal.
|
||||
func GetState(fd int) (*State, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{st}, nil
|
||||
}
|
||||
@@ -108,25 +59,23 @@ func GetState(fd int) (*State, error) {
|
||||
// Restore restores the terminal connected to the given file descriptor to a
|
||||
// previous state.
|
||||
func Restore(fd int, state *State) error {
|
||||
_, _, err := syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(state.mode), 0)
|
||||
return err
|
||||
return windows.SetConsoleMode(windows.Handle(fd), state.mode)
|
||||
}
|
||||
|
||||
// GetSize returns the dimensions of the given terminal.
|
||||
func GetSize(fd int) (width, height int, err error) {
|
||||
var info consoleScreenBufferInfo
|
||||
_, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&info)), 0)
|
||||
if e != 0 {
|
||||
return 0, 0, error(e)
|
||||
var info windows.ConsoleScreenBufferInfo
|
||||
if err := windows.GetConsoleScreenBufferInfo(windows.Handle(fd), &info); err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
return int(info.size.x), int(info.size.y), nil
|
||||
return int(info.Size.X), int(info.Size.Y), nil
|
||||
}
|
||||
|
||||
// passwordReader is an io.Reader that reads from a specific Windows HANDLE.
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return syscall.Read(syscall.Handle(r), buf)
|
||||
return windows.Read(windows.Handle(r), buf)
|
||||
}
|
||||
|
||||
// ReadPassword reads a line of input from a terminal without local echo. This
|
||||
@@ -134,21 +83,19 @@ func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
// returned does not include the \n.
|
||||
func ReadPassword(fd int) ([]byte, error) {
|
||||
var st uint32
|
||||
_, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
old := st
|
||||
|
||||
st &^= (enableEchoInput)
|
||||
st |= (enableProcessedInput | enableLineInput | enableProcessedOutput)
|
||||
_, _, e = syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(st), 0)
|
||||
if e != 0 {
|
||||
return nil, error(e)
|
||||
st &^= (windows.ENABLE_ECHO_INPUT)
|
||||
st |= (windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
syscall.Syscall(procSetConsoleMode.Addr(), 2, uintptr(fd), uintptr(old), 0)
|
||||
windows.SetConsoleMode(windows.Handle(fd), old)
|
||||
}()
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
|
||||
+48
@@ -276,6 +276,54 @@ func TestValidTerminalMode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWindowChange(t *testing.T) {
|
||||
server := newServer(t)
|
||||
defer server.Shutdown()
|
||||
conn := server.Dial(clientConfig())
|
||||
defer conn.Close()
|
||||
|
||||
session, err := conn.NewSession()
|
||||
if err != nil {
|
||||
t.Fatalf("session failed: %v", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
stdout, err := session.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to acquire stdout pipe: %s", err)
|
||||
}
|
||||
|
||||
stdin, err := session.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to acquire stdin pipe: %s", err)
|
||||
}
|
||||
|
||||
tm := ssh.TerminalModes{ssh.ECHO: 0}
|
||||
if err = session.RequestPty("xterm", 80, 40, tm); err != nil {
|
||||
t.Fatalf("req-pty failed: %s", err)
|
||||
}
|
||||
|
||||
if err := session.WindowChange(100, 100); err != nil {
|
||||
t.Fatalf("window-change failed: %s", err)
|
||||
}
|
||||
|
||||
err = session.Shell()
|
||||
if err != nil {
|
||||
t.Fatalf("session failed: %s", err)
|
||||
}
|
||||
|
||||
stdin.Write([]byte("stty size && exit\n"))
|
||||
|
||||
var buf bytes.Buffer
|
||||
if _, err := io.Copy(&buf, stdout); err != nil {
|
||||
t.Fatalf("reading failed: %s", err)
|
||||
}
|
||||
|
||||
if sttyOutput := buf.String(); !strings.Contains(sttyOutput, "100 100") {
|
||||
t.Fatalf("terminal WindowChange failure: expected \"100 100\" stty output, got %s", sttyOutput)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCiphers(t *testing.T) {
|
||||
var config ssh.Config
|
||||
config.SetDefaults()
|
||||
|
||||
Reference in New Issue
Block a user