github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/crypto/ssh/agent/client.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6    Package agent implements a client to an ssh-agent daemon.
     7  
     8  References:
     9    [PROTOCOL.agent]:    http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent?rev=HEAD
    10  */
    11  package agent // import "golang.org/x/crypto/ssh/agent"
    12  
    13  import (
    14  	"bytes"
    15  	"crypto/dsa"
    16  	"crypto/ecdsa"
    17  	"crypto/elliptic"
    18  	"crypto/rsa"
    19  	"encoding/base64"
    20  	"encoding/binary"
    21  	"errors"
    22  	"fmt"
    23  	"io"
    24  	"math/big"
    25  	"sync"
    26  
    27  	"golang.org/x/crypto/ssh"
    28  )
    29  
    30  // Agent represents the capabilities of an ssh-agent.
    31  type Agent interface {
    32  	// List returns the identities known to the agent.
    33  	List() ([]*Key, error)
    34  
    35  	// Sign has the agent sign the data using a protocol 2 key as defined
    36  	// in [PROTOCOL.agent] section 2.6.2.
    37  	Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error)
    38  
    39  	// Add adds a private key to the agent.
    40  	Add(key AddedKey) error
    41  
    42  	// Remove removes all identities with the given public key.
    43  	Remove(key ssh.PublicKey) error
    44  
    45  	// RemoveAll removes all identities.
    46  	RemoveAll() error
    47  
    48  	// Lock locks the agent. Sign and Remove will fail, and List will empty an empty list.
    49  	Lock(passphrase []byte) error
    50  
    51  	// Unlock undoes the effect of Lock
    52  	Unlock(passphrase []byte) error
    53  
    54  	// Signers returns signers for all the known keys.
    55  	Signers() ([]ssh.Signer, error)
    56  }
    57  
    58  // AddedKey describes an SSH key to be added to an Agent.
    59  type AddedKey struct {
    60  	// PrivateKey must be a *rsa.PrivateKey, *dsa.PrivateKey or
    61  	// *ecdsa.PrivateKey, which will be inserted into the agent.
    62  	PrivateKey interface{}
    63  	// Certificate, if not nil, is communicated to the agent and will be
    64  	// stored with the key.
    65  	Certificate *ssh.Certificate
    66  	// Comment is an optional, free-form string.
    67  	Comment string
    68  	// LifetimeSecs, if not zero, is the number of seconds that the
    69  	// agent will store the key for.
    70  	LifetimeSecs uint32
    71  	// ConfirmBeforeUse, if true, requests that the agent confirm with the
    72  	// user before each use of this key.
    73  	ConfirmBeforeUse bool
    74  }
    75  
    76  // See [PROTOCOL.agent], section 3.
    77  const (
    78  	agentRequestV1Identities = 1
    79  
    80  	// 3.2 Requests from client to agent for protocol 2 key operations
    81  	agentAddIdentity         = 17
    82  	agentRemoveIdentity      = 18
    83  	agentRemoveAllIdentities = 19
    84  	agentAddIdConstrained    = 25
    85  
    86  	// 3.3 Key-type independent requests from client to agent
    87  	agentAddSmartcardKey            = 20
    88  	agentRemoveSmartcardKey         = 21
    89  	agentLock                       = 22
    90  	agentUnlock                     = 23
    91  	agentAddSmartcardKeyConstrained = 26
    92  
    93  	// 3.7 Key constraint identifiers
    94  	agentConstrainLifetime = 1
    95  	agentConstrainConfirm  = 2
    96  )
    97  
    98  // maxAgentResponseBytes is the maximum agent reply size that is accepted. This
    99  // is a sanity check, not a limit in the spec.
   100  const maxAgentResponseBytes = 16 << 20
   101  
   102  // Agent messages:
   103  // These structures mirror the wire format of the corresponding ssh agent
   104  // messages found in [PROTOCOL.agent].
   105  
   106  // 3.4 Generic replies from agent to client
   107  const agentFailure = 5
   108  
   109  type failureAgentMsg struct{}
   110  
   111  const agentSuccess = 6
   112  
   113  type successAgentMsg struct{}
   114  
   115  // See [PROTOCOL.agent], section 2.5.2.
   116  const agentRequestIdentities = 11
   117  
   118  type requestIdentitiesAgentMsg struct{}
   119  
   120  // See [PROTOCOL.agent], section 2.5.2.
   121  const agentIdentitiesAnswer = 12
   122  
   123  type identitiesAnswerAgentMsg struct {
   124  	NumKeys uint32 `sshtype:"12"`
   125  	Keys    []byte `ssh:"rest"`
   126  }
   127  
   128  // See [PROTOCOL.agent], section 2.6.2.
   129  const agentSignRequest = 13
   130  
   131  type signRequestAgentMsg struct {
   132  	KeyBlob []byte `sshtype:"13"`
   133  	Data    []byte
   134  	Flags   uint32
   135  }
   136  
   137  // See [PROTOCOL.agent], section 2.6.2.
   138  
   139  // 3.6 Replies from agent to client for protocol 2 key operations
   140  const agentSignResponse = 14
   141  
   142  type signResponseAgentMsg struct {
   143  	SigBlob []byte `sshtype:"14"`
   144  }
   145  
   146  type publicKey struct {
   147  	Format string
   148  	Rest   []byte `ssh:"rest"`
   149  }
   150  
   151  // Key represents a protocol 2 public key as defined in
   152  // [PROTOCOL.agent], section 2.5.2.
   153  type Key struct {
   154  	Format  string
   155  	Blob    []byte
   156  	Comment string
   157  }
   158  
   159  func clientErr(err error) error {
   160  	return fmt.Errorf("agent: client error: %v", err)
   161  }
   162  
   163  // String returns the storage form of an agent key with the format, base64
   164  // encoded serialized key, and the comment if it is not empty.
   165  func (k *Key) String() string {
   166  	s := string(k.Format) + " " + base64.StdEncoding.EncodeToString(k.Blob)
   167  
   168  	if k.Comment != "" {
   169  		s += " " + k.Comment
   170  	}
   171  
   172  	return s
   173  }
   174  
   175  // Type returns the public key type.
   176  func (k *Key) Type() string {
   177  	return k.Format
   178  }
   179  
   180  // Marshal returns key blob to satisfy the ssh.PublicKey interface.
   181  func (k *Key) Marshal() []byte {
   182  	return k.Blob
   183  }
   184  
   185  // Verify satisfies the ssh.PublicKey interface, but is not
   186  // implemented for agent keys.
   187  func (k *Key) Verify(data []byte, sig *ssh.Signature) error {
   188  	return errors.New("agent: agent key does not know how to verify")
   189  }
   190  
   191  type wireKey struct {
   192  	Format string
   193  	Rest   []byte `ssh:"rest"`
   194  }
   195  
   196  func parseKey(in []byte) (out *Key, rest []byte, err error) {
   197  	var record struct {
   198  		Blob    []byte
   199  		Comment string
   200  		Rest    []byte `ssh:"rest"`
   201  	}
   202  
   203  	if err := ssh.Unmarshal(in, &record); err != nil {
   204  		return nil, nil, err
   205  	}
   206  
   207  	var wk wireKey
   208  	if err := ssh.Unmarshal(record.Blob, &wk); err != nil {
   209  		return nil, nil, err
   210  	}
   211  
   212  	return &Key{
   213  		Format:  wk.Format,
   214  		Blob:    record.Blob,
   215  		Comment: record.Comment,
   216  	}, record.Rest, nil
   217  }
   218  
   219  // client is a client for an ssh-agent process.
   220  type client struct {
   221  	// conn is typically a *net.UnixConn
   222  	conn io.ReadWriter
   223  	// mu is used to prevent concurrent access to the agent
   224  	mu sync.Mutex
   225  }
   226  
   227  // NewClient returns an Agent that talks to an ssh-agent process over
   228  // the given connection.
   229  func NewClient(rw io.ReadWriter) Agent {
   230  	return &client{conn: rw}
   231  }
   232  
   233  // call sends an RPC to the agent. On success, the reply is
   234  // unmarshaled into reply and replyType is set to the first byte of
   235  // the reply, which contains the type of the message.
   236  func (c *client) call(req []byte) (reply interface{}, err error) {
   237  	c.mu.Lock()
   238  	defer c.mu.Unlock()
   239  
   240  	msg := make([]byte, 4+len(req))
   241  	binary.BigEndian.PutUint32(msg, uint32(len(req)))
   242  	copy(msg[4:], req)
   243  	if _, err = c.conn.Write(msg); err != nil {
   244  		return nil, clientErr(err)
   245  	}
   246  
   247  	var respSizeBuf [4]byte
   248  	if _, err = io.ReadFull(c.conn, respSizeBuf[:]); err != nil {
   249  		return nil, clientErr(err)
   250  	}
   251  	respSize := binary.BigEndian.Uint32(respSizeBuf[:])
   252  	if respSize > maxAgentResponseBytes {
   253  		return nil, clientErr(err)
   254  	}
   255  
   256  	buf := make([]byte, respSize)
   257  	if _, err = io.ReadFull(c.conn, buf); err != nil {
   258  		return nil, clientErr(err)
   259  	}
   260  	reply, err = unmarshal(buf)
   261  	if err != nil {
   262  		return nil, clientErr(err)
   263  	}
   264  	return reply, err
   265  }
   266  
   267  func (c *client) simpleCall(req []byte) error {
   268  	resp, err := c.call(req)
   269  	if err != nil {
   270  		return err
   271  	}
   272  	if _, ok := resp.(*successAgentMsg); ok {
   273  		return nil
   274  	}
   275  	return errors.New("agent: failure")
   276  }
   277  
   278  func (c *client) RemoveAll() error {
   279  	return c.simpleCall([]byte{agentRemoveAllIdentities})
   280  }
   281  
   282  func (c *client) Remove(key ssh.PublicKey) error {
   283  	req := ssh.Marshal(&agentRemoveIdentityMsg{
   284  		KeyBlob: key.Marshal(),
   285  	})
   286  	return c.simpleCall(req)
   287  }
   288  
   289  func (c *client) Lock(passphrase []byte) error {
   290  	req := ssh.Marshal(&agentLockMsg{
   291  		Passphrase: passphrase,
   292  	})
   293  	return c.simpleCall(req)
   294  }
   295  
   296  func (c *client) Unlock(passphrase []byte) error {
   297  	req := ssh.Marshal(&agentUnlockMsg{
   298  		Passphrase: passphrase,
   299  	})
   300  	return c.simpleCall(req)
   301  }
   302  
   303  // List returns the identities known to the agent.
   304  func (c *client) List() ([]*Key, error) {
   305  	// see [PROTOCOL.agent] section 2.5.2.
   306  	req := []byte{agentRequestIdentities}
   307  
   308  	msg, err := c.call(req)
   309  	if err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	switch msg := msg.(type) {
   314  	case *identitiesAnswerAgentMsg:
   315  		if msg.NumKeys > maxAgentResponseBytes/8 {
   316  			return nil, errors.New("agent: too many keys in agent reply")
   317  		}
   318  		keys := make([]*Key, msg.NumKeys)
   319  		data := msg.Keys
   320  		for i := uint32(0); i < msg.NumKeys; i++ {
   321  			var key *Key
   322  			var err error
   323  			if key, data, err = parseKey(data); err != nil {
   324  				return nil, err
   325  			}
   326  			keys[i] = key
   327  		}
   328  		return keys, nil
   329  	case *failureAgentMsg:
   330  		return nil, errors.New("agent: failed to list keys")
   331  	}
   332  	panic("unreachable")
   333  }
   334  
   335  // Sign has the agent sign the data using a protocol 2 key as defined
   336  // in [PROTOCOL.agent] section 2.6.2.
   337  func (c *client) Sign(key ssh.PublicKey, data []byte) (*ssh.Signature, error) {
   338  	req := ssh.Marshal(signRequestAgentMsg{
   339  		KeyBlob: key.Marshal(),
   340  		Data:    data,
   341  	})
   342  
   343  	msg, err := c.call(req)
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  
   348  	switch msg := msg.(type) {
   349  	case *signResponseAgentMsg:
   350  		var sig ssh.Signature
   351  		if err := ssh.Unmarshal(msg.SigBlob, &sig); err != nil {
   352  			return nil, err
   353  		}
   354  
   355  		return &sig, nil
   356  	case *failureAgentMsg:
   357  		return nil, errors.New("agent: failed to sign challenge")
   358  	}
   359  	panic("unreachable")
   360  }
   361  
   362  // unmarshal parses an agent message in packet, returning the parsed
   363  // form and the message type of packet.
   364  func unmarshal(packet []byte) (interface{}, error) {
   365  	if len(packet) < 1 {
   366  		return nil, errors.New("agent: empty packet")
   367  	}
   368  	var msg interface{}
   369  	switch packet[0] {
   370  	case agentFailure:
   371  		return new(failureAgentMsg), nil
   372  	case agentSuccess:
   373  		return new(successAgentMsg), nil
   374  	case agentIdentitiesAnswer:
   375  		msg = new(identitiesAnswerAgentMsg)
   376  	case agentSignResponse:
   377  		msg = new(signResponseAgentMsg)
   378  	default:
   379  		return nil, fmt.Errorf("agent: unknown type tag %d", packet[0])
   380  	}
   381  	if err := ssh.Unmarshal(packet, msg); err != nil {
   382  		return nil, err
   383  	}
   384  	return msg, nil
   385  }
   386  
   387  type rsaKeyMsg struct {
   388  	Type        string `sshtype:"17"`
   389  	N           *big.Int
   390  	E           *big.Int
   391  	D           *big.Int
   392  	Iqmp        *big.Int // IQMP = Inverse Q Mod P
   393  	P           *big.Int
   394  	Q           *big.Int
   395  	Comments    string
   396  	Constraints []byte `ssh:"rest"`
   397  }
   398  
   399  type dsaKeyMsg struct {
   400  	Type        string `sshtype:"17"`
   401  	P           *big.Int
   402  	Q           *big.Int
   403  	G           *big.Int
   404  	Y           *big.Int
   405  	X           *big.Int
   406  	Comments    string
   407  	Constraints []byte `ssh:"rest"`
   408  }
   409  
   410  type ecdsaKeyMsg struct {
   411  	Type        string `sshtype:"17"`
   412  	Curve       string
   413  	KeyBytes    []byte
   414  	D           *big.Int
   415  	Comments    string
   416  	Constraints []byte `ssh:"rest"`
   417  }
   418  
   419  // Insert adds a private key to the agent.
   420  func (c *client) insertKey(s interface{}, comment string, constraints []byte) error {
   421  	var req []byte
   422  	switch k := s.(type) {
   423  	case *rsa.PrivateKey:
   424  		if len(k.Primes) != 2 {
   425  			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
   426  		}
   427  		k.Precompute()
   428  		req = ssh.Marshal(rsaKeyMsg{
   429  			Type:        ssh.KeyAlgoRSA,
   430  			N:           k.N,
   431  			E:           big.NewInt(int64(k.E)),
   432  			D:           k.D,
   433  			Iqmp:        k.Precomputed.Qinv,
   434  			P:           k.Primes[0],
   435  			Q:           k.Primes[1],
   436  			Comments:    comment,
   437  			Constraints: constraints,
   438  		})
   439  	case *dsa.PrivateKey:
   440  		req = ssh.Marshal(dsaKeyMsg{
   441  			Type:        ssh.KeyAlgoDSA,
   442  			P:           k.P,
   443  			Q:           k.Q,
   444  			G:           k.G,
   445  			Y:           k.Y,
   446  			X:           k.X,
   447  			Comments:    comment,
   448  			Constraints: constraints,
   449  		})
   450  	case *ecdsa.PrivateKey:
   451  		nistID := fmt.Sprintf("nistp%d", k.Params().BitSize)
   452  		req = ssh.Marshal(ecdsaKeyMsg{
   453  			Type:        "ecdsa-sha2-" + nistID,
   454  			Curve:       nistID,
   455  			KeyBytes:    elliptic.Marshal(k.Curve, k.X, k.Y),
   456  			D:           k.D,
   457  			Comments:    comment,
   458  			Constraints: constraints,
   459  		})
   460  	default:
   461  		return fmt.Errorf("agent: unsupported key type %T", s)
   462  	}
   463  
   464  	// if constraints are present then the message type needs to be changed.
   465  	if len(constraints) != 0 {
   466  		req[0] = agentAddIdConstrained
   467  	}
   468  
   469  	resp, err := c.call(req)
   470  	if err != nil {
   471  		return err
   472  	}
   473  	if _, ok := resp.(*successAgentMsg); ok {
   474  		return nil
   475  	}
   476  	return errors.New("agent: failure")
   477  }
   478  
   479  type rsaCertMsg struct {
   480  	Type        string `sshtype:"17"`
   481  	CertBytes   []byte
   482  	D           *big.Int
   483  	Iqmp        *big.Int // IQMP = Inverse Q Mod P
   484  	P           *big.Int
   485  	Q           *big.Int
   486  	Comments    string
   487  	Constraints []byte `ssh:"rest"`
   488  }
   489  
   490  type dsaCertMsg struct {
   491  	Type        string `sshtype:"17"`
   492  	CertBytes   []byte
   493  	X           *big.Int
   494  	Comments    string
   495  	Constraints []byte `ssh:"rest"`
   496  }
   497  
   498  type ecdsaCertMsg struct {
   499  	Type        string `sshtype:"17"`
   500  	CertBytes   []byte
   501  	D           *big.Int
   502  	Comments    string
   503  	Constraints []byte `ssh:"rest"`
   504  }
   505  
   506  // Insert adds a private key to the agent. If a certificate is given,
   507  // that certificate is added instead as public key.
   508  func (c *client) Add(key AddedKey) error {
   509  	var constraints []byte
   510  
   511  	if secs := key.LifetimeSecs; secs != 0 {
   512  		constraints = append(constraints, agentConstrainLifetime)
   513  
   514  		var secsBytes [4]byte
   515  		binary.BigEndian.PutUint32(secsBytes[:], secs)
   516  		constraints = append(constraints, secsBytes[:]...)
   517  	}
   518  
   519  	if key.ConfirmBeforeUse {
   520  		constraints = append(constraints, agentConstrainConfirm)
   521  	}
   522  
   523  	if cert := key.Certificate; cert == nil {
   524  		return c.insertKey(key.PrivateKey, key.Comment, constraints)
   525  	} else {
   526  		return c.insertCert(key.PrivateKey, cert, key.Comment, constraints)
   527  	}
   528  }
   529  
   530  func (c *client) insertCert(s interface{}, cert *ssh.Certificate, comment string, constraints []byte) error {
   531  	var req []byte
   532  	switch k := s.(type) {
   533  	case *rsa.PrivateKey:
   534  		if len(k.Primes) != 2 {
   535  			return fmt.Errorf("agent: unsupported RSA key with %d primes", len(k.Primes))
   536  		}
   537  		k.Precompute()
   538  		req = ssh.Marshal(rsaCertMsg{
   539  			Type:        cert.Type(),
   540  			CertBytes:   cert.Marshal(),
   541  			D:           k.D,
   542  			Iqmp:        k.Precomputed.Qinv,
   543  			P:           k.Primes[0],
   544  			Q:           k.Primes[1],
   545  			Comments:    comment,
   546  			Constraints: constraints,
   547  		})
   548  	case *dsa.PrivateKey:
   549  		req = ssh.Marshal(dsaCertMsg{
   550  			Type:      cert.Type(),
   551  			CertBytes: cert.Marshal(),
   552  			X:         k.X,
   553  			Comments:  comment,
   554  		})
   555  	case *ecdsa.PrivateKey:
   556  		req = ssh.Marshal(ecdsaCertMsg{
   557  			Type:      cert.Type(),
   558  			CertBytes: cert.Marshal(),
   559  			D:         k.D,
   560  			Comments:  comment,
   561  		})
   562  	default:
   563  		return fmt.Errorf("agent: unsupported key type %T", s)
   564  	}
   565  
   566  	// if constraints are present then the message type needs to be changed.
   567  	if len(constraints) != 0 {
   568  		req[0] = agentAddIdConstrained
   569  	}
   570  
   571  	signer, err := ssh.NewSignerFromKey(s)
   572  	if err != nil {
   573  		return err
   574  	}
   575  	if bytes.Compare(cert.Key.Marshal(), signer.PublicKey().Marshal()) != 0 {
   576  		return errors.New("agent: signer and cert have different public key")
   577  	}
   578  
   579  	resp, err := c.call(req)
   580  	if err != nil {
   581  		return err
   582  	}
   583  	if _, ok := resp.(*successAgentMsg); ok {
   584  		return nil
   585  	}
   586  	return errors.New("agent: failure")
   587  }
   588  
   589  // Signers provides a callback for client authentication.
   590  func (c *client) Signers() ([]ssh.Signer, error) {
   591  	keys, err := c.List()
   592  	if err != nil {
   593  		return nil, err
   594  	}
   595  
   596  	var result []ssh.Signer
   597  	for _, k := range keys {
   598  		result = append(result, &agentKeyringSigner{c, k})
   599  	}
   600  	return result, nil
   601  }
   602  
   603  type agentKeyringSigner struct {
   604  	agent *client
   605  	pub   ssh.PublicKey
   606  }
   607  
   608  func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
   609  	return s.pub
   610  }
   611  
   612  func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) (*ssh.Signature, error) {
   613  	// The agent has its own entropy source, so the rand argument is ignored.
   614  	return s.agent.Sign(s.pub, data)
   615  }