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