monotone

monotone Mtn Source Tree

Root/src/packet.cc

1// Copyright (C) 2002 Graydon Hoare <graydon@pobox.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 <sstream>
12#include <botan/botan.h>
13#include <botan/rsa.h>
14
15#include "cset.hh"
16#include "constants.hh"
17#include "packet.hh"
18#include "revision.hh"
19#include "sanity.hh"
20#include "transforms.hh"
21#include "simplestring_xform.hh"
22#include "cert.hh"
23#include "key_store.hh" // for keypair
24#include "char_classifiers.hh"
25#include "lazy_rng.hh"
26
27using std::istream;
28using std::istringstream;
29using std::make_pair;
30using std::map;
31using std::ostream;
32using std::pair;
33using std::string;
34
35using boost::shared_ptr;
36
37// --- packet writer ---
38
39packet_writer::packet_writer(ostream & o) : ost(o) {}
40
41void
42packet_writer::consume_file_data(file_id const & ident,
43 file_data const & dat)
44{
45 base64<gzip<data> > packed;
46 pack(dat.inner(), packed);
47 ost << "[fdata " << ident << "]\n"
48 << trim(packed()) << '\n'
49 << "[end]\n";
50}
51
52void
53packet_writer::consume_file_delta(file_id const & old_id,
54 file_id const & new_id,
55 file_delta const & del)
56{
57 base64<gzip<delta> > packed;
58 pack(del.inner(), packed);
59 ost << "[fdelta " << old_id << '\n'
60 << " " << new_id << "]\n"
61 << trim(packed()) << '\n'
62 << "[end]\n";
63}
64
65void
66packet_writer::consume_revision_data(revision_id const & ident,
67 revision_data const & dat)
68{
69 base64<gzip<data> > packed;
70 pack(dat.inner(), packed);
71 ost << "[rdata " << ident << "]\n"
72 << trim(packed()) << '\n'
73 << "[end]\n";
74}
75
76void
77packet_writer::consume_revision_cert(cert const & t)
78{
79 ost << "[rcert " << encode_hexenc(t.ident.inner()(),
80 t.ident.inner().made_from) << '\n'
81 << " " << t.name() << '\n'
82 << " " << t.key.inner() << '\n'
83 << " " << trim(encode_base64(t.value)()) << "]\n"
84 << trim(encode_base64(t.sig)()) << '\n'
85 << "[end]\n";
86}
87
88void
89packet_writer::consume_public_key(key_name const & ident,
90 rsa_pub_key const & k)
91{
92 ost << "[pubkey " << ident() << "]\n"
93 << trim(encode_base64(k)()) << '\n'
94 << "[end]\n";
95}
96
97void
98packet_writer::consume_key_pair(key_name const & ident,
99 keypair const & kp)
100{
101 ost << "[keypair " << ident() << "]\n"
102 << trim(encode_base64(kp.pub)()) << "#\n"
103 << trim(encode_base64(kp.priv)()) << '\n'
104 << "[end]\n";
105}
106
107void
108packet_writer::consume_old_private_key(key_name const & ident,
109 old_arc4_rsa_priv_key const & k)
110{
111 ost << "[privkey " << ident() << "]\n"
112 << trim(encode_base64(k)()) << '\n'
113 << "[end]\n";
114}
115
116
117// --- reading packets from streams ---
118namespace
119{
120 struct
121 feed_packet_consumer : public origin_aware
122 {
123 size_t & count;
124 packet_consumer & cons;
125 feed_packet_consumer(size_t & count, packet_consumer & c,
126 origin::type whence)
127 : origin_aware(whence), count(count), cons(c)
128 {}
129 void validate_id(string const & id) const
130 {
131 E(id.size() == constants::idlen
132 && id.find_first_not_of(constants::legal_id_bytes) == string::npos,
133 made_from,
134 F("malformed packet: invalid identifier"));
135 }
136 void validate_base64(string const & s) const
137 {
138 E(!s.empty()
139 && s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
140 made_from,
141 F("malformed packet: invalid base64 block"));
142 }
143 void validate_arg_base64(string const & s) const
144 {
145 E(s.find_first_not_of(constants::legal_base64_bytes) == string::npos,
146 made_from,
147 F("malformed packet: invalid base64 block"));
148 }
149 void validate_key(string const & k) const
150 {
151 E(!k.empty()
152 && k.find_first_not_of(constants::legal_key_name_bytes) == string::npos,
153 made_from,
154 F("malformed packet: invalid key name"));
155 }
156 void validate_public_key_data(string const & name, string const & keydata) const
157 {
158 string decoded = decode_base64_as<string>(keydata, origin::user);
159 Botan::SecureVector<Botan::byte> key_block
160 (reinterpret_cast<Botan::byte const *>(decoded.c_str()), decoded.size());
161 try
162 {
163 Botan::X509::load_key(key_block);
164 }
165 catch (Botan::Decoding_Error const & e)
166 {
167 E(false, origin::user,
168 F("malformed packet: invalid public key data for '%s': %s")
169 % name % e.what());
170 }
171 }
172 void validate_private_key_data(string const & name, string const & keydata) const
173 {
174 string decoded = decode_base64_as<string>(keydata, origin::user);
175 Botan::DataSource_Memory ds(decoded);
176 try
177 {
178#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
179 Botan::PKCS8::load_key(ds, lazy_rng::get(), Dummy_UI());
180#elif BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,7,7)
181 Botan::PKCS8::load_key(ds, lazy_rng::get(), string());
182#else
183 Botan::PKCS8::load_key(ds, string());
184#endif
185 }
186 catch (Botan::Decoding_Error const & e)
187 {
188 E(false, origin::user,
189 F("malformed packet: invalid private key data for '%s': %s")
190 % name % e.what());
191 }
192 // since we do not want to prompt for a password to decode it finally,
193 // we ignore all other exceptions
194#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
195 catch (Passphrase_Required) {}
196#else
197 catch (Botan::Invalid_Argument) {}
198#endif
199 }
200 void validate_certname(string const & cn) const
201 {
202 E(!cn.empty()
203 && cn.find_first_not_of(constants::legal_cert_name_bytes) == string::npos,
204 made_from,
205 F("malformed packet: invalid cert name"));
206 }
207 void validate_no_more_args(istringstream & iss) const
208 {
209 string next;
210 iss >> next;
211 E(next.empty(), made_from,
212 F("malformed packet: too many arguments in header"));
213 }
214
215 void data_packet(string const & args, string const & body,
216 bool is_revision) const
217 {
218 L(FL("read %s data packet") % (is_revision ? "revision" : "file"));
219 validate_id(args);
220 validate_base64(body);
221
222 id hash(decode_hexenc_as<id>(args, made_from));
223 data contents;
224 unpack(base64<gzip<data> >(body, made_from), contents);
225 if (is_revision)
226 cons.consume_revision_data(revision_id(hash),
227 revision_data(contents));
228 else
229 cons.consume_file_data(file_id(hash),
230 file_data(contents));
231 }
232
233 void fdelta_packet(string const & args, string const & body) const
234 {
235 L(FL("read delta packet"));
236 istringstream iss(args);
237 string src_id; iss >> src_id; validate_id(src_id);
238 string dst_id; iss >> dst_id; validate_id(dst_id);
239 validate_no_more_args(iss);
240 validate_base64(body);
241
242 id src_hash(decode_hexenc_as<id>(src_id, made_from)),
243 dst_hash(decode_hexenc_as<id>(dst_id, made_from));
244 delta contents;
245 unpack(base64<gzip<delta> >(body, made_from), contents);
246 cons.consume_file_delta(file_id(src_hash),
247 file_id(dst_hash),
248 file_delta(contents));
249 }
250 static void read_rest(istream& in, string& dest)
251 {
252
253 while (true)
254 {
255 string t;
256 in >> t;
257 if (t.empty()) break;
258 dest += t;
259 }
260 }
261 void rcert_packet(string const & args, string const & body) const
262 {
263 L(FL("read cert packet"));
264 istringstream iss(args);
265 string certid; iss >> certid; validate_id(certid);
266 string name; iss >> name; validate_certname(name);
267 string keyid; iss >> keyid; validate_id(keyid);
268 string val;
269 read_rest(iss,val); validate_arg_base64(val);
270
271 revision_id hash(decode_hexenc_as<revision_id>(certid, made_from));
272 validate_base64(body);
273
274 // canonicalize the base64 encodings to permit searches
275 cert t = cert(hash,
276 cert_name(name, made_from),
277 decode_base64_as<cert_value>(val, made_from),
278 decode_hexenc_as<key_id>(keyid, made_from),
279 decode_base64_as<rsa_sha1_signature>(body, made_from));
280 cons.consume_revision_cert(t);
281 }
282
283 void pubkey_packet(string const & args, string const & body) const
284 {
285 L(FL("read pubkey packet"));
286 validate_key(args);
287 validate_base64(body);
288 validate_public_key_data(args, body);
289
290 cons.consume_public_key(key_name(args, made_from),
291 decode_base64_as<rsa_pub_key>(body, made_from));
292 }
293
294 void keypair_packet(string const & args, string const & body) const
295 {
296 L(FL("read keypair packet"));
297 string::size_type hashpos = body.find('#');
298 string pub(body, 0, hashpos);
299 string priv(body, hashpos+1);
300
301 validate_key(args);
302 validate_base64(pub);
303 validate_public_key_data(args, pub);
304 validate_base64(priv);
305 validate_private_key_data(args, priv);
306
307 cons.consume_key_pair(key_name(args, made_from),
308 keypair(decode_base64_as<rsa_pub_key>(pub, made_from),
309 decode_base64_as<rsa_priv_key>(priv, made_from)));
310 }
311
312 void privkey_packet(string const & args, string const & body) const
313 {
314 L(FL("read privkey packet"));
315 validate_key(args);
316 validate_base64(body);
317 cons.consume_old_private_key(key_name(args, made_from),
318 decode_base64_as<old_arc4_rsa_priv_key>(body, made_from));
319 }
320
321 void operator()(string const & type,
322 string const & args,
323 string const & body) const
324 {
325 if (type == "rdata")
326 data_packet(args, body, true);
327 else if (type == "fdata")
328 data_packet(args, body, false);
329 else if (type == "fdelta")
330 fdelta_packet(args, body);
331 else if (type == "rcert")
332 rcert_packet(args, body);
333 else if (type == "pubkey")
334 pubkey_packet(args, body);
335 else if (type == "keypair")
336 keypair_packet(args, body);
337 else if (type == "privkey")
338 privkey_packet(args, body);
339 else
340 {
341 W(F("unknown packet type '%s'") % type);
342 return;
343 }
344 ++count;
345 }
346 };
347} // anonymous namespace
348
349static size_t
350extract_packets(string const & s, packet_consumer & cons)
351{
352 size_t count = 0;
353 feed_packet_consumer feeder(count, cons, origin::user);
354
355 string::const_iterator p, tbeg, tend, abeg, aend, bbeg, bend;
356
357 enum extract_state {
358 skipping, open_bracket, scanning_type, found_type,
359 scanning_args, found_args, scanning_body,
360 end_1, end_2, end_3, end_4, end_5
361 } state = skipping;
362
363 for (p = s.begin(); p != s.end(); p++)
364 switch (state)
365 {
366 case skipping: if (*p == '[') state = open_bracket; break;
367 case open_bracket:
368 if (is_alpha (*p))
369 state = scanning_type;
370 else
371 state = skipping;
372 tbeg = p;
373 break;
374 case scanning_type:
375 if (!is_alpha (*p))
376 {
377 state = is_space(*p) ? found_type : skipping;
378 tend = p;
379 }
380 break;
381 case found_type:
382 if (!is_space (*p))
383 {
384 state = (*p != ']') ? scanning_args : skipping;
385 abeg = p;
386 }
387 break;
388 case scanning_args:
389 if (*p == ']')
390 {
391 state = found_args;
392 aend = p;
393 }
394 break;
395 case found_args:
396 state = (*p != '[' && *p != ']') ? scanning_body : skipping;
397 bbeg = p;
398 break;
399 case scanning_body:
400 if (*p == '[')
401 {
402 state = end_1;
403 bend = p;
404 }
405 else if (*p == ']')
406 state = skipping;
407 break;
408
409 case end_1: state = (*p == 'e') ? end_2 : skipping; break;
410 case end_2: state = (*p == 'n') ? end_3 : skipping; break;
411 case end_3: state = (*p == 'd') ? end_4 : skipping; break;
412 case end_4:
413 if (*p == ']')
414 feeder(string(tbeg, tend), string(abeg, aend), string(bbeg, bend));
415 state = skipping;
416 break;
417 default:
418 I(false);
419 }
420 return count;
421}
422
423// this is same as rfind, but search area is haystack[start:] (from start to end of string)
424// haystack is searched, needle is pattern
425static size_t
426rfind_in_substr(std::string const& haystack, size_t start, std::string const& needle)
427{
428 I(start <= haystack.size());
429 const std::string::const_iterator result =
430 std::find_end(haystack.begin() + start, haystack.end(),
431 needle.begin(), needle.end());
432
433 if (result == haystack.end())
434 return std::string::npos;
435 else
436 return distance(haystack.begin(), result);
437}
438
439size_t
440read_packets(istream & in, packet_consumer & cons)
441{
442 string accum, tmp;
443 size_t count = 0;
444 size_t const bufsz = 0xff;
445 char buf[bufsz];
446 static string const end("[end]");
447 while(in)
448 {
449 size_t const next_search_pos = (accum.size() >= end.size())
450 ? accum.size() - end.size() : 0;
451 in.read(buf, bufsz);
452 accum.append(buf, in.gcount());
453 string::size_type endpos = string::npos;
454 endpos = rfind_in_substr(accum, next_search_pos, end);
455 if (endpos != string::npos)
456 {
457 endpos += end.size();
458 string tmp = accum.substr(0, endpos);
459 count += extract_packets(tmp, cons);
460 if (endpos < accum.size() - 1)
461 accum = accum.substr(endpos+1);
462 else
463 accum.clear();
464 }
465 }
466 return count;
467}
468
469// Dummy User_Interface implementation for Botan
470#if BOTAN_VERSION_CODE >= BOTAN_VERSION_CODE_FOR(1,9,11)
471std::string
472Dummy_UI::get_passphrase(const std::string &, const std::string &,
473 Botan::User_Interface::UI_Result&) const
474{
475 throw Passphrase_Required("Passphrase required");
476}
477#endif
478
479// Local Variables:
480// mode: C++
481// fill-column: 76
482// c-file-style: "gnu"
483// indent-tabs-mode: nil
484// End:
485// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

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