OLD | NEW |
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 Loading... |
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 } |
OLD | NEW |