monotone

monotone Mtn Source Tree

Root/ssh_agent.cc

1// Copyright (C) 2007 Justin Patrin <papercrane@reversefold.com>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <stdlib.h>
12
13#include "ssh_agent.hh"
14#include "sanity.hh"
15#include "netio.hh"
16#include "botan/numthry.h"
17#include "numeric_vocab.hh"
18#include "netxx/stream.h"
19#include "botan/rsa.h"
20#include "botan/bigint.h"
21#include <boost/shared_ptr.hpp>
22#include "platform.hh"
23#include "key_store.hh"
24
25#ifdef WIN32
26#include "win32/ssh_agent_platform.hh"
27#else
28#include "unix/ssh_agent_platform.hh"
29#endif
30
31using std::string;
32using std::vector;
33
34using boost::shared_ptr;
35using boost::shared_dynamic_cast;
36
37using Botan::RSA_PublicKey;
38using Botan::RSA_PrivateKey;
39using Botan::BigInt;
40using Botan::SecureVector;
41using Botan::X509_PublicKey;
42using Netxx::Stream;
43
44struct ssh_agent_state : ssh_agent_platform
45{
46 vector<RSA_PublicKey> keys; // cache
47
48 void read_packet(string & packet);
49 void write_packet(string const & packet);
50};
51
52/*
53 * The ssh-agent network format is essentially based on a u32 which
54 * is the length of the packet followed by that number of bytes.
55 *
56 * u32 encoding is big-endian
57 *
58 * The packet to ask for the keys that ssh-agent has is in this format:
59 * u32 = 1
60 * command = 11
61 *
62 * The response packet:
63 * u32 = length
64 * data
65 * byte = packet type (12)
66 * u32 = number of keys
67 * u32 = length of key
68 * data
69 * u32 = length of type
70 * data = string, the type of key (ssh-rsa, ssh-dss)
71 * if(rsa)
72 * u32 = length of 'e'
73 * data = binary encoded BigInt, 'e'
74 * u32 = length of 'n'
75 * data = binary encoded BigInt, 'n'
76 * if(dss)
77 * u32 = length of 'p'
78 * data = binary encoded BigInt, 'p'
79 * u32 = length of 'q'
80 * data = binary encoded BigInt, 'q'
81 * u32 = length of 'g'
82 * data = binary encoded BigInt, 'g'
83 * u32 = length of 'pub_key'
84 * data = binary encoded BigInt, 'pub_key'
85 * u32 = length of comment
86 * data = comment (path to key file)
87 * (repeat for number of keys)
88 *
89 * To ask for ssh-agent to sign data use this packet format:
90 * byte = packet type (13)
91 * u32 = length of data
92 * data
93 * u32 = length of key data
94 * key data
95 * (rsa)
96 * u32 = length of type
97 * data = type (ssh-rsa)
98 * u32 = length of 'e'
99 * data = binary encoded BigInt, 'e'
100 * u32 = length of 'n'
101 * data = binary encoded BigInt, 'n'
102 * (dss)
103 * NOT IMPLEMENTED, should be same as above
104 * u32 = length of data to sign
105 * data to sign
106 * u32 = flags (0)
107 *
108 * Response packet for signing request is:
109 * u32 = length of packet
110 * data
111 * byte = packet type (14)
112 * u32 = signature length
113 * data = signature
114 * u32 = type length
115 * data = type (ssh-rsa)
116 * u32 = signed data length
117 * data = signed data
118 */
119
120//
121// Helper functions for packing and unpacking data from the wire protocol.
122//
123
124static u32
125get_long(char const * buf)
126{
127 L((FL("ssh_agent: get_long: %u %u %u %u")
128 % widen<u32,char>(buf[0])
129 % widen<u32,char>(buf[1])
130 % widen<u32,char>(buf[2])
131 % widen<u32,char>(buf[3])));
132 return ((widen<u32,char>(buf[0]) << 24)
133 | (widen<u32,char>(buf[1]) << 16)
134 | (widen<u32,char>(buf[2]) << 8)
135 | widen<u32,char>(buf[3]));
136}
137
138static u32
139get_long_from_buf(string const & buf, u32 & loc)
140{
141 E(buf.length() >= loc + 4,
142 F("string not long enough to get a long"));
143 u32 ret = get_long(buf.data() + loc);
144 loc += 4;
145 return ret;
146}
147
148static void
149get_string_from_buf(string const & buf,
150 u32 & loc,
151 u32 & len,
152 string & out)
153{
154 L(FL("ssh_agent: get_string_from_buf: buf length: %u, loc: %u" )
155 % buf.length()
156 % loc);
157 len = get_long_from_buf(buf, loc);
158 L(FL("ssh_agent: get_string_from_buf: len: %u" ) % len);
159 E(loc + len <= buf.length(),
160 F("ssh_agent: length (%i) of buf less than loc (%u) + len (%u)")
161 % buf.length()
162 % loc
163 % len);
164 out = buf.substr(loc, len);
165 L(FL("ssh_agent: get_string_from_buf: out length: %u") % out.length());
166 loc += len;
167}
168
169static void
170put_long(u32 l, char * buf)
171{
172 buf[0] = (char)(unsigned char)(l >> 24);
173 buf[1] = (char)(unsigned char)(l >> 16);
174 buf[2] = (char)(unsigned char)(l >> 8);
175 buf[3] = (char)(unsigned char)(l);
176 L(FL("ssh_agent: long_to_buf: %u %u %u %u")
177 % (u32)(unsigned char)buf[0]
178 % (u32)(unsigned char)buf[1]
179 % (u32)(unsigned char)buf[2]
180 % (u32)(unsigned char)buf[3]);
181}
182
183static void
184put_long_into_buf(u32 l, string & buf)
185{
186 char lb[4];
187 L(FL("ssh_agent: put_long_into_buf: long: %u, buf len: %i")
188 % l
189 % buf.length());
190 put_long(l, lb);
191 buf.append(lb, 4);
192 L(FL("ssh_agent: put_long_into_buf: buf len now %i") % buf.length());
193}
194
195static void
196put_string_into_buf(string const & str, string & buf)
197{
198 L(FL("ssh_agent: put_string_into_buf: str len %i, buf len %i")
199 % str.length()
200 % buf.length());
201 put_long_into_buf(str.length(), buf);
202 buf.append(str.c_str(), str.length());
203 L(FL("ssh_agent: put_string_into_buf: buf len now %i") % buf.length());
204}
205
206static void
207put_bigint_into_buf(BigInt const & bi, string & buf)
208{
209 L(FL("ssh_agent: put_bigint_into_buf: bigint.bytes(): %u, bigint: %s")
210 % bi.bytes()
211 % bi);
212 SecureVector<Botan::byte> bi_buf = BigInt::encode(bi);
213 string bi_str;
214 if (*bi_buf.begin() & 0x80)
215 bi_str.append(1, static_cast<char>(0));
216 bi_str.append((char *) bi_buf.begin(), bi_buf.size());
217 put_string_into_buf(bi_str, buf);
218 L(FL("ssh_agent: put_bigint_into_buf: buf len now %i") % buf.length());
219}
220
221static void
222put_public_key_into_buf(RSA_PublicKey const & key, string & buf)
223{
224 L(FL("ssh_agent: put_public_key_into_buf: key e: %s, n: %s")
225 % key.get_e()
226 % key.get_n());
227 put_string_into_buf("ssh-rsa", buf);
228 put_bigint_into_buf(key.get_e(), buf);
229 put_bigint_into_buf(key.get_n(), buf);
230 L(FL("ssh_agent: put_public_key_into_buf: buf len now %i") % buf.length());
231}
232
233static void
234put_private_key_into_buf(RSA_PrivateKey const & key, string & buf)
235{
236 L(FL("ssh_agent: put_private_key_into_buf: key e: %s, n: %s")
237 % key.get_e()
238 % key.get_n());
239 put_string_into_buf("ssh-rsa", buf);
240 put_bigint_into_buf(key.get_n(), buf);
241 put_bigint_into_buf(key.get_e(), buf);
242 put_bigint_into_buf(key.get_d(), buf);
243 BigInt iqmp = inverse_mod(key.get_q(), key.get_p());
244 put_bigint_into_buf(iqmp, buf);
245 put_bigint_into_buf(key.get_p(), buf);
246 put_bigint_into_buf(key.get_q(), buf);
247 L(FL("ssh_agent: put_private_key_into_buf: buf len now %i") % buf.length());
248}
249
250//
251// Non-platform-specific ssh_agent_state methods, dealing with the basic
252// protocol packet format.
253//
254
255void
256ssh_agent_state::read_packet(string & packet)
257{
258 u32 len;
259 string len_buf;
260 read_data(4, len_buf);
261 u32 l = 0;
262 len = get_long_from_buf(len_buf, l);
263 E(len > 0, F("ssh_agent: fetch_packet: zero-length packet from ssh-agent"));
264
265 L(FL("ssh_agent: fetch_packet: response len %u") % len);
266
267 read_data(len, packet);
268}
269
270void
271ssh_agent_state::write_packet(string const & packet)
272{
273 string sized_packet;
274 put_string_into_buf(packet, sized_packet);
275 write_data(sized_packet);
276}
277
278//
279// ssh_agent public methods.
280//
281
282ssh_agent::ssh_agent()
283 : s(new ssh_agent_state)
284{
285}
286
287ssh_agent::~ssh_agent()
288{}
289
290bool
291ssh_agent::connected()
292{
293 return s->connected();
294}
295
296vector<RSA_PublicKey> const
297ssh_agent::get_keys()
298{
299 if (!s->connected())
300 {
301 L(FL("ssh_agent: get_keys: stream not initialized, no agent"));
302 return s->keys;
303 }
304
305 const char get_keys_cmd[1] = { 11 };
306 s->write_packet(string(get_keys_cmd, sizeof get_keys_cmd));
307
308 string packet;
309 s->read_packet(packet);
310
311 //first byte is packet type
312 u32 packet_loc = 0;
313 E(packet.at(0) == 12, F("ssh_agent: packet type (%u) != 12")
314 % (u32)packet.at(0));
315 packet_loc += 1;
316
317 u32 num_keys = get_long_from_buf(packet, packet_loc);
318 L(FL("ssh_agent: %u keys") % num_keys);
319
320 for (u32 key_num = 0; key_num < num_keys; ++key_num)
321 {
322 L(FL("ssh_agent: getting key # %u") % key_num);
323
324 u32 key_len;
325 string key;
326 get_string_from_buf(packet, packet_loc, key_len, key);
327
328 u32 key_loc = 0, slen;
329 string type;
330 get_string_from_buf(key, key_loc, slen, type);
331
332 L(FL("ssh_agent: type: %s") % type);
333
334 if (type == "ssh-rsa")
335 {
336 L(FL("ssh_agent: RSA"));
337 string e_str;
338 get_string_from_buf(key, key_loc, slen, e_str);
339 BigInt e = BigInt::decode((unsigned char *)(e_str.c_str()),
340 e_str.length(),
341 BigInt::Binary);
342 L(FL("ssh_agent: e: %s, len %u") % e % slen);
343 string n_str;
344 get_string_from_buf(key, key_loc, slen, n_str);
345 BigInt n = BigInt::decode((unsigned char *)(n_str.c_str()),
346 n_str.length(),
347 BigInt::Binary);
348 L(FL("ssh_agent: n: %s, len %u") % n % slen);
349
350 E(key.length() == key_loc,
351 F("ssh_agent: get_keys: not all or too many key bytes consumed,"
352 " location (%u), length (%i)")
353 % key_loc
354 % key.length());
355
356 RSA_PublicKey rsa_key(n, e);
357 s->keys.push_back(rsa_key);
358 }
359 else
360 L(FL("ssh_agent: ignoring key of type '%s'") % type);
361
362 L(FL("ssh_agent: packet length %u, packet loc %u, key length %u,"
363 " key loc, %u")
364 % packet.length()
365 % packet_loc
366 % key.length()
367 % key_loc);
368
369 string comment;
370 u32 comment_len;
371 get_string_from_buf(packet, packet_loc, comment_len, comment);
372 L(FL("ssh_agent: comment_len: %u, comment: %s") % comment_len % comment);
373 }
374 E(packet.length() == packet_loc,
375 F("ssh_agent: get_keys: not all or too many packet bytes consumed,"
376 " location (%u), length (%i)")
377 % packet_loc
378 % packet.length());
379
380 return s->keys;
381}
382
383bool
384ssh_agent::has_key(const keypair & key)
385{
386 //grab the monotone public key as an RSA_PublicKey
387 SecureVector<Botan::byte> pub_block;
388 pub_block.set(reinterpret_cast<Botan::byte const *>((key.pub)().data()),
389 (key.pub)().size());
390 L(FL("has_key: building %d-byte pub key") % pub_block.size());
391 shared_ptr<X509_PublicKey> x509_key =
392 shared_ptr<X509_PublicKey>(Botan::X509::load_key(pub_block));
393 shared_ptr<RSA_PublicKey> pub_key = shared_dynamic_cast<RSA_PublicKey>(x509_key);
394
395 if (!pub_key)
396 throw informative_failure("has_key: Failed to get monotone RSA public key");
397
398 vector<RSA_PublicKey> ssh_keys = get_keys();
399 for (vector<RSA_PublicKey>::const_iterator
400 si = ssh_keys.begin(); si != ssh_keys.end(); ++si)
401 {
402 if ((*pub_key).get_e() == (*si).get_e()
403 && (*pub_key).get_n() == (*si).get_n())
404 {
405 L(FL("has_key: key found"));
406 return true;
407 }
408 }
409 return false;
410}
411
412void
413ssh_agent::sign_data(RSA_PublicKey const & key,
414 string const & data,
415 string & out)
416{
417 E(connected(),
418 F("ssh_agent: get_keys: attempted to sign data when not connected"));
419
420 L(FL("ssh_agent: sign_data: key e: %s, n: %s, data len: %i")
421 % key.get_e()
422 % key.get_n()
423 % data.length());
424 string packet_out;
425 string key_buf;
426 string full_sig;
427
428 packet_out.append(1, (char)13); // command byte
429 put_public_key_into_buf(key, key_buf);
430 put_string_into_buf(key_buf, packet_out);
431
432 put_string_into_buf(data, packet_out);
433 u32 flags = 0;
434 put_long_into_buf(flags, packet_out);
435
436 L(FL("ssh_agent: sign_data: data_out length: %u") % packet_out.length());
437 s->write_packet(packet_out);
438
439 string packet_in;
440 s->read_packet(packet_in);
441
442 u32 packet_in_loc = 0;
443 if (packet_in.at(0) != 14)
444 {
445 L(FL("ssh_agent: sign_data: packet_in type (%u) != 14")
446 % (u32)packet_in.at(0));
447 return;
448 }
449 packet_in_loc += 1;
450
451 u32 full_sig_len;
452 get_string_from_buf(packet_in, packet_in_loc, full_sig_len, full_sig);
453 L(FL("ssh_agent: sign_data: signed data length: %u (%u)")
454 % full_sig_len
455 % full_sig.length());
456
457 string type;
458 u32 full_sig_loc = 0, type_len, out_len;
459 get_string_from_buf(full_sig, full_sig_loc, type_len, type);
460 L(FL("ssh_agent: sign_data: type (%u), '%s'") % type_len % type);
461 get_string_from_buf(full_sig, full_sig_loc, out_len, out);
462 L(FL("ssh_agent: sign_data: output length %u") % out_len);
463 E(full_sig.length() == full_sig_loc,
464 (F("ssh_agent: sign_data: not all or too many signature bytes consumed,"
465 " location (%u), length (%i)")
466 % full_sig_loc
467 % full_sig.length()));
468
469 E(packet_in.length() == packet_in_loc,
470 (F("ssh_agent: sign_data: not all or too many packet bytes consumed,"
471 " location (%u), length (%i)")
472 % packet_in_loc
473 % packet_in.length()));
474}
475
476void
477ssh_agent::add_identity(RSA_PrivateKey const & key, string const & comment)
478{
479 E(s->connected(),
480 F("ssh_agent: add_identity: attempted to add a key when not connected"));
481
482 L(FL("ssh_agent: add_identity: key e: %s, n: %s, comment len: %i")
483 % key.get_e()
484 % key.get_n()
485 % comment.length());
486 string packet_out;
487
488 packet_out.append(1, (char)17); // command byte
489 put_private_key_into_buf(key, packet_out);
490 put_string_into_buf(comment, packet_out);
491 s->write_packet(packet_out);
492
493 string packet_in;
494 s->read_packet(packet_in);
495 E(packet_in.length() == 1,
496 F("ssh_agent: add_identity: response packet of unexpected size (%u)")
497 % packet_in.length());
498 E(packet_in.at(0) == 6, F("ssh_agent: packet type (%u) != 6")
499 % (u32)packet_in.at(0));
500}
501
502// Local Variables:
503// mode: C++
504// fill-column: 76
505// c-file-style: "gnu"
506// indent-tabs-mode: nil
507// End:
508// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:
509

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status