Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(1607)

Side by Side Diff: ssh/agent/agent.go

Issue 38940043: code review 38940043: gosshnew/ssh/agent: move ssh-agent support into separat... (Closed)
Patch Set: diff -r bb1e8d449b04 https://hanwen%40google.com@code.google.com/p/gosshnew/ Created 10 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | ssh/agent/agent_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 The Go Authors. All rights reserved. 1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style 2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file. 3 // license that can be found in the LICENSE file.
4 4
5 package ssh 5 /*
6 Package agent implements a client to an ssh-agent daemon.
7
8 References:
9 [PROTOCOL.agent]: http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PRO TOCOL.agent
10 */
11 package agent
6 12
7 import ( 13 import (
14 "crypto/rsa"
8 "encoding/base64" 15 "encoding/base64"
16 "encoding/binary"
9 "errors" 17 "errors"
10 "fmt" 18 "fmt"
11 "io" 19 "io"
12 "math/big" 20 "math/big"
13 "sync" 21 "sync"
22
23 "code.google.com/p/gosshnew/ssh"
14 ) 24 )
15 25
16 // See [PROTOCOL.agent], section 3. 26 // See [PROTOCOL.agent], section 3.
17 const ( 27 const (
18 // 3.2 Requests from client to agent for protocol 2 key operations 28 // 3.2 Requests from client to agent for protocol 2 key operations
19 agentRemoveIdentity = 18 29 agentRemoveIdentity = 18
20 agentRemoveAllIdentities = 19 30 agentRemoveAllIdentities = 19
21 agentAddIdConstrained = 25 31 agentAddIdConstrained = 25
22 32
23 // 3.3 Key-type independent requests from client to agent 33 // 3.3 Key-type independent requests from client to agent
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
83 // AgentKey represents a protocol 2 key as defined in [PROTOCOL.agent], 93 // AgentKey represents a protocol 2 key as defined in [PROTOCOL.agent],
84 // section 2.5.2. 94 // section 2.5.2.
85 type AgentKey struct { 95 type AgentKey struct {
86 blob []byte 96 blob []byte
87 Comment string 97 Comment string
88 } 98 }
89 99
90 // String returns the storage form of an agent key with the format, base64 100 // String returns the storage form of an agent key with the format, base64
91 // encoded serialized key, and the comment if it is not empty. 101 // encoded serialized key, and the comment if it is not empty.
92 func (ak *AgentKey) String() string { 102 func (ak *AgentKey) String() string {
93 » algo, _, ok := parseString(ak.blob) 103 » k, _, ok := ssh.ParsePublicKey(ak.blob)
94 if !ok { 104 if !ok {
95 return "ssh: malformed key" 105 return "ssh: malformed key"
96 } 106 }
97 107
98 » s := string(algo) + " " + base64.StdEncoding.EncodeToString(ak.blob) 108 » s := string(k.PublicKeyAlgo()) + " " + base64.StdEncoding.EncodeToString (ak.blob)
99 109
100 if ak.Comment != "" { 110 if ak.Comment != "" {
101 s += " " + ak.Comment 111 s += " " + ak.Comment
102 } 112 }
103 113
104 return s 114 return s
105 } 115 }
106 116
107 // Key returns an agent's public key as one of the supported key or certificate types. 117 // Key returns an agent's public key as one of the supported key or certificate types.
108 func (ak *AgentKey) Key() (PublicKey, error) { 118 func (ak *AgentKey) Key() (ssh.PublicKey, error) {
109 » if key, _, ok := ParsePublicKey(ak.blob); ok { 119 » if key, _, ok := ssh.ParsePublicKey(ak.blob); ok {
110 return key, nil 120 return key, nil
111 } 121 }
112 return nil, errors.New("ssh: failed to parse key blob") 122 return nil, errors.New("ssh: failed to parse key blob")
113 } 123 }
114 124
115 func parseAgentKey(in []byte) (out *AgentKey, rest []byte, ok bool) { 125 func parseAgentKey(in []byte) (out *AgentKey, rest []byte, err error) {
116 » ak := new(AgentKey) 126 » type parseHelper struct {
117 127 » » Blob []byte
118 » if ak.blob, in, ok = parseString(in); !ok { 128 » » Comment string
119 » » return 129 » » Rest []byte `ssh:"rest"`
120 } 130 }
121 131
122 » comment, in, ok := parseString(in) 132 » ak := new(parseHelper)
123 » if !ok { 133 » if err := ssh.Unmarshal(in, ak); err != nil {
124 » » return 134 » » return nil, nil, err
125 } 135 }
126 ak.Comment = string(comment)
127 136
128 » return ak, in, true 137 » return &AgentKey{ak.Blob, ak.Comment}, ak.Rest, nil
129 } 138 }
130 139
131 // AgentClient provides a means to communicate with an ssh agent process based 140 // AgentClient is a client for an ssh-agent process.
132 // on the protocol described in [PROTOCOL.agent]?rev=1.6.
133 type AgentClient struct { 141 type AgentClient struct {
134 // conn is typically represented by using a *net.UnixConn 142 // conn is typically represented by using a *net.UnixConn
135 » conn io.ReadWriter 143 » conn io.ReadWriteCloser
136 // mu is used to prevent concurrent access to the agent 144 // mu is used to prevent concurrent access to the agent
137 mu sync.Mutex 145 mu sync.Mutex
138 } 146 }
139 147
140 // NewAgentClient creates and returns a new *AgentClient using the 148 // NewAgentClient creates and returns a new *AgentClient using the
141 // passed in io.ReadWriter as a connection to a ssh agent. 149 // passed in io.ReadWriter as a connection to a ssh agent.
142 func NewAgentClient(rw io.ReadWriter) *AgentClient { 150 func NewAgentClient(rwc io.ReadWriteCloser) *AgentClient {
143 » return &AgentClient{conn: rw} 151 » return &AgentClient{conn: rwc}
144 } 152 }
145 153
146 // sendAndReceive sends req to the agent and waits for a reply. On success, 154 func (c *AgentClient) Close() error {
147 // the reply is unmarshaled into reply and replyType is set to the first byte of 155 » // Not really needed for network connections, but oh well.
156 » c.mu.Lock()
157 » defer c.mu.Unlock()
158 » return c.conn.Close()
159 }
160
161 // call sends an RPC to the agent. On success, the reply is
162 // unmarshaled into reply and replyType is set to the first byte of
148 // the reply, which contains the type of the message. 163 // the reply, which contains the type of the message.
149 func (ac *AgentClient) sendAndReceive(req []byte) (reply interface{}, err error) { 164 func (ac *AgentClient) call(req []byte) (reply interface{}, err error) {
150 » // ac.mu prevents multiple, concurrent requests. Since the agent is typi cally
151 » // on the same machine, we don't attempt to pipeline the requests.
152 ac.mu.Lock() 165 ac.mu.Lock()
153 defer ac.mu.Unlock() 166 defer ac.mu.Unlock()
154 167
155 » msg := make([]byte, stringLength(len(req))) 168 » msg := make([]byte, 4+len(req))
156 » marshalString(msg, req) 169 » binary.BigEndian.PutUint32(msg, uint32(len(req)))
170 » copy(msg[4:], req)
157 if _, err = ac.conn.Write(msg); err != nil { 171 if _, err = ac.conn.Write(msg); err != nil {
158 return 172 return
159 } 173 }
160 174
161 var respSizeBuf [4]byte 175 var respSizeBuf [4]byte
162 if _, err = io.ReadFull(ac.conn, respSizeBuf[:]); err != nil { 176 if _, err = io.ReadFull(ac.conn, respSizeBuf[:]); err != nil {
163 return 177 return
164 } 178 }
165 » respSize, _, _ := parseUint32(respSizeBuf[:]) 179 » respSize := binary.BigEndian.Uint32(respSizeBuf[:])
166
167 if respSize > maxAgentResponseBytes { 180 if respSize > maxAgentResponseBytes {
168 err = errors.New("ssh: agent reply too large") 181 err = errors.New("ssh: agent reply too large")
169 return 182 return
170 } 183 }
171 184
172 buf := make([]byte, respSize) 185 buf := make([]byte, respSize)
173 if _, err = io.ReadFull(ac.conn, buf); err != nil { 186 if _, err = io.ReadFull(ac.conn, buf); err != nil {
174 return 187 return
175 } 188 }
176 » return unmarshalAgentMsg(buf) 189 » return unmarshal(buf)
177 } 190 }
178 191
179 // RequestIdentities queries the agent for protocol 2 keys as defined in 192 // List returns the identities known to the agent.
180 // [PROTOCOL.agent] section 2.5.2. 193 func (ac *AgentClient) List() ([]*AgentKey, error) {
181 func (ac *AgentClient) RequestIdentities() ([]*AgentKey, error) { 194 » // see [PROTOCOL.agent] section 2.5.2.
182 req := []byte{agentRequestIdentities} 195 req := []byte{agentRequestIdentities}
183 196
184 » msg, err := ac.sendAndReceive(req) 197 » msg, err := ac.call(req)
185 if err != nil { 198 if err != nil {
186 return nil, err 199 return nil, err
187 } 200 }
188 201
189 switch msg := msg.(type) { 202 switch msg := msg.(type) {
190 case *identitiesAnswerAgentMsg: 203 case *identitiesAnswerAgentMsg:
191 if msg.NumKeys > maxAgentResponseBytes/8 { 204 if msg.NumKeys > maxAgentResponseBytes/8 {
192 return nil, errors.New("ssh: too many keys in agent repl y") 205 return nil, errors.New("ssh: too many keys in agent repl y")
193 } 206 }
194 keys := make([]*AgentKey, msg.NumKeys) 207 keys := make([]*AgentKey, msg.NumKeys)
195 data := msg.Keys 208 data := msg.Keys
196 for i := uint32(0); i < msg.NumKeys; i++ { 209 for i := uint32(0); i < msg.NumKeys; i++ {
197 var key *AgentKey 210 var key *AgentKey
198 » » » var ok bool 211 » » » var err error
199 » » » if key, data, ok = parseAgentKey(data); !ok { 212 » » » if key, data, err = parseAgentKey(data); err != nil {
200 » » » » return nil, ParseError{agentIdentitiesAnswer} 213 » » » » return nil, err
201 } 214 }
202 keys[i] = key 215 keys[i] = key
203 } 216 }
204 return keys, nil 217 return keys, nil
205 case *failureAgentMsg: 218 case *failureAgentMsg:
206 return nil, errors.New("ssh: failed to list keys") 219 return nil, errors.New("ssh: failed to list keys")
207 } 220 }
208 panic("unreachable") 221 panic("unreachable")
209 } 222 }
210 223
211 // SignRequest requests the signing of data by the agent using a protocol 2 key 224 // Sign has the agent sign the data using a protocol 2 key as defined
212 // as defined in [PROTOCOL.agent] section 2.6.2. 225 // in [PROTOCOL.agent] section 2.6.2.
213 func (ac *AgentClient) SignRequest(key PublicKey, data []byte) ([]byte, error) { 226 func (ac *AgentClient) Sign(key ssh.PublicKey, data []byte) ([]byte, error) {
214 » req := Marshal(signRequestAgentMsg{ 227 » req := ssh.Marshal(signRequestAgentMsg{
215 » » KeyBlob: MarshalPublicKey(key), 228 » » KeyBlob: ssh.MarshalPublicKey(key),
216 Data: data, 229 Data: data,
217 }) 230 })
218 231
219 » msg, err := ac.sendAndReceive(req) 232 » msg, err := ac.call(req)
220 if err != nil { 233 if err != nil {
221 return nil, err 234 return nil, err
222 } 235 }
223 236
224 switch msg := msg.(type) { 237 switch msg := msg.(type) {
225 case *signResponseAgentMsg: 238 case *signResponseAgentMsg:
226 return msg.SigBlob, nil 239 return msg.SigBlob, nil
227 case *failureAgentMsg: 240 case *failureAgentMsg:
228 return nil, errors.New("ssh: failed to sign challenge") 241 return nil, errors.New("ssh: failed to sign challenge")
229 } 242 }
230 panic("unreachable") 243 panic("unreachable")
231 } 244 }
232 245
233 // unmarshalAgentMsg parses an agent message in packet, returning the parsed 246 // unmarshal parses an agent message in packet, returning the parsed
234 // form and the message type of packet. 247 // form and the message type of packet.
235 func unmarshalAgentMsg(packet []byte) (interface{}, error) { 248 func unmarshal(packet []byte) (interface{}, error) {
236 if len(packet) < 1 { 249 if len(packet) < 1 {
237 » » return nil, ParseError{0} 250 » » return nil, ssh.ParseError{0}
238 } 251 }
239 var msg interface{} 252 var msg interface{}
240 switch packet[0] { 253 switch packet[0] {
241 case agentFailure: 254 case agentFailure:
242 return new(failureAgentMsg), nil 255 return new(failureAgentMsg), nil
243 case agentSuccess: 256 case agentSuccess:
244 return new(successAgentMsg), nil 257 return new(successAgentMsg), nil
245 case agentIdentitiesAnswer: 258 case agentIdentitiesAnswer:
246 msg = new(identitiesAnswerAgentMsg) 259 msg = new(identitiesAnswerAgentMsg)
247 case agentSignResponse: 260 case agentSignResponse:
248 msg = new(signResponseAgentMsg) 261 msg = new(signResponseAgentMsg)
249 default: 262 default:
250 » » return nil, UnexpectedMessageError{0, packet[0]} 263 » » return nil, ssh.UnexpectedMessageError{0, packet[0]}
251 } 264 }
252 » if err := Unmarshal(packet, msg); err != nil { 265 » if err := ssh.Unmarshal(packet, msg); err != nil {
253 return nil, err 266 return nil, err
254 } 267 }
255 return msg, nil 268 return msg, nil
256 } 269 }
257 270
258 const agentAddIdentity = 17 271 const agentAddIdentity = 17
259 272
260 type rsaKeyMsg struct { 273 type rsaKeyMsg struct {
261 Type string `sshtype:"17"` 274 Type string `sshtype:"17"`
262 N *big.Int 275 N *big.Int
263 E *big.Int 276 E *big.Int
264 D *big.Int 277 D *big.Int
265 Iqmp *big.Int // IQMP = Inverse Q Mod P 278 Iqmp *big.Int // IQMP = Inverse Q Mod P
266 P *big.Int 279 P *big.Int
267 Q *big.Int 280 Q *big.Int
268 Comments string 281 Comments string
269 } 282 }
270 283
271 func (ac *AgentClient) insert(s Signer, comment string) error { 284 // Insert adds a private key to the agent. Currently, only
285 // *rsa.PrivateKey is supported.
286 func (ac *AgentClient) Insert(s interface{}, comment string) error {
272 switch k := s.(type) { 287 switch k := s.(type) {
273 » case *rsaPrivateKey: 288 » case *rsa.PrivateKey:
274 » » req := Marshal(rsaKeyMsg{ 289 » » req := ssh.Marshal(rsaKeyMsg{
275 » » » Type: KeyAlgoRSA, 290 » » » Type: ssh.KeyAlgoRSA,
276 N: k.N, 291 N: k.N,
277 E: big.NewInt(int64(k.E)), 292 E: big.NewInt(int64(k.E)),
278 D: k.D, 293 D: k.D,
279 Iqmp: k.Precomputed.Qinv, 294 Iqmp: k.Precomputed.Qinv,
280 P: k.Primes[0], 295 P: k.Primes[0],
281 Q: k.Primes[1], 296 Q: k.Primes[1],
282 Comments: comment, 297 Comments: comment,
283 }) 298 })
284 » » resp, err := ac.sendAndReceive(req) 299 » » resp, err := ac.call(req)
285 if err != nil { 300 if err != nil {
286 return err 301 return err
287 } 302 }
288 if _, ok := resp.(*successAgentMsg); ok { 303 if _, ok := resp.(*successAgentMsg); ok {
289 return nil 304 return nil
290 } 305 }
291 return errors.New("ssh: failure") 306 return errors.New("ssh: failure")
292 } 307 }
293 return fmt.Errorf("ssh: unsupported key type %T", s) 308 return fmt.Errorf("ssh: unsupported key type %T", s)
294 } 309 }
310
311 type agentKeyringSigner struct {
312 agent *AgentClient
313 pub ssh.PublicKey
314 }
315
316 func (s *agentKeyringSigner) PublicKey() ssh.PublicKey {
317 return s.pub
318 }
319
320 func (s *agentKeyringSigner) Sign(rand io.Reader, data []byte) ([]byte, error) {
321 // The agent has its own entropy source, so the rand argument is ignored .
322 encoded, err := s.agent.Sign(s.pub, data)
323 if err != nil {
324 return nil, err
325 }
326 // TODO(hanwen): this should move into Sign?
327 var sig struct {
328 Algo string
329 Blob []byte
330 }
331 if err := ssh.Unmarshal(encoded, &sig); err != nil {
332 return nil, err
333 }
334
335 return sig.Blob, nil
336 }
337
338 // Signers implements the ssh.ClientKeyring interface.
339 func (c *AgentClient) Signers() ([]ssh.Signer, error) {
340 keys, err := c.List()
341 if err != nil {
342 return nil, err
343 }
344
345 var result []ssh.Signer
346 for _, k := range keys {
347 pub, err := k.Key()
348 if err != nil {
349 // TODO(hanwen): revise this? should never make it into an AgentKey?
350 continue
351 }
352 result = append(result, &agentKeyringSigner{c, pub})
353 }
354 return result, nil
355 }
OLDNEW
« no previous file with comments | « no previous file | ssh/agent/agent_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b