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 "cert.hh"
21#include "constants.hh"
22#include "database.hh"
23#include "interner.hh"
24#include "keys.hh"
25#include "key_store.hh"
26#include "netio.hh"
27#include "options.hh"
28#include "project.hh"
29#include "revision.hh"
30#include "sanity.hh"
31#include "simplestring_xform.hh"
32#include "transforms.hh"
33#include "ui.hh"
34
35using std::make_pair;
36using std::map;
37using std::pair;
38using std::set;
39using std::string;
40using std::vector;
41using std::remove_if;
42
43using boost::shared_ptr;
44using boost::get;
45using boost::tuple;
46using boost::lexical_cast;
47
48// The alternaive is to #include "cert.hh" in vocab.*, which is even
49// uglier.
50
51#include "vocab_macros.hh"
52cc_DECORATE(revision)
53cc_DECORATE(manifest)
54template <typename T>
55static inline void
56verify(T & val)
57{}
58template class revision<cert>;
59template class manifest<cert>;
60
61// FIXME: the bogus-cert family of functions is ridiculous
62// and needs to be replaced, or at least factored.
63
64struct
65bogus_cert_p
66{
67 database & db;
68 bogus_cert_p(database & db) : db(db) {};
69
70 bool cert_is_bogus(cert const & c) const
71 {
72 cert_status status = check_cert(db, c);
73 if (status == cert_ok)
74 {
75 L(FL("cert ok"));
76 return false;
77 }
78 else if (status == cert_bad)
79 {
80 string txt;
81 cert_signable_text(c, txt);
82 W(F("ignoring bad signature by '%s' on '%s'") % c.key() % txt);
83 return true;
84 }
85 else
86 {
87 I(status == cert_unknown);
88 string txt;
89 cert_signable_text(c, txt);
90 W(F("ignoring unknown signature by '%s' on '%s'") % c.key() % txt);
91 return true;
92 }
93 }
94
95 bool operator()(revision<cert> const & c) const
96 {
97 return cert_is_bogus(c.inner());
98 }
99
100 bool operator()(manifest<cert> const & c) const
101 {
102 return cert_is_bogus(c.inner());
103 }
104};
105
106void
107erase_bogus_certs(database & db,
108 vector< manifest<cert> > & certs)
109{
110 typedef vector< manifest<cert> >::iterator it;
111 it e = remove_if(certs.begin(), certs.end(), bogus_cert_p(db));
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< manifest_id, cert_name, 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(manifest_id(i->inner().ident.inner()),
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 if (db.hook_get_manifest_cert_trust(*(i->second.first),
143 get<0>(i->first),
144 get<1>(i->first),
145 get<2>(i->first)))
146 {
147 if (global_sanity.debug_p())
148 L(FL("trust function liked %d signers of %s cert on manifest %s")
149 % i->second.first->size()
150 % get<1>(i->first)
151 % get<0>(i->first));
152 tmp_certs.push_back(*(i->second.second));
153 }
154 else
155 {
156 W(F("trust function disliked %d signers of %s cert on manifest %s")
157 % i->second.first->size()
158 % get<1>(i->first)
159 % get<0>(i->first));
160 }
161 }
162 certs = tmp_certs;
163}
164
165void
166erase_bogus_certs(database & db,
167 vector< revision<cert> > & certs)
168{
169 typedef vector< revision<cert> >::iterator it;
170 it e = remove_if(certs.begin(), certs.end(), bogus_cert_p(db));
171 certs.erase(e, certs.end());
172
173 vector< revision<cert> > tmp_certs;
174
175 // sorry, this is a crazy data structure
176 typedef tuple< revision_id, cert_name, 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 if (db.hook_get_revision_cert_trust(*(i->second.first),
202 get<0>(i->first),
203 get<1>(i->first),
204 get<2>(i->first)))
205 {
206 if (global_sanity.debug_p())
207 L(FL("trust function liked %d signers of %s cert on revision %s")
208 % i->second.first->size()
209 % get<1>(i->first)
210 % get<0>(i->first));
211 tmp_certs.push_back(*(i->second.second));
212 }
213 else
214 {
215 W(F("trust function disliked %d signers of %s cert on revision %s")
216 % i->second.first->size()
217 % get<1>(i->first)
218 % get<0>(i->first));
219 }
220 }
221 certs = tmp_certs;
222}
223
224
225// cert-managing routines
226
227cert::cert()
228{}
229
230cert::cert(std::string const & s)
231{
232 read_cert(s, *this);
233}
234cert::cert(std::string const & s, made_from_t m)
235 : origin_aware(m)
236{
237 read_cert(s, *this);
238}
239
240cert::cert(revision_id const & ident,
241 cert_name const & name,
242 cert_value const & value,
243 rsa_keypair_id const & key)
244 : ident(ident), name(name), value(value), key(key)
245{}
246
247cert::cert(revision_id const & ident,
248 cert_name const & name,
249 cert_value const & value,
250 rsa_keypair_id const & key,
251 rsa_sha1_signature const & sig)
252 : ident(ident), name(name), value(value), key(key), sig(sig)
253{}
254
255bool
256cert::operator<(cert const & other) const
257{
258 return (ident < other.ident)
259 || ((ident == other.ident) && name < other.name)
260 || (((ident == other.ident) && name == other.name)
261 && value < other.value)
262 || ((((ident == other.ident) && name == other.name)
263 && value == other.value) && key < other.key)
264 || (((((ident == other.ident) && name == other.name)
265 && value == other.value) && key == other.key) && sig < other.sig);
266}
267
268bool
269cert::operator==(cert const & other) const
270{
271 return
272 (ident == other.ident)
273 && (name == other.name)
274 && (value == other.value)
275 && (key == other.key)
276 && (sig == other.sig);
277}
278
279// netio support
280
281void
282read_cert(string const & in, cert & t)
283{
284 size_t pos = 0;
285 id hash = id(extract_substring(in, pos,
286 constants::merkle_hash_length_in_bytes,
287 "cert hash"));
288 revision_id ident = revision_id(extract_substring(in, pos,
289 constants::merkle_hash_length_in_bytes,
290 "cert ident"));
291 string name, val, key, sig;
292 extract_variable_length_string(in, name, pos, "cert name");
293 extract_variable_length_string(in, val, pos, "cert val");
294 extract_variable_length_string(in, key, pos, "cert key");
295 extract_variable_length_string(in, sig, pos, "cert sig");
296 assert_end_of_buffer(in, pos, "cert");
297
298 cert tmp(ident, cert_name(name), cert_value(val), rsa_keypair_id(key),
299 rsa_sha1_signature(sig));
300
301 id check;
302 cert_hash_code(tmp, check);
303 if (!(check == hash))
304 throw bad_decode(F("calculated cert hash '%s' does not match '%s'")
305 % check % hash);
306 t = tmp;
307}
308
309void
310write_cert(cert const & t, string & out)
311{
312 string name, key;
313 id hash;
314
315 cert_hash_code(t, hash);
316
317 out.append(hash());
318 out.append(t.ident.inner()());
319 insert_variable_length_string(t.name(), out);
320 insert_variable_length_string(t.value(), out);
321 insert_variable_length_string(t.key(), out);
322 insert_variable_length_string(t.sig(), out);
323}
324
325void
326cert_signable_text(cert const & t, string & out)
327{
328 base64<cert_value> val_encoded(encode_base64(t.value));
329 string ident_encoded(encode_hexenc(t.ident.inner()()));
330
331 out.clear();
332 out.reserve(4 + t.name().size() + ident_encoded.size()
333 + val_encoded().size());
334
335 out += '[';
336 out.append(t.name());
337 out += '@';
338 out.append(ident_encoded);
339 out += ':';
340 append_without_ws(out, val_encoded());
341 out += ']';
342
343 L(FL("cert: signable text %s") % out);
344}
345
346void
347cert_hash_code(cert const & t, id & out)
348{
349 base64<rsa_sha1_signature> sig_encoded(encode_base64(t.sig));
350 base64<cert_value> val_encoded(encode_base64(t.value));
351 string ident_encoded(encode_hexenc(t.ident.inner()()));
352 string tmp;
353 tmp.reserve(4 + ident_encoded.size()
354 + t.name().size() + val_encoded().size()
355 + t.key().size() + sig_encoded().size());
356 tmp.append(ident_encoded);
357 tmp += ':';
358 tmp.append(t.name());
359 tmp += ':';
360 append_without_ws(tmp, val_encoded());
361 tmp += ':';
362 tmp.append(t.key());
363 tmp += ':';
364 append_without_ws(tmp, sig_encoded());
365
366 data tdat(tmp);
367 calculate_ident(tdat, out);
368}
369
370cert_status
371check_cert(database & db, cert const & t)
372{
373 string signed_text;
374 cert_signable_text(t, signed_text);
375 return db.check_signature(t.key, signed_text, t.sig);
376}
377
378bool
379put_simple_revision_cert(database & db,
380 key_store & keys,
381 revision_id const & id,
382 cert_name const & nm,
383 cert_value const & val)
384{
385 I(!keys.signing_key().empty());
386
387 cert t(id, nm, val, keys.signing_key);
388 string signed_text;
389 cert_signable_text(t, signed_text);
390 load_key_pair(keys, t.key);
391 keys.make_signature(db, t.key, signed_text, t.sig);
392
393 revision<cert> cc(t);
394 return db.put_revision_cert(cc);
395}
396
397// "special certs"
398
399// Guess which branch is appropriate for a commit below IDENT.
400// OPTS may override. Branch name is returned in BRANCHNAME.
401// Does not modify branch state in OPTS.
402void
403guess_branch(options & opts, project_t & project,
404 revision_id const & ident, branch_name & branchname)
405{
406 if (opts.branch_given && !opts.branchname().empty())
407 branchname = opts.branchname;
408 else
409 {
410 N(!ident.inner()().empty(),
411 F("no branch found for empty revision, "
412 "please provide a branch name"));
413
414 set<branch_name> branches;
415 project.get_revision_branches(ident, branches);
416
417 N(!branches.empty(),
418 F("no branch certs found for revision %s, "
419 "please provide a branch name") % ident);
420
421 N(branches.size() == 1,
422 F("multiple branch certs found for revision %s, "
423 "please provide a branch name") % ident);
424
425 set<branch_name>::iterator i = branches.begin();
426 I(i != branches.end());
427 branchname = *i;
428 }
429}
430
431// As above, but set the branch name in the options
432// if it wasn't already set.
433void
434guess_branch(options & opts, project_t & project, revision_id const & ident)
435{
436 branch_name branchname;
437 guess_branch(opts, project, ident, branchname);
438 opts.branchname = branchname;
439}
440
441void
442cert_revision_in_branch(database & db,
443 key_store & keys,
444 revision_id const & rev,
445 branch_name const & branch)
446{
447 put_simple_revision_cert(db, keys, rev, branch_cert_name,
448 cert_value(branch()));
449}
450
451void
452cert_revision_suspended_in_branch(database & db,
453 key_store & keys,
454 revision_id const & rev,
455 branch_name const & branch)
456{
457 put_simple_revision_cert(db, keys, rev, suspend_cert_name,
458 cert_value(branch()));
459}
460
461
462// "standard certs"
463
464void
465cert_revision_date_time(database & db,
466 key_store & keys,
467 revision_id const & rev,
468 date_t const & t)
469{
470 cert_value val = cert_value(t.as_iso_8601_extended());
471 put_simple_revision_cert(db, keys, rev, date_cert_name, val);
472}
473
474void
475cert_revision_author(database & db,
476 key_store & keys,
477 revision_id const & rev,
478 string const & author)
479{
480 put_simple_revision_cert(db, keys, rev, author_cert_name,
481 cert_value(author));
482}
483
484void
485cert_revision_tag(database & db,
486 key_store & keys,
487 revision_id const & rev,
488 string const & tagname)
489{
490 put_simple_revision_cert(db, keys, rev, tag_cert_name,
491 cert_value(tagname));
492}
493
494void
495cert_revision_changelog(database & db,
496 key_store & keys,
497 revision_id const & rev,
498 utf8 const & log)
499{
500 put_simple_revision_cert(db, keys, rev, changelog_cert_name,
501 cert_value(log()));
502}
503
504void
505cert_revision_comment(database & db,
506 key_store & keys,
507 revision_id const & rev,
508 utf8 const & comment)
509{
510 put_simple_revision_cert(db, keys, rev, comment_cert_name,
511 cert_value(comment()));
512}
513
514void
515cert_revision_testresult(database & db,
516 key_store & keys,
517 revision_id const & rev,
518 string const & results)
519{
520 bool passed = false;
521 if (lowercase(results) == "true" ||
522 lowercase(results) == "yes" ||
523 lowercase(results) == "pass" ||
524 results == "1")
525 passed = true;
526 else if (lowercase(results) == "false" ||
527 lowercase(results) == "no" ||
528 lowercase(results) == "fail" ||
529 results == "0")
530 passed = false;
531 else
532 throw informative_failure("could not interpret test results, "
533 "tried '0/1' 'yes/no', 'true/false', "
534 "'pass/fail'");
535
536 put_simple_revision_cert(db, keys, rev, testresult_cert_name,
537 cert_value(lexical_cast<string>(passed)));
538}
539
540// Local Variables:
541// mode: C++
542// fill-column: 76
543// c-file-style: "gnu"
544// indent-tabs-mode: nil
545// End:
546// 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