| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- // 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 ssh
- import (
- "encoding/base64"
- "errors"
- "io"
- "sync"
- )
- // See [PROTOCOL.agent], section 3.
- const (
- // 3.2 Requests from client to agent for protocol 2 key operations
- agentRequestIdentities = 11
- agentSignRequest = 13
- 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.4 Generic replies from agent to client
- agentFailure = 5
- agentSuccess = 6
- // 3.6 Replies from agent to client for protocol 2 key operations
- agentIdentitiesAnswer = 12
- agentSignResponse = 14
- // 3.7 Key constraint identifiers
- agentConstrainLifetime = 1
- agentConstrainConfirm = 2
- )
- // 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].
- type failureAgentMsg struct{}
- type successAgentMsg struct{}
- // See [PROTOCOL.agent], section 2.5.2.
- type requestIdentitiesAgentMsg struct{}
- // See [PROTOCOL.agent], section 2.5.2.
- type identitiesAnswerAgentMsg struct {
- NumKeys uint32
- Keys []byte `ssh:"rest"`
- }
- // See [PROTOCOL.agent], section 2.6.2.
- type signRequestAgentMsg struct {
- KeyBlob []byte
- Data []byte
- Flags uint32
- }
- // See [PROTOCOL.agent], section 2.6.2.
- type signResponseAgentMsg struct {
- SigBlob []byte
- }
- // AgentKey represents a protocol 2 key as defined in [PROTOCOL.agent],
- // section 2.5.2.
- type AgentKey struct {
- blob []byte
- Comment string
- }
- // 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 (ak *AgentKey) String() string {
- algo, _, ok := parseString(ak.blob)
- if !ok {
- return "ssh: malformed key"
- }
- s := string(algo) + " " + base64.StdEncoding.EncodeToString(ak.blob)
- if ak.Comment != "" {
- s += " " + ak.Comment
- }
- return s
- }
- // Key returns an agent's public key as one of the supported key or certificate types.
- func (ak *AgentKey) Key() (interface{}, error) {
- if key, _, ok := parsePubKey(ak.blob); ok {
- return key, nil
- }
- return nil, errors.New("ssh: failed to parse key blob")
- }
- func parseAgentKey(in []byte) (out *AgentKey, rest []byte, ok bool) {
- ak := new(AgentKey)
- if ak.blob, in, ok = parseString(in); !ok {
- return
- }
- comment, in, ok := parseString(in)
- if !ok {
- return
- }
- ak.Comment = string(comment)
- return ak, in, true
- }
- // AgentClient provides a means to communicate with an ssh agent process based
- // on the protocol described in [PROTOCOL.agent]?rev=1.6.
- type AgentClient struct {
- // conn is typically represented by using a *net.UnixConn
- conn io.ReadWriter
- // mu is used to prevent concurrent access to the agent
- mu sync.Mutex
- }
- // NewAgentClient creates and returns a new *AgentClient using the
- // passed in io.ReadWriter as a connection to a ssh agent.
- func NewAgentClient(rw io.ReadWriter) *AgentClient {
- return &AgentClient{conn: rw}
- }
- // sendAndReceive sends req to the agent and waits for a reply. 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 (ac *AgentClient) sendAndReceive(req []byte) (reply interface{}, replyType uint8, err error) {
- // ac.mu prevents multiple, concurrent requests. Since the agent is typically
- // on the same machine, we don't attempt to pipeline the requests.
- ac.mu.Lock()
- defer ac.mu.Unlock()
- msg := make([]byte, stringLength(len(req)))
- marshalString(msg, req)
- if _, err = ac.conn.Write(msg); err != nil {
- return
- }
- var respSizeBuf [4]byte
- if _, err = io.ReadFull(ac.conn, respSizeBuf[:]); err != nil {
- return
- }
- respSize, _, _ := parseUint32(respSizeBuf[:])
- if respSize > maxAgentResponseBytes {
- err = errors.New("ssh: agent reply too large")
- return
- }
- buf := make([]byte, respSize)
- if _, err = io.ReadFull(ac.conn, buf); err != nil {
- return
- }
- return unmarshalAgentMsg(buf)
- }
- // RequestIdentities queries the agent for protocol 2 keys as defined in
- // [PROTOCOL.agent] section 2.5.2.
- func (ac *AgentClient) RequestIdentities() ([]*AgentKey, error) {
- req := marshal(agentRequestIdentities, requestIdentitiesAgentMsg{})
- msg, msgType, err := ac.sendAndReceive(req)
- if err != nil {
- return nil, err
- }
- switch msg := msg.(type) {
- case *identitiesAnswerAgentMsg:
- if msg.NumKeys > maxAgentResponseBytes/8 {
- return nil, errors.New("ssh: too many keys in agent reply")
- }
- keys := make([]*AgentKey, msg.NumKeys)
- data := msg.Keys
- for i := uint32(0); i < msg.NumKeys; i++ {
- var key *AgentKey
- var ok bool
- if key, data, ok = parseAgentKey(data); !ok {
- return nil, ParseError{agentIdentitiesAnswer}
- }
- keys[i] = key
- }
- return keys, nil
- case *failureAgentMsg:
- return nil, errors.New("ssh: failed to list keys")
- }
- return nil, UnexpectedMessageError{agentIdentitiesAnswer, msgType}
- }
- // SignRequest requests the signing of data by the agent using a protocol 2 key
- // as defined in [PROTOCOL.agent] section 2.6.2.
- func (ac *AgentClient) SignRequest(key interface{}, data []byte) ([]byte, error) {
- req := marshal(agentSignRequest, signRequestAgentMsg{
- KeyBlob: serializePublicKey(key),
- Data: data,
- })
- msg, msgType, err := ac.sendAndReceive(req)
- if err != nil {
- return nil, err
- }
- switch msg := msg.(type) {
- case *signResponseAgentMsg:
- return msg.SigBlob, nil
- case *failureAgentMsg:
- return nil, errors.New("ssh: failed to sign challenge")
- }
- return nil, UnexpectedMessageError{agentSignResponse, msgType}
- }
- // unmarshalAgentMsg parses an agent message in packet, returning the parsed
- // form and the message type of packet.
- func unmarshalAgentMsg(packet []byte) (interface{}, uint8, error) {
- if len(packet) < 1 {
- return nil, 0, ParseError{0}
- }
- var msg interface{}
- switch packet[0] {
- case agentFailure:
- msg = new(failureAgentMsg)
- case agentSuccess:
- msg = new(successAgentMsg)
- case agentIdentitiesAnswer:
- msg = new(identitiesAnswerAgentMsg)
- case agentSignResponse:
- msg = new(signResponseAgentMsg)
- default:
- return nil, 0, UnexpectedMessageError{0, packet[0]}
- }
- if err := unmarshal(msg, packet, packet[0]); err != nil {
- return nil, 0, err
- }
- return msg, packet[0], nil
- }
|