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}
234
235cert::cert(revision_id const & ident,
236 cert_name const & name,
237 cert_value const & value,
238 rsa_keypair_id const & key)
239 : ident(ident), name(name), value(value), key(key)
240{}
241
242cert::cert(revision_id const & ident,
243 cert_name const & name,
244 cert_value const & value,
245 rsa_keypair_id const & key,
246 rsa_sha1_signature const & sig)
247 : ident(ident), name(name), value(value), key(key), sig(sig)
248{}
249
250bool
251cert::operator<(cert const & other) const
252{
253 return (ident < other.ident)
254 || ((ident == other.ident) && name < other.name)
255 || (((ident == other.ident) && name == other.name)
256 && value < other.value)
257 || ((((ident == other.ident) && name == other.name)
258 && value == other.value) && key < other.key)
259 || (((((ident == other.ident) && name == other.name)
260 && value == other.value) && key == other.key) && sig < other.sig);
261}
262
263bool
264cert::operator==(cert const & other) const
265{
266 return
267 (ident == other.ident)
268 && (name == other.name)
269 && (value == other.value)
270 && (key == other.key)
271 && (sig == other.sig);
272}
273
274// netio support
275
276void
277read_cert(string const & in, cert & t)
278{
279 size_t pos = 0;
280 id hash = id(extract_substring(in, pos,
281 constants::merkle_hash_length_in_bytes,
282 "cert hash"));
283 revision_id ident = revision_id(extract_substring(in, pos,
284 constants::merkle_hash_length_in_bytes,
285 "cert ident"));
286 string name, val, key, sig;
287 extract_variable_length_string(in, name, pos, "cert name");
288 extract_variable_length_string(in, val, pos, "cert val");
289 extract_variable_length_string(in, key, pos, "cert key");
290 extract_variable_length_string(in, sig, pos, "cert sig");
291 assert_end_of_buffer(in, pos, "cert");
292
293 cert tmp(ident, cert_name(name), cert_value(val), rsa_keypair_id(key),
294 rsa_sha1_signature(sig));
295
296 id check;
297 cert_hash_code(tmp, check);
298 if (!(check == hash))
299 throw bad_decode(F("calculated cert hash '%s' does not match '%s'")
300 % check % hash);
301 t = tmp;
302}
303
304void
305write_cert(cert const & t, string & out)
306{
307 string name, key;
308 id hash;
309
310 cert_hash_code(t, hash);
311
312 out.append(hash());
313 out.append(t.ident.inner()());
314 insert_variable_length_string(t.name(), out);
315 insert_variable_length_string(t.value(), out);
316 insert_variable_length_string(t.key(), out);
317 insert_variable_length_string(t.sig(), out);
318}
319
320void
321cert_signable_text(cert const & t, string & out)
322{
323 base64<cert_value> val_encoded(encode_base64(t.value));
324 string ident_encoded(encode_hexenc(t.ident.inner()()));
325
326 out.clear();
327 out.reserve(4 + t.name().size() + ident_encoded.size()
328 + val_encoded().size());
329
330 out += '[';
331 out.append(t.name());
332 out += '@';
333 out.append(ident_encoded);
334 out += ':';
335 append_without_ws(out, val_encoded());
336 out += ']';
337
338 L(FL("cert: signable text %s") % out);
339}
340
341void
342cert_hash_code(cert const & t, id & out)
343{
344 base64<rsa_sha1_signature> sig_encoded(encode_base64(t.sig));
345 base64<cert_value> val_encoded(encode_base64(t.value));
346 string ident_encoded(encode_hexenc(t.ident.inner()()));
347 string tmp;
348 tmp.reserve(4 + ident_encoded.size()
349 + t.name().size() + val_encoded().size()
350 + t.key().size() + sig_encoded().size());
351 tmp.append(ident_encoded);
352 tmp += ':';
353 tmp.append(t.name());
354 tmp += ':';
355 append_without_ws(tmp, val_encoded());
356 tmp += ':';
357 tmp.append(t.key());
358 tmp += ':';
359 append_without_ws(tmp, sig_encoded());
360
361 data tdat(tmp);
362 calculate_ident(tdat, out);
363}
364
365cert_status
366check_cert(database & db, cert const & t)
367{
368 string signed_text;
369 cert_signable_text(t, signed_text);
370 return db.check_signature(t.key, signed_text, t.sig);
371}
372
373bool
374put_simple_revision_cert(database & db,
375 key_store & keys,
376 revision_id const & id,
377 cert_name const & nm,
378 cert_value const & val)
379{
380 I(!keys.signing_key().empty());
381
382 cert t(id, nm, val, keys.signing_key);
383 string signed_text;
384 cert_signable_text(t, signed_text);
385 load_key_pair(keys, t.key);
386 keys.make_signature(db, t.key, signed_text, t.sig);
387
388 revision<cert> cc(t);
389 return db.put_revision_cert(cc);
390}
391
392// "special certs"
393
394// Guess which branch is appropriate for a commit below IDENT.
395// OPTS may override. Branch name is returned in BRANCHNAME.
396// Does not modify branch state in OPTS.
397void
398guess_branch(options & opts, project_t & project,
399 revision_id const & ident, branch_name & branchname)
400{
401 if (opts.branch_given && !opts.branchname().empty())
402 branchname = opts.branchname;
403 else
404 {
405 N(!ident.inner()().empty(),
406 F("no branch found for empty revision, "
407 "please provide a branch name"));
408
409 set<branch_name> branches;
410 project.get_revision_branches(ident, branches);
411
412 N(branches.size() != 0,
413 F("no branch certs found for revision %s, "
414 "please provide a branch name") % ident);
415
416 N(branches.size() == 1,
417 F("multiple branch certs found for revision %s, "
418 "please provide a branch name") % ident);
419
420 set<branch_name>::iterator i = branches.begin();
421 I(i != branches.end());
422 branchname = *i;
423 }
424}
425
426// As above, but set the branch name in the options
427// if it wasn't already set.
428void
429guess_branch(options & opts, project_t & project, revision_id const & ident)
430{
431 branch_name branchname;
432 guess_branch(opts, project, ident, branchname);
433 opts.branchname = branchname;
434}
435
436void
437cert_revision_in_branch(database & db,
438 key_store & keys,
439 revision_id const & rev,
440 branch_name const & branch)
441{
442 put_simple_revision_cert(db, keys, rev, branch_cert_name,
443 cert_value(branch()));
444}
445
446void
447cert_revision_suspended_in_branch(database & db,
448 key_store & keys,
449 revision_id const & rev,
450 branch_name const & branch)
451{
452 put_simple_revision_cert(db, keys, rev, suspend_cert_name,
453 cert_value(branch()));
454}
455
456
457// "standard certs"
458
459void
460cert_revision_date_time(database & db,
461 key_store & keys,
462 revision_id const & rev,
463 date_t const & t)
464{
465 cert_value val = cert_value(t.as_iso_8601_extended());
466 put_simple_revision_cert(db, keys, rev, date_cert_name, val);
467}
468
469void
470cert_revision_author(database & db,
471 key_store & keys,
472 revision_id const & rev,
473 string const & author)
474{
475 put_simple_revision_cert(db, keys, rev, author_cert_name,
476 cert_value(author));
477}
478
479void
480cert_revision_tag(database & db,
481 key_store & keys,
482 revision_id const & rev,
483 string const & tagname)
484{
485 put_simple_revision_cert(db, keys, rev, tag_cert_name,
486 cert_value(tagname));
487}
488
489void
490cert_revision_changelog(database & db,
491 key_store & keys,
492 revision_id const & rev,
493 utf8 const & log)
494{
495 put_simple_revision_cert(db, keys, rev, changelog_cert_name,
496 cert_value(log()));
497}
498
499void
500cert_revision_comment(database & db,
501 key_store & keys,
502 revision_id const & rev,
503 utf8 const & comment)
504{
505 put_simple_revision_cert(db, keys, rev, comment_cert_name,
506 cert_value(comment()));
507}
508
509void
510cert_revision_testresult(database & db,
511 key_store & keys,
512 revision_id const & rev,
513 string const & results)
514{
515 bool passed = false;
516 if (lowercase(results) == "true" ||
517 lowercase(results) == "yes" ||
518 lowercase(results) == "pass" ||
519 results == "1")
520 passed = true;
521 else if (lowercase(results) == "false" ||
522 lowercase(results) == "no" ||
523 lowercase(results) == "fail" ||
524 results == "0")
525 passed = false;
526 else
527 throw informative_failure("could not interpret test results, "
528 "tried '0/1' 'yes/no', 'true/false', "
529 "'pass/fail'");
530
531 put_simple_revision_cert(db, keys, rev, testresult_cert_name,
532 cert_value(lexical_cast<string>(passed)));
533}
534
535// Local Variables:
536// mode: C++
537// fill-column: 76
538// c-file-style: "gnu"
539// indent-tabs-mode: nil
540// End:
541// 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