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

Archive Download this file

Branches

Tags

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