Update to a somewhat usable version.
[fides] / fides.cc
1 /* fides.c - Light-weight, decentralised trust and authorisation management
2    Copyright (C) 2008-2009  Guus Sliepen <guus@tinc-vpn.org>
3   
4    Fides is free software; you can redistribute it and/or modify
5    it under the terms of the GNU Lesser General Public License as
6    published by the Free Software Foundation; either version 2.1 of
7    the License, or (at your option) any later version.
8   
9    Fides is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12    GNU Lesser General Public License for more details.
13   
14    You should have received a copy of the GNU Lesser General Public
15    License along with this program; if not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <cstdio>
19 #include <cstring>
20 #include <cstdlib>
21 #include <stdint.h>
22 #include <getopt.h>
23 #include <sysexits.h>
24 #include <iostream>
25 #include <fstream>
26 #include <botan/types.h>
27 #include <botan/botan.h>
28 #include <botan/ecdsa.h>
29 #include <botan/look_pk.h>
30 #include <botan/lookup.h>
31 #include <botan/filters.h>
32 #include <botan/sha2_32.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <list>
37
38 #include "fides.h"
39
40 using namespace std;
41
42 // Global state
43
44 Botan::LibraryInitializer libinit;
45 Botan::AutoSeeded_RNG fides::rng;
46
47 // Public key functions
48
49 fides::publickey::publickey(): pub(0), trust(0) {
50 }
51
52 fides::publickey::~publickey() {
53         delete pub;
54 }
55
56 void fides::publickey::load(istream &in) {
57         try {
58                 Botan::DataSource_Stream source(in);
59                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
60         } catch(Botan::Exception &e) {
61                 throw exception(e.what());
62         }
63 }
64
65 void fides::publickey::load(const std::string &filename) {
66         ifstream in(filename.c_str());
67         load(in);
68 }
69
70 void fides::publickey::save(ostream &out) {
71         out << to_string();
72 }
73
74 void fides::publickey::save(const std::string &filename) {
75         ofstream out(filename.c_str());
76         save(out);
77 }
78
79 void fides::publickey::from_string(const std::string &in) {
80         try {
81                 Botan::DataSource_Memory source(in);
82                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
83         } catch(Botan::Exception &e) {
84                 throw exception(e.what());
85         }
86 }
87
88 string fides::publickey::to_string() {
89         return Botan::X509::PEM_encode(*pub);
90 }
91
92 string fides::publickey::fingerprint(unsigned int bits) {
93         // TODO: find out if there is a standard way to get a hash of an ECDSA public key
94         Botan::SHA_256 sha256;
95         Botan::SecureVector<Botan::byte> hash = sha256.process(Botan::X509::PEM_encode(*pub));
96         return string((const char *)hash.begin(), bits / 8);
97 }
98
99 bool fides::publickey::verify(const std::string &statement, const std::string &signature) {
100         auto_ptr<Botan::PK_Verifier> verifier(Botan::get_pk_verifier(*pub, "EMSA1(SHA-512)"));
101         verifier->update((const Botan::byte *)statement.data(), statement.size());
102         Botan::SecureVector<Botan::byte> sig;
103         sig.set((const Botan::byte *)signature.data(), signature.size());
104         return verifier->check_signature(sig);
105 }
106
107 // Private key functions
108
109 fides::privatekey::privatekey(): priv(0) {
110 }
111
112 fides::privatekey::~privatekey() {
113         delete priv;
114         pub = 0;
115 }
116
117 void fides::privatekey::generate(const std::string &field) {
118         Botan::EC_Domain_Params domain = Botan::get_EC_Dom_Pars_by_oid(field);
119         pub = priv = new Botan::ECDSA_PrivateKey(rng, domain);
120 }
121
122 void fides::privatekey::generate(unsigned int bits) {
123         switch(bits) {
124                 case 112: return generate("1.3.132.0.6");
125                 case 128: return generate("1.3.132.0.28");
126                 case 160: return generate("1.3.132.0.9");
127                 case 192: return generate("1.3.132.0.31");
128                 case 224: return generate("1.3.132.0.32");
129                 case 256: return generate("1.3.132.0.10");
130                 case 384: return generate("1.3.132.0.34");
131                 case 521: return generate("1.3.132.0.35");
132                 default: throw exception("Unsupported number of bits for private key");
133         }
134 }
135
136 void fides::privatekey::load_private(istream &in) {
137         try {
138                 Botan::DataSource_Stream stream(in);
139                 pub = priv = dynamic_cast<Botan::ECDSA_PrivateKey *>(Botan::PKCS8::load_key(stream, rng, ""));
140         } catch(Botan::Exception &e) {
141                 throw exception(e.what());
142         }
143 }
144
145 void fides::privatekey::load_private(const std::string &filename) {
146         ifstream in(filename.c_str());
147         load_private(in);
148 }
149
150 void fides::privatekey::save_private(ostream &out) {
151         out << Botan::PKCS8::PEM_encode(*priv);
152 }
153
154 void fides::privatekey::save_private(const std::string &filename) {
155         ofstream out(filename.c_str());
156         save_private(out);
157 }
158
159 string fides::privatekey::sign(const std::string &statement) {
160         auto_ptr<Botan::PK_Signer> signer(Botan::get_pk_signer(*priv, "EMSA1(SHA-512)"));
161         Botan::SecureVector<Botan::byte> sig = signer->sign_message((const Botan::byte *)statement.data(), statement.size(), rng);
162         return string((const char *)sig.begin(), (size_t)sig.size());
163 }
164
165 // Base64 and hex encoding/decoding functions
166
167 string fides::hexencode(const string &in) {
168         Botan::Pipe pipe(new Botan::Hex_Encoder);
169         pipe.process_msg((Botan::byte *)in.data(), in.size());
170         return pipe.read_all_as_string();
171 }
172
173 string fides::hexdecode(const string &in) {
174         Botan::Pipe pipe(new Botan::Hex_Decoder);
175         pipe.process_msg((Botan::byte *)in.data(), in.size());
176         return pipe.read_all_as_string();
177 }
178
179 string fides::b64encode(const string &in) {
180         Botan::Pipe pipe(new Botan::Base64_Encoder);
181         pipe.process_msg((Botan::byte *)in.data(), in.size());
182         return pipe.read_all_as_string();
183 }
184
185 string fides::b64decode(const string &in) {
186         Botan::Pipe pipe(new Botan::Base64_Decoder);
187         pipe.process_msg((Botan::byte *)in.data(), in.size());
188         return pipe.read_all_as_string();
189 }
190
191 // Certificate functions
192
193 fides::certificate::certificate(publickey *key, struct timeval timestamp, const std::string &statement, const std::string &signature): signer(key), timestamp(timestamp), statement(statement), signature(signature) {}
194
195 bool fides::certificate::validate() {
196         string data = signer->fingerprint(256);
197         data += string((const char *)&timestamp, sizeof timestamp);
198         data += statement;
199         return signer->verify(data, signature);
200 }
201
202 fides::certificate::certificate(privatekey *key, struct timeval timestamp, const std::string &statement): signer(key), timestamp(timestamp), statement(statement) {
203         string data = signer->fingerprint(256);
204         data += string((const char *)&timestamp, sizeof timestamp);
205         data += statement;
206         signature = key->sign(data);
207 }
208
209 string fides::certificate::fingerprint(unsigned int bits) {
210         return signature.substr(signature.size() - bits / 8);   
211 }
212
213 string fides::certificate::to_string() const {
214         string data = fides::hexencode(signer->fingerprint());
215         data += ' ';
216         char ts[100];
217         snprintf(ts, sizeof ts, "%lu.%06lu", timestamp.tv_sec, timestamp.tv_usec);
218         data += ts;
219         data += ' ';
220         data += fides::b64encode(signature);
221         data += ' ';
222         data += statement;
223         return data;
224 }
225
226 static void help(ostream &out, const string &argv0) {
227         out << "Usage: " << argv0 << "<command> [arguments]\n"
228         "\n"
229         "Available commands are:\n"
230         "\n"
231         "  init      Initialise fides, generate a public/private keypair.\n"
232         "  version   Show version and copyright information.\n"
233         "  help      Show this help message.\n"
234         "\n"
235         "  trust <keyid>\n"
236         "            Trust allow/deny packets signed by the specified key.\n"
237         "  distrust <keyid>\n"
238         "            Distrust allow/deny packets signed by the specified key.\n"
239         "  dctrust <keyid>\n"
240         "            Don't care about allow/deny packets signed by the specified key.\n"
241         "  is_trusted <keyid>\n"
242         "            Returns 0 if key is trusted, 1 otherwise\n"
243         "  is_distrusted <keyid>\n"
244         "            Returns 0 if key is distrusted, 1 otherwise\n"
245         "\n"
246         "  sign <stuff ...>\n"
247         "            Sign stuff.\n"
248         "  allow <stuff ...>\n"
249         "            Allow stuff.\n"
250         "  deny <stuff ...> \n"
251         "            Deny stuff.\n"
252         "  dontcare <stuff ...> \n"
253         "            Don't care about stuff.\n"
254         "  is_allowed <stuff ...>\n"
255         "            Returns 0 if stuff is allowed, 1 otherwise\n"
256         "  is_denied <stuff ...>\n"
257         "            Returns 0 if stuff is denied, 1 otherwise\n"
258         "\n"
259         "  import [filename]\n"
260         "            Import keys and certificates from file, or stdin if unspecified.\n"
261         "  export [filename]\n"
262         "            Export keys and certificates to file, or stdout if unspecified.\n"
263         "  test <stuff ...>\n"
264         "            Tell whether stuff is allowed or not by counting relevant certificates\n"
265         "  find <regexp>\n"
266         "            Find all certificates matching regexp\n"
267         "  fsck      Verify the signature on all information collected.\n";
268 }
269
270 static void version(ostream &out = cout) {
271         out << "fides version 0.1\n"
272         "Copyright (c) 2008-2009 Guus Sliepen <guus@tinc-vpn.org>\n"
273         "\n"
274         "This program is free software; you can redistribute it and/or modify\n"
275         "it under the terms of the GNU General Public License as published by\n"
276         "the Free Software Foundation; either version 2 of the License, or\n"
277         "(at your option) any later version.\n";
278 }
279
280 // Utility functions
281
282 static vector<string> dirlist(const string &path) {
283         vector<string> files;
284
285         DIR *dir = opendir(path.c_str());
286         if(!dir)
287                 return files;
288
289         struct dirent entry, *result = &entry;
290         
291         while(result) {
292                 readdir_r(dir, &entry, &result);
293                 if(!result)
294                         break;
295                 struct stat st;
296                 if(result->d_type == DT_UNKNOWN) {
297                         if(stat((path + "/" + result->d_name).c_str(), &st))
298                                 continue;
299                         if(S_ISREG(st.st_mode))
300                                 files.push_back(result->d_name);
301                 } else if(result->d_type == DT_REG) {
302                         files.push_back(result->d_name);
303                 }
304         }
305
306         closedir(dir);
307
308         return files;
309 }
310
311 void fides::certificate_save(const certificate *cert, const string &filename) {
312         ofstream file(filename.c_str());
313         file << cert->to_string() << '\n';
314 }
315
316 fides::certificate *fides::certificate_load(const string &filename) {
317         ifstream file(filename.c_str());
318         string data;
319         getline(file, data);
320         return certificate_from_string(data);
321 }
322
323 fides::certificate *fides::certificate_from_string(const string &data) {
324         size_t b, e;
325         e = data.find(' ', 0);
326         if(e == string::npos)
327                 throw exception("Invalid certificate");
328         string fingerprint = hexdecode(data.substr(0, e));
329         publickey *signer = find_key(fingerprint);
330         if(!signer)
331                 throw exception("Unknown public key");
332         b = e + 1;
333         e = data.find('.', b);
334         if(e == string::npos)
335                 throw exception("Invalid certificate");
336         struct timeval timestamp;
337         timestamp.tv_sec = atol(data.c_str() + b);
338         b = e + 1;
339         timestamp.tv_usec = atol(data.c_str() + b);
340         e = data.find(' ', b);
341         if(e == string::npos)
342                 throw exception("Invalid certificate");
343         b = e + 1;
344         e = data.find(' ', b);
345         if(e == string::npos)
346                 throw exception("Invalid certificate");
347         string signature = fides::b64decode(data.substr(b, e - b));
348         b = e + 1;
349         string statement = data.substr(b);
350
351         return new certificate(signer, timestamp, statement, signature);
352 }
353
354 // Fides main functions
355
356 fides::fides(const string &dir): homedir(dir) {
357         cerr << "Fides initialising\n";
358
359         // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
360         if(homedir.empty())
361                 homedir = getenv("FIDES_HOME") ?: "";
362         if(homedir.empty()) {
363                 char cwd[PATH_MAX];
364                 homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
365                 homedir += "/.fides";
366         }
367
368         // Derived directories
369         homedir += '/';
370         certdir = homedir + "certs/";
371         keydir = homedir + "keys/";
372         obsoletedir = homedir + ".obsolete_certs/";
373
374         // Ensure the homedir and its subdirectories exist
375         mkdir(homedir.c_str(), 0700);
376         mkdir(certdir.c_str(), 0700);
377         mkdir(keydir.c_str(), 0700);
378         mkdir(obsoletedir.c_str(), 0700);
379
380         try {
381                 mykey.load_private(homedir + "priv");
382                 firstrun = false;
383         } catch(fides::exception &e) {
384                 cerr << "Fides generating keypair\n";
385                 mykey.generate();
386                 mykey.save_private(homedir + "priv");
387                 mykey.save(keydir + hexencode(mykey.fingerprint()));
388                 firstrun = true;
389         }
390         vector<string> files = dirlist(keydir);
391         for(size_t i = 0; i < files.size(); ++i) {
392                 cerr << "Loading key " << files[i] << '\n';
393
394                 publickey *key = new publickey();
395                 key->load(keydir + files[i]);
396                 keys[hexdecode(files[i])] = key;
397         }
398
399         keys[mykey.fingerprint()] = &mykey;
400
401         files = dirlist(certdir);
402         for(size_t i = 0; i < files.size(); ++i) {
403                 cerr << "Loading certificate " << files[i] << '\n';
404                 certificate *cert = certificate_load(certdir + files[i]);
405                 if(false && !cert->validate()) {
406                         cerr << "Bad certificate!\n";
407                         continue;
408                 }
409                 certs[hexdecode(files[i])] = cert;
410         }
411
412         // TODO: save and load this value
413         latest.tv_sec = 0;
414         latest.tv_usec = 0;
415
416         update_trust();
417 }
418
419 fides::~fides() {
420         cerr << "Fides exitting\n";
421         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
422                 delete i->second;
423         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
424                 if(i->second != &mykey)
425                         delete i->second;
426 }
427
428 bool fides::fsck() {
429         int errors = 0;
430
431         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i) {
432                 if(!i->second->validate()) {
433                         cerr << "Validation of certificate failed: " << i->second->to_string() << '\n';
434                         errors++;
435                 }
436         }
437
438         cerr << errors << " errors in " << certs.size() << " certificates\n";
439         return !errors;
440 }
441
442 string fides::get_homedir() {
443         return homedir;
444 }
445
446 bool fides::is_firstrun() {
447         return firstrun;
448 }
449
450 fides::publickey *fides::find_key(const string &fingerprint) {
451         map<string, publickey *>::iterator i;
452         i = keys.find(fingerprint);
453         if(i != keys.end())
454                 return i->second;
455         else
456                 return 0;
457 }
458
459 vector<fides::certificate *> fides::find_certificates(publickey *signer, const string &regex) {
460         vector<certificate *> found;
461         map<string, certificate *>::iterator i;
462         regexp regexp(regex);
463         for(i = certs.begin(); i != certs.end(); ++i) {
464                 if(!i->second) {
465                         cerr << "No certificate for " << hexencode(i->first) << '\n';
466                         continue;
467                 }
468                 if(i->second->signer == signer)
469                         if(regexp.match(i->second->statement))
470                                 found.push_back(i->second);
471         }
472         return found;
473 }
474
475 vector<fides::certificate *> fides::find_certificates(const string &regex) {
476         vector<certificate *> found;
477         map<string, certificate *>::iterator i;
478         regexp regexp(regex);
479         for(i = certs.begin(); i != certs.end(); ++i)
480                 if(regexp.match(i->second->statement))
481                         found.push_back(i->second);
482         return found;
483 }
484
485 vector<fides::certificate *> fides::find_certificates(publickey *signer) {
486         vector<certificate *> found;
487         map<string, certificate *>::iterator i;
488         for(i = certs.begin(); i != certs.end(); ++i)
489                 if(i->second->signer == signer)
490                         found.push_back(i->second);
491         return found;
492 }
493
494 void fides::import_all(istream &in) {
495         string line, pem;
496         bool is_pem = false;
497
498         while(getline(in, line)) {
499                 if(line.empty())
500                         continue;
501
502                 if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
503                         pem += line + '\n';
504                         if(!line.compare(0, 9, "-----END ")) {
505                                 fides::publickey *key = new publickey();
506                                 key->from_string(pem);
507                                 cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
508                                 merge(key);
509                                 is_pem = false;
510                         } else {
511                                 is_pem = true;
512                         }
513                         continue;
514                 }
515
516                 fides::certificate *cert = certificate_from_string(line);
517                 cerr << "Importing certificate " << hexencode(cert->fingerprint()) << '\n';
518                 merge(cert);
519         }
520 }
521
522 void fides::export_all(ostream &out) {
523         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
524                 out << i->second->to_string();
525         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
526                 out << i->second->to_string() << '\n';
527 }
528
529 static int init() {
530         fides fides;
531         if(fides.is_firstrun()) {
532                 cout << "New keys generated in " << fides.get_homedir() << '\n';
533         } else {
534                 cout << "Fides already initialised\n";
535         }
536         return 0;
537 }
538
539 void fides::trust(publickey *key) {
540         string full = "t+ " + hexencode(key->fingerprint());
541         sign(full);
542 }
543
544 void fides::distrust(publickey *key) {
545         string full = "t- " + hexencode(key->fingerprint());
546         sign(full);
547 }
548
549 void fides::dctrust(publickey *key) {
550         string full = "t0 " + hexencode(key->fingerprint());
551         sign(full);
552 }
553
554 static int is_trusted(int argc, char *const argv[]) {
555         if(argc < 1)
556                 return EX_USAGE;
557
558         fides fides;
559         fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
560         if(!key) {
561                 cerr << "Unknown key!\n";
562                 return 1;
563         }
564         return fides.is_trusted(key) ? 0 : 1;
565 }
566
567 static int is_distrusted(int argc, char *const argv[]) {
568         if(argc < 1)
569                 return EX_USAGE;
570
571         fides fides;
572         fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
573         if(!key) {
574                 cerr << "Unknown key!\n";
575                 return 1;
576         }
577         return fides.is_distrusted(key) ? 0 : 1;
578 }
579
580 static int trust(int argc, char *const argv[]) {
581         if(argc < 1)
582                 return EX_USAGE;
583
584         fides fides;
585         fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
586         if(key)
587                 fides.trust(key);
588         else {
589                 cerr << "Unknown key!\n";
590                 return -1;
591         }
592         return 0;
593 }
594
595 static int dctrust(int argc, char *const argv[]) {
596         if(argc < 1)
597                 return EX_USAGE;
598
599         fides fides;
600         fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
601         if(key)
602                 fides.dctrust(key);
603         else {
604                 cerr << "Unknown key!\n";
605                 return -1;
606         }
607         return 0;
608 }
609
610 static int distrust(int argc, char *const argv[]) {
611         if(argc < 1)
612                 return EX_USAGE;
613
614         fides fides;
615         fides::publickey *key = fides.find_key(fides::hexdecode(argv[0]));
616         if(key)
617                 fides.distrust(key);
618         else {
619                 cerr << "Unknown key!\n";
620                 return -1;
621         }
622         return 0;
623 }
624
625 void fides::update_trust() {
626         // clear trust on all keys
627         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
628                 i->second->trust = 0;
629
630         // Start by checking all trust certificates from ourself.
631         // If another key is positively or negatively trusted, update its trust score
632         // and add it to the the list of new keys to check.
633         // Then add our own key to the list of already checked keys.
634         // Then check all the trust certificates of those on the tocheck list, etc.
635         // Already checked keys are never updated anymore (TODO: is that smart?)
636         // Certificates of keys with a zero or negative trust score are not processed.
637
638         set<publickey *> checked;
639         set<publickey *> tocheck;
640         set<publickey *> newkeys;
641         set<publickey *>::iterator i;
642
643         mykey.trust = 3;
644         tocheck.insert(&mykey);
645
646         while(tocheck.size()) {
647                 // add
648                 checked.insert(tocheck.begin(), tocheck.end());
649                 newkeys.clear();
650
651                 // loop over all keys whose certificates need to be checked
652
653                 for(i = tocheck.begin(); i != tocheck.end(); ++i) {
654                         cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
655
656                         // except if this key is not trusted
657
658                         if((*i)->trust <= 0)
659                                 continue;
660
661                         // find all non-zero trust certificates of this key
662
663                         vector<certificate *> matches = find_certificates(*i, "^t[+-] ");
664
665                         // update trust value of those keys
666
667                         for(size_t j = 0; j < matches.size(); j++) {
668                                 publickey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
669
670                                 if(!other) {
671                                         cerr << "Trust certificate for unknown key: " << matches[j]->to_string() << '\n';
672                                         continue;
673                                 }
674
675                                 // except for keys we already checked
676
677                                 if(checked.find(other) != checked.end()) {
678                                         cerr << "Skipping trust certificate for already checked key: " << matches[j]->to_string() << '\n';
679                                         continue;
680                                 }
681
682                                 // update trust
683
684                                 if(matches[j]->statement[1] == '+')
685                                         other->trust++;
686                                 else
687                                         other->trust--;
688
689                                 newkeys.insert(other);
690                         }
691                 }
692
693                 tocheck = newkeys;
694         }       
695 }       
696
697 void fides::merge(publickey *key) {
698         if(keys.find(key->fingerprint()) != keys.end()) {
699                 cerr << "Key already known\n";
700                 return;
701         }
702
703         keys[key->fingerprint()] = key;
704         key->save(keydir + hexencode(key->fingerprint()));
705 }
706
707 void fides::merge(certificate *cert) {
708         // TODO: check if cert is already in database
709         // TODO: check if cert obsoletes other certs
710
711         // If we already know this certificate, drop it.
712         if(certs.find(cert->fingerprint()) != certs.end()) {
713                 cerr << "Certificate already known\n";
714                 return;
715         }
716
717         // If the certificate does not validate, drop it.
718         if(!cert->validate()) {
719                 // TODO: this should not happen, be wary of DoS attacks
720                 cerr << "Certificate invalid\n";
721                 return;
722         }
723
724         // TODO: move these regexps to the class?
725         regexp authexp("^a[+0-] ");
726         regexp trustexp("^t[+0-] ");
727         vector<certificate *> others;
728
729         // Is this an authorisation cert?
730         if(authexp.match(cert->statement)) {
731                 // Find certs identical except for the +/-/0
732                 // TODO: escape statement in regexp
733                 others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
734                 if(others.size()) {
735                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
736                                 cerr << "Certificate is overruled by a newer certificate\n";
737                                 return;
738                         }
739                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
740                                 // TODO: this should not happen, be wary of DoS attacks
741                                 cerr << "Certificate has same timestamp as another timestamp!\n";
742                                 return;
743                         }
744                         cerr << "Certificate overrules an older certificate!\n";
745                         // save new cert first
746                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
747                         certs[cert->fingerprint()] = cert;
748
749                         // delete old one
750                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
751                         certs.erase(others[0]->fingerprint());
752                         delete others[0];
753                         return;
754                 }
755         }
756
757         // Is this a trust cert?
758         // TODO: it's just the same as above!
759         if(trustexp.match(cert->statement)) {
760                 // Find certs identical except for the +/-/0
761                 // TODO: escape statement in regexp
762                 others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
763                 if(others.size()) {
764                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
765                                 cerr << "Certificate is overruled by a newer certificate\n";
766                                 return;
767                         }
768                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
769                                 // TODO: this should not happen, be wary of DoS attacks
770                                 cerr << "Certificate has same timestamp as another timestamp!\n";
771                                 return;
772                         }
773                         cerr << "Certificate overrules an older certificate!\n";
774                         // delete old one
775                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
776                         certs.erase(others[0]->fingerprint());
777                         delete others[0];
778                         certs[cert->fingerprint()] = cert;
779                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
780                         return;
781                 }
782         }
783
784         // Did somebody sign the exact same statement twice?
785         // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
786         others = find_certificates(cert->signer, string("^") + cert->statement + '$');
787         if(others.size()) {
788                 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
789                         cerr << "Certificate is overruled by a newer certificate\n";
790                         return;
791                 }
792                 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
793                         // TODO: this should not happen, be wary of DoS attacks
794                         cerr << "Certificate has same timestamp as another timestamp!\n";
795                         return;
796                 }
797                 cerr << "Certificate overrules an older certificate!\n";
798                 // delete old one
799                 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
800                 certs.erase(others[0]->fingerprint());
801                 delete others[0];
802                 certs[cert->fingerprint()] = cert;
803                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
804                 return;
805         }
806
807         cerr << "Certificate is new\n";
808         certs[cert->fingerprint()] = cert;
809         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
810 }
811
812 void fides::auth_stats(const string &statement, int &self, int &trusted, int &all) {
813         self = trusted = all = 0;
814         vector<certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
815         for(size_t i = 0; i < matches.size(); ++i) {
816                 char code = matches[i]->statement[1];
817                 int diff = 0;
818                 if(code == '+')
819                         diff = 1;
820                 else if(code == '-')
821                         diff = -1;
822                 if(matches[i]->signer == &mykey)
823                         self += diff;
824                 if(matches[i]->signer->trust > 0)
825                         trusted += diff;
826                 all += diff;
827         }
828 }
829
830 bool fides::is_trusted(publickey *key) {
831         return key->trust > 0;
832 }
833
834 bool fides::is_distrusted(publickey *key) {
835         return key->trust < 0;
836 }
837
838 bool fides::is_allowed(const string &statement, publickey *key) {
839         int self, trusted, all;
840
841         if(key)
842                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
843         else
844                 auth_stats(statement, self, trusted, all);
845                 
846         if(self)
847                 return self > 0;
848         else if(trusted)
849                 return trusted > 0;
850         else
851                 return false;
852 }
853
854 bool fides::is_denied(const string &statement, publickey *key) {
855         int self, trusted, all;
856
857         if(key)
858                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
859         else
860                 auth_stats(statement, self, trusted, all);
861
862         if(self)
863                 return self < 0;
864         else if(trusted)
865                 return trusted < 0;
866         else
867                 return false;
868 }
869
870 void fides::sign(const string &statement) {
871         // Try to set "latest" to now, but ensure monoticity
872         struct timeval now;
873         gettimeofday(&now, 0);
874         if(timercmp(&latest, &now, >=)) {
875                 latest.tv_usec++;
876                 if(latest.tv_usec >= 1000000) {
877                         latest.tv_sec++;
878                         latest.tv_usec -= 1000000;
879                 }
880         } else {
881                 latest = now;
882         }
883
884         // Create a new certificate and merge it with our database
885         merge(new certificate(&mykey, latest, statement));
886 }
887
888 void fides::allow(const string &statement, publickey *key) {
889         string full = "a+ ";
890         if(key)
891                 full += hexencode(key->fingerprint()) + ' ';
892         full += statement;
893         sign(full);
894 }
895
896 void fides::dontcare(const string &statement, publickey *key) {
897         string full = "a0 ";
898         if(key)
899                 full += hexencode(key->fingerprint()) + ' ';
900         full += statement;
901         sign(full);
902 }
903
904 void fides::deny(const string &statement, publickey *key) {
905         string full = "a- ";
906         if(key)
907                 full += hexencode(key->fingerprint()) + ' ';
908         full += statement;
909         sign(full);
910 }
911
912 static int sign(int argc, char *const argv[]) {
913         if(argc < 1)
914                 return EX_USAGE;
915
916         fides fides;
917         fides.sign(argv[0]);
918         return 0;
919 }
920
921 static int allow(int argc, char *const argv[]) {
922         if(argc < 1)
923                 return EX_USAGE;
924
925         fides fides;
926         fides.allow(argv[0]);
927         return 0;
928 }
929
930 static int dontcare(int argc, char *const argv[]) {
931         if(argc < 1)
932                 return EX_USAGE;
933
934         fides fides;
935         fides.dontcare(argv[0]);
936         return 0;
937 }
938
939 static int deny(int argc, char *const argv[]) {
940         if(argc < 1)
941                 return EX_USAGE;
942
943         fides fides;
944         fides.deny(argv[0]);
945         return 0;
946 }
947
948 static int import(int argc, char *const argv[]) {
949         fides fides;
950         
951         if(argc) {
952                 ifstream in(argv[0]);
953                 fides.import_all(in);
954         } else
955                 fides.import_all(cin);
956         return 0;
957 }
958
959 static int exprt(int argc, char *const argv[]) {
960         fides fides;
961
962         if(argc) {
963                 ofstream out(argv[0]);
964                 fides.export_all(out);
965         } else
966                 fides.export_all(cout);
967         return 0;
968 }
969
970 static int find(int argc, char *const argv[]) {
971         if(argc < 1)
972                 return EX_USAGE;
973
974         // Find certificates matching statement
975         fides fides;
976         const vector<fides::certificate *> &certs = fides.find_certificates(argv[0]);
977         for(size_t i = 0; i < certs.size(); ++i)
978                 cout << i << ' ' << certs[i]->to_string() << '\n';
979         return 0;
980 }
981
982 static int is_allowed(int argc, char *const argv[]) {
983         if(argc < 1)
984                 return EX_USAGE;
985
986         fides fides;
987         return fides.is_allowed(argv[0]) ? 0 : 1;
988 }
989
990 static int is_denied(int argc, char *const argv[]) {
991         if(argc < 1)
992                 return EX_USAGE;
993
994         fides fides;
995         return fides.is_denied(argv[0]) ? 0 : 1;
996 }
997
998 static int test(int argc, char *const argv[]) {
999         if(argc < 1)
1000                 return EX_USAGE;
1001
1002         fides fides;
1003         int self, trusted, all;
1004         fides.auth_stats(argv[0], self, trusted, all);
1005         cout << "Self: " << self << ", trusted: " << trusted << ", all: " << all << '\n';
1006         return 0;
1007 }
1008
1009 static int fsck() {
1010         fides fides;
1011         if(fides.fsck()) {
1012                 cout << "Everything OK\n";
1013                 return 0;
1014         } else {
1015                 cout << "Integrity failure!\n";
1016                 return 1;
1017         }
1018 }
1019
1020 int main(int argc, char *const argv[]) {
1021         int r;
1022         int option_index;
1023
1024         static struct option const long_options[] = {
1025                 {"homedir", required_argument, NULL, 2},
1026                 {"help", no_argument, NULL, 'h'},
1027                 {"version", no_argument, NULL, 3},
1028                 {NULL, 0, NULL, 0}
1029         };
1030
1031         while((r = getopt_long(argc, argv, "h", long_options, &option_index)) != EOF) {
1032                 switch (r) {
1033                         case 0:                         /* long option */
1034                                 break;
1035                         case 1:                         /* non-option */
1036                                 break;
1037                         case 2:
1038                                 //homedir = strdup(optarg);
1039                                 break;
1040                         case 3:
1041                                 version();
1042                                 return 0;
1043                         case 'h':
1044                                 help(cout, argv[0]);
1045                                 return 0;
1046                 }
1047         }
1048
1049         if(argc < 2) {
1050                 help(cerr, argv[0]);
1051                 return EX_USAGE;
1052         }
1053
1054         if(!strcmp(argv[1], "help")) {
1055                 help(cout, argv[0]);
1056                 return 0;
1057         }
1058
1059         if(!strcmp(argv[1], "version")) {
1060                 version();
1061                 return 0;
1062         }
1063
1064         if(!strcmp(argv[1], "init"))
1065                 return init();
1066
1067         if(!strcmp(argv[1], "trust"))
1068                 return trust(argc - 2, argv + 2);
1069
1070         if(!strcmp(argv[1], "dctrust"))
1071                 return dctrust(argc - 2, argv + 2);
1072
1073         if(!strcmp(argv[1], "distrust"))
1074                 return distrust(argc - 2, argv + 2);
1075
1076         if(!strcmp(argv[1], "is_trusted"))
1077                 return is_trusted(argc - 2, argv + 2);
1078
1079         if(!strcmp(argv[1], "is_distrusted"))
1080                 return is_distrusted(argc - 2, argv + 2);
1081
1082         if(!strcmp(argv[1], "is_allowed"))
1083                 return is_allowed(argc - 2, argv + 2);
1084
1085         if(!strcmp(argv[1], "is_denied"))
1086                 return is_denied(argc - 2, argv + 2);
1087
1088         if(!strcmp(argv[1], "allow"))
1089                 return allow(argc - 2, argv + 2);
1090
1091         if(!strcmp(argv[1], "dontcare"))
1092                 return dontcare(argc - 2, argv + 2);
1093
1094         if(!strcmp(argv[1], "deny"))
1095                 return deny(argc - 2, argv + 2);
1096
1097         if(!strcmp(argv[1], "sign"))
1098                 return sign(argc - 2, argv + 2);
1099
1100         if(!strcmp(argv[1], "import"))
1101                 return import(argc - 2, argv + 2);
1102
1103         if(!strcmp(argv[1], "export"))
1104                 return exprt(argc - 2, argv + 2);
1105
1106         if(!strcmp(argv[1], "test"))
1107                 return test(argc - 2, argv + 2);
1108
1109         if(!strcmp(argv[1], "find"))
1110                 return find(argc - 2, argv + 2);
1111
1112         if(!strcmp(argv[1], "fsck"))
1113                 return fsck();
1114
1115         cerr << "Unknown command: " << argv[1] << '\n';
1116         return EX_USAGE;
1117 }
1118