monotone

monotone Mtn Source Tree

Root/cert.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 <limits>
11#include <sstream>
12#include <string>
13#include <vector>
14
15#include <boost/date_time/gregorian/gregorian.hpp>
16#include <boost/date_time/posix_time/posix_time.hpp>
17#include <boost/lexical_cast.hpp>
18#include <boost/shared_ptr.hpp>
19#include <boost/tuple/tuple.hpp>
20#include <boost/tuple/tuple_comparison.hpp>
21
22#include "app_state.hh"
23#include "cert.hh"
24#include "constants.hh"
25#include "interner.hh"
26#include "keys.hh"
27#include "netio.hh"
28#include "options.hh"
29#include "packet.hh"
30#include "revision.hh"
31#include "sanity.hh"
32#include "simplestring_xform.hh"
33#include "transforms.hh"
34#include "ui.hh"
35
36using std::make_pair;
37using std::map;
38using std::pair;
39using std::set;
40using std::string;
41using std::vector;
42using std::remove_if;
43
44using boost::shared_ptr;
45using boost::get;
46using boost::tuple;
47using boost::lexical_cast;
48
49// The alternaive is to #include "cert.hh" in vocab.*, which is even
50// uglier.
51
52#include "vocab_macros.hh"
53cc_DECORATE(revision)
54cc_DECORATE(manifest)
55template <typename T>
56static inline void
57verify(T & val)
58{}
59template class revision<cert>;
60template class manifest<cert>;
61
62// FIXME: the bogus-cert family of functions is ridiculous
63// and needs to be replaced, or at least factored.
64
65struct
66bogus_cert_p
67{
68 app_state & app;
69 bogus_cert_p(app_state & a) : app(a) {};
70
71 bool cert_is_bogus(cert const & c) const
72 {
73 cert_status status = check_cert(app, c);
74 if (status == cert_ok)
75 {
76 L(FL("cert ok"));
77 return false;
78 }
79 else if (status == cert_bad)
80 {
81 string txt;
82 cert_signable_text(c, txt);
83 W(F("ignoring bad signature by '%s' on '%s'") % c.key() % txt);
84 return true;
85 }
86 else
87 {
88 I(status == cert_unknown);
89 string txt;
90 cert_signable_text(c, txt);
91 W(F("ignoring unknown signature by '%s' on '%s'") % c.key() % txt);
92 return true;
93 }
94 }
95
96 bool operator()(revision<cert> const & c) const
97 {
98 return cert_is_bogus(c.inner());
99 }
100
101 bool operator()(manifest<cert> const & c) const
102 {
103 return cert_is_bogus(c.inner());
104 }
105};
106
107
108void
109erase_bogus_certs(vector< manifest<cert> > & certs,
110 app_state & app)
111{
112 typedef vector< manifest<cert> >::iterator it;
113 it e = remove_if(certs.begin(), certs.end(), bogus_cert_p(app));
114 certs.erase(e, certs.end());
115
116 vector< manifest<cert> > tmp_certs;
117
118 // Sorry, this is a crazy data structure
119 typedef tuple< hexenc<id>, cert_name, base64<cert_value> > trust_key;
120 typedef map< trust_key,
121 pair< shared_ptr< set<rsa_keypair_id> >, it > > trust_map;
122 trust_map trust;
123
124 for (it i = certs.begin(); i != certs.end(); ++i)
125 {
126 trust_key key = trust_key(i->inner().ident,
127 i->inner().name,
128 i->inner().value);
129 trust_map::iterator j = trust.find(key);
130 shared_ptr< set<rsa_keypair_id> > s;
131 if (j == trust.end())
132 {
133 s.reset(new set<rsa_keypair_id>());
134 trust.insert(make_pair(key, make_pair(s, i)));
135 }
136 else
137 s = j->second.first;
138 s->insert(i->inner().key);
139 }
140
141 for (trust_map::const_iterator i = trust.begin();
142 i != trust.end(); ++i)
143 {
144 cert_value decoded_value;
145 decode_base64(get<2>(i->first), decoded_value);
146 if (app.lua.hook_get_manifest_cert_trust(*(i->second.first),
147 get<0>(i->first),
148 get<1>(i->first),
149 decoded_value))
150 {
151 L(FL("trust function liked %d signers of %s cert on manifest %s")
152 % i->second.first->size() % get<1>(i->first) % get<0>(i->first));
153 tmp_certs.push_back(*(i->second.second));
154 }
155 else
156 {
157 W(F("trust function disliked %d signers of %s cert on manifest %s")
158 % i->second.first->size() % get<1>(i->first) % get<0>(i->first));
159 }
160 }
161 certs = tmp_certs;
162}
163
164void
165erase_bogus_certs(vector< revision<cert> > & certs,
166 app_state & app)
167{
168 typedef vector< revision<cert> >::iterator it;
169 it e = remove_if(certs.begin(), certs.end(), bogus_cert_p(app));
170 certs.erase(e, certs.end());
171
172 vector< revision<cert> > tmp_certs;
173
174 // sorry, this is a crazy data structure
175 typedef tuple< hexenc<id>,
176 cert_name, base64<cert_value> > trust_key;
177 typedef map< trust_key,
178 pair< shared_ptr< set<rsa_keypair_id> >, it > > trust_map;
179 trust_map trust;
180
181 for (it i = certs.begin(); i != certs.end(); ++i)
182 {
183 trust_key key = trust_key(i->inner().ident,
184 i->inner().name,
185 i->inner().value);
186 trust_map::iterator j = trust.find(key);
187 shared_ptr< set<rsa_keypair_id> > s;
188 if (j == trust.end())
189 {
190 s.reset(new set<rsa_keypair_id>());
191 trust.insert(make_pair(key, make_pair(s, i)));
192 }
193 else
194 s = j->second.first;
195 s->insert(i->inner().key);
196 }
197
198 for (trust_map::const_iterator i = trust.begin();
199 i != trust.end(); ++i)
200 {
201 cert_value decoded_value;
202 decode_base64(get<2>(i->first), decoded_value);
203 if (app.lua.hook_get_revision_cert_trust(*(i->second.first),
204 get<0>(i->first),
205 get<1>(i->first),
206 decoded_value))
207 {
208 L(FL("trust function liked %d signers of %s cert on revision %s")
209 % i->second.first->size() % get<1>(i->first) % get<0>(i->first));
210 tmp_certs.push_back(*(i->second.second));
211 }
212 else
213 {
214 W(F("trust function disliked %d signers of %s cert on revision %s")
215 % i->second.first->size() % get<1>(i->first) % get<0>(i->first));
216 }
217 }
218 certs = tmp_certs;
219}
220
221
222// cert-managing routines
223
224cert::cert()
225{}
226
227cert::cert(hexenc<id> const & ident,
228 cert_name const & name,
229 base64<cert_value> const & value,
230 rsa_keypair_id const & key)
231 : ident(ident), name(name), value(value), key(key)
232{}
233
234cert::cert(hexenc<id> const & ident,
235 cert_name const & name,
236 base64<cert_value> const & value,
237 rsa_keypair_id const & key,
238 base64<rsa_sha1_signature> const & sig)
239 : ident(ident), name(name), value(value), key(key), sig(sig)
240{}
241
242bool
243cert::operator<(cert const & other) const
244{
245 return (ident < other.ident)
246 || ((ident == other.ident) && name < other.name)
247 || (((ident == other.ident) && name == other.name)
248 && value < other.value)
249 || ((((ident == other.ident) && name == other.name)
250 && value == other.value) && key < other.key)
251 || (((((ident == other.ident) && name == other.name)
252 && value == other.value) && key == other.key) && sig < other.sig);
253}
254
255bool
256cert::operator==(cert const & other) const
257{
258 return
259 (ident == other.ident)
260 && (name == other.name)
261 && (value == other.value)
262 && (key == other.key)
263 && (sig == other.sig);
264}
265
266// netio support
267
268void
269read_cert(string const & in, cert & t)
270{
271 size_t pos = 0;
272 id hash = extract_substring(in, pos,
273 constants::merkle_hash_length_in_bytes,
274 "cert hash");
275 id ident = extract_substring(in, pos,
276 constants::merkle_hash_length_in_bytes,
277 "cert ident");
278 string name, val, key, sig;
279 extract_variable_length_string(in, name, pos, "cert name");
280 extract_variable_length_string(in, val, pos, "cert val");
281 extract_variable_length_string(in, key, pos, "cert key");
282 extract_variable_length_string(in, sig, pos, "cert sig");
283 assert_end_of_buffer(in, pos, "cert");
284
285 hexenc<id> hid;
286 base64<cert_value> bval;
287 base64<rsa_sha1_signature> bsig;
288
289 encode_hexenc(ident, hid);
290 encode_base64(cert_value(val), bval);
291 encode_base64(rsa_sha1_signature(sig), bsig);
292
293 cert tmp(hid, cert_name(name), bval, rsa_keypair_id(key), bsig);
294
295 hexenc<id> hcheck;
296 id check;
297 cert_hash_code(tmp, hcheck);
298 decode_hexenc(hcheck, check);
299 if (!(check == hash))
300 {
301 hexenc<id> hhash;
302 encode_hexenc(hash, hhash);
303 throw bad_decode(F("calculated cert hash '%s' does not match '%s'")
304 % hcheck % hhash);
305 }
306 t = tmp;
307}
308
309void
310write_cert(cert const & t, string & out)
311{
312 string name, key;
313 hexenc<id> hash;
314 id ident_decoded, hash_decoded;
315 rsa_sha1_signature sig_decoded;
316 cert_value value_decoded;
317
318 cert_hash_code(t, hash);
319 decode_base64(t.value, value_decoded);
320 decode_base64(t.sig, sig_decoded);
321 decode_hexenc(t.ident, ident_decoded);
322 decode_hexenc(hash, hash_decoded);
323
324 out.append(hash_decoded());
325 out.append(ident_decoded());
326 insert_variable_length_string(t.name(), out);
327 insert_variable_length_string(value_decoded(), out);
328 insert_variable_length_string(t.key(), out);
329 insert_variable_length_string(sig_decoded(), out);
330}
331
332void
333cert_signable_text(cert const & t,
334 string & out)
335{
336 out = (FL("[%s@%s:%s]") % t.name % t.ident % remove_ws(t.value())).str();
337 L(FL("cert: signable text %s") % out);
338}
339
340void
341cert_hash_code(cert const & t, hexenc<id> & out)
342{
343 string tmp;
344 tmp.reserve(4+t.ident().size() + t.name().size() + t.value().size() +
345 t.key().size() + t.sig().size());
346 tmp.append(t.ident());
347 tmp += ':';
348 tmp.append(t.name());
349 tmp += ':';
350 append_without_ws(tmp,t.value());
351 tmp += ':';
352 tmp.append(t.key());
353 tmp += ':';
354 append_without_ws(tmp,t.sig());
355
356 data tdat(tmp);
357 calculate_ident(tdat, out);
358}
359
360bool
361priv_key_exists(app_state & app, rsa_keypair_id const & id)
362{
363
364 return app.keys.key_pair_exists(id);
365}
366
367// Loads a key pair for a given key id, from either a lua hook
368// or the key store. This will bomb out if the same keyid exists
369// in both with differing contents.
370
371void
372load_key_pair(app_state & app,
373 rsa_keypair_id const & id,
374 keypair & kp)
375{
376
377 static map<rsa_keypair_id, keypair> keys;
378 bool persist_ok = (!keys.empty()) || app.lua.hook_persist_phrase_ok();
379
380 if (persist_ok && keys.find(id) != keys.end())
381 {
382 kp = keys[id];
383 }
384 else
385 {
386 N(app.keys.key_pair_exists(id),
387 F("no key pair '%s' found in key store '%s'")
388 % id % app.keys.get_key_dir());
389 app.keys.get_key_pair(id, kp);
390 if (persist_ok)
391 keys.insert(make_pair(id, kp));
392 }
393}
394
395void
396calculate_cert(app_state & app, cert & t)
397{
398 string signed_text;
399 keypair kp;
400 cert_signable_text(t, signed_text);
401
402 load_key_pair(app, t.key, kp);
403 if (!app.db.public_key_exists(t.key))
404 app.db.put_key(t.key, kp.pub);
405
406 make_signature(app, t.key, kp.priv, signed_text, t.sig);
407}
408
409cert_status
410check_cert(app_state & app, cert const & t)
411{
412
413 base64< rsa_pub_key > pub;
414
415 static map<rsa_keypair_id, base64< rsa_pub_key > > pubkeys;
416 bool persist_ok = (!pubkeys.empty()) || app.lua.hook_persist_phrase_ok();
417
418 if (persist_ok
419 && pubkeys.find(t.key) != pubkeys.end())
420 {
421 pub = pubkeys[t.key];
422 }
423 else
424 {
425 if (!app.db.public_key_exists(t.key))
426 return cert_unknown;
427 app.db.get_key(t.key, pub);
428 if (persist_ok)
429 pubkeys.insert(make_pair(t.key, pub));
430 }
431
432 string signed_text;
433 cert_signable_text(t, signed_text);
434 if (check_signature(app, t.key, pub, signed_text, t.sig))
435 return cert_ok;
436 else
437 return cert_bad;
438}
439
440
441// "special certs"
442
443string const branch_cert_name("branch");
444
445void
446get_user_key(rsa_keypair_id & key, app_state & app)
447{
448
449 if (app.signing_key() != "")
450 {
451 key = app.signing_key;
452 return;
453 }
454
455 if (app.branch_name() != "")
456 {
457 cert_value branch(app.branch_name());
458 if (app.lua.hook_get_branch_key(branch, key))
459 return;
460 }
461
462 vector<rsa_keypair_id> all_privkeys;
463 app.keys.get_keys(all_privkeys);
464 N(!all_privkeys.empty(),
465 F("you have no private key to make signatures with\n"
466 "perhaps you need to 'genkey <your email>'"));
467 N(all_privkeys.size() == 1,
468 F("you have multiple private keys\n"
469 "pick one to use for signatures by adding '-k<keyname>' to your command"));
470 key = all_privkeys[0];
471}
472
473void
474guess_branch(revision_id const & ident,
475 app_state & app,
476 cert_value & branchname)
477{
478 if ((app.branch_name() != "")
479 && app.is_explicit_option(OPT_BRANCH_NAME))
480 {
481 branchname = app.branch_name();
482 }
483 else
484 {
485 N(!ident.inner()().empty(),
486 F("no branch found for empty revision, "
487 "please provide a branch name"));
488
489 vector< revision<cert> > certs;
490 cert_name branch(branch_cert_name);
491 app.db.get_revision_certs(ident, branch, certs);
492 erase_bogus_certs(certs, app);
493
494 N(certs.size() != 0,
495 F("no branch certs found for revision %s, "
496 "please provide a branch name") % ident);
497
498 N(certs.size() == 1,
499 F("multiple branch certs found for revision %s, "
500 "please provide a branch name") % ident);
501
502 decode_base64(certs[0].inner().value, branchname);
503 app.set_branch(branchname());
504 }
505}
506
507void
508make_simple_cert(hexenc<id> const & id,
509 cert_name const & nm,
510 cert_value const & cv,
511 app_state & app,
512 cert & c)
513{
514 rsa_keypair_id key;
515 get_user_key(key, app);
516 base64<cert_value> encoded_val;
517 encode_base64(cv, encoded_val);
518 cert t(id, nm, encoded_val, key);
519 calculate_cert(app, t);
520 c = t;
521}
522
523static void
524put_simple_revision_cert(revision_id const & id,
525 cert_name const & nm,
526 cert_value const & val,
527 app_state & app,
528 packet_consumer & pc)
529{
530 cert t;
531 make_simple_cert(id.inner(), nm, val, app, t);
532 revision<cert> cc(t);
533 pc.consume_revision_cert(cc);
534}
535
536void
537cert_revision_in_branch(revision_id const & rev,
538 cert_value const & branchname,
539 app_state & app,
540 packet_consumer & pc)
541{
542 put_simple_revision_cert (rev, branch_cert_name,
543 branchname, app, pc);
544}
545
546void
547get_branch_heads(cert_value const & branchname,
548 app_state & app,
549 set<revision_id> & heads)
550{
551 vector< revision<cert> > certs;
552 base64<cert_value> branch_encoded;
553
554 encode_base64(branchname, branch_encoded);
555 app.db.get_revision_certs(cert_name(branch_cert_name),
556 branch_encoded, certs);
557
558 erase_bogus_certs(certs, app);
559
560 heads.clear();
561
562 for (vector< revision<cert> >::const_iterator i = certs.begin();
563 i != certs.end(); ++i)
564 heads.insert(revision_id(i->inner().ident));
565
566 erase_ancestors(heads, app);
567}
568
569
570// "standard certs"
571
572string const date_cert_name = "date";
573string const author_cert_name = "author";
574string const tag_cert_name = "tag";
575string const changelog_cert_name = "changelog";
576string const comment_cert_name = "comment";
577string const testresult_cert_name = "testresult";
578
579
580void
581cert_revision_date_time(revision_id const & m,
582 boost::posix_time::ptime t,
583 app_state & app,
584 packet_consumer & pc)
585{
586 string val = boost::posix_time::to_iso_extended_string(t);
587 put_simple_revision_cert(m, date_cert_name, val, app, pc);
588}
589
590void
591cert_revision_date_time(revision_id const & m,
592 time_t t,
593 app_state & app,
594 packet_consumer & pc)
595{
596 // make sure you do all your CVS conversions by 2038!
597 boost::posix_time::ptime tmp(boost::gregorian::date(1970,1,1),
598 boost::posix_time::seconds(static_cast<long>(t)));
599 cert_revision_date_time(m, tmp, app, pc);
600}
601
602void
603cert_revision_date_now(revision_id const & m,
604 app_state & app,
605 packet_consumer & pc)
606{
607 cert_revision_date_time
608 (m, boost::posix_time::second_clock::universal_time(), app, pc);
609}
610
611void
612cert_revision_author(revision_id const & m,
613 string const & author,
614 app_state & app,
615 packet_consumer & pc)
616{
617 put_simple_revision_cert(m, author_cert_name,
618 author, app, pc);
619}
620
621void
622cert_revision_author_default(revision_id const & m,
623 app_state & app,
624 packet_consumer & pc)
625{
626 string author;
627 if (!app.lua.hook_get_author(app.branch_name(), author))
628 {
629 rsa_keypair_id key;
630 get_user_key(key, app),
631 author = key();
632 }
633 cert_revision_author(m, author, app, pc);
634}
635
636void
637cert_revision_tag(revision_id const & m,
638 string const & tagname,
639 app_state & app,
640 packet_consumer & pc)
641{
642 put_simple_revision_cert(m, tag_cert_name,
643 tagname, app, pc);
644}
645
646
647void
648cert_revision_changelog(revision_id const & m,
649 string const & changelog,
650 app_state & app,
651 packet_consumer & pc)
652{
653 put_simple_revision_cert(m, changelog_cert_name,
654 changelog, app, pc);
655}
656
657void
658cert_revision_comment(revision_id const & m,
659 string const & comment,
660 app_state & app,
661 packet_consumer & pc)
662{
663 put_simple_revision_cert(m, comment_cert_name,
664 comment, app, pc);
665}
666
667void
668cert_revision_testresult(revision_id const & r,
669 string const & results,
670 app_state & app,
671 packet_consumer & pc)
672{
673 bool passed = false;
674 if (lowercase(results) == "true" ||
675 lowercase(results) == "yes" ||
676 lowercase(results) == "pass" ||
677 results == "1")
678 passed = true;
679 else if (lowercase(results) == "false" ||
680 lowercase(results) == "no" ||
681 lowercase(results) == "fail" ||
682 results == "0")
683 passed = false;
684 else
685 throw informative_failure("could not interpret test results, "
686 "tried '0/1' 'yes/no', 'true/false', "
687 "'pass/fail'");
688
689 put_simple_revision_cert(r, testresult_cert_name, lexical_cast<string>(passed), app, pc);
690}
691
692
693#ifdef BUILD_UNIT_TESTS
694#include "unit_tests.hh"
695
696#endif // BUILD_UNIT_TESTS
697
698// Local Variables:
699// mode: C++
700// fill-column: 76
701// c-file-style: "gnu"
702// indent-tabs-mode: nil
703// End:
704// 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