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

Archive Download this file

Branches

Tags

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