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