1 /* fides.cc - Light-weight, decentralised trust and authorisation management
2 Copyright (C) 2008-2009 Guus Sliepen <guus@tinc-vpn.org>
4 Fides is free software; you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License as
6 published by the Free Software Foundation; either version 2.1 of
7 the License, or (at your option) any later version.
9 Fides is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
14 You should have received a copy of the GNU Lesser General Public
15 License along with this program; if not, see <http://www.gnu.org/licenses/>.
21 #include <sys/types.h>
30 #define FIDES_DEBUG false
33 #define debug if(FIDES_DEBUG)
38 static regexp authexp("^a[+0-] ");
39 static regexp trustexp("^t[+0-] ");
41 /// Saves a certificate to a file.
43 /// @param cert Certificate to save.
44 /// @param filename File to save the certificate to.
45 void Manager::certificate_save(const Certificate *cert, const std::string &filename) const {
46 ofstream file(filename.c_str());
47 file << cert->to_string() << '\n';
50 /// Loads a certificate from a file.
52 /// @param filename File to save the certificate to.
53 /// @return The certificate.
54 Certificate *Manager::certificate_load(const std::string &filename) {
55 ifstream file(filename.c_str());
58 return certificate_from_string(data);
61 /// Loads a certificate from a string.
63 /// @param data String containing the certificate in textual form.
64 /// @return The certificate.
65 Certificate *Manager::certificate_from_string(const std::string &data) {
67 e = data.find(' ', 0);
69 throw exception("Invalid Certificate");
70 string fingerprint = hexdecode(data.substr(0, e));
71 const PublicKey *signer = find_key(fingerprint);
73 throw exception("Unknown public key");
75 e = data.find('.', b);
77 throw exception("Invalid Certificate");
78 struct timeval timestamp;
79 timestamp.tv_sec = atol(data.c_str() + b);
81 timestamp.tv_usec = atol(data.c_str() + b);
82 e = data.find(' ', b);
84 throw exception("Invalid Certificate");
86 e = data.find(' ', b);
88 throw exception("Invalid Certificate");
89 string signature = b64decode(data.substr(b, e - b));
91 string statement = data.substr(b);
93 return new Certificate(signer, timestamp, statement, signature);
98 /// \brief Interaction with a Fides database.
100 /// A Manager object manages a database of public keys and certificates.
101 /// New certificates can be created, certificates can be imported and exported,
102 /// and queries can be done on the database.
105 /// Creates a new handle on a Fides database.
107 /// Will load the private key, known public keys and certificates.
108 /// After that it will calculate the trust value of all keys.
110 /// @param dir Directory where Fides stores the keys and certificates.
111 /// If no directory is specified, the following environment variables
112 /// are used, in the given order:
116 Manager::Manager(const std::string &dir): homedir(dir) {
117 debug cerr << "Fides initialising\n";
119 // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
121 homedir = getenv("FIDES_HOME") ?: "";
122 if(homedir.empty()) {
124 homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
125 homedir += "/.fides";
128 // Derived directories
130 certdir = homedir + "certs/";
131 keydir = homedir + "keys/";
132 obsoletedir = homedir + ".obsolete_certs/";
134 // Ensure the homedir and its subdirectories exist
135 mkdir(homedir.c_str(), 0700);
136 mkdir(certdir.c_str(), 0700);
137 mkdir(keydir.c_str(), 0700);
138 mkdir(obsoletedir.c_str(), 0700);
141 mykey.load_private(homedir + "priv");
143 } catch(exception &e) {
144 cerr << "Fides generating keypair\n";
146 mykey.save_private(homedir + "priv");
147 mykey.save(keydir + hexencode(mykey.fingerprint()));
150 vector<string> files = dirlist(keydir);
151 for(size_t i = 0; i < files.size(); ++i) {
152 debug cerr << "Loading key " << files[i] << '\n';
154 PublicKey *key = new PublicKey();
155 key->load(keydir + files[i]);
156 keys[hexdecode(files[i])] = key;
159 keys[mykey.fingerprint()] = &mykey;
161 files = dirlist(certdir);
162 for(size_t i = 0; i < files.size(); ++i) {
163 debug cerr << "Loading Certificate " << files[i] << '\n';
164 Certificate *cert = certificate_load(certdir + files[i]);
165 if(false && !cert->validate()) {
166 cerr << "Bad Certificate in database: " << cert->to_string() << '\n';
169 certs[hexdecode(files[i])] = cert;
172 /// \TODO save and load this value
179 Manager::~Manager() {
180 debug cerr << "Fides exitting\n";
181 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i)
183 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
184 if(i->second != &mykey)
188 /// Checks the validaty of all certificates.
190 /// @return True if all known certificates are valid, false otherwise.
191 bool Manager::fsck() const {
194 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i) {
195 if(!i->second->validate()) {
196 cerr << "Validation of Certificate failed: " << i->second->to_string() << '\n';
201 cerr << errors << " errors in " << certs.size() << " Certificates\n";
205 /// Returns the base directory used by Fides.
207 /// @return The home directory.
208 string Manager::get_homedir() const {
212 /// Tests whether this is the first time Fides has run and has generated new keys.
214 /// @return True if this is the first time, false otherwise.
215 bool Manager::is_firstrun() const {
219 /// Find the public key corresponding to a given fingerprint.
221 /// @param fingerprint String containing a fingerprint.
222 /// @return Pointer to the public key corresponding to the fingerprint, or NULL if it was not found.
223 PublicKey *Manager::find_key(const std::string &fingerprint) const {
224 map<string, PublicKey *>::const_iterator i;
225 i = keys.find(fingerprint);
232 /// Find all certificates from a give public key and that match a regular expression.
234 /// @param signer Public key to match certificates to.
235 /// @param regex Regular expression to match the statement of each certificate to.
236 /// @return A vector of certificates that match the criteria.
237 vector<const Certificate *> Manager::find_certificates(const PublicKey *signer, const std::string ®ex) const {
238 vector<const Certificate *> found;
239 map<string, Certificate *>::const_iterator i;
240 regexp regexp(regex);
241 for(i = certs.begin(); i != certs.end(); ++i) {
243 cerr << "No Certificate for " << hexencode(i->first) << '\n';
246 if(i->second->signer == signer)
247 if(regexp.match(i->second->statement))
248 found.push_back(i->second);
253 /// Find all certificates that match a regular expression.
255 /// @param regex Regular expression to match the statement of each certificate to.
256 /// @return A vector of certificates that match the criteria.
257 vector<const Certificate *> Manager::find_certificates(const std::string ®ex) const {
258 vector<const Certificate *> found;
259 map<string, Certificate *>::const_iterator i;
260 regexp regexp(regex);
261 for(i = certs.begin(); i != certs.end(); ++i)
262 if(regexp.match(i->second->statement))
263 found.push_back(i->second);
267 /// Find all certificates from a give public key.
269 /// @param signer Public key to match certificates to.
270 /// @return A vector of certificates that match the criteria.
271 vector<const Certificate *> Manager::find_certificates(const PublicKey *signer) const {
272 vector<const Certificate *> found;
273 map<string, Certificate *>::const_iterator i;
274 for(i = certs.begin(); i != certs.end(); ++i)
275 if(i->second->signer == signer)
276 found.push_back(i->second);
280 /// Import public keys and certificates from a stream.
282 /// @param in Stream to read from.
283 void Manager::import_all(std::istream &in) {
287 while(getline(in, line)) {
291 if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
293 if(!line.compare(0, 9, "-----END ")) {
294 PublicKey *key = new PublicKey();
295 key->from_string(pem);
296 debug cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
305 Certificate *cert = certificate_from_string(line);
306 debug cerr << "Importing Certificate " << hexencode(cert->fingerprint()) << '\n';
311 /// Export all public keys and certificates to a stream.
313 /// @param out Stream to write to.
314 void Manager::export_all(std::ostream &out) const {
315 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
316 out << i->second->to_string();
317 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i)
318 out << i->second->to_string() << '\n';
321 /// Trust a public key.
323 /// This creates a certificate that says that we trust the given public key.
324 /// If a key is trusted, then authorisation certificates from that key are taken into account
325 /// when calling functions such as Manager::is_allowed().
327 /// @param key Public key to trust.
328 void Manager::trust(const PublicKey *key) {
329 string full = "t+ " + hexencode(key->fingerprint());
333 /// Distrust a public key.
335 /// This creates a certificate that says that we distrust the given public key.
336 /// If a key is distrusted, then authorisation certificates from that key are not taken into account
337 /// when calling functions such as Manager::is_allowed().
339 /// @param key Public key to trust.
340 void Manager::distrust(const PublicKey *key) {
341 string full = "t- " + hexencode(key->fingerprint());
345 /// Don't care about a public key.
347 /// This creates a certificate that says that we neither trust nor distrust the given public key.
348 /// This key and certificates created by it are then treated as if we have never trusted nor distrusted this key.
350 /// @param key Public key to trust.
351 void Manager::dctrust(const PublicKey *key) {
352 string full = "t0 " + hexencode(key->fingerprint());
356 /// Recalculate the trust value of all known public keys.
357 void Manager::update_trust() {
358 // clear trust on all keys
359 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
360 i->second->trust = 0;
362 // Start by checking all trust certificates from ourself.
363 // If another key is positively or negatively trusted, update its trust score
364 // and add it to the the list of new keys to check.
365 // Then add our own key to the list of already checked keys.
366 // Then check all the trust certificates of those on the tocheck list, etc.
367 // Already checked keys are never updated anymore (TODO: is that smart?)
368 // Certificates of keys with a zero or negative trust score are not processed.
370 set<PublicKey *> checked;
371 set<PublicKey *> tocheck;
372 set<PublicKey *> newkeys;
373 set<PublicKey *>::iterator i;
376 tocheck.insert(&mykey);
378 while(tocheck.size()) {
380 checked.insert(tocheck.begin(), tocheck.end());
383 // loop over all keys whose certificates need to be checked
385 for(i = tocheck.begin(); i != tocheck.end(); ++i) {
386 debug cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
388 // except if this key is not trusted
393 // find all non-zero trust certificates of this key
395 vector<const Certificate *> matches = find_certificates(*i, "^t[+-] ");
397 // update trust value of those keys
399 for(size_t j = 0; j < matches.size(); j++) {
400 PublicKey *other = find_key(hexdecode(matches[j]->statement.substr(3)));
403 cerr << "Trust Certificate for unknown key: " << matches[j]->to_string() << '\n';
407 // except for keys we already checked
409 if(checked.find(other) != checked.end()) {
410 debug cerr << "Skipping trust Certificate for already checked key: " << matches[j]->to_string() << '\n';
416 if(matches[j]->statement[1] == '+')
421 newkeys.insert(other);
429 /// Merges a public key into the database.
431 /// @param key The public key to merge.
432 void Manager::merge(PublicKey *key) {
433 if(keys.find(key->fingerprint()) != keys.end()) {
434 debug cerr << "Key already known\n";
438 keys[key->fingerprint()] = key;
439 key->save(keydir + hexencode(key->fingerprint()));
442 /// Merges a certificate into the database.
444 /// The database is searched to find if there are certificates from the same signer
445 /// with similar statements.
446 /// If the given certificate is similar to another one in our database,
447 /// then the certificate with the newer timestamp wins and will be allowed in the database,
448 /// the older certificate will be removed.
450 /// @param cert The certificate to merge.
451 void Manager::merge(Certificate *cert) {
452 // TODO: check if cert is already in database
453 // TODO: check if cert obsoletes other certs
455 // If we already know this certificate, drop it.
456 if(certs.find(cert->fingerprint()) != certs.end()) {
457 debug cerr << "Certificate already known\n";
461 // If the certificate does not validate, drop it.
462 if(!cert->validate()) {
463 // TODO: this should not happen, be wary of DoS attacks
464 cerr << "Trying to merge invalid Certificate: " << cert->to_string() << '\n';
468 vector<const Certificate *> others;
470 // Is this an authorisation cert?
471 if(authexp.match(cert->statement)) {
472 // Find certs identical except for the +/-/0
473 // TODO: escape statement in regexp
474 others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
476 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
477 debug cerr << "Certificate is overruled by a newer Certificate\n";
480 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
481 // TODO: this should not happen, be wary of DoS attacks
482 debug cerr << "Certificate has same timestamp as another timestamp!\n";
485 debug cerr << "Certificate overrules an older Certificate!\n";
486 // save new cert first
487 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
488 certs[cert->fingerprint()] = cert;
491 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
492 certs.erase(others[0]->fingerprint());
498 // Is this a trust cert?
499 // TODO: it's just the same as above!
500 if(trustexp.match(cert->statement)) {
501 // Find certs identical except for the +/-/0
502 // TODO: escape statement in regexp
503 others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
505 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
506 debug cerr << "Certificate is overruled by a newer Certificate\n";
509 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
510 // TODO: this should not happen, be wary of DoS attacks
511 debug cerr << "Certificate has same timestamp as another timestamp!\n";
514 debug cerr << "Certificate overrules an older Certificate!\n";
516 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
517 certs.erase(others[0]->fingerprint());
519 certs[cert->fingerprint()] = cert;
520 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
525 // Did somebody sign the exact same statement twice?
526 // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
527 others = find_certificates(cert->signer, string("^") + cert->statement + '$');
529 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
530 debug cerr << "Certificate is overruled by a newer Certificate\n";
533 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
534 // TODO: this should not happen, be wary of DoS attacks
535 debug cerr << "Certificate has same timestamp as another timestamp!\n";
538 debug cerr << "Certificate overrules an older Certificate!\n";
540 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
541 certs.erase(others[0]->fingerprint());
543 certs[cert->fingerprint()] = cert;
544 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
548 debug cerr << "Certificate is new\n";
549 certs[cert->fingerprint()] = cert;
550 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
553 /// Calculates whether a statement is allowed or denied.
555 /// @param statement The statement to calculate the authorisation values for.
556 /// @param self Will be set to 1 if we allow the statement,
557 /// 0 if we neither allowed nor denied it,
558 /// or -1 if we denied it.
559 /// @param trusted Will be positive if the majority of the trusted public keys
560 /// gave a positive authorisation, 0 if there is a tie,
561 /// or negative if the majority gave a negative authorisation.
562 /// @param all Same as trusted but for all public keys.
563 void Manager::auth_stats(const std::string &statement, int &self, int &trusted, int &all) const {
564 self = trusted = all = 0;
565 vector<const Certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
566 for(size_t i = 0; i < matches.size(); ++i) {
567 char code = matches[i]->statement[1];
573 if(matches[i]->signer == &mykey)
575 if(matches[i]->signer->trust > 0)
581 /// Tests whether the given public key is trusted.
583 /// @param key The public key to test.
584 /// @return True if the key is explicitly trusted, false otherwise.
585 bool Manager::is_trusted(const PublicKey *key) const {
586 return key->trust > 0;
589 /// Tests whether the given public key is distrusted.
591 /// @param key The public key to test.
592 /// @return True if the key is explicitly distrusted, false otherwise.
593 bool Manager::is_distrusted(const PublicKey *key) const {
594 return key->trust < 0;
597 /// Tests whether the given statement is allowed.
599 /// @param statement The statement to test.
600 /// @param key The public key to test.
601 /// @return True if the statement is allowed for the given key, false otherwise.
602 bool Manager::is_allowed(const std::string &statement, const PublicKey *key) const {
603 int self, trusted, all;
606 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
608 auth_stats(statement, self, trusted, all);
618 /// Tests whether the given statement is denied.
620 /// @param statement The statement to test.
621 /// @param key The public key to test.
622 /// @return True if the statement is denied for the given key, false otherwise.
623 bool Manager::is_denied(const std::string &statement, const PublicKey *key) const {
624 int self, trusted, all;
627 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
629 auth_stats(statement, self, trusted, all);
639 /// Creates a certificate for the given statement.
641 /// @param statement The statement to create a certificate for.
642 void Manager::sign(const std::string &statement) {
643 // Try to set "latest" to now, but ensure monoticity
645 gettimeofday(&now, 0);
646 if(timercmp(&latest, &now, >=)) {
648 if(latest.tv_usec >= 1000000) {
650 latest.tv_usec -= 1000000;
656 // Create a new certificate and merge it with our database
657 merge(new Certificate(&mykey, latest, statement));
660 void Manager::allow(const std::string &statement, const PublicKey *key) {
663 full += hexencode(key->fingerprint()) + ' ';
668 void Manager::dontcare(const std::string &statement, const PublicKey *key) {
671 full += hexencode(key->fingerprint()) + ' ';
676 void Manager::deny(const std::string &statement, const PublicKey *key) {
679 full += hexencode(key->fingerprint()) + ' ';
687 fides_manager *fides_init_manager(char *homedir) {
688 return new Fides::Manager(homedir);
691 void fides_exit_manager(fides_manager *m) {
695 bool fides_is_firstrun(fides_manager *m) {
696 return m->is_firstrun();
699 bool fides_fsck(fides_manager *m) {
703 char *fides_get_homedir(fides_manager *m) {
704 return strdup(m->get_homedir().c_str());
707 void fides_sign(fides_manager *m, const char *statement) {
711 void fides_allow(fides_manager *m, const char *statement, const fides_publickey *key) {
712 m->allow(statement, key);
715 void fides_dontcare(fides_manager *m, const char *statement, const fides_publickey *key) {
716 m->dontcare(statement, key);
719 void fides_deny(fides_manager *m, const char *statement, const fides_publickey *key) {
720 m->deny(statement, key);
723 bool fides_is_allowed(fides_manager *m, const char *statement, const fides_publickey *key) {
724 return m->is_allowed(statement, key);
727 bool fides_is_denied(fides_manager *m, const char *statement, const fides_publickey *key) {
728 return m->is_denied(statement, key);
731 void fides_auth_stats(fides_manager *m, const char *statement, int *self, int *trusted, int *all) {
732 return m->auth_stats(statement, *self, *trusted, *all);
735 void fides_trust(fides_manager *m, const fides_publickey *key) {
739 void fides_dctrust(fides_manager *m, const fides_publickey *key) {
743 void fides_distrust(fides_manager *m, const fides_publickey *key) {
747 bool fides_is_trusted(fides_manager *m, const fides_publickey *key) {
748 return m->is_trusted(key);
751 bool fides_is_distrusted(fides_manager *m, const fides_publickey *key) {
752 return m->is_distrusted(key);
755 fides_publickey *fides_find_key(fides_manager *m, const char *fingerprint) {
756 return m->find_key(fingerprint);
759 void fides_update_trust(fides_manager *m) {
764 const fides_certificate **find_certificates(fides_manager *m, const fides_publickey *key, const char *statement) {
765 fides_certificate **result = 0;
766 vector<const Certificate *> certs = m->find_certificates(key, statement);
768 result = malloc(sizeof *result * certs->size());
769 for(int i = 0; i < certs->size(); i++)
770 result[i] = certs[i];
775 const fides_certificate *fides_import_certificate(fides_manager *m, const char *certificate) {
776 m->import_certificate(certificate);
779 char *fides_export_certificate(fides_manager *m, const fides_certificate *certificate) {
780 return strdup(m->export_certificate(certificate).c_str());
783 const fides_publickey *fides_import_key(fides_manager *m, const char *key) {
784 return m->import_key(key);
787 char *fides_export_key(fides_manager *m, const fides_publickey *key) {
788 return strdup(m->export_key(key).c_str());
791 void fides_import_all(fides_manager *m, FILE *in) {
795 void fides_export_all(fides_manager *m, FILE *out) {
800 fides_certificate *fides_certificate_from_string(fides_manager *m, const char *certificate) {
801 return m->certificate_from_string(certificate);
804 fides_certificate *fides_certificate_load(fides_manager *m, const char *filename) {
805 return m->certificate_load(filename);
808 void fides_certificate_save(fides_manager *m, const fides_certificate *cert, const char *filename) {
809 m->certificate_save(cert, filename);