# # # add_dir "tests/bad_and_unknown_certs" # # add_file "tests/bad_and_unknown_certs/__driver__.lua" # content [f43f4dde0eed523aa4e85d46b75b22e0d264ac94] # # patch "NEWS" # from [a5c303d1604b8e394608ee7a8fdfd03e774a02c3] # to [3584bdcb72d68cfa4395c3463b0922747afc50a3] # # patch "database.cc" # from [5a61650edb2b4f71f83c39f327062de5ed827485] # to [87d9f88fe45a8734efdf378c0504d1eac5f53aa6] # ============================================================ --- tests/bad_and_unknown_certs/__driver__.lua f43f4dde0eed523aa4e85d46b75b22e0d264ac94 +++ tests/bad_and_unknown_certs/__driver__.lua f43f4dde0eed523aa4e85d46b75b22e0d264ac94 @@ -0,0 +1,17 @@ +mtn_setup() + +addfile("foo", "bar") +commit() +rev_1 = base_revision() + +writefile("foo", "foo") +commit() +rev_2 = base_revision() + +check(mtn("genkey", "address@hidden"), 0, false, false, string.rep("address@hidden", 2)) +check(mtn("approve", rev_1, "address@hidden"), 0, false, false) +check(mtn("dropkey", "address@hidden"), 0, false, false) +check(mtn("heads"), 0, true, true) +check(qgrep(rev_1, "stdout")) +check(qgrep("unknown.*" .. rev_2, "stderr")) +check(not qgrep("unknown.*" .. rev_1, "stderr")) \ No newline at end of file ============================================================ --- NEWS a5c303d1604b8e394608ee7a8fdfd03e774a02c3 +++ NEWS 3584bdcb72d68cfa4395c3463b0922747afc50a3 @@ -31,6 +31,11 @@ xxx xxx xx xx:xx:xx UTC 2010 with the new information displayed by 'commit' so that all three commands display revisions similarly. + - Monotone will only warn about bad certs if there are not also matching + trusted certs. So if someone commits a bad branch cert, monotone will + only warn about that bad cert until someone else approves that + revision into the same branch. + New features - New automation command 'update' which behaves identical to ============================================================ --- database.cc 5a61650edb2b4f71f83c39f327062de5ed827485 +++ database.cc 87d9f88fe45a8734efdf378c0504d1eac5f53aa6 @@ -3819,96 +3819,86 @@ namespace { // FIXME: the bogus-cert family of functions is ridiculous // and needs to be replaced, or at least factored. namespace { - struct - bogus_cert_p + struct trust_value { - database & db; - bogus_cert_p(database & db) : db(db) {}; - - bool operator()(cert const & c) const - { - cert_status status = db.check_cert(c); - if (status == cert_ok) - { - L(FL("cert ok")); - return false; - } - else if (status == cert_bad) - { - string txt; - c.signable_text(txt); - W(F("ignoring bad signature by '%s' on '%s'") % c.key % txt); - return true; - } - else - { - I(status == cert_unknown); - string txt; - c.signable_text(txt); - W(F("ignoring unknown signature by '%s' on '%s'") % c.key % txt); - return true; - } - } + set good_sigs; + set bad_sigs; + set unknown_sigs; }; - + + // returns *one* of each trusted cert key/value + // if two keys signed the same thing, we get two certs as input and + // just pick one (assuming neither is invalid) to use in the output void erase_bogus_certs_internal(vector & certs, database & db, database::cert_trust_checker const & checker) { - typedef vector::iterator it; - it e = remove_if(certs.begin(), certs.end(), bogus_cert_p(db)); - certs.erase(e, certs.end()); - - vector tmp_certs; - // sorry, this is a crazy data structure typedef tuple trust_key; - typedef map< trust_key, - pair< shared_ptr< set >, it > > trust_map; + typedef map< trust_key, trust_value > trust_map; trust_map trust; - for (it i = certs.begin(); i != certs.end(); ++i) + for (vector::iterator i = certs.begin(); i != certs.end(); ++i) { trust_key key = trust_key(i->ident.inner(), i->name, i->value); - trust_map::iterator j = trust.find(key); - shared_ptr< set > s; - if (j == trust.end()) + trust_value & value = trust[key]; + switch (db.check_cert(*i)) { - s.reset(new set()); - trust.insert(make_pair(key, make_pair(s, i))); + case cert_ok: + value.good_sigs.insert(i->key); + break; + case cert_bad: + value.bad_sigs.insert(i->key); + break; + case cert_unknown: + value.unknown_sigs.insert(i->key); + break; } - else - s = j->second.first; - s->insert(i->key); } + certs.clear(); + for (trust_map::const_iterator i = trust.begin(); i != trust.end(); ++i) { - if (checker(*(i->second.first), + cert out(typecast_vocab(get<0>(i->first)), + get<1>(i->first), get<2>(i->first), key_id()); + if (!i->second.good_sigs.empty() && + checker(i->second.good_sigs, get<0>(i->first), get<1>(i->first), get<2>(i->first))) { - if (global_sanity.debug_p()) - L(FL("trust function liked %d signers of %s cert on revision %s") - % i->second.first->size() - % get<1>(i->first) - % get<0>(i->first)); - tmp_certs.push_back(*(i->second.second)); + L(FL("trust function liked %d signers of %s cert on revision %s") + % i->second.good_sigs.size() + % get<1>(i->first) + % get<0>(i->first)); + out.key = *i->second.good_sigs.begin(); + certs.push_back(out); } else { + string txt; + out.signable_text(txt); + for (set::const_iterator b = i->second.bad_sigs.begin(); + b != i->second.bad_sigs.end(); ++b) + { + W(F("ignoring bad signature by '%s' on '%s'") % *b % txt); + } + for (set::const_iterator u = i->second.unknown_sigs.begin(); + u != i->second.unknown_sigs.end(); ++u) + { + W(F("ignoring unknown signature by '%s' on '%s'") % *u % txt); + } W(F("trust function disliked %d signers of %s cert on revision %s") - % i->second.first->size() + % i->second.good_sigs.size() % get<1>(i->first) % get<0>(i->first)); } } - certs = tmp_certs; } // the lua hook wants key_identity_info, but all that's been // pulled from the certs is key_id. So this is needed to translate.