monotone

monotone Mtn Source Tree

Root/cert.cc

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

Archive Download this file

Branches

Tags

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