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