Files
crypto/ssh/agent.go
T
Adam Langley 63f855d724 ssh: cosmetic cleanups
These are the cosmetic cleanups from the bits of code that I
rereviewed.

1) stringLength now takes a int; the length of the string. Too many
   callers were allocating with stringLength([]byte(s)) and
   stringLength only needs to call len().

2) agent.go now has sendAndReceive to remove logic that was
   duplicated.

3) We now reject negative DH values

4) We now reject empty packets rather than crashing.

R=dave, jonathan.mark.pittman
CC=golang-dev
https://golang.org/cl/6061052
2012-04-20 15:17:42 -04:00

242 lines
6.3 KiB
Go

// 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
// References
// PROTOCOL.agent: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.agent
import (
"encoding/base64"
"errors"
"io"
)
// 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 a *rsa.PublicKey, *dsa.PublicKey, or
// *OpenSSHCertV01.
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. It contains an
// embedded io.ReadWriter that is typically represented by using a *net.UnixConn.
type AgentClient struct {
io.ReadWriter
}
// 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) {
msg := make([]byte, stringLength(len(req)))
marshalString(msg, req)
if _, err = ac.Write(msg); err != nil {
return
}
var respSizeBuf [4]byte
if _, err = io.ReadFull(ac, 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, 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. Supported key types include
// *rsa.PublicKey, *dsa.PublicKey, *OpenSSHCertV01.
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
}