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

Archive Download this file

Branches

Tags

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