]> tinc-vpn.org Git - fides/commitdiff
Autoconfiscation.
authorGuus Sliepen <guus@sliepen.eu.org>
Sat, 24 Jan 2009 15:38:40 +0000 (16:38 +0100)
committerGuus Sliepen <guus@sliepen.eu.org>
Sat, 24 Jan 2009 15:38:40 +0000 (16:38 +0100)
Makefile [deleted file]
Makefile.am [new file with mode: 0644]
configure.ac [new file with mode: 0644]
fides.cc [deleted file]
fides.h [deleted file]
lib/Makefile.am [new file with mode: 0644]
lib/fides.cc [new file with mode: 0644]
lib/fides.h [new file with mode: 0644]
src/Makefile.am [new file with mode: 0644]
src/fides.cc [new file with mode: 0644]

diff --git a/Makefile b/Makefile
deleted file mode 100644 (file)
index a7a4bd6..0000000
--- a/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-all: fides
-
-CFLAGS ?= -Wall -g -O0
-LDFLAGS ?= -Wall -g -O0
-
-fides: fides.o
-       $(CXX) $(LDFLAGS) -o $@ $< -lbotan
-
-%.o: %.cc %.h
-       $(CXX) $(CFLAGS) -g -c -Wall -o $@ $<
-
-clean:
-       rm -f *.o fides
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..1e0060f
--- /dev/null
@@ -0,0 +1,6 @@
+AUTOMAKE_OPTIONS = foreign
+
+SUBDIRS = lib src
+
+ChangeLog:
+       git log > ChangeLog
diff --git a/configure.ac b/configure.ac
new file mode 100644 (file)
index 0000000..415538b
--- /dev/null
@@ -0,0 +1,21 @@
+AC_INIT
+AM_INIT_AUTOMAKE([fides], [0.1])
+AC_PREREQ(2.61)
+AC_COPYRIGHT([Copyright (c) 2008-2009 Guus Sliepen <guus@tinc-vpn.org>])
+AC_REVISION($Revision$)
+AC_CONFIG_SRCDIR(src/fides.cc)
+
+AC_GNU_SOURCE
+
+AC_PROG_CXX
+AC_PROG_CXXCPP
+AC_PROG_INSTALL
+AC_PROG_MAKE_SET
+AC_PROG_LIBTOOL
+
+PKG_CHECK_MODULES(BOTAN, botan-1.8 >= 1.8.0)
+AC_SUBST(BOTAN_CFLAGS)
+AC_SUBST(BOTAN_LIBS)
+
+AC_CONFIG_FILES([Makefile lib/Makefile src/Makefile])
+AC_OUTPUT
diff --git a/fides.cc b/fides.cc
deleted file mode 100644 (file)
index eb2de1a..0000000
--- a/fides.cc
+++ /dev/null
@@ -1,1118 +0,0 @@
-/* fides.c - Light-weight, decentralised trust and authorisation management
-   Copyright (C) 2008-2009  Guus Sliepen <guus@tinc-vpn.org>
-  
-   Fides is free software; you can redistribute it and/or modify
-   it under the terms of the GNU Lesser General Public License as
-   published by the Free Software Foundation; either version 2.1 of
-   the License, or (at your option) any later version.
-  
-   Fides is distributed in the hope that it will be useful,
-   but WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-   GNU Lesser General Public License for more details.
-  
-   You should have received a copy of the GNU Lesser General Public
-   License along with this program; if not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <cstdio>
-#include <cstring>
-#include <cstdlib>
-#include <stdint.h>
-#include <getopt.h>
-#include <sysexits.h>
-#include <iostream>
-#include <fstream>
-#include <botan/types.h>
-#include <botan/botan.h>
-#include <botan/ecdsa.h>
-#include <botan/look_pk.h>
-#include <botan/lookup.h>
-#include <botan/filters.h>
-#include <botan/sha2_32.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <dirent.h>
-#include <list>
-
-#include "fides.h"
-
-using namespace std;
-
-// Global state
-
-Botan::LibraryInitializer libinit;
-Botan::AutoSeeded_RNG fides::rng;
-
-// Public key functions
-
-fides::publickey::publickey(): pub(0), trust(0) {
-}
-
-fides::publickey::~publickey() {
-       delete pub;
-}
-
-void fides::publickey::load(istream &in) {
-       try {
-               Botan::DataSource_Stream source(in);
-               pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
-       } catch(Botan::Exception &e) {
-               throw exception(e.what());
-       }
-}
-
-void fides::publickey::load(const std::string &filename) {
-       ifstream in(filename.c_str());
-       load(in);
-}
-
-void fides::publickey::save(ostream &out) {
-       out << to_string();
-}
-
-void fides::publickey::save(const std::string &filename) {
-       ofstream out(filename.c_str());
-       save(out);
-}
-
-void fides::publickey::from_string(const std::string &in) {
-       try {
-               Botan::DataSource_Memory source(in);
-               pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
-       } catch(Botan::Exception &e) {
-               throw exception(e.what());
-       }
-}
-
-string fides::publickey::to_string() {
-       return Botan::X509::PEM_encode(*pub);
-}
-
-string fides::publickey::fingerprint(unsigned int bits) {
-       // TODO: find out if there is a standard way to get a hash of an ECDSA public key
-       Botan::SHA_256 sha256;
-       Botan::SecureVector<Botan::byte> hash = sha256.process(Botan::X509::PEM_encode(*pub));
-       return string((const char *)hash.begin(), bits / 8);
-}
-
-bool fides::publickey::verify(const std::string &statement, const std::string &signature) {
-       auto_ptr<Botan::PK_Verifier> verifier(Botan::get_pk_verifier(*pub, "EMSA1(SHA-512)"));
-       verifier->update((const Botan::byte *)statement.data(), statement.size());
-       Botan::SecureVector<Botan::byte> sig;
-       sig.set((const Botan::byte *)signature.data(), signature.size());
-       return verifier->check_signature(sig);
-}
-
-// Private key functions
-
-fides::privatekey::privatekey(): priv(0) {
-}
-
-fides::privatekey::~privatekey() {
-       delete priv;
-       pub = 0;
-}
-
-void fides::privatekey::generate(const std::string &field) {
-       Botan::EC_Domain_Params domain = Botan::get_EC_Dom_Pars_by_oid(field);
-       pub = priv = new Botan::ECDSA_PrivateKey(rng, domain);
-}
-
-void fides::privatekey::generate(unsigned int bits) {
-       switch(bits) {
-               case 112: return generate("1.3.132.0.6");
-               case 128: return generate("1.3.132.0.28");
-               case 160: return generate("1.3.132.0.9");
-               case 192: return generate("1.3.132.0.31");
-               case 224: return generate("1.3.132.0.32");
-               case 256: return generate("1.3.132.0.10");
-               case 384: return generate("1.3.132.0.34");
-               case 521: return generate("1.3.132.0.35");
-               default: throw exception("Unsupported number of bits for private key");
-       }
-}
-
-void fides::privatekey::load_private(istream &in) {
-       try {
-               Botan::DataSource_Stream stream(in);
-               pub = priv = dynamic_cast<Botan::ECDSA_PrivateKey *>(Botan::PKCS8::load_key(stream, rng, ""));
-       } catch(Botan::Exception &e) {
-               throw exception(e.what());
-       }
-}
-
-void fides::privatekey::load_private(const std::string &filename) {
-       ifstream in(filename.c_str());
-       load_private(in);
-}
-
-void fides::privatekey::save_private(ostream &out) {
-       out << Botan::PKCS8::PEM_encode(*priv);
-}
-
-void fides::privatekey::save_private(const std::string &filename) {
-       ofstream out(filename.c_str());
-       save_private(out);
-}
-
-string fides::privatekey::sign(const std::string &statement) {
-       auto_ptr<Botan::PK_Signer> signer(Botan::get_pk_signer(*priv, "EMSA1(SHA-512)"));
-       Botan::SecureVector<Botan::byte> sig = signer->sign_message((const Botan::byte *)statement.data(), statement.size(), rng);
-       return string((const char *)sig.begin(), (size_t)sig.size());
-}
-
-// Base64 and hex encoding/decoding functions
-
-string fides::hexencode(const string &in) {
-       Botan::Pipe pipe(new Botan::Hex_Encoder);
-       pipe.process_msg((Botan::byte *)in.data(), in.size());
-       return pipe.read_all_as_string();
-}
-
-string fides::hexdecode(const string &in) {
-       Botan::Pipe pipe(new Botan::Hex_Decoder);
-       pipe.process_msg((Botan::byte *)in.data(), in.size());
-       return pipe.read_all_as_string();
-}
-
-string fides::b64encode(const string &in) {
-       Botan::Pipe pipe(new Botan::Base64_Encoder);
-       pipe.process_msg((Botan::byte *)in.data(), in.size());
-       return pipe.read_all_as_string();
-}
-
-string fides::b64decode(const string &in) {
-       Botan::Pipe pipe(new Botan::Base64_Decoder);
-       pipe.process_msg((Botan::byte *)in.data(), in.size());
-       return pipe.read_all_as_string();
-}
-
-// Certificate functions
-
-fides::certificate::certificate(publickey *key, struct timeval timestamp, const std::string &statement, const std::string &signature): signer(key), timestamp(timestamp), statement(statement), signature(signature) {}
-
-bool fides::certificate::validate() {
-       string data = signer->fingerprint(256);
-       data += string((const char *)&timestamp, sizeof timestamp);
-       data += statement;
-       return signer->verify(data, signature);
-}
-
-fides::certificate::certificate(privatekey *key, struct timeval timestamp, const std::string &statement): signer(key), timestamp(timestamp), statement(statement) {
-       string data = signer->fingerprint(256);
-       data += string((const char *)&timestamp, sizeof timestamp);
-       data += statement;
-       signature = key->sign(data);
-}
-
-string fides::certificate::fingerprint(unsigned int bits) {
-       return signature.substr(signature.size() - bits / 8);   
-}
-
-string fides::certificate::to_string() const {
-       string data = fides::hexencode(signer->fingerprint());
-       data += ' ';
-       char ts[100];
-       snprintf(ts, sizeof ts, "%lu.%06lu", timestamp.tv_sec, timestamp.tv_usec);
-       data += ts;
-       data += ' ';
-       data += fides::b64encode(signature);
-       data += ' ';
-       data += statement;
-       return data;
-}
-
-static void help(ostream &out, const string &argv0) {
-       out << "Usage: " << argv0 << "<command> [arguments]\n"
-       "\n"
-       "Available commands are:\n"
-       "\n"
-       "  init      Initialise fides, generate a public/private keypair.\n"
-       "  version   Show version and copyright information.\n"
-       "  help      Show this help message.\n"
-       "\n"
-       "  trust <keyid>\n"
-       "            Trust allow/deny packets signed by the specified key.\n"
-       "  distrust <keyid>\n"
-       "            Distrust allow/deny packets signed by the specified key.\n"
-       "  dctrust <keyid>\n"
-       "            Don't care about allow/deny packets signed by the specified key.\n"
-       "  is_trusted <keyid>\n"
-       "            Returns 0 if key is trusted, 1 otherwise\n"
-       "  is_distrusted <keyid>\n"
-       "            Returns 0 if key is distrusted, 1 otherwise\n"
-       "\n"
-       "  sign <stuff ...>\n"
-       "            Sign stuff.\n"
-       "  allow <stuff ...>\n"
-       "            Allow stuff.\n"
-       "  deny <stuff ...> \n"
-       "            Deny stuff.\n"
-       "  dontcare <stuff ...> \n"
-       "            Don't care about stuff.\n"
-       "  is_allowed <stuff ...>\n"
-       "            Returns 0 if stuff is allowed, 1 otherwise\n"
-       "  is_denied <stuff ...>\n"
-       "            Returns 0 if stuff is denied, 1 otherwise\n"
-       "\n"
-       "  import [filename]\n"
-       "            Import keys and certificates from file, or stdin if unspecified.\n"
-       "  export [filename]\n"
-       "            Export keys and certificates to file, or stdout if unspecified.\n"
-       "  test <stuff ...>\n"
-       "            Tell whether stuff is allowed or not by counting relevant certificates\n"
-       "  find <regexp>\n"
-       "            Find all certificates matching regexp\n"
-       "  fsck      Verify the signature on all information collected.\n";
-}
-
-static void version(ostream &out = cout) {
-       out << "fides version 0.1\n"
-       "Copyright (c) 2008-2009 Guus Sliepen <guus@tinc-vpn.org>\n"
-       "\n"
-       "This program is free software; you can redistribute it and/or modify\n"
-       "it under the terms of the GNU General Public License as published by\n"
-       "the Free Software Foundation; either version 2 of the License, or\n"
-       "(at your option) any later version.\n";
-}
-
-// Utility functions
-
-static vector<string> dirlist(const string &path) {
-       vector<string> files;
-
-       DIR *dir = opendir(path.c_str());
-       if(!dir)
-               return files;
-
-       struct dirent entry, *result = &entry;
-       
-       while(result) {
-               readdir_r(dir, &entry, &result);
-               if(!result)
-                       break;
-               struct stat st;
-               if(result->d_type == DT_UNKNOWN) {
-                       if(stat((path + "/" + result->d_name).c_str(), &st))
-                               continue;
-                       if(S_ISREG(st.st_mode))
-                               files.push_back(result->d_name);
-               } else if(result->d_type == DT_REG) {
-                       files.push_back(result->d_name);
-               }
-       }
-
-       closedir(dir);
-
-       return files;
-}
-
-void fides::certificate_save(const certificate *cert, const string &filename) {
-       ofstream file(filename.c_str());
-       file << cert->to_string() << '\n';
-}
-
-fides::certificate *fides::certificate_load(const string &filename) {
-       ifstream file(filename.c_str());
-       string data;
-       getline(file, data);
-       return certificate_from_string(data);
-}
-
-fides::certificate *fides::certificate_from_string(const string &data) {
-       size_t b, e;
-       e = data.find(' ', 0);
-       if(e == string::npos)
-               throw exception("Invalid certificate");
-       string fingerprint = hexdecode(data.substr(0, e));
-       publickey *signer = find_key(fingerprint);
-       if(!signer)
-               throw exception("Unknown public key");
-       b = e + 1;
-       e = data.find('.', b);
-       if(e == string::npos)
-               throw exception("Invalid certificate");
-       struct timeval timestamp;
-       timestamp.tv_sec = atol(data.c_str() + b);
-       b = e + 1;
-       timestamp.tv_usec = atol(data.c_str() + b);
-       e = data.find(' ', b);
-       if(e == string::npos)
-               throw exception("Invalid certificate");
-       b = e + 1;
-       e = data.find(' ', b);
-       if(e == string::npos)
-               throw exception("Invalid certificate");
-       string signature = fides::b64decode(data.substr(b, e - b));
-       b = e + 1;
-       string statement = data.substr(b);
-
-       return new certificate(signer, timestamp, statement, signature);
-}
-
-// Fides main functions
-
-fides::fides(const string &dir): homedir(dir) {
-       cerr << "Fides initialising\n";
-
-       // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
-       if(homedir.empty())
-               homedir = getenv("FIDES_HOME") ?: "";
-       if(homedir.empty()) {
-               char cwd[PATH_MAX];
-               homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
-               homedir += "/.fides";
-       }
-
-       // Derived directories
-       homedir += '/';
-       certdir = homedir + "certs/";
-       keydir = homedir + "keys/";
-       obsoletedir = homedir + ".obsolete_certs/";
-
-       // Ensure the homedir and its subdirectories exist
-       mkdir(homedir.c_str(), 0700);
-       mkdir(certdir.c_str(), 0700);
-       mkdir(keydir.c_str(), 0700);
-       mkdir(obsoletedir.c_str(), 0700);
-
-       try {
-               mykey.load_private(homedir + "priv");
-               firstrun = false;
-       } catch(fides::exception &e) {
-               cerr << "Fides generating keypair\n";
-               mykey.generate();
-               mykey.save_private(homedir + "priv");
-               mykey.save(keydir + hexencode(mykey.fingerprint()));
-               firstrun = true;
-       }
-       vector<string> files = dirlist(keydir);
-       for(size_t i = 0; i < files.size(); ++i) {
-               cerr << "Loading key " << files[i] << '\n';
-
-               publickey *key = new publickey();
-               key->load(keydir + files[i]);
-               keys[hexdecode(files[i])] = key;
-       }
-
-       keys[mykey.fingerprint()] = &mykey;
-
-       files = dirlist(certdir);
-       for(size_t i = 0; i < files.size(); ++i) {
-               cerr << "Loading certificate " << files[i] << '\n';
-               certificate *cert = certificate_load(certdir + files[i]);
-               if(false && !cert->validate()) {
-                       cerr << "Bad certificate!\n";
-                       continue;
-               }
-               certs[hexdecode(files[i])] = cert;
-       }
-
-       // TODO: save and load this value
-       latest.tv_sec = 0;
-       latest.tv_usec = 0;
-
-       update_trust();
-}
-
-fides::~fides() {
-       cerr << "Fides exitting\n";
-       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
-               delete i->second;
-       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
-               if(i->second != &mykey)
-                       delete i->second;
-}
-
-bool fides::fsck() {
-       int errors = 0;
-
-       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i) {
-               if(!i->second->validate()) {
-                       cerr << "Validation of certificate failed: " << i->second->to_string() << '\n';
-                       errors++;
-               }
-       }
-
-       cerr << errors << " errors in " << certs.size() << " certificates\n";
-       return !errors;
-}
-
-string fides::get_homedir() {
-       return homedir;
-}
-
-bool fides::is_firstrun() {
-       return firstrun;
-}
-
-fides::publickey *fides::find_key(const string &fingerprint) {
-       map<string, publickey *>::iterator i;
-       i = keys.find(fingerprint);
-       if(i != keys.end())
-               return i->second;
-       else
-               return 0;
-}
-
-vector<fides::certificate *> fides::find_certificates(publickey *signer, const string &regex) {
-       vector<certificate *> found;
-       map<string, certificate *>::iterator i;
-       regexp regexp(regex);
-       for(i = certs.begin(); i != certs.end(); ++i) {
-               if(!i->second) {
-                       cerr << "No certificate for " << hexencode(i->first) << '\n';
-                       continue;
-               }
-               if(i->second->signer == signer)
-                       if(regexp.match(i->second->statement))
-                               found.push_back(i->second);
-       }
-       return found;
-}
-
-vector<fides::certificate *> fides::find_certificates(const string &regex) {
-       vector<certificate *> found;
-       map<string, certificate *>::iterator i;
-       regexp regexp(regex);
-       for(i = certs.begin(); i != certs.end(); ++i)
-               if(regexp.match(i->second->statement))
-                       found.push_back(i->second);
-       return found;
-}
-
-vector<fides::certificate *> fides::find_certificates(publickey *signer) {
-       vector<certificate *> found;
-       map<string, certificate *>::iterator i;
-       for(i = certs.begin(); i != certs.end(); ++i)
-               if(i->second->signer == signer)
-                       found.push_back(i->second);
-       return found;
-}
-
-void fides::import_all(istream &in) {
-       string line, pem;
-       bool is_pem = false;
-
-       while(getline(in, line)) {
-               if(line.empty())
-                       continue;
-
-               if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
-                       pem += line + '\n';
-                       if(!line.compare(0, 9, "-----END ")) {
-                               fides::publickey *key = new publickey();
-                               key->from_string(pem);
-                               cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
-                               merge(key);
-                               is_pem = false;
-                       } else {
-                               is_pem = true;
-                       }
-                       continue;
-               }
-
-               fides::certificate *cert = certificate_from_string(line);
-               cerr << "Importing certificate " << hexencode(cert->fingerprint()) << '\n';
-               merge(cert);
-       }
-}
-
-void fides::export_all(ostream &out) {
-       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
-               out << i->second->to_string();
-       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
-               out << i->second->to_string() << '\n';
-}
-
-static int init() {
-       fides fides;
-       if(fides.is_firstrun()) {
-               cout << "New keys generated in " << fides.get_homedir() << '\n';
-       } else {
-               cout << "Fides already initialised\n";
-       }
-       return 0;
-}
-
-void fides::trust(publickey *key) {
-       string full = "t+ " + hexencode(key->fingerprint());
-       sign(full);
-}
-
-void fides::distrust(publickey *key) {
-       string full = "t- " + hexencode(key->fingerprint());
-       sign(full);
-}
-
-void fides::dctrust(publickey *key) {
-       string full = "t0 " + hexencode(key->fingerprint());
-       sign(full);
-}
-
-static int is_trusted(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
-       if(!key) {
-               cerr << "Unknown key!\n";
-               return 1;
-       }
-       return fides.is_trusted(key) ? 0 : 1;
-}
-
-static int is_distrusted(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
-       if(!key) {
-               cerr << "Unknown key!\n";
-               return 1;
-       }
-       return fides.is_distrusted(key) ? 0 : 1;
-}
-
-static int trust(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
-       if(key)
-               fides.trust(key);
-       else {
-               cerr << "Unknown key!\n";
-               return -1;
-       }
-       return 0;
-}
-
-static int dctrust(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
-       if(key)
-               fides.dctrust(key);
-       else {
-               cerr << "Unknown key!\n";
-               return -1;
-       }
-       return 0;
-}
-
-static int distrust(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
-       if(key)
-               fides.distrust(key);
-       else {
-               cerr << "Unknown key!\n";
-               return -1;
-       }
-       return 0;
-}
-
-void fides::update_trust() {
-       // clear trust on all keys
-       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
-               i->second->trust = 0;
-
-       // Start by checking all trust certificates from ourself.
-       // If another key is positively or negatively trusted, update its trust score
-       // and add it to the the list of new keys to check.
-       // Then add our own key to the list of already checked keys.
-       // Then check all the trust certificates of those on the tocheck list, etc.
-       // Already checked keys are never updated anymore (TODO: is that smart?)
-       // Certificates of keys with a zero or negative trust score are not processed.
-
-       set<publickey *> checked;
-       set<publickey *> tocheck;
-       set<publickey *> newkeys;
-       set<publickey *>::iterator i;
-
-       mykey.trust = 3;
-       tocheck.insert(&mykey);
-
-       while(tocheck.size()) {
-               // add
-               checked.insert(tocheck.begin(), tocheck.end());
-               newkeys.clear();
-
-               // loop over all keys whose certificates need to be checked
-
-               for(i = tocheck.begin(); i != tocheck.end(); ++i) {
-                       cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
-
-                       // except if this key is not trusted
-
-                       if((*i)->trust <= 0)
-                               continue;
-
-                       // find all non-zero trust certificates of this key
-
-                       vector<certificate *> matches = find_certificates(*i, "^t[+-] ");
-
-                       // update trust value of those keys
-
-                       for(size_t j = 0; j < matches.size(); j++) {
-                               publickey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
-
-                               if(!other) {
-                                       cerr << "Trust certificate for unknown key: " << matches[j]->to_string() << '\n';
-                                       continue;
-                               }
-
-                               // except for keys we already checked
-
-                               if(checked.find(other) != checked.end()) {
-                                       cerr << "Skipping trust certificate for already checked key: " << matches[j]->to_string() << '\n';
-                                       continue;
-                               }
-
-                               // update trust
-
-                               if(matches[j]->statement[1] == '+')
-                                       other->trust++;
-                               else
-                                       other->trust--;
-
-                               newkeys.insert(other);
-                       }
-               }
-
-               tocheck = newkeys;
-       }       
-}      
-
-void fides::merge(publickey *key) {
-       if(keys.find(key->fingerprint()) != keys.end()) {
-               cerr << "Key already known\n";
-               return;
-       }
-
-       keys[key->fingerprint()] = key;
-       key->save(keydir + hexencode(key->fingerprint()));
-}
-
-void fides::merge(certificate *cert) {
-       // TODO: check if cert is already in database
-       // TODO: check if cert obsoletes other certs
-
-       // If we already know this certificate, drop it.
-       if(certs.find(cert->fingerprint()) != certs.end()) {
-               cerr << "Certificate already known\n";
-               return;
-       }
-
-       // If the certificate does not validate, drop it.
-       if(!cert->validate()) {
-               // TODO: this should not happen, be wary of DoS attacks
-               cerr << "Certificate invalid\n";
-               return;
-       }
-
-       // TODO: move these regexps to the class?
-       regexp authexp("^a[+0-] ");
-       regexp trustexp("^t[+0-] ");
-       vector<certificate *> others;
-
-       // Is this an authorisation cert?
-       if(authexp.match(cert->statement)) {
-               // Find certs identical except for the +/-/0
-               // TODO: escape statement in regexp
-               others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
-               if(others.size()) {
-                       if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
-                               cerr << "Certificate is overruled by a newer certificate\n";
-                               return;
-                       }
-                       if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
-                               // TODO: this should not happen, be wary of DoS attacks
-                               cerr << "Certificate has same timestamp as another timestamp!\n";
-                               return;
-                       }
-                       cerr << "Certificate overrules an older certificate!\n";
-                       // save new cert first
-                       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
-                       certs[cert->fingerprint()] = cert;
-
-                       // delete old one
-                       rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
-                       certs.erase(others[0]->fingerprint());
-                       delete others[0];
-                       return;
-               }
-       }
-
-       // Is this a trust cert?
-       // TODO: it's just the same as above!
-       if(trustexp.match(cert->statement)) {
-               // Find certs identical except for the +/-/0
-               // TODO: escape statement in regexp
-               others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
-               if(others.size()) {
-                       if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
-                               cerr << "Certificate is overruled by a newer certificate\n";
-                               return;
-                       }
-                       if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
-                               // TODO: this should not happen, be wary of DoS attacks
-                               cerr << "Certificate has same timestamp as another timestamp!\n";
-                               return;
-                       }
-                       cerr << "Certificate overrules an older certificate!\n";
-                       // delete old one
-                       rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
-                       certs.erase(others[0]->fingerprint());
-                       delete others[0];
-                       certs[cert->fingerprint()] = cert;
-                       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
-                       return;
-               }
-       }
-
-       // Did somebody sign the exact same statement twice?
-       // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
-       others = find_certificates(cert->signer, string("^") + cert->statement + '$');
-       if(others.size()) {
-               if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
-                       cerr << "Certificate is overruled by a newer certificate\n";
-                       return;
-               }
-               if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
-                       // TODO: this should not happen, be wary of DoS attacks
-                       cerr << "Certificate has same timestamp as another timestamp!\n";
-                       return;
-               }
-               cerr << "Certificate overrules an older certificate!\n";
-               // delete old one
-               rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
-               certs.erase(others[0]->fingerprint());
-               delete others[0];
-               certs[cert->fingerprint()] = cert;
-               certificate_save(cert, certdir + hexencode(cert->fingerprint()));
-               return;
-       }
-
-       cerr << "Certificate is new\n";
-       certs[cert->fingerprint()] = cert;
-       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
-}
-
-void fides::auth_stats(const string &statement, int &self, int &trusted, int &all) {
-       self = trusted = all = 0;
-       vector<certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
-       for(size_t i = 0; i < matches.size(); ++i) {
-               char code = matches[i]->statement[1];
-               int diff = 0;
-               if(code == '+')
-                       diff = 1;
-               else if(code == '-')
-                       diff = -1;
-               if(matches[i]->signer == &mykey)
-                       self += diff;
-               if(matches[i]->signer->trust > 0)
-                       trusted += diff;
-               all += diff;
-       }
-}
-
-bool fides::is_trusted(publickey *key) {
-       return key->trust > 0;
-}
-
-bool fides::is_distrusted(publickey *key) {
-       return key->trust < 0;
-}
-
-bool fides::is_allowed(const string &statement, publickey *key) {
-       int self, trusted, all;
-
-       if(key)
-               auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
-       else
-               auth_stats(statement, self, trusted, all);
-               
-       if(self)
-               return self > 0;
-       else if(trusted)
-               return trusted > 0;
-       else
-               return false;
-}
-
-bool fides::is_denied(const string &statement, publickey *key) {
-       int self, trusted, all;
-
-       if(key)
-               auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
-       else
-               auth_stats(statement, self, trusted, all);
-
-       if(self)
-               return self < 0;
-       else if(trusted)
-               return trusted < 0;
-       else
-               return false;
-}
-
-void fides::sign(const string &statement) {
-       // Try to set "latest" to now, but ensure monoticity
-       struct timeval now;
-       gettimeofday(&now, 0);
-       if(timercmp(&latest, &now, >=)) {
-               latest.tv_usec++;
-               if(latest.tv_usec >= 1000000) {
-                       latest.tv_sec++;
-                       latest.tv_usec -= 1000000;
-               }
-       } else {
-               latest = now;
-       }
-
-       // Create a new certificate and merge it with our database
-       merge(new certificate(&mykey, latest, statement));
-}
-
-void fides::allow(const string &statement, publickey *key) {
-       string full = "a+ ";
-       if(key)
-               full += hexencode(key->fingerprint()) + ' ';
-       full += statement;
-       sign(full);
-}
-
-void fides::dontcare(const string &statement, publickey *key) {
-       string full = "a0 ";
-       if(key)
-               full += hexencode(key->fingerprint()) + ' ';
-       full += statement;
-       sign(full);
-}
-
-void fides::deny(const string &statement, publickey *key) {
-       string full = "a- ";
-       if(key)
-               full += hexencode(key->fingerprint()) + ' ';
-       full += statement;
-       sign(full);
-}
-
-static int sign(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides.sign(argv[0]);
-       return 0;
-}
-
-static int allow(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides.allow(argv[0]);
-       return 0;
-}
-
-static int dontcare(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides.dontcare(argv[0]);
-       return 0;
-}
-
-static int deny(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       fides.deny(argv[0]);
-       return 0;
-}
-
-static int import(int argc, char *const argv[]) {
-       fides fides;
-       
-       if(argc) {
-               ifstream in(argv[0]);
-               fides.import_all(in);
-       } else
-               fides.import_all(cin);
-       return 0;
-}
-
-static int exprt(int argc, char *const argv[]) {
-       fides fides;
-
-       if(argc) {
-               ofstream out(argv[0]);
-               fides.export_all(out);
-       } else
-               fides.export_all(cout);
-       return 0;
-}
-
-static int find(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       // Find certificates matching statement
-       fides fides;
-       const vector<fides::certificate *> &certs = fides.find_certificates(argv[0]);
-       for(size_t i = 0; i < certs.size(); ++i)
-               cout << i << ' ' << certs[i]->to_string() << '\n';
-       return 0;
-}
-
-static int is_allowed(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       return fides.is_allowed(argv[0]) ? 0 : 1;
-}
-
-static int is_denied(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       return fides.is_denied(argv[0]) ? 0 : 1;
-}
-
-static int test(int argc, char *const argv[]) {
-       if(argc < 1)
-               return EX_USAGE;
-
-       fides fides;
-       int self, trusted, all;
-       fides.auth_stats(argv[0], self, trusted, all);
-       cout << "Self: " << self << ", trusted: " << trusted << ", all: " << all << '\n';
-       return 0;
-}
-
-static int fsck() {
-       fides fides;
-       if(fides.fsck()) {
-               cout << "Everything OK\n";
-               return 0;
-       } else {
-               cout << "Integrity failure!\n";
-               return 1;
-       }
-}
-
-int main(int argc, char *const argv[]) {
-       int r;
-       int option_index;
-
-       static struct option const long_options[] = {
-               {"homedir", required_argument, NULL, 2},
-               {"help", no_argument, NULL, 'h'},
-               {"version", no_argument, NULL, 3},
-               {NULL, 0, NULL, 0}
-       };
-
-        while((r = getopt_long(argc, argv, "h", long_options, &option_index)) != EOF) {
-                switch (r) {
-                        case 0:                         /* long option */
-                                break;
-                       case 1:                         /* non-option */
-                               break;
-                       case 2:
-                               //homedir = strdup(optarg);
-                               break;
-                       case 3:
-                               version();
-                               return 0;
-                       case 'h':
-                               help(cout, argv[0]);
-                               return 0;
-               }
-       }
-
-       if(argc < 2) {
-               help(cerr, argv[0]);
-               return EX_USAGE;
-       }
-
-       if(!strcmp(argv[1], "help")) {
-               help(cout, argv[0]);
-               return 0;
-       }
-
-       if(!strcmp(argv[1], "version")) {
-               version();
-               return 0;
-       }
-
-       if(!strcmp(argv[1], "init"))
-               return init();
-
-       if(!strcmp(argv[1], "trust"))
-               return trust(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "dctrust"))
-               return dctrust(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "distrust"))
-               return distrust(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "is_trusted"))
-               return is_trusted(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "is_distrusted"))
-               return is_distrusted(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "is_allowed"))
-               return is_allowed(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "is_denied"))
-               return is_denied(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "allow"))
-               return allow(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "dontcare"))
-               return dontcare(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "deny"))
-               return deny(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "sign"))
-               return sign(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "import"))
-               return import(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "export"))
-               return exprt(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "test"))
-               return test(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "find"))
-               return find(argc - 2, argv + 2);
-
-       if(!strcmp(argv[1], "fsck"))
-               return fsck();
-
-       cerr << "Unknown command: " << argv[1] << '\n';
-       return EX_USAGE;
-}
-
diff --git a/fides.h b/fides.h
deleted file mode 100644 (file)
index 7d94719..0000000
--- a/fides.h
+++ /dev/null
@@ -1,170 +0,0 @@
-#ifndef __FIDES_H__
-#define __FIDES_H__
-
-#include <stdexcept>
-#include <regex.h>
-#include <botan/botan.h>
-#include <sys/time.h>
-#include <map>
-
-class fides {
-       std::string homedir;
-       std::string certdir;
-       std::string obsoletedir;
-       std::string keydir;
-
-       bool firstrun;
-       struct timeval latest;
-       static Botan::AutoSeeded_RNG rng;
-
-       public:
-       // Utility functions
-
-       static std::string b64encode(const std::string &in);
-       static std::string b64decode(const std::string &in);
-       static std::string hexencode(const std::string &in);
-       static std::string hexdecode(const std::string &in);
-
-       class regexp {
-               regex_t comp;
-
-               public:
-               static const int EXTENDED = REG_EXTENDED;
-               static const int ICASE = REG_ICASE;
-               static const int NOSUB = REG_NOSUB;
-               static const int NEWLINE = REG_NEWLINE;
-
-               static const int NOTBOL = REG_NOTBOL;
-               static const int NOTEAL = REG_NOTEOL;
-
-               regexp(const std::string &exp, int cflags = 0) {
-                       int err = regcomp(&comp, exp.c_str(), cflags | NOSUB);
-                       if(err)
-                               throw exception("Could not compile regular expression");
-               }
-
-               ~regexp() {
-                       regfree(&comp);
-               }
-
-               bool match(const std::string &in, int eflags = 0) {
-                       return regexec(&comp, in.c_str(), 0, 0, eflags) == 0;
-               }
-       };
-
-       // Exception class
-
-       class exception: public std::runtime_error {
-                public:
-                exception(const std::string reason): runtime_error(reason) {}
-        };
-
-       // Objects manipulated by fides
-
-       class publickey {
-               protected:
-               Botan::ECDSA_PublicKey *pub;
-
-               public:
-               publickey();
-               ~publickey();
-
-               int trust;
-               void load(std::istream &in);
-               void save(std::ostream &out);
-               void load(const std::string &filename);
-               void save(const std::string &filename);
-               bool verify(const std::string &data, const std::string &signature);
-               std::string to_string();
-               void from_string(const std::string &in);
-               std::string fingerprint(unsigned int bits = 64);
-       };
-
-       class privatekey: public publickey {
-               Botan::ECDSA_PrivateKey *priv;
-
-               public:
-               privatekey();
-               ~privatekey();
-
-               void load_private(std::istream &in);
-               void save_private(std::ostream &out);
-               void load_private(const std::string &filename);
-               void save_private(const std::string &filename);
-               void generate(const std::string &field);
-               void generate(unsigned int bits = 224);
-               std::string sign(const std::string &data);
-       };
-
-       class certificate {
-               friend class fides;
-               publickey *signer;
-               struct timeval timestamp;
-               std::string statement;
-               std::string signature;
-
-               public:
-               certificate(publickey *pub, struct timeval timestamp, const std::string &statement, const std::string &signature);
-               certificate(privatekey *priv, struct timeval timestamp, const std::string &statement);
-
-               std::string to_string() const;
-               std::string fingerprint(unsigned int bits = 64);
-               bool validate();
-       };
-
-       // Fides class itself
-
-       private:
-       privatekey mykey;
-       std::map<std::string, publickey *> keys;
-       std::map<std::string, certificate *> certs;
-       std::set<publickey *> trustedkeys;
-
-       void merge(certificate *cert);
-       void merge(publickey *key);
-
-       public:
-       fides(const std::string &homedir = "");
-       ~fides();
-
-       bool is_firstrun();
-       bool fsck();
-       std::string get_homedir();
-
-       void sign(const std::string &statement);
-
-       void allow(const std::string &statement, publickey *key = 0);
-       void dontcare(const std::string &statement, publickey *key = 0);
-       void deny(const std::string &statement, publickey *key = 0);
-       bool is_allowed(const std::string &statement, publickey *key = 0);
-       bool is_denied(const std::string &statement, publickey *key = 0);
-
-       void auth_stats(const std::string &statement, int &self, int &trusted, int &all);
-       void trust(publickey *key);
-       void dctrust(publickey *key);
-       void distrust(publickey *key);
-       bool is_trusted(publickey *key);
-       bool is_distrusted(publickey *key);
-       publickey *find_key(const std::string &fingerprint);
-       void update_trust();
-
-       std::vector<certificate *> find_certificates(publickey *key, const std::string &statement);
-       std::vector<certificate *> find_certificates(const std::string &statement);
-       std::vector<certificate *> find_certificates(publickey *key);
-
-       certificate *import_certificate(const std::string &certificate);
-       std::string export_certificate(const certificate *);
-
-       publickey *import_key(const std::string &key);
-       std::string export_key(const publickey *key);
-
-       void import_all(std::istream &in);
-       void export_all(std::ostream &out);
-
-       certificate *certificate_from_string(const std::string &certificate);
-       certificate *certificate_load(const std::string &filename);
-       void certificate_save(const certificate *cert, const std::string &filename);
-
-};
-
-#endif
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644 (file)
index 0000000..cb39600
--- /dev/null
@@ -0,0 +1,4 @@
+lib_LTLIBRARIES = libfides.la
+libfides_la_SOURCES = fides.cc
+include_HEADERS = fides.h
+libfides_la_LIBADD = $(BOTAN_LIBS)
diff --git a/lib/fides.cc b/lib/fides.cc
new file mode 100644 (file)
index 0000000..c4dff16
--- /dev/null
@@ -0,0 +1,774 @@
+/* fides.cc - Light-weight, decentralised trust and authorisation management
+   Copyright (C) 2008-2009  Guus Sliepen <guus@tinc-vpn.org>
+  
+   Fides is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+  
+   Fides is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU Lesser General Public License for more details.
+  
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+#include <stdint.h>
+#include <iostream>
+#include <fstream>
+#include <botan/types.h>
+#include <botan/botan.h>
+#include <botan/ecdsa.h>
+#include <botan/look_pk.h>
+#include <botan/lookup.h>
+#include <botan/filters.h>
+#include <botan/sha2_32.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <list>
+
+#include "fides.h"
+
+using namespace std;
+
+// Global state
+
+Botan::LibraryInitializer libinit;
+Botan::AutoSeeded_RNG fides::rng;
+
+// Public key functions
+
+fides::publickey::publickey(): pub(0), trust(0) {
+}
+
+fides::publickey::~publickey() {
+       delete pub;
+}
+
+void fides::publickey::load(istream &in) {
+       try {
+               Botan::DataSource_Stream source(in);
+               pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
+       } catch(Botan::Exception &e) {
+               throw exception(e.what());
+       }
+}
+
+void fides::publickey::load(const std::string &filename) {
+       ifstream in(filename.c_str());
+       load(in);
+}
+
+void fides::publickey::save(ostream &out) {
+       out << to_string();
+}
+
+void fides::publickey::save(const std::string &filename) {
+       ofstream out(filename.c_str());
+       save(out);
+}
+
+void fides::publickey::from_string(const std::string &in) {
+       try {
+               Botan::DataSource_Memory source(in);
+               pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
+       } catch(Botan::Exception &e) {
+               throw exception(e.what());
+       }
+}
+
+string fides::publickey::to_string() {
+       return Botan::X509::PEM_encode(*pub);
+}
+
+string fides::publickey::fingerprint(unsigned int bits) {
+       // TODO: find out if there is a standard way to get a hash of an ECDSA public key
+       Botan::SHA_256 sha256;
+       Botan::SecureVector<Botan::byte> hash = sha256.process(Botan::X509::PEM_encode(*pub));
+       return string((const char *)hash.begin(), bits / 8);
+}
+
+bool fides::publickey::verify(const std::string &statement, const std::string &signature) {
+       auto_ptr<Botan::PK_Verifier> verifier(Botan::get_pk_verifier(*pub, "EMSA1(SHA-512)"));
+       verifier->update((const Botan::byte *)statement.data(), statement.size());
+       Botan::SecureVector<Botan::byte> sig;
+       sig.set((const Botan::byte *)signature.data(), signature.size());
+       return verifier->check_signature(sig);
+}
+
+// Private key functions
+
+fides::privatekey::privatekey(): priv(0) {
+}
+
+fides::privatekey::~privatekey() {
+       delete priv;
+       pub = 0;
+}
+
+void fides::privatekey::generate(const std::string &field) {
+       Botan::EC_Domain_Params domain = Botan::get_EC_Dom_Pars_by_oid(field);
+       pub = priv = new Botan::ECDSA_PrivateKey(rng, domain);
+}
+
+void fides::privatekey::generate(unsigned int bits) {
+       switch(bits) {
+               case 112: return generate("1.3.132.0.6");
+               case 128: return generate("1.3.132.0.28");
+               case 160: return generate("1.3.132.0.9");
+               case 192: return generate("1.3.132.0.31");
+               case 224: return generate("1.3.132.0.32");
+               case 256: return generate("1.3.132.0.10");
+               case 384: return generate("1.3.132.0.34");
+               case 521: return generate("1.3.132.0.35");
+               default: throw exception("Unsupported number of bits for private key");
+       }
+}
+
+void fides::privatekey::load_private(istream &in) {
+       try {
+               Botan::DataSource_Stream stream(in);
+               pub = priv = dynamic_cast<Botan::ECDSA_PrivateKey *>(Botan::PKCS8::load_key(stream, rng, ""));
+       } catch(Botan::Exception &e) {
+               throw exception(e.what());
+       }
+}
+
+void fides::privatekey::load_private(const std::string &filename) {
+       ifstream in(filename.c_str());
+       load_private(in);
+}
+
+void fides::privatekey::save_private(ostream &out) {
+       out << Botan::PKCS8::PEM_encode(*priv);
+}
+
+void fides::privatekey::save_private(const std::string &filename) {
+       ofstream out(filename.c_str());
+       save_private(out);
+}
+
+string fides::privatekey::sign(const std::string &statement) {
+       auto_ptr<Botan::PK_Signer> signer(Botan::get_pk_signer(*priv, "EMSA1(SHA-512)"));
+       Botan::SecureVector<Botan::byte> sig = signer->sign_message((const Botan::byte *)statement.data(), statement.size(), rng);
+       return string((const char *)sig.begin(), (size_t)sig.size());
+}
+
+// Base64 and hex encoding/decoding functions
+
+string fides::hexencode(const string &in) {
+       Botan::Pipe pipe(new Botan::Hex_Encoder);
+       pipe.process_msg((Botan::byte *)in.data(), in.size());
+       return pipe.read_all_as_string();
+}
+
+string fides::hexdecode(const string &in) {
+       Botan::Pipe pipe(new Botan::Hex_Decoder);
+       pipe.process_msg((Botan::byte *)in.data(), in.size());
+       return pipe.read_all_as_string();
+}
+
+string fides::b64encode(const string &in) {
+       Botan::Pipe pipe(new Botan::Base64_Encoder);
+       pipe.process_msg((Botan::byte *)in.data(), in.size());
+       return pipe.read_all_as_string();
+}
+
+string fides::b64decode(const string &in) {
+       Botan::Pipe pipe(new Botan::Base64_Decoder);
+       pipe.process_msg((Botan::byte *)in.data(), in.size());
+       return pipe.read_all_as_string();
+}
+
+// Certificate functions
+
+fides::certificate::certificate(publickey *key, struct timeval timestamp, const std::string &statement, const std::string &signature): signer(key), timestamp(timestamp), statement(statement), signature(signature) {}
+
+bool fides::certificate::validate() {
+       string data = signer->fingerprint(256);
+       data += string((const char *)&timestamp, sizeof timestamp);
+       data += statement;
+       return signer->verify(data, signature);
+}
+
+fides::certificate::certificate(privatekey *key, struct timeval timestamp, const std::string &statement): signer(key), timestamp(timestamp), statement(statement) {
+       string data = signer->fingerprint(256);
+       data += string((const char *)&timestamp, sizeof timestamp);
+       data += statement;
+       signature = key->sign(data);
+}
+
+string fides::certificate::fingerprint(unsigned int bits) {
+       return signature.substr(signature.size() - bits / 8);   
+}
+
+string fides::certificate::to_string() const {
+       string data = fides::hexencode(signer->fingerprint());
+       data += ' ';
+       char ts[100];
+       snprintf(ts, sizeof ts, "%lu.%06lu", timestamp.tv_sec, timestamp.tv_usec);
+       data += ts;
+       data += ' ';
+       data += fides::b64encode(signature);
+       data += ' ';
+       data += statement;
+       return data;
+}
+
+// Utility functions
+
+static vector<string> dirlist(const string &path) {
+       vector<string> files;
+
+       DIR *dir = opendir(path.c_str());
+       if(!dir)
+               return files;
+
+       struct dirent entry, *result = &entry;
+       
+       while(result) {
+               readdir_r(dir, &entry, &result);
+               if(!result)
+                       break;
+               struct stat st;
+               if(result->d_type == DT_UNKNOWN) {
+                       if(stat((path + "/" + result->d_name).c_str(), &st))
+                               continue;
+                       if(S_ISREG(st.st_mode))
+                               files.push_back(result->d_name);
+               } else if(result->d_type == DT_REG) {
+                       files.push_back(result->d_name);
+               }
+       }
+
+       closedir(dir);
+
+       return files;
+}
+
+void fides::certificate_save(const certificate *cert, const string &filename) {
+       ofstream file(filename.c_str());
+       file << cert->to_string() << '\n';
+}
+
+fides::certificate *fides::certificate_load(const string &filename) {
+       ifstream file(filename.c_str());
+       string data;
+       getline(file, data);
+       return certificate_from_string(data);
+}
+
+fides::certificate *fides::certificate_from_string(const string &data) {
+       size_t b, e;
+       e = data.find(' ', 0);
+       if(e == string::npos)
+               throw exception("Invalid certificate");
+       string fingerprint = hexdecode(data.substr(0, e));
+       publickey *signer = find_key(fingerprint);
+       if(!signer)
+               throw exception("Unknown public key");
+       b = e + 1;
+       e = data.find('.', b);
+       if(e == string::npos)
+               throw exception("Invalid certificate");
+       struct timeval timestamp;
+       timestamp.tv_sec = atol(data.c_str() + b);
+       b = e + 1;
+       timestamp.tv_usec = atol(data.c_str() + b);
+       e = data.find(' ', b);
+       if(e == string::npos)
+               throw exception("Invalid certificate");
+       b = e + 1;
+       e = data.find(' ', b);
+       if(e == string::npos)
+               throw exception("Invalid certificate");
+       string signature = fides::b64decode(data.substr(b, e - b));
+       b = e + 1;
+       string statement = data.substr(b);
+
+       return new certificate(signer, timestamp, statement, signature);
+}
+
+// Fides main functions
+
+fides::fides(const string &dir): homedir(dir) {
+       cerr << "Fides initialising\n";
+
+       // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
+       if(homedir.empty())
+               homedir = getenv("FIDES_HOME") ?: "";
+       if(homedir.empty()) {
+               char cwd[PATH_MAX];
+               homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
+               homedir += "/.fides";
+       }
+
+       // Derived directories
+       homedir += '/';
+       certdir = homedir + "certs/";
+       keydir = homedir + "keys/";
+       obsoletedir = homedir + ".obsolete_certs/";
+
+       // Ensure the homedir and its subdirectories exist
+       mkdir(homedir.c_str(), 0700);
+       mkdir(certdir.c_str(), 0700);
+       mkdir(keydir.c_str(), 0700);
+       mkdir(obsoletedir.c_str(), 0700);
+
+       try {
+               mykey.load_private(homedir + "priv");
+               firstrun = false;
+       } catch(fides::exception &e) {
+               cerr << "Fides generating keypair\n";
+               mykey.generate();
+               mykey.save_private(homedir + "priv");
+               mykey.save(keydir + hexencode(mykey.fingerprint()));
+               firstrun = true;
+       }
+       vector<string> files = dirlist(keydir);
+       for(size_t i = 0; i < files.size(); ++i) {
+               cerr << "Loading key " << files[i] << '\n';
+
+               publickey *key = new publickey();
+               key->load(keydir + files[i]);
+               keys[hexdecode(files[i])] = key;
+       }
+
+       keys[mykey.fingerprint()] = &mykey;
+
+       files = dirlist(certdir);
+       for(size_t i = 0; i < files.size(); ++i) {
+               cerr << "Loading certificate " << files[i] << '\n';
+               certificate *cert = certificate_load(certdir + files[i]);
+               if(false && !cert->validate()) {
+                       cerr << "Bad certificate!\n";
+                       continue;
+               }
+               certs[hexdecode(files[i])] = cert;
+       }
+
+       // TODO: save and load this value
+       latest.tv_sec = 0;
+       latest.tv_usec = 0;
+
+       update_trust();
+}
+
+fides::~fides() {
+       cerr << "Fides exitting\n";
+       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
+               delete i->second;
+       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
+               if(i->second != &mykey)
+                       delete i->second;
+}
+
+bool fides::fsck() {
+       int errors = 0;
+
+       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i) {
+               if(!i->second->validate()) {
+                       cerr << "Validation of certificate failed: " << i->second->to_string() << '\n';
+                       errors++;
+               }
+       }
+
+       cerr << errors << " errors in " << certs.size() << " certificates\n";
+       return !errors;
+}
+
+string fides::get_homedir() {
+       return homedir;
+}
+
+bool fides::is_firstrun() {
+       return firstrun;
+}
+
+fides::publickey *fides::find_key(const string &fingerprint) {
+       map<string, publickey *>::iterator i;
+       i = keys.find(fingerprint);
+       if(i != keys.end())
+               return i->second;
+       else
+               return 0;
+}
+
+vector<fides::certificate *> fides::find_certificates(publickey *signer, const string &regex) {
+       vector<certificate *> found;
+       map<string, certificate *>::iterator i;
+       regexp regexp(regex);
+       for(i = certs.begin(); i != certs.end(); ++i) {
+               if(!i->second) {
+                       cerr << "No certificate for " << hexencode(i->first) << '\n';
+                       continue;
+               }
+               if(i->second->signer == signer)
+                       if(regexp.match(i->second->statement))
+                               found.push_back(i->second);
+       }
+       return found;
+}
+
+vector<fides::certificate *> fides::find_certificates(const string &regex) {
+       vector<certificate *> found;
+       map<string, certificate *>::iterator i;
+       regexp regexp(regex);
+       for(i = certs.begin(); i != certs.end(); ++i)
+               if(regexp.match(i->second->statement))
+                       found.push_back(i->second);
+       return found;
+}
+
+vector<fides::certificate *> fides::find_certificates(publickey *signer) {
+       vector<certificate *> found;
+       map<string, certificate *>::iterator i;
+       for(i = certs.begin(); i != certs.end(); ++i)
+               if(i->second->signer == signer)
+                       found.push_back(i->second);
+       return found;
+}
+
+void fides::import_all(istream &in) {
+       string line, pem;
+       bool is_pem = false;
+
+       while(getline(in, line)) {
+               if(line.empty())
+                       continue;
+
+               if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
+                       pem += line + '\n';
+                       if(!line.compare(0, 9, "-----END ")) {
+                               fides::publickey *key = new publickey();
+                               key->from_string(pem);
+                               cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
+                               merge(key);
+                               is_pem = false;
+                       } else {
+                               is_pem = true;
+                       }
+                       continue;
+               }
+
+               fides::certificate *cert = certificate_from_string(line);
+               cerr << "Importing certificate " << hexencode(cert->fingerprint()) << '\n';
+               merge(cert);
+       }
+}
+
+void fides::export_all(ostream &out) {
+       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
+               out << i->second->to_string();
+       for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
+               out << i->second->to_string() << '\n';
+}
+
+void fides::trust(publickey *key) {
+       string full = "t+ " + hexencode(key->fingerprint());
+       sign(full);
+}
+
+void fides::distrust(publickey *key) {
+       string full = "t- " + hexencode(key->fingerprint());
+       sign(full);
+}
+
+void fides::dctrust(publickey *key) {
+       string full = "t0 " + hexencode(key->fingerprint());
+       sign(full);
+}
+
+void fides::update_trust() {
+       // clear trust on all keys
+       for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
+               i->second->trust = 0;
+
+       // Start by checking all trust certificates from ourself.
+       // If another key is positively or negatively trusted, update its trust score
+       // and add it to the the list of new keys to check.
+       // Then add our own key to the list of already checked keys.
+       // Then check all the trust certificates of those on the tocheck list, etc.
+       // Already checked keys are never updated anymore (TODO: is that smart?)
+       // Certificates of keys with a zero or negative trust score are not processed.
+
+       set<publickey *> checked;
+       set<publickey *> tocheck;
+       set<publickey *> newkeys;
+       set<publickey *>::iterator i;
+
+       mykey.trust = 3;
+       tocheck.insert(&mykey);
+
+       while(tocheck.size()) {
+               // add
+               checked.insert(tocheck.begin(), tocheck.end());
+               newkeys.clear();
+
+               // loop over all keys whose certificates need to be checked
+
+               for(i = tocheck.begin(); i != tocheck.end(); ++i) {
+                       cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
+
+                       // except if this key is not trusted
+
+                       if((*i)->trust <= 0)
+                               continue;
+
+                       // find all non-zero trust certificates of this key
+
+                       vector<certificate *> matches = find_certificates(*i, "^t[+-] ");
+
+                       // update trust value of those keys
+
+                       for(size_t j = 0; j < matches.size(); j++) {
+                               publickey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
+
+                               if(!other) {
+                                       cerr << "Trust certificate for unknown key: " << matches[j]->to_string() << '\n';
+                                       continue;
+                               }
+
+                               // except for keys we already checked
+
+                               if(checked.find(other) != checked.end()) {
+                                       cerr << "Skipping trust certificate for already checked key: " << matches[j]->to_string() << '\n';
+                                       continue;
+                               }
+
+                               // update trust
+
+                               if(matches[j]->statement[1] == '+')
+                                       other->trust++;
+                               else
+                                       other->trust--;
+
+                               newkeys.insert(other);
+                       }
+               }
+
+               tocheck = newkeys;
+       }       
+}      
+
+void fides::merge(publickey *key) {
+       if(keys.find(key->fingerprint()) != keys.end()) {
+               cerr << "Key already known\n";
+               return;
+       }
+
+       keys[key->fingerprint()] = key;
+       key->save(keydir + hexencode(key->fingerprint()));
+}
+
+void fides::merge(certificate *cert) {
+       // TODO: check if cert is already in database
+       // TODO: check if cert obsoletes other certs
+
+       // If we already know this certificate, drop it.
+       if(certs.find(cert->fingerprint()) != certs.end()) {
+               cerr << "Certificate already known\n";
+               return;
+       }
+
+       // If the certificate does not validate, drop it.
+       if(!cert->validate()) {
+               // TODO: this should not happen, be wary of DoS attacks
+               cerr << "Certificate invalid\n";
+               return;
+       }
+
+       // TODO: move these regexps to the class?
+       regexp authexp("^a[+0-] ");
+       regexp trustexp("^t[+0-] ");
+       vector<certificate *> others;
+
+       // Is this an authorisation cert?
+       if(authexp.match(cert->statement)) {
+               // Find certs identical except for the +/-/0
+               // TODO: escape statement in regexp
+               others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
+               if(others.size()) {
+                       if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
+                               cerr << "Certificate is overruled by a newer certificate\n";
+                               return;
+                       }
+                       if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
+                               // TODO: this should not happen, be wary of DoS attacks
+                               cerr << "Certificate has same timestamp as another timestamp!\n";
+                               return;
+                       }
+                       cerr << "Certificate overrules an older certificate!\n";
+                       // save new cert first
+                       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
+                       certs[cert->fingerprint()] = cert;
+
+                       // delete old one
+                       rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
+                       certs.erase(others[0]->fingerprint());
+                       delete others[0];
+                       return;
+               }
+       }
+
+       // Is this a trust cert?
+       // TODO: it's just the same as above!
+       if(trustexp.match(cert->statement)) {
+               // Find certs identical except for the +/-/0
+               // TODO: escape statement in regexp
+               others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
+               if(others.size()) {
+                       if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
+                               cerr << "Certificate is overruled by a newer certificate\n";
+                               return;
+                       }
+                       if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
+                               // TODO: this should not happen, be wary of DoS attacks
+                               cerr << "Certificate has same timestamp as another timestamp!\n";
+                               return;
+                       }
+                       cerr << "Certificate overrules an older certificate!\n";
+                       // delete old one
+                       rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
+                       certs.erase(others[0]->fingerprint());
+                       delete others[0];
+                       certs[cert->fingerprint()] = cert;
+                       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
+                       return;
+               }
+       }
+
+       // Did somebody sign the exact same statement twice?
+       // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
+       others = find_certificates(cert->signer, string("^") + cert->statement + '$');
+       if(others.size()) {
+               if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
+                       cerr << "Certificate is overruled by a newer certificate\n";
+                       return;
+               }
+               if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
+                       // TODO: this should not happen, be wary of DoS attacks
+                       cerr << "Certificate has same timestamp as another timestamp!\n";
+                       return;
+               }
+               cerr << "Certificate overrules an older certificate!\n";
+               // delete old one
+               rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
+               certs.erase(others[0]->fingerprint());
+               delete others[0];
+               certs[cert->fingerprint()] = cert;
+               certificate_save(cert, certdir + hexencode(cert->fingerprint()));
+               return;
+       }
+
+       cerr << "Certificate is new\n";
+       certs[cert->fingerprint()] = cert;
+       certificate_save(cert, certdir + hexencode(cert->fingerprint()));
+}
+
+void fides::auth_stats(const string &statement, int &self, int &trusted, int &all) {
+       self = trusted = all = 0;
+       vector<certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
+       for(size_t i = 0; i < matches.size(); ++i) {
+               char code = matches[i]->statement[1];
+               int diff = 0;
+               if(code == '+')
+                       diff = 1;
+               else if(code == '-')
+                       diff = -1;
+               if(matches[i]->signer == &mykey)
+                       self += diff;
+               if(matches[i]->signer->trust > 0)
+                       trusted += diff;
+               all += diff;
+       }
+}
+
+bool fides::is_trusted(publickey *key) {
+       return key->trust > 0;
+}
+
+bool fides::is_distrusted(publickey *key) {
+       return key->trust < 0;
+}
+
+bool fides::is_allowed(const string &statement, publickey *key) {
+       int self, trusted, all;
+
+       if(key)
+               auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
+       else
+               auth_stats(statement, self, trusted, all);
+               
+       if(self)
+               return self > 0;
+       else if(trusted)
+               return trusted > 0;
+       else
+               return false;
+}
+
+bool fides::is_denied(const string &statement, publickey *key) {
+       int self, trusted, all;
+
+       if(key)
+               auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
+       else
+               auth_stats(statement, self, trusted, all);
+
+       if(self)
+               return self < 0;
+       else if(trusted)
+               return trusted < 0;
+       else
+               return false;
+}
+
+void fides::sign(const string &statement) {
+       // Try to set "latest" to now, but ensure monoticity
+       struct timeval now;
+       gettimeofday(&now, 0);
+       if(timercmp(&latest, &now, >=)) {
+               latest.tv_usec++;
+               if(latest.tv_usec >= 1000000) {
+                       latest.tv_sec++;
+                       latest.tv_usec -= 1000000;
+               }
+       } else {
+               latest = now;
+       }
+
+       // Create a new certificate and merge it with our database
+       merge(new certificate(&mykey, latest, statement));
+}
+
+void fides::allow(const string &statement, publickey *key) {
+       string full = "a+ ";
+       if(key)
+               full += hexencode(key->fingerprint()) + ' ';
+       full += statement;
+       sign(full);
+}
+
+void fides::dontcare(const string &statement, publickey *key) {
+       string full = "a0 ";
+       if(key)
+               full += hexencode(key->fingerprint()) + ' ';
+       full += statement;
+       sign(full);
+}
+
+void fides::deny(const string &statement, publickey *key) {
+       string full = "a- ";
+       if(key)
+               full += hexencode(key->fingerprint()) + ' ';
+       full += statement;
+       sign(full);
+}
+
diff --git a/lib/fides.h b/lib/fides.h
new file mode 100644 (file)
index 0000000..7d94719
--- /dev/null
@@ -0,0 +1,170 @@
+#ifndef __FIDES_H__
+#define __FIDES_H__
+
+#include <stdexcept>
+#include <regex.h>
+#include <botan/botan.h>
+#include <sys/time.h>
+#include <map>
+
+class fides {
+       std::string homedir;
+       std::string certdir;
+       std::string obsoletedir;
+       std::string keydir;
+
+       bool firstrun;
+       struct timeval latest;
+       static Botan::AutoSeeded_RNG rng;
+
+       public:
+       // Utility functions
+
+       static std::string b64encode(const std::string &in);
+       static std::string b64decode(const std::string &in);
+       static std::string hexencode(const std::string &in);
+       static std::string hexdecode(const std::string &in);
+
+       class regexp {
+               regex_t comp;
+
+               public:
+               static const int EXTENDED = REG_EXTENDED;
+               static const int ICASE = REG_ICASE;
+               static const int NOSUB = REG_NOSUB;
+               static const int NEWLINE = REG_NEWLINE;
+
+               static const int NOTBOL = REG_NOTBOL;
+               static const int NOTEAL = REG_NOTEOL;
+
+               regexp(const std::string &exp, int cflags = 0) {
+                       int err = regcomp(&comp, exp.c_str(), cflags | NOSUB);
+                       if(err)
+                               throw exception("Could not compile regular expression");
+               }
+
+               ~regexp() {
+                       regfree(&comp);
+               }
+
+               bool match(const std::string &in, int eflags = 0) {
+                       return regexec(&comp, in.c_str(), 0, 0, eflags) == 0;
+               }
+       };
+
+       // Exception class
+
+       class exception: public std::runtime_error {
+                public:
+                exception(const std::string reason): runtime_error(reason) {}
+        };
+
+       // Objects manipulated by fides
+
+       class publickey {
+               protected:
+               Botan::ECDSA_PublicKey *pub;
+
+               public:
+               publickey();
+               ~publickey();
+
+               int trust;
+               void load(std::istream &in);
+               void save(std::ostream &out);
+               void load(const std::string &filename);
+               void save(const std::string &filename);
+               bool verify(const std::string &data, const std::string &signature);
+               std::string to_string();
+               void from_string(const std::string &in);
+               std::string fingerprint(unsigned int bits = 64);
+       };
+
+       class privatekey: public publickey {
+               Botan::ECDSA_PrivateKey *priv;
+
+               public:
+               privatekey();
+               ~privatekey();
+
+               void load_private(std::istream &in);
+               void save_private(std::ostream &out);
+               void load_private(const std::string &filename);
+               void save_private(const std::string &filename);
+               void generate(const std::string &field);
+               void generate(unsigned int bits = 224);
+               std::string sign(const std::string &data);
+       };
+
+       class certificate {
+               friend class fides;
+               publickey *signer;
+               struct timeval timestamp;
+               std::string statement;
+               std::string signature;
+
+               public:
+               certificate(publickey *pub, struct timeval timestamp, const std::string &statement, const std::string &signature);
+               certificate(privatekey *priv, struct timeval timestamp, const std::string &statement);
+
+               std::string to_string() const;
+               std::string fingerprint(unsigned int bits = 64);
+               bool validate();
+       };
+
+       // Fides class itself
+
+       private:
+       privatekey mykey;
+       std::map<std::string, publickey *> keys;
+       std::map<std::string, certificate *> certs;
+       std::set<publickey *> trustedkeys;
+
+       void merge(certificate *cert);
+       void merge(publickey *key);
+
+       public:
+       fides(const std::string &homedir = "");
+       ~fides();
+
+       bool is_firstrun();
+       bool fsck();
+       std::string get_homedir();
+
+       void sign(const std::string &statement);
+
+       void allow(const std::string &statement, publickey *key = 0);
+       void dontcare(const std::string &statement, publickey *key = 0);
+       void deny(const std::string &statement, publickey *key = 0);
+       bool is_allowed(const std::string &statement, publickey *key = 0);
+       bool is_denied(const std::string &statement, publickey *key = 0);
+
+       void auth_stats(const std::string &statement, int &self, int &trusted, int &all);
+       void trust(publickey *key);
+       void dctrust(publickey *key);
+       void distrust(publickey *key);
+       bool is_trusted(publickey *key);
+       bool is_distrusted(publickey *key);
+       publickey *find_key(const std::string &fingerprint);
+       void update_trust();
+
+       std::vector<certificate *> find_certificates(publickey *key, const std::string &statement);
+       std::vector<certificate *> find_certificates(const std::string &statement);
+       std::vector<certificate *> find_certificates(publickey *key);
+
+       certificate *import_certificate(const std::string &certificate);
+       std::string export_certificate(const certificate *);
+
+       publickey *import_key(const std::string &key);
+       std::string export_key(const publickey *key);
+
+       void import_all(std::istream &in);
+       void export_all(std::ostream &out);
+
+       certificate *certificate_from_string(const std::string &certificate);
+       certificate *certificate_load(const std::string &filename);
+       void certificate_save(const certificate *cert, const std::string &filename);
+
+};
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644 (file)
index 0000000..3a5b9e9
--- /dev/null
@@ -0,0 +1,11 @@
+AM_CPPFLAGS = \
+       -I$(top_srcdir)/lib
+
+bin_PROGRAMS = fides
+
+fides_SOURCES = fides.cc
+
+noinst_HEADERS = 
+
+fides_LDADD = \
+       $(top_srcdir)/lib/libfides.la
diff --git a/src/fides.cc b/src/fides.cc
new file mode 100644 (file)
index 0000000..006b503
--- /dev/null
@@ -0,0 +1,382 @@
+/* fides.c - Light-weight, decentralised trust and authorisation management
+   Copyright (C) 2008-2009  Guus Sliepen <guus@tinc-vpn.org>
+  
+   Fides is free software; you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation; either version 2.1 of
+   the License, or (at your option) any later version.
+  
+   Fides is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU Lesser General Public License for more details.
+  
+   You should have received a copy of the GNU Lesser General Public
+   License along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+#include <stdint.h>
+#include <getopt.h>
+#include <sysexits.h>
+#include <iostream>
+#include <fstream>
+#include <botan/types.h>
+#include <botan/botan.h>
+#include <botan/ecdsa.h>
+#include <botan/look_pk.h>
+#include <botan/lookup.h>
+#include <botan/filters.h>
+#include <botan/sha2_32.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <list>
+
+#include "fides.h"
+
+using namespace std;
+
+static void help(ostream &out, const string &argv0) {
+       out << "Usage: " << argv0 << "<command> [arguments]\n"
+       "\n"
+       "Available commands are:\n"
+       "\n"
+       "  init      Initialise fides, generate a public/private keypair.\n"
+       "  version   Show version and copyright information.\n"
+       "  help      Show this help message.\n"
+       "\n"
+       "  trust <keyid>\n"
+       "            Trust allow/deny packets signed by the specified key.\n"
+       "  distrust <keyid>\n"
+       "            Distrust allow/deny packets signed by the specified key.\n"
+       "  dctrust <keyid>\n"
+       "            Don't care about allow/deny packets signed by the specified key.\n"
+       "  is_trusted <keyid>\n"
+       "            Returns 0 if key is trusted, 1 otherwise\n"
+       "  is_distrusted <keyid>\n"
+       "            Returns 0 if key is distrusted, 1 otherwise\n"
+       "\n"
+       "  sign <stuff ...>\n"
+       "            Sign stuff.\n"
+       "  allow <stuff ...>\n"
+       "            Allow stuff.\n"
+       "  deny <stuff ...> \n"
+       "            Deny stuff.\n"
+       "  dontcare <stuff ...> \n"
+       "            Don't care about stuff.\n"
+       "  is_allowed <stuff ...>\n"
+       "            Returns 0 if stuff is allowed, 1 otherwise\n"
+       "  is_denied <stuff ...>\n"
+       "            Returns 0 if stuff is denied, 1 otherwise\n"
+       "\n"
+       "  import [filename]\n"
+       "            Import keys and certificates from file, or stdin if unspecified.\n"
+       "  export [filename]\n"
+       "            Export keys and certificates to file, or stdout if unspecified.\n"
+       "  test <stuff ...>\n"
+       "            Tell whether stuff is allowed or not by counting relevant certificates\n"
+       "  find <regexp>\n"
+       "            Find all certificates matching regexp\n"
+       "  fsck      Verify the signature on all information collected.\n";
+}
+
+static void version(ostream &out = cout) {
+       out << "fides version 0.1\n"
+       "Copyright (c) 2008-2009 Guus Sliepen <guus@tinc-vpn.org>\n"
+       "\n"
+       "This program is free software; you can redistribute it and/or modify\n"
+       "it under the terms of the GNU General Public License as published by\n"
+       "the Free Software Foundation; either version 2 of the License, or\n"
+       "(at your option) any later version.\n";
+}
+
+static int init() {
+       fides fides;
+       if(fides.is_firstrun()) {
+               cout << "New keys generated in " << fides.get_homedir() << '\n';
+       } else {
+               cout << "Fides already initialised\n";
+       }
+       return 0;
+}
+
+static int is_trusted(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
+       if(!key) {
+               cerr << "Unknown key!\n";
+               return 1;
+       }
+       return fides.is_trusted(key) ? 0 : 1;
+}
+
+static int is_distrusted(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
+       if(!key) {
+               cerr << "Unknown key!\n";
+               return 1;
+       }
+       return fides.is_distrusted(key) ? 0 : 1;
+}
+
+static int trust(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
+       if(key)
+               fides.trust(key);
+       else {
+               cerr << "Unknown key!\n";
+               return -1;
+       }
+       return 0;
+}
+
+static int dctrust(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
+       if(key)
+               fides.dctrust(key);
+       else {
+               cerr << "Unknown key!\n";
+               return -1;
+       }
+       return 0;
+}
+
+static int distrust(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
+       if(key)
+               fides.distrust(key);
+       else {
+               cerr << "Unknown key!\n";
+               return -1;
+       }
+       return 0;
+}
+
+static int sign(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides.sign(argv[0]);
+       return 0;
+}
+
+static int allow(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides.allow(argv[0]);
+       return 0;
+}
+
+static int dontcare(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides.dontcare(argv[0]);
+       return 0;
+}
+
+static int deny(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       fides.deny(argv[0]);
+       return 0;
+}
+
+static int import(int argc, char *const argv[]) {
+       fides fides;
+       
+       if(argc) {
+               ifstream in(argv[0]);
+               fides.import_all(in);
+       } else
+               fides.import_all(cin);
+       return 0;
+}
+
+static int exprt(int argc, char *const argv[]) {
+       fides fides;
+
+       if(argc) {
+               ofstream out(argv[0]);
+               fides.export_all(out);
+       } else
+               fides.export_all(cout);
+       return 0;
+}
+
+static int find(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       // Find certificates matching statement
+       fides fides;
+       const vector<fides::certificate *> &certs = fides.find_certificates(argv[0]);
+       for(size_t i = 0; i < certs.size(); ++i)
+               cout << i << ' ' << certs[i]->to_string() << '\n';
+       return 0;
+}
+
+static int is_allowed(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       return fides.is_allowed(argv[0]) ? 0 : 1;
+}
+
+static int is_denied(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       return fides.is_denied(argv[0]) ? 0 : 1;
+}
+
+static int test(int argc, char *const argv[]) {
+       if(argc < 1)
+               return EX_USAGE;
+
+       fides fides;
+       int self, trusted, all;
+       fides.auth_stats(argv[0], self, trusted, all);
+       cout << "Self: " << self << ", trusted: " << trusted << ", all: " << all << '\n';
+       return 0;
+}
+
+static int fsck() {
+       fides fides;
+       if(fides.fsck()) {
+               cout << "Everything OK\n";
+               return 0;
+       } else {
+               cout << "Integrity failure!\n";
+               return 1;
+       }
+}
+
+int main(int argc, char *const argv[]) {
+       int r;
+       int option_index;
+
+       static struct option const long_options[] = {
+               {"homedir", required_argument, NULL, 2},
+               {"help", no_argument, NULL, 'h'},
+               {"version", no_argument, NULL, 3},
+               {NULL, 0, NULL, 0}
+       };
+
+        while((r = getopt_long(argc, argv, "h", long_options, &option_index)) != EOF) {
+                switch (r) {
+                        case 0:                         /* long option */
+                                break;
+                       case 1:                         /* non-option */
+                               break;
+                       case 2:
+                               //homedir = strdup(optarg);
+                               break;
+                       case 3:
+                               version();
+                               return 0;
+                       case 'h':
+                               help(cout, argv[0]);
+                               return 0;
+               }
+       }
+
+       if(argc < 2) {
+               help(cerr, argv[0]);
+               return EX_USAGE;
+       }
+
+       if(!strcmp(argv[1], "help")) {
+               help(cout, argv[0]);
+               return 0;
+       }
+
+       if(!strcmp(argv[1], "version")) {
+               version();
+               return 0;
+       }
+
+       if(!strcmp(argv[1], "init"))
+               return init();
+
+       if(!strcmp(argv[1], "trust"))
+               return trust(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "dctrust"))
+               return dctrust(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "distrust"))
+               return distrust(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "is_trusted"))
+               return is_trusted(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "is_distrusted"))
+               return is_distrusted(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "is_allowed"))
+               return is_allowed(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "is_denied"))
+               return is_denied(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "allow"))
+               return allow(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "dontcare"))
+               return dontcare(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "deny"))
+               return deny(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "sign"))
+               return sign(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "import"))
+               return import(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "export"))
+               return exprt(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "test"))
+               return test(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "find"))
+               return find(argc - 2, argv + 2);
+
+       if(!strcmp(argv[1], "fsck"))
+               return fsck();
+
+       cerr << "Unknown command: " << argv[1] << '\n';
+       return EX_USAGE;
+}