mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-01 00:23:56 +08:00 
			
		
		
		
	
							
								
								
									
										789
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										789
									
								
								vendor/golang.org/x/crypto/ssh/agent/client.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,789 @@ | ||||
| // Copyright 2012 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| // 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]: https://tools.ietf.org/html/draft-miller-ssh-agent-00 | ||||
| package agent // import "golang.org/x/crypto/ssh/agent" | ||||
|  | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"crypto/dsa" | ||||
| 	"crypto/ecdsa" | ||||
| 	"crypto/elliptic" | ||||
| 	"crypto/rsa" | ||||
| 	"encoding/base64" | ||||
| 	"encoding/binary" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"io" | ||||
| 	"math/big" | ||||
| 	"sync" | ||||
|  | ||||
| 	"crypto" | ||||
| 	"golang.org/x/crypto/ed25519" | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // SignatureFlags represent additional flags that can be passed to the signature | ||||
| // requests an defined in [PROTOCOL.agent] section 4.5.1. | ||||
| type SignatureFlags uint32 | ||||
|  | ||||
| // SignatureFlag values as defined in [PROTOCOL.agent] section 5.3. | ||||
| const ( | ||||
| 	SignatureFlagReserved SignatureFlags = 1 << iota | ||||
| 	SignatureFlagRsaSha256 | ||||
| 	SignatureFlagRsaSha512 | ||||
| ) | ||||
|  | ||||
| // Agent represents the capabilities of an ssh-agent. | ||||
| type Agent interface { | ||||
| 	// List returns the identities known to the agent. | ||||
| 	List() ([]*Key, error) | ||||
|  | ||||
| 	// Sign has the agent sign the data using a protocol 2 key as defined | ||||
| 	// in [PROTOCOL.agent] section 2.6.2. | ||||
| 	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, 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 | ||||
|  | ||||
| 	// RemoveAll removes all identities. | ||||
| 	RemoveAll() error | ||||
|  | ||||
| 	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list. | ||||
| 	Lock(passphrase []byte) error | ||||
|  | ||||
| 	// Unlock undoes the effect of Lock | ||||
| 	Unlock(passphrase []byte) error | ||||
|  | ||||
| 	// Signers returns signers for all the known keys. | ||||
| 	Signers() ([]ssh.Signer, error) | ||||
| } | ||||
|  | ||||
| type ExtendedAgent interface { | ||||
| 	Agent | ||||
|  | ||||
| 	// SignWithFlags signs like Sign, but allows for additional flags to be sent/received | ||||
| 	SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) | ||||
|  | ||||
| 	// Extension processes a custom extension request. Standard-compliant agents are not | ||||
| 	// required to support any extensions, but this method allows agents to implement | ||||
| 	// vendor-specific methods or add experimental features. See [PROTOCOL.agent] section 4.7. | ||||
| 	// If agent extensions are unsupported entirely this method MUST return an | ||||
| 	// ErrExtensionUnsupported error. Similarly, if just the specific extensionType in | ||||
| 	// the request is unsupported by the agent then ErrExtensionUnsupported MUST be | ||||
| 	// returned. | ||||
| 	// | ||||
| 	// In the case of success, since [PROTOCOL.agent] section 4.7 specifies that the contents | ||||
| 	// of the response are unspecified (including the type of the message), the complete | ||||
| 	// response will be returned as a []byte slice, including the "type" byte of the message. | ||||
| 	Extension(extensionType string, contents []byte) ([]byte, 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 | ||||
| 	// *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 | ||||
| 	// ConstraintExtensions are the experimental or private-use constraints | ||||
| 	// defined by users. | ||||
| 	ConstraintExtensions []ConstraintExtension | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 3. | ||||
| const ( | ||||
| 	agentRequestV1Identities   = 1 | ||||
| 	agentRemoveAllV1Identities = 9 | ||||
|  | ||||
| 	// 3.2 Requests from client to agent for protocol 2 key operations | ||||
| 	agentAddIdentity         = 17 | ||||
| 	agentRemoveIdentity      = 18 | ||||
| 	agentRemoveAllIdentities = 19 | ||||
| 	agentAddIDConstrained    = 25 | ||||
|  | ||||
| 	// 3.3 Key-type independent requests from client to agent | ||||
| 	agentAddSmartcardKey            = 20 | ||||
| 	agentRemoveSmartcardKey         = 21 | ||||
| 	agentLock                       = 22 | ||||
| 	agentUnlock                     = 23 | ||||
| 	agentAddSmartcardKeyConstrained = 26 | ||||
|  | ||||
| 	// 3.7 Key constraint identifiers | ||||
| 	agentConstrainLifetime  = 1 | ||||
| 	agentConstrainConfirm   = 2 | ||||
| 	agentConstrainExtension = 3 | ||||
| ) | ||||
|  | ||||
| // maxAgentResponseBytes is the maximum agent reply size that is accepted. This | ||||
| // is a sanity check, not a limit in the spec. | ||||
| const maxAgentResponseBytes = 16 << 20 | ||||
|  | ||||
| // Agent messages: | ||||
| // These structures mirror the wire format of the corresponding ssh agent | ||||
| // messages found in [PROTOCOL.agent]. | ||||
|  | ||||
| // 3.4 Generic replies from agent to client | ||||
| const agentFailure = 5 | ||||
|  | ||||
| type failureAgentMsg struct{} | ||||
|  | ||||
| const agentSuccess = 6 | ||||
|  | ||||
| type successAgentMsg struct{} | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.2. | ||||
| const agentRequestIdentities = 11 | ||||
|  | ||||
| type requestIdentitiesAgentMsg struct{} | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.2. | ||||
| const agentIdentitiesAnswer = 12 | ||||
|  | ||||
| type identitiesAnswerAgentMsg struct { | ||||
| 	NumKeys uint32 `sshtype:"12"` | ||||
| 	Keys    []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.6.2. | ||||
| const agentSignRequest = 13 | ||||
|  | ||||
| type signRequestAgentMsg struct { | ||||
| 	KeyBlob []byte `sshtype:"13"` | ||||
| 	Data    []byte | ||||
| 	Flags   uint32 | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.6.2. | ||||
|  | ||||
| // 3.6 Replies from agent to client for protocol 2 key operations | ||||
| const agentSignResponse = 14 | ||||
|  | ||||
| type signResponseAgentMsg struct { | ||||
| 	SigBlob []byte `sshtype:"14"` | ||||
| } | ||||
|  | ||||
| type publicKey struct { | ||||
| 	Format string | ||||
| 	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"` | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 4.7 | ||||
| const agentExtension = 27 | ||||
| const agentExtensionFailure = 28 | ||||
|  | ||||
| // ErrExtensionUnsupported indicates that an extension defined in | ||||
| // [PROTOCOL.agent] section 4.7 is unsupported by the agent. Specifically this | ||||
| // error indicates that the agent returned a standard SSH_AGENT_FAILURE message | ||||
| // as the result of a SSH_AGENTC_EXTENSION request. Note that the protocol | ||||
| // specification (and therefore this error) does not distinguish between a | ||||
| // specific extension being unsupported and extensions being unsupported entirely. | ||||
| var ErrExtensionUnsupported = errors.New("agent: extension unsupported") | ||||
|  | ||||
| type extensionAgentMsg struct { | ||||
| 	ExtensionType string `sshtype:"27"` | ||||
| 	Contents      []byte | ||||
| } | ||||
|  | ||||
| // Key represents a protocol 2 public key as defined in | ||||
| // [PROTOCOL.agent], section 2.5.2. | ||||
| type Key struct { | ||||
| 	Format  string | ||||
| 	Blob    []byte | ||||
| 	Comment string | ||||
| } | ||||
|  | ||||
| func clientErr(err error) error { | ||||
| 	return fmt.Errorf("agent: client error: %v", err) | ||||
| } | ||||
|  | ||||
| // String returns the storage form of an agent key with the format, base64 | ||||
| // encoded serialized key, and the comment if it is not empty. | ||||
| func (k *Key) String() string { | ||||
| 	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob) | ||||
|  | ||||
| 	if k.Comment != "" { | ||||
| 		s += " " + k.Comment | ||||
| 	} | ||||
|  | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| // Type returns the public key type. | ||||
| func (k *Key) Type() string { | ||||
| 	return k.Format | ||||
| } | ||||
|  | ||||
| // Marshal returns key blob to satisfy the ssh.PublicKey interface. | ||||
| func (k *Key) Marshal() []byte { | ||||
| 	return k.Blob | ||||
| } | ||||
|  | ||||
| // Verify satisfies the ssh.PublicKey interface. | ||||
| func (k *Key) Verify(data []byte, sig *ssh.Signature) error { | ||||
| 	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 { | ||||
| 	Format string | ||||
| 	Rest   []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| func parseKey(in []byte) (out *Key, rest []byte, err error) { | ||||
| 	var record struct { | ||||
| 		Blob    []byte | ||||
| 		Comment string | ||||
| 		Rest    []byte `ssh:"rest"` | ||||
| 	} | ||||
|  | ||||
| 	if err := ssh.Unmarshal(in, &record); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	var wk wireKey | ||||
| 	if err := ssh.Unmarshal(record.Blob, &wk); err != nil { | ||||
| 		return nil, nil, err | ||||
| 	} | ||||
|  | ||||
| 	return &Key{ | ||||
| 		Format:  wk.Format, | ||||
| 		Blob:    record.Blob, | ||||
| 		Comment: record.Comment, | ||||
| 	}, record.Rest, nil | ||||
| } | ||||
|  | ||||
| // client is a client for an ssh-agent process. | ||||
| type client struct { | ||||
| 	// conn is typically a *net.UnixConn | ||||
| 	conn io.ReadWriter | ||||
| 	// mu is used to prevent concurrent access to the agent | ||||
| 	mu sync.Mutex | ||||
| } | ||||
|  | ||||
| // NewClient returns an Agent that talks to an ssh-agent process over | ||||
| // the given connection. | ||||
| func NewClient(rw io.ReadWriter) ExtendedAgent { | ||||
| 	return &client{conn: rw} | ||||
| } | ||||
|  | ||||
| // call sends an RPC to the agent. On success, the reply is | ||||
| // unmarshaled into reply and replyType is set to the first byte of | ||||
| // the reply, which contains the type of the message. | ||||
| func (c *client) call(req []byte) (reply interface{}, err error) { | ||||
| 	buf, err := c.callRaw(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	reply, err = unmarshal(buf) | ||||
| 	if err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	return reply, nil | ||||
| } | ||||
|  | ||||
| // callRaw sends an RPC to the agent. On success, the raw | ||||
| // bytes of the response are returned; no unmarshalling is | ||||
| // performed on the response. | ||||
| func (c *client) callRaw(req []byte) (reply []byte, err error) { | ||||
| 	c.mu.Lock() | ||||
| 	defer c.mu.Unlock() | ||||
|  | ||||
| 	msg := make([]byte, 4+len(req)) | ||||
| 	binary.BigEndian.PutUint32(msg, uint32(len(req))) | ||||
| 	copy(msg[4:], req) | ||||
| 	if _, err = c.conn.Write(msg); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
|  | ||||
| 	var respSizeBuf [4]byte | ||||
| 	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	respSize := binary.BigEndian.Uint32(respSizeBuf[:]) | ||||
| 	if respSize > maxAgentResponseBytes { | ||||
| 		return nil, clientErr(errors.New("response too large")) | ||||
| 	} | ||||
|  | ||||
| 	buf := make([]byte, respSize) | ||||
| 	if _, err = io.ReadFull(c.conn, buf); err != nil { | ||||
| 		return nil, clientErr(err) | ||||
| 	} | ||||
| 	return buf, nil | ||||
| } | ||||
|  | ||||
| func (c *client) simpleCall(req []byte) error { | ||||
| 	resp, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| func (c *client) RemoveAll() error { | ||||
| 	return c.simpleCall([]byte{agentRemoveAllIdentities}) | ||||
| } | ||||
|  | ||||
| func (c *client) Remove(key ssh.PublicKey) error { | ||||
| 	req := ssh.Marshal(&agentRemoveIdentityMsg{ | ||||
| 		KeyBlob: key.Marshal(), | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| func (c *client) Lock(passphrase []byte) error { | ||||
| 	req := ssh.Marshal(&agentLockMsg{ | ||||
| 		Passphrase: passphrase, | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| func (c *client) Unlock(passphrase []byte) error { | ||||
| 	req := ssh.Marshal(&agentUnlockMsg{ | ||||
| 		Passphrase: passphrase, | ||||
| 	}) | ||||
| 	return c.simpleCall(req) | ||||
| } | ||||
|  | ||||
| // List returns the identities known to the agent. | ||||
| func (c *client) List() ([]*Key, error) { | ||||
| 	// see [PROTOCOL.agent] section 2.5.2. | ||||
| 	req := []byte{agentRequestIdentities} | ||||
|  | ||||
| 	msg, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch msg := msg.(type) { | ||||
| 	case *identitiesAnswerAgentMsg: | ||||
| 		if msg.NumKeys > maxAgentResponseBytes/8 { | ||||
| 			return nil, errors.New("agent: too many keys in agent reply") | ||||
| 		} | ||||
| 		keys := make([]*Key, msg.NumKeys) | ||||
| 		data := msg.Keys | ||||
| 		for i := uint32(0); i < msg.NumKeys; i++ { | ||||
| 			var key *Key | ||||
| 			var err error | ||||
| 			if key, data, err = parseKey(data); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			keys[i] = key | ||||
| 		} | ||||
| 		return keys, nil | ||||
| 	case *failureAgentMsg: | ||||
| 		return nil, errors.New("agent: failed to list keys") | ||||
| 	} | ||||
| 	panic("unreachable") | ||||
| } | ||||
|  | ||||
| // Sign has the agent sign the data using a protocol 2 key as defined | ||||
| // in [PROTOCOL.agent] section 2.6.2. | ||||
| func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | ||||
| 	return c.SignWithFlags(key, data, 0) | ||||
| } | ||||
|  | ||||
| func (c *client) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { | ||||
| 	req := ssh.Marshal(signRequestAgentMsg{ | ||||
| 		KeyBlob: key.Marshal(), | ||||
| 		Data:    data, | ||||
| 		Flags:   uint32(flags), | ||||
| 	}) | ||||
|  | ||||
| 	msg, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	switch msg := msg.(type) { | ||||
| 	case *signResponseAgentMsg: | ||||
| 		var sig ssh.Signature | ||||
| 		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return &sig, nil | ||||
| 	case *failureAgentMsg: | ||||
| 		return nil, errors.New("agent: failed to sign challenge") | ||||
| 	} | ||||
| 	panic("unreachable") | ||||
| } | ||||
|  | ||||
| // unmarshal parses an agent message in packet, returning the parsed | ||||
| // form and the message type of packet. | ||||
| func unmarshal(packet []byte) (interface{}, error) { | ||||
| 	if len(packet) < 1 { | ||||
| 		return nil, errors.New("agent: empty packet") | ||||
| 	} | ||||
| 	var msg interface{} | ||||
| 	switch packet[0] { | ||||
| 	case agentFailure: | ||||
| 		return new(failureAgentMsg), nil | ||||
| 	case agentSuccess: | ||||
| 		return new(successAgentMsg), nil | ||||
| 	case agentIdentitiesAnswer: | ||||
| 		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]) | ||||
| 	} | ||||
| 	if err := ssh.Unmarshal(packet, msg); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	return msg, nil | ||||
| } | ||||
|  | ||||
| type rsaKeyMsg struct { | ||||
| 	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|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|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, constraints []byte) error { | ||||
| 	var req []byte | ||||
| 	switch k := s.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if len(k.Primes) != 2 { | ||||
| 			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) | ||||
| 		} | ||||
| 		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, | ||||
| 			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, | ||||
| 			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, | ||||
| 			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 | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| type rsaCertMsg struct { | ||||
| 	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|25"` | ||||
| 	CertBytes   []byte | ||||
| 	X           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| type ecdsaCertMsg struct { | ||||
| 	Type        string `sshtype:"17|25"` | ||||
| 	CertBytes   []byte | ||||
| 	D           *big.Int | ||||
| 	Comments    string | ||||
| 	Constraints []byte `ssh:"rest"` | ||||
| } | ||||
|  | ||||
| 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(key AddedKey) error { | ||||
| 	var constraints []byte | ||||
|  | ||||
| 	if secs := key.LifetimeSecs; secs != 0 { | ||||
| 		constraints = append(constraints, ssh.Marshal(constrainLifetimeAgentMsg{secs})...) | ||||
| 	} | ||||
|  | ||||
| 	if key.ConfirmBeforeUse { | ||||
| 		constraints = append(constraints, agentConstrainConfirm) | ||||
| 	} | ||||
|  | ||||
| 	cert := key.Certificate | ||||
| 	if cert == nil { | ||||
| 		return c.insertKey(key.PrivateKey, key.Comment, constraints) | ||||
| 	} | ||||
| 	return c.insertCert(key.PrivateKey, cert, key.Comment, constraints) | ||||
| } | ||||
|  | ||||
| func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error { | ||||
| 	var req []byte | ||||
| 	switch k := s.(type) { | ||||
| 	case *rsa.PrivateKey: | ||||
| 		if len(k.Primes) != 2 { | ||||
| 			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes)) | ||||
| 		} | ||||
| 		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, | ||||
| 			Constraints: constraints, | ||||
| 		}) | ||||
| 	case *dsa.PrivateKey: | ||||
| 		req = ssh.Marshal(dsaCertMsg{ | ||||
| 			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, | ||||
| 			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 | ||||
| 	} | ||||
| 	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 { | ||||
| 		return errors.New("agent: signer and cert have different public key") | ||||
| 	} | ||||
|  | ||||
| 	resp, err := c.call(req) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if _, ok := resp.(*successAgentMsg); ok { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return errors.New("agent: failure") | ||||
| } | ||||
|  | ||||
| // Signers provides a callback for client authentication. | ||||
| func (c *client) Signers() ([]ssh.Signer, error) { | ||||
| 	keys, err := c.List() | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
|  | ||||
| 	var result []ssh.Signer | ||||
| 	for _, k := range keys { | ||||
| 		result = append(result, &agentKeyringSigner{c, k}) | ||||
| 	} | ||||
| 	return result, nil | ||||
| } | ||||
|  | ||||
| type agentKeyringSigner struct { | ||||
| 	agent *client | ||||
| 	pub   ssh.PublicKey | ||||
| } | ||||
|  | ||||
| func (s *agentKeyringSigner) PublicKey() ssh.PublicKey { | ||||
| 	return s.pub | ||||
| } | ||||
|  | ||||
| func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) { | ||||
| 	// The agent has its own entropy source, so the rand argument is ignored. | ||||
| 	return s.agent.Sign(s.pub, data) | ||||
| } | ||||
|  | ||||
| func (s *agentKeyringSigner) SignWithOpts(rand io.Reader, data []byte, opts crypto.SignerOpts) (*ssh.Signature, error) { | ||||
| 	var flags SignatureFlags | ||||
| 	if opts != nil { | ||||
| 		switch opts.HashFunc() { | ||||
| 		case crypto.SHA256: | ||||
| 			flags = SignatureFlagRsaSha256 | ||||
| 		case crypto.SHA512: | ||||
| 			flags = SignatureFlagRsaSha512 | ||||
| 		} | ||||
| 	} | ||||
| 	return s.agent.SignWithFlags(s.pub, data, flags) | ||||
| } | ||||
|  | ||||
| // Calls an extension method. It is up to the agent implementation as to whether or not | ||||
| // any particular extension is supported and may always return an error. Because the | ||||
| // type of the response is up to the implementation, this returns the bytes of the | ||||
| // response and does not attempt any type of unmarshalling. | ||||
| func (c *client) Extension(extensionType string, contents []byte) ([]byte, error) { | ||||
| 	req := ssh.Marshal(extensionAgentMsg{ | ||||
| 		ExtensionType: extensionType, | ||||
| 		Contents:      contents, | ||||
| 	}) | ||||
| 	buf, err := c.callRaw(req) | ||||
| 	if err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	if len(buf) == 0 { | ||||
| 		return nil, errors.New("agent: failure; empty response") | ||||
| 	} | ||||
| 	// [PROTOCOL.agent] section 4.7 indicates that an SSH_AGENT_FAILURE message | ||||
| 	// represents an agent that does not support the extension | ||||
| 	if buf[0] == agentFailure { | ||||
| 		return nil, ErrExtensionUnsupported | ||||
| 	} | ||||
| 	if buf[0] == agentExtensionFailure { | ||||
| 		return nil, errors.New("agent: generic extension failure") | ||||
| 	} | ||||
|  | ||||
| 	return buf, nil | ||||
| } | ||||
							
								
								
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										103
									
								
								vendor/golang.org/x/crypto/ssh/agent/forward.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,103 @@ | ||||
| // Copyright 2014 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 ( | ||||
| 	"errors" | ||||
| 	"io" | ||||
| 	"net" | ||||
| 	"sync" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| // RequestAgentForwarding sets up agent forwarding for the session. | ||||
| // ForwardToAgent or ForwardToRemote should be called to route | ||||
| // the authentication requests. | ||||
| func RequestAgentForwarding(session *ssh.Session) error { | ||||
| 	ok, err := session.SendRequest("auth-agent-req@openssh.com", true, nil) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if !ok { | ||||
| 		return errors.New("forwarding request denied") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // ForwardToAgent routes authentication requests to the given keyring. | ||||
| func ForwardToAgent(client *ssh.Client, keyring Agent) error { | ||||
| 	channels := client.HandleChannelOpen(channelType) | ||||
| 	if channels == nil { | ||||
| 		return errors.New("agent: already have handler for " + channelType) | ||||
| 	} | ||||
|  | ||||
| 	go func() { | ||||
| 		for ch := range channels { | ||||
| 			channel, reqs, err := ch.Accept() | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			go ssh.DiscardRequests(reqs) | ||||
| 			go func() { | ||||
| 				ServeAgent(keyring, channel) | ||||
| 				channel.Close() | ||||
| 			}() | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| const channelType = "auth-agent@openssh.com" | ||||
|  | ||||
| // ForwardToRemote routes authentication requests to the ssh-agent | ||||
| // process serving on the given unix socket. | ||||
| func ForwardToRemote(client *ssh.Client, addr string) error { | ||||
| 	channels := client.HandleChannelOpen(channelType) | ||||
| 	if channels == nil { | ||||
| 		return errors.New("agent: already have handler for " + channelType) | ||||
| 	} | ||||
| 	conn, err := net.Dial("unix", addr) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	conn.Close() | ||||
|  | ||||
| 	go func() { | ||||
| 		for ch := range channels { | ||||
| 			channel, reqs, err := ch.Accept() | ||||
| 			if err != nil { | ||||
| 				continue | ||||
| 			} | ||||
| 			go ssh.DiscardRequests(reqs) | ||||
| 			go forwardUnixSocket(channel, addr) | ||||
| 		} | ||||
| 	}() | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func forwardUnixSocket(channel ssh.Channel, addr string) { | ||||
| 	conn, err := net.Dial("unix", addr) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	var wg sync.WaitGroup | ||||
| 	wg.Add(2) | ||||
| 	go func() { | ||||
| 		io.Copy(conn, channel) | ||||
| 		conn.(*net.UnixConn).CloseWrite() | ||||
| 		wg.Done() | ||||
| 	}() | ||||
| 	go func() { | ||||
| 		io.Copy(channel, conn) | ||||
| 		channel.CloseWrite() | ||||
| 		wg.Done() | ||||
| 	}() | ||||
|  | ||||
| 	wg.Wait() | ||||
| 	conn.Close() | ||||
| 	channel.Close() | ||||
| } | ||||
							
								
								
									
										241
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								vendor/golang.org/x/crypto/ssh/agent/keyring.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| // Copyright 2014 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 ( | ||||
| 	"bytes" | ||||
| 	"crypto/rand" | ||||
| 	"crypto/subtle" | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"sync" | ||||
| 	"time" | ||||
|  | ||||
| 	"golang.org/x/crypto/ssh" | ||||
| ) | ||||
|  | ||||
| type privKey struct { | ||||
| 	signer  ssh.Signer | ||||
| 	comment string | ||||
| 	expire  *time.Time | ||||
| } | ||||
|  | ||||
| type keyring struct { | ||||
| 	mu   sync.Mutex | ||||
| 	keys []privKey | ||||
|  | ||||
| 	locked     bool | ||||
| 	passphrase []byte | ||||
| } | ||||
|  | ||||
| var errLocked = errors.New("agent: locked") | ||||
|  | ||||
| // NewKeyring returns an Agent that holds keys in memory.  It is safe | ||||
| // for concurrent use by multiple goroutines. | ||||
| func NewKeyring() Agent { | ||||
| 	return &keyring{} | ||||
| } | ||||
|  | ||||
| // RemoveAll removes all identities. | ||||
| func (r *keyring) RemoveAll() error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.keys = nil | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // removeLocked does the actual key removal. The caller must already be holding the | ||||
| // keyring mutex. | ||||
| func (r *keyring) removeLocked(want []byte) error { | ||||
| 	found := false | ||||
| 	for i := 0; i < len(r.keys); { | ||||
| 		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] | ||||
| 			continue | ||||
| 		} else { | ||||
| 			i++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if !found { | ||||
| 		return errors.New("agent: key not found") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Remove removes all identities with the given public key. | ||||
| func (r *keyring) Remove(key ssh.PublicKey) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	return r.removeLocked(key.Marshal()) | ||||
| } | ||||
|  | ||||
| // Lock locks the agent. Sign and Remove will fail, and List will return an empty list. | ||||
| func (r *keyring) Lock(passphrase []byte) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.locked = true | ||||
| 	r.passphrase = passphrase | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Unlock undoes the effect of Lock | ||||
| func (r *keyring) Unlock(passphrase []byte) error { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if !r.locked { | ||||
| 		return errors.New("agent: not locked") | ||||
| 	} | ||||
| 	if 1 != subtle.ConstantTimeCompare(passphrase, r.passphrase) { | ||||
| 		return fmt.Errorf("agent: incorrect passphrase") | ||||
| 	} | ||||
|  | ||||
| 	r.locked = false | ||||
| 	r.passphrase = nil | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // expireKeysLocked removes expired keys from the keyring. If a key was added | ||||
| // with a lifetimesecs contraint and seconds >= lifetimesecs seconds have | ||||
| // ellapsed, it is removed. The caller *must* be holding the keyring mutex. | ||||
| func (r *keyring) expireKeysLocked() { | ||||
| 	for _, k := range r.keys { | ||||
| 		if k.expire != nil && time.Now().After(*k.expire) { | ||||
| 			r.removeLocked(k.signer.PublicKey().Marshal()) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // List returns the identities known to the agent. | ||||
| func (r *keyring) List() ([]*Key, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		// section 2.7: locked agents return empty. | ||||
| 		return nil, nil | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	var ids []*Key | ||||
| 	for _, k := range r.keys { | ||||
| 		pub := k.signer.PublicKey() | ||||
| 		ids = append(ids, &Key{ | ||||
| 			Format:  pub.Type(), | ||||
| 			Blob:    pub.Marshal(), | ||||
| 			Comment: k.comment}) | ||||
| 	} | ||||
| 	return ids, nil | ||||
| } | ||||
|  | ||||
| // Insert adds a private key to the keyring. If a certificate | ||||
| // 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(key.PrivateKey) | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	if cert := key.Certificate; cert != nil { | ||||
| 		signer, err = ssh.NewCertSigner(cert, signer) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	p := privKey{ | ||||
| 		signer:  signer, | ||||
| 		comment: key.Comment, | ||||
| 	} | ||||
|  | ||||
| 	if key.LifetimeSecs > 0 { | ||||
| 		t := time.Now().Add(time.Duration(key.LifetimeSecs) * time.Second) | ||||
| 		p.expire = &t | ||||
| 	} | ||||
|  | ||||
| 	r.keys = append(r.keys, p) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Sign returns a signature for the data. | ||||
| func (r *keyring) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) { | ||||
| 	return r.SignWithFlags(key, data, 0) | ||||
| } | ||||
|  | ||||
| func (r *keyring) SignWithFlags(key ssh.PublicKey, data []byte, flags SignatureFlags) (*ssh.Signature, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return nil, errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	wanted := key.Marshal() | ||||
| 	for _, k := range r.keys { | ||||
| 		if bytes.Equal(k.signer.PublicKey().Marshal(), wanted) { | ||||
| 			if flags == 0 { | ||||
| 				return k.signer.Sign(rand.Reader, data) | ||||
| 			} else { | ||||
| 				if algorithmSigner, ok := k.signer.(ssh.AlgorithmSigner); !ok { | ||||
| 					return nil, fmt.Errorf("agent: signature does not support non-default signature algorithm: %T", k.signer) | ||||
| 				} else { | ||||
| 					var algorithm string | ||||
| 					switch flags { | ||||
| 					case SignatureFlagRsaSha256: | ||||
| 						algorithm = ssh.SigAlgoRSASHA2256 | ||||
| 					case SignatureFlagRsaSha512: | ||||
| 						algorithm = ssh.SigAlgoRSASHA2512 | ||||
| 					default: | ||||
| 						return nil, fmt.Errorf("agent: unsupported signature flags: %d", flags) | ||||
| 					} | ||||
| 					return algorithmSigner.SignWithAlgorithm(rand.Reader, data, algorithm) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil, errors.New("not found") | ||||
| } | ||||
|  | ||||
| // Signers returns signers for all the known keys. | ||||
| func (r *keyring) Signers() ([]ssh.Signer, error) { | ||||
| 	r.mu.Lock() | ||||
| 	defer r.mu.Unlock() | ||||
| 	if r.locked { | ||||
| 		return nil, errLocked | ||||
| 	} | ||||
|  | ||||
| 	r.expireKeysLocked() | ||||
| 	s := make([]ssh.Signer, 0, len(r.keys)) | ||||
| 	for _, k := range r.keys { | ||||
| 		s = append(s, k.signer) | ||||
| 	} | ||||
| 	return s, nil | ||||
| } | ||||
|  | ||||
| // The keyring does not support any extensions | ||||
| func (r *keyring) Extension(extensionType string, contents []byte) ([]byte, error) { | ||||
| 	return nil, ErrExtensionUnsupported | ||||
| } | ||||
							
								
								
									
										567
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										567
									
								
								vendor/golang.org/x/crypto/ssh/agent/server.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,567 @@ | ||||
| // Copyright 2012 The Go Authors. All rights reserved. | ||||
| // Use of this source code is governed by a BSD-style | ||||
| // license that can be found in the LICENSE file. | ||||
|  | ||||
| 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" | ||||
| ) | ||||
|  | ||||
| // Server wraps an Agent and uses it to implement the agent side of | ||||
| // the SSH-agent, wire protocol. | ||||
| type server struct { | ||||
| 	agent Agent | ||||
| } | ||||
|  | ||||
| func (s *server) processRequestBytes(reqData []byte) []byte { | ||||
| 	rep, err := s.processRequest(reqData) | ||||
| 	if err != nil { | ||||
| 		if err != errLocked { | ||||
| 			// TODO(hanwen): provide better logging interface? | ||||
| 			log.Printf("agent %d: %v", reqData[0], err) | ||||
| 		} | ||||
| 		return []byte{agentFailure} | ||||
| 	} | ||||
|  | ||||
| 	if err == nil && rep == nil { | ||||
| 		return []byte{agentSuccess} | ||||
| 	} | ||||
|  | ||||
| 	return ssh.Marshal(rep) | ||||
| } | ||||
|  | ||||
| func marshalKey(k *Key) []byte { | ||||
| 	var record struct { | ||||
| 		Blob    []byte | ||||
| 		Comment string | ||||
| 	} | ||||
| 	record.Blob = k.Marshal() | ||||
| 	record.Comment = k.Comment | ||||
|  | ||||
| 	return ssh.Marshal(&record) | ||||
| } | ||||
|  | ||||
| // See [PROTOCOL.agent], section 2.5.1. | ||||
| const agentV1IdentitiesAnswer = 2 | ||||
|  | ||||
| type agentV1IdentityMsg struct { | ||||
| 	Numkeys uint32 `sshtype:"2"` | ||||
| } | ||||
|  | ||||
| type agentRemoveIdentityMsg struct { | ||||
| 	KeyBlob []byte `sshtype:"18"` | ||||
| } | ||||
|  | ||||
| type agentLockMsg struct { | ||||
| 	Passphrase []byte `sshtype:"22"` | ||||
| } | ||||
|  | ||||
| type agentUnlockMsg struct { | ||||
| 	Passphrase []byte `sshtype:"23"` | ||||
| } | ||||
|  | ||||
| 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 { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		var wk wireKey | ||||
| 		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return nil, s.agent.Remove(&Key{Format: wk.Format, Blob: req.KeyBlob}) | ||||
|  | ||||
| 	case agentRemoveAllIdentities: | ||||
| 		return nil, s.agent.RemoveAll() | ||||
|  | ||||
| 	case agentLock: | ||||
| 		var req agentLockMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		return nil, s.agent.Lock(req.Passphrase) | ||||
|  | ||||
| 	case agentUnlock: | ||||
| 		var req agentUnlockMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return nil, s.agent.Unlock(req.Passphrase) | ||||
|  | ||||
| 	case agentSignRequest: | ||||
| 		var req signRequestAgentMsg | ||||
| 		if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		var wk wireKey | ||||
| 		if err := ssh.Unmarshal(req.KeyBlob, &wk); err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		k := &Key{ | ||||
| 			Format: wk.Format, | ||||
| 			Blob:   req.KeyBlob, | ||||
| 		} | ||||
|  | ||||
| 		var sig *ssh.Signature | ||||
| 		var err error | ||||
| 		if extendedAgent, ok := s.agent.(ExtendedAgent); ok { | ||||
| 			sig, err = extendedAgent.SignWithFlags(k, req.Data, SignatureFlags(req.Flags)) | ||||
| 		} else { | ||||
| 			sig, err = s.agent.Sign(k, req.Data) | ||||
| 		} | ||||
|  | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
| 		return &signResponseAgentMsg{SigBlob: ssh.Marshal(sig)}, nil | ||||
|  | ||||
| 	case agentRequestIdentities: | ||||
| 		keys, err := s.agent.List() | ||||
| 		if err != nil { | ||||
| 			return nil, err | ||||
| 		} | ||||
|  | ||||
| 		rep := identitiesAnswerAgentMsg{ | ||||
| 			NumKeys: uint32(len(keys)), | ||||
| 		} | ||||
| 		for _, k := range keys { | ||||
| 			rep.Keys = append(rep.Keys, marshalKey(k)...) | ||||
| 		} | ||||
| 		return rep, nil | ||||
|  | ||||
| 	case agentAddIDConstrained, agentAddIdentity: | ||||
| 		return nil, s.insertIdentity(data) | ||||
|  | ||||
| 	case agentExtension: | ||||
| 		// Return a stub object where the whole contents of the response gets marshaled. | ||||
| 		var responseStub struct { | ||||
| 			Rest []byte `ssh:"rest"` | ||||
| 		} | ||||
|  | ||||
| 		if extendedAgent, ok := s.agent.(ExtendedAgent); !ok { | ||||
| 			// If this agent doesn't implement extensions, [PROTOCOL.agent] section 4.7 | ||||
| 			// requires that we return a standard SSH_AGENT_FAILURE message. | ||||
| 			responseStub.Rest = []byte{agentFailure} | ||||
| 		} else { | ||||
| 			var req extensionAgentMsg | ||||
| 			if err := ssh.Unmarshal(data, &req); err != nil { | ||||
| 				return nil, err | ||||
| 			} | ||||
| 			res, err := extendedAgent.Extension(req.ExtensionType, req.Contents) | ||||
| 			if err != nil { | ||||
| 				// If agent extensions are unsupported, return a standard SSH_AGENT_FAILURE | ||||
| 				// message as required by [PROTOCOL.agent] section 4.7. | ||||
| 				if err == ErrExtensionUnsupported { | ||||
| 					responseStub.Rest = []byte{agentFailure} | ||||
| 				} else { | ||||
| 					// As the result of any other error processing an extension request, | ||||
| 					// [PROTOCOL.agent] section 4.7 requires that we return a | ||||
| 					// SSH_AGENT_EXTENSION_FAILURE code. | ||||
| 					responseStub.Rest = []byte{agentExtensionFailure} | ||||
| 				} | ||||
| 			} else { | ||||
| 				if len(res) == 0 { | ||||
| 					return nil, nil | ||||
| 				} | ||||
| 				responseStub.Rest = res | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		return responseStub, nil | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 		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() | ||||
|  | ||||
| 	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) { | ||||
| 	var k ed25519KeyMsg | ||||
| 	if err := ssh.Unmarshal(req, &k); err != nil { | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	priv := ed25519.PrivateKey(k.Priv) | ||||
|  | ||||
| 	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) { | ||||
| 	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, | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	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") | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	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() | ||||
|  | ||||
| 	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) { | ||||
| 	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, | ||||
| 	} | ||||
|  | ||||
| 	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) { | ||||
| 	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 | ||||
| 	} | ||||
|  | ||||
| 	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 { | ||||
| 	var record struct { | ||||
| 		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: | ||||
| 		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) | ||||
| 	} | ||||
|  | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	return s.agent.Add(*addedKey) | ||||
| } | ||||
|  | ||||
| // ServeAgent serves the agent protocol on the given connection. It | ||||
| // returns when an I/O error occurs. | ||||
| func ServeAgent(agent Agent, c io.ReadWriter) error { | ||||
| 	s := &server{agent} | ||||
|  | ||||
| 	var length [4]byte | ||||
| 	for { | ||||
| 		if _, err := io.ReadFull(c, length[:]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		l := binary.BigEndian.Uint32(length[:]) | ||||
| 		if l > maxAgentResponseBytes { | ||||
| 			// We also cap requests. | ||||
| 			return fmt.Errorf("agent: request too large: %d", l) | ||||
| 		} | ||||
|  | ||||
| 		req := make([]byte, l) | ||||
| 		if _, err := io.ReadFull(c, req); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
|  | ||||
| 		repData := s.processRequestBytes(req) | ||||
| 		if len(repData) > maxAgentResponseBytes { | ||||
| 			return fmt.Errorf("agent: reply too large: %d bytes", len(repData)) | ||||
| 		} | ||||
|  | ||||
| 		binary.BigEndian.PutUint32(length[:], uint32(len(repData))) | ||||
| 		if _, err := c.Write(length[:]); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if _, err := c.Write(repData); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Tonis Tiigi
					Tonis Tiigi