Fix #includes and copyright statements.
[fides] / lib / fides.cc
1 /* fides.cc - 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 <iostream>
19 #include <fstream>
20 #include <cstdlib>
21 #include <sys/types.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24 #include <dirent.h>
25 #include <botan/types.h>
26 #include <botan/botan.h>
27 #include <botan/ecdsa.h>
28 #include <botan/look_pk.h>
29 #include <botan/lookup.h>
30 #include <botan/filters.h>
31 #include <botan/sha2_32.h>
32 #include <list>
33
34 #include "fides.h"
35
36 using namespace std;
37
38 // Global state
39
40 Botan::LibraryInitializer libinit;
41 Botan::AutoSeeded_RNG fides::rng;
42
43 // Public key functions
44
45 fides::publickey::publickey(): pub(0), trust(0) {
46 }
47
48 fides::publickey::~publickey() {
49         delete pub;
50 }
51
52 void fides::publickey::load(istream &in) {
53         try {
54                 Botan::DataSource_Stream source(in);
55                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
56         } catch(Botan::Exception &e) {
57                 throw exception(e.what());
58         }
59 }
60
61 void fides::publickey::load(const std::string &filename) {
62         ifstream in(filename.c_str());
63         load(in);
64 }
65
66 void fides::publickey::save(ostream &out) {
67         out << to_string();
68 }
69
70 void fides::publickey::save(const std::string &filename) {
71         ofstream out(filename.c_str());
72         save(out);
73 }
74
75 void fides::publickey::from_string(const std::string &in) {
76         try {
77                 Botan::DataSource_Memory source(in);
78                 pub = dynamic_cast<Botan::ECDSA_PublicKey *>(Botan::X509::load_key(source));
79         } catch(Botan::Exception &e) {
80                 throw exception(e.what());
81         }
82 }
83
84 string fides::publickey::to_string() {
85         return Botan::X509::PEM_encode(*pub);
86 }
87
88 string fides::publickey::fingerprint(unsigned int bits) {
89         // TODO: find out if there is a standard way to get a hash of an ECDSA public key
90         Botan::SHA_256 sha256;
91         Botan::SecureVector<Botan::byte> hash = sha256.process(Botan::X509::PEM_encode(*pub));
92         return string((const char *)hash.begin(), bits / 8);
93 }
94
95 bool fides::publickey::verify(const std::string &statement, const std::string &signature) {
96         auto_ptr<Botan::PK_Verifier> verifier(Botan::get_pk_verifier(*pub, "EMSA1(SHA-512)"));
97         verifier->update((const Botan::byte *)statement.data(), statement.size());
98         Botan::SecureVector<Botan::byte> sig;
99         sig.set((const Botan::byte *)signature.data(), signature.size());
100         return verifier->check_signature(sig);
101 }
102
103 // Private key functions
104
105 fides::privatekey::privatekey(): priv(0) {
106 }
107
108 fides::privatekey::~privatekey() {
109         delete priv;
110         pub = 0;
111 }
112
113 void fides::privatekey::generate(const std::string &field) {
114         Botan::EC_Domain_Params domain = Botan::get_EC_Dom_Pars_by_oid(field);
115         pub = priv = new Botan::ECDSA_PrivateKey(rng, domain);
116 }
117
118 void fides::privatekey::generate(unsigned int bits) {
119         switch(bits) {
120                 case 112: return generate("1.3.132.0.6");
121                 case 128: return generate("1.3.132.0.28");
122                 case 160: return generate("1.3.132.0.9");
123                 case 192: return generate("1.3.132.0.31");
124                 case 224: return generate("1.3.132.0.32");
125                 case 256: return generate("1.3.132.0.10");
126                 case 384: return generate("1.3.132.0.34");
127                 case 521: return generate("1.3.132.0.35");
128                 default: throw exception("Unsupported number of bits for private key");
129         }
130 }
131
132 void fides::privatekey::load_private(istream &in) {
133         try {
134                 Botan::DataSource_Stream stream(in);
135                 pub = priv = dynamic_cast<Botan::ECDSA_PrivateKey *>(Botan::PKCS8::load_key(stream, rng, ""));
136         } catch(Botan::Exception &e) {
137                 throw exception(e.what());
138         }
139 }
140
141 void fides::privatekey::load_private(const std::string &filename) {
142         ifstream in(filename.c_str());
143         load_private(in);
144 }
145
146 void fides::privatekey::save_private(ostream &out) {
147         out << Botan::PKCS8::PEM_encode(*priv);
148 }
149
150 void fides::privatekey::save_private(const std::string &filename) {
151         ofstream out(filename.c_str());
152         save_private(out);
153 }
154
155 string fides::privatekey::sign(const std::string &statement) {
156         auto_ptr<Botan::PK_Signer> signer(Botan::get_pk_signer(*priv, "EMSA1(SHA-512)"));
157         Botan::SecureVector<Botan::byte> sig = signer->sign_message((const Botan::byte *)statement.data(), statement.size(), rng);
158         return string((const char *)sig.begin(), (size_t)sig.size());
159 }
160
161 // Base64 and hex encoding/decoding functions
162
163 string fides::hexencode(const string &in) {
164         Botan::Pipe pipe(new Botan::Hex_Encoder);
165         pipe.process_msg((Botan::byte *)in.data(), in.size());
166         return pipe.read_all_as_string();
167 }
168
169 string fides::hexdecode(const string &in) {
170         Botan::Pipe pipe(new Botan::Hex_Decoder);
171         pipe.process_msg((Botan::byte *)in.data(), in.size());
172         return pipe.read_all_as_string();
173 }
174
175 string fides::b64encode(const string &in) {
176         Botan::Pipe pipe(new Botan::Base64_Encoder);
177         pipe.process_msg((Botan::byte *)in.data(), in.size());
178         return pipe.read_all_as_string();
179 }
180
181 string fides::b64decode(const string &in) {
182         Botan::Pipe pipe(new Botan::Base64_Decoder);
183         pipe.process_msg((Botan::byte *)in.data(), in.size());
184         return pipe.read_all_as_string();
185 }
186
187 // Certificate functions
188
189 fides::certificate::certificate(publickey *key, struct timeval timestamp, const std::string &statement, const std::string &signature): signer(key), timestamp(timestamp), statement(statement), signature(signature) {}
190
191 bool fides::certificate::validate() {
192         string data = signer->fingerprint(256);
193         data += string((const char *)&timestamp, sizeof timestamp);
194         data += statement;
195         return signer->verify(data, signature);
196 }
197
198 fides::certificate::certificate(privatekey *key, struct timeval timestamp, const std::string &statement): signer(key), timestamp(timestamp), statement(statement) {
199         string data = signer->fingerprint(256);
200         data += string((const char *)&timestamp, sizeof timestamp);
201         data += statement;
202         signature = key->sign(data);
203 }
204
205 string fides::certificate::fingerprint(unsigned int bits) {
206         return signature.substr(signature.size() - bits / 8);   
207 }
208
209 string fides::certificate::to_string() const {
210         string data = fides::hexencode(signer->fingerprint());
211         data += ' ';
212         char ts[100];
213         snprintf(ts, sizeof ts, "%lu.%06lu", timestamp.tv_sec, timestamp.tv_usec);
214         data += ts;
215         data += ' ';
216         data += fides::b64encode(signature);
217         data += ' ';
218         data += statement;
219         return data;
220 }
221
222 // Utility functions
223
224 static vector<string> dirlist(const string &path) {
225         vector<string> files;
226
227         DIR *dir = opendir(path.c_str());
228         if(!dir)
229                 return files;
230
231         struct dirent entry, *result = &entry;
232         
233         while(result) {
234                 readdir_r(dir, &entry, &result);
235                 if(!result)
236                         break;
237                 struct stat st;
238                 if(result->d_type == DT_UNKNOWN) {
239                         if(stat((path + "/" + result->d_name).c_str(), &st))
240                                 continue;
241                         if(S_ISREG(st.st_mode))
242                                 files.push_back(result->d_name);
243                 } else if(result->d_type == DT_REG) {
244                         files.push_back(result->d_name);
245                 }
246         }
247
248         closedir(dir);
249
250         return files;
251 }
252
253 void fides::certificate_save(const certificate *cert, const string &filename) {
254         ofstream file(filename.c_str());
255         file << cert->to_string() << '\n';
256 }
257
258 fides::certificate *fides::certificate_load(const string &filename) {
259         ifstream file(filename.c_str());
260         string data;
261         getline(file, data);
262         return certificate_from_string(data);
263 }
264
265 fides::certificate *fides::certificate_from_string(const string &data) {
266         size_t b, e;
267         e = data.find(' ', 0);
268         if(e == string::npos)
269                 throw exception("Invalid certificate");
270         string fingerprint = hexdecode(data.substr(0, e));
271         publickey *signer = find_key(fingerprint);
272         if(!signer)
273                 throw exception("Unknown public key");
274         b = e + 1;
275         e = data.find('.', b);
276         if(e == string::npos)
277                 throw exception("Invalid certificate");
278         struct timeval timestamp;
279         timestamp.tv_sec = atol(data.c_str() + b);
280         b = e + 1;
281         timestamp.tv_usec = atol(data.c_str() + b);
282         e = data.find(' ', b);
283         if(e == string::npos)
284                 throw exception("Invalid certificate");
285         b = e + 1;
286         e = data.find(' ', b);
287         if(e == string::npos)
288                 throw exception("Invalid certificate");
289         string signature = fides::b64decode(data.substr(b, e - b));
290         b = e + 1;
291         string statement = data.substr(b);
292
293         return new certificate(signer, timestamp, statement, signature);
294 }
295
296 // Fides main functions
297
298 fides::fides(const string &dir): homedir(dir) {
299         cerr << "Fides initialising\n";
300
301         // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
302         if(homedir.empty())
303                 homedir = getenv("FIDES_HOME") ?: "";
304         if(homedir.empty()) {
305                 char cwd[PATH_MAX];
306                 homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
307                 homedir += "/.fides";
308         }
309
310         // Derived directories
311         homedir += '/';
312         certdir = homedir + "certs/";
313         keydir = homedir + "keys/";
314         obsoletedir = homedir + ".obsolete_certs/";
315
316         // Ensure the homedir and its subdirectories exist
317         mkdir(homedir.c_str(), 0700);
318         mkdir(certdir.c_str(), 0700);
319         mkdir(keydir.c_str(), 0700);
320         mkdir(obsoletedir.c_str(), 0700);
321
322         try {
323                 mykey.load_private(homedir + "priv");
324                 firstrun = false;
325         } catch(fides::exception &e) {
326                 cerr << "Fides generating keypair\n";
327                 mykey.generate();
328                 mykey.save_private(homedir + "priv");
329                 mykey.save(keydir + hexencode(mykey.fingerprint()));
330                 firstrun = true;
331         }
332         vector<string> files = dirlist(keydir);
333         for(size_t i = 0; i < files.size(); ++i) {
334                 cerr << "Loading key " << files[i] << '\n';
335
336                 publickey *key = new publickey();
337                 key->load(keydir + files[i]);
338                 keys[hexdecode(files[i])] = key;
339         }
340
341         keys[mykey.fingerprint()] = &mykey;
342
343         files = dirlist(certdir);
344         for(size_t i = 0; i < files.size(); ++i) {
345                 cerr << "Loading certificate " << files[i] << '\n';
346                 certificate *cert = certificate_load(certdir + files[i]);
347                 if(false && !cert->validate()) {
348                         cerr << "Bad certificate!\n";
349                         continue;
350                 }
351                 certs[hexdecode(files[i])] = cert;
352         }
353
354         // TODO: save and load this value
355         latest.tv_sec = 0;
356         latest.tv_usec = 0;
357
358         update_trust();
359 }
360
361 fides::~fides() {
362         cerr << "Fides exitting\n";
363         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
364                 delete i->second;
365         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
366                 if(i->second != &mykey)
367                         delete i->second;
368 }
369
370 bool fides::fsck() {
371         int errors = 0;
372
373         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i) {
374                 if(!i->second->validate()) {
375                         cerr << "Validation of certificate failed: " << i->second->to_string() << '\n';
376                         errors++;
377                 }
378         }
379
380         cerr << errors << " errors in " << certs.size() << " certificates\n";
381         return !errors;
382 }
383
384 string fides::get_homedir() {
385         return homedir;
386 }
387
388 bool fides::is_firstrun() {
389         return firstrun;
390 }
391
392 fides::publickey *fides::find_key(const string &fingerprint) {
393         map<string, publickey *>::iterator i;
394         i = keys.find(fingerprint);
395         if(i != keys.end())
396                 return i->second;
397         else
398                 return 0;
399 }
400
401 vector<fides::certificate *> fides::find_certificates(publickey *signer, const string &regex) {
402         vector<certificate *> found;
403         map<string, certificate *>::iterator i;
404         regexp regexp(regex);
405         for(i = certs.begin(); i != certs.end(); ++i) {
406                 if(!i->second) {
407                         cerr << "No certificate for " << hexencode(i->first) << '\n';
408                         continue;
409                 }
410                 if(i->second->signer == signer)
411                         if(regexp.match(i->second->statement))
412                                 found.push_back(i->second);
413         }
414         return found;
415 }
416
417 vector<fides::certificate *> fides::find_certificates(const string &regex) {
418         vector<certificate *> found;
419         map<string, certificate *>::iterator i;
420         regexp regexp(regex);
421         for(i = certs.begin(); i != certs.end(); ++i)
422                 if(regexp.match(i->second->statement))
423                         found.push_back(i->second);
424         return found;
425 }
426
427 vector<fides::certificate *> fides::find_certificates(publickey *signer) {
428         vector<certificate *> found;
429         map<string, certificate *>::iterator i;
430         for(i = certs.begin(); i != certs.end(); ++i)
431                 if(i->second->signer == signer)
432                         found.push_back(i->second);
433         return found;
434 }
435
436 void fides::import_all(istream &in) {
437         string line, pem;
438         bool is_pem = false;
439
440         while(getline(in, line)) {
441                 if(line.empty())
442                         continue;
443
444                 if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
445                         pem += line + '\n';
446                         if(!line.compare(0, 9, "-----END ")) {
447                                 fides::publickey *key = new publickey();
448                                 key->from_string(pem);
449                                 cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
450                                 merge(key);
451                                 is_pem = false;
452                         } else {
453                                 is_pem = true;
454                         }
455                         continue;
456                 }
457
458                 fides::certificate *cert = certificate_from_string(line);
459                 cerr << "Importing certificate " << hexencode(cert->fingerprint()) << '\n';
460                 merge(cert);
461         }
462 }
463
464 void fides::export_all(ostream &out) {
465         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
466                 out << i->second->to_string();
467         for(map<string, certificate *>::iterator i = certs.begin(); i != certs.end(); ++i)
468                 out << i->second->to_string() << '\n';
469 }
470
471 void fides::trust(publickey *key) {
472         string full = "t+ " + hexencode(key->fingerprint());
473         sign(full);
474 }
475
476 void fides::distrust(publickey *key) {
477         string full = "t- " + hexencode(key->fingerprint());
478         sign(full);
479 }
480
481 void fides::dctrust(publickey *key) {
482         string full = "t0 " + hexencode(key->fingerprint());
483         sign(full);
484 }
485
486 void fides::update_trust() {
487         // clear trust on all keys
488         for(map<string, publickey *>::iterator i = keys.begin(); i != keys.end(); ++i)
489                 i->second->trust = 0;
490
491         // Start by checking all trust certificates from ourself.
492         // If another key is positively or negatively trusted, update its trust score
493         // and add it to the the list of new keys to check.
494         // Then add our own key to the list of already checked keys.
495         // Then check all the trust certificates of those on the tocheck list, etc.
496         // Already checked keys are never updated anymore (TODO: is that smart?)
497         // Certificates of keys with a zero or negative trust score are not processed.
498
499         set<publickey *> checked;
500         set<publickey *> tocheck;
501         set<publickey *> newkeys;
502         set<publickey *>::iterator i;
503
504         mykey.trust = 3;
505         tocheck.insert(&mykey);
506
507         while(tocheck.size()) {
508                 // add
509                 checked.insert(tocheck.begin(), tocheck.end());
510                 newkeys.clear();
511
512                 // loop over all keys whose certificates need to be checked
513
514                 for(i = tocheck.begin(); i != tocheck.end(); ++i) {
515                         cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
516
517                         // except if this key is not trusted
518
519                         if((*i)->trust <= 0)
520                                 continue;
521
522                         // find all non-zero trust certificates of this key
523
524                         vector<certificate *> matches = find_certificates(*i, "^t[+-] ");
525
526                         // update trust value of those keys
527
528                         for(size_t j = 0; j < matches.size(); j++) {
529                                 publickey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
530
531                                 if(!other) {
532                                         cerr << "Trust certificate for unknown key: " << matches[j]->to_string() << '\n';
533                                         continue;
534                                 }
535
536                                 // except for keys we already checked
537
538                                 if(checked.find(other) != checked.end()) {
539                                         cerr << "Skipping trust certificate for already checked key: " << matches[j]->to_string() << '\n';
540                                         continue;
541                                 }
542
543                                 // update trust
544
545                                 if(matches[j]->statement[1] == '+')
546                                         other->trust++;
547                                 else
548                                         other->trust--;
549
550                                 newkeys.insert(other);
551                         }
552                 }
553
554                 tocheck = newkeys;
555         }       
556 }       
557
558 void fides::merge(publickey *key) {
559         if(keys.find(key->fingerprint()) != keys.end()) {
560                 cerr << "Key already known\n";
561                 return;
562         }
563
564         keys[key->fingerprint()] = key;
565         key->save(keydir + hexencode(key->fingerprint()));
566 }
567
568 void fides::merge(certificate *cert) {
569         // TODO: check if cert is already in database
570         // TODO: check if cert obsoletes other certs
571
572         // If we already know this certificate, drop it.
573         if(certs.find(cert->fingerprint()) != certs.end()) {
574                 cerr << "Certificate already known\n";
575                 return;
576         }
577
578         // If the certificate does not validate, drop it.
579         if(!cert->validate()) {
580                 // TODO: this should not happen, be wary of DoS attacks
581                 cerr << "Certificate invalid\n";
582                 return;
583         }
584
585         // TODO: move these regexps to the class?
586         regexp authexp("^a[+0-] ");
587         regexp trustexp("^t[+0-] ");
588         vector<certificate *> others;
589
590         // Is this an authorisation cert?
591         if(authexp.match(cert->statement)) {
592                 // Find certs identical except for the +/-/0
593                 // TODO: escape statement in regexp
594                 others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
595                 if(others.size()) {
596                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
597                                 cerr << "Certificate is overruled by a newer certificate\n";
598                                 return;
599                         }
600                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
601                                 // TODO: this should not happen, be wary of DoS attacks
602                                 cerr << "Certificate has same timestamp as another timestamp!\n";
603                                 return;
604                         }
605                         cerr << "Certificate overrules an older certificate!\n";
606                         // save new cert first
607                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
608                         certs[cert->fingerprint()] = cert;
609
610                         // delete old one
611                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
612                         certs.erase(others[0]->fingerprint());
613                         delete others[0];
614                         return;
615                 }
616         }
617
618         // Is this a trust cert?
619         // TODO: it's just the same as above!
620         if(trustexp.match(cert->statement)) {
621                 // Find certs identical except for the +/-/0
622                 // TODO: escape statement in regexp
623                 others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
624                 if(others.size()) {
625                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
626                                 cerr << "Certificate is overruled by a newer certificate\n";
627                                 return;
628                         }
629                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
630                                 // TODO: this should not happen, be wary of DoS attacks
631                                 cerr << "Certificate has same timestamp as another timestamp!\n";
632                                 return;
633                         }
634                         cerr << "Certificate overrules an older certificate!\n";
635                         // delete old one
636                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
637                         certs.erase(others[0]->fingerprint());
638                         delete others[0];
639                         certs[cert->fingerprint()] = cert;
640                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
641                         return;
642                 }
643         }
644
645         // Did somebody sign the exact same statement twice?
646         // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
647         others = find_certificates(cert->signer, string("^") + cert->statement + '$');
648         if(others.size()) {
649                 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
650                         cerr << "Certificate is overruled by a newer certificate\n";
651                         return;
652                 }
653                 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
654                         // TODO: this should not happen, be wary of DoS attacks
655                         cerr << "Certificate has same timestamp as another timestamp!\n";
656                         return;
657                 }
658                 cerr << "Certificate overrules an older certificate!\n";
659                 // delete old one
660                 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
661                 certs.erase(others[0]->fingerprint());
662                 delete others[0];
663                 certs[cert->fingerprint()] = cert;
664                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
665                 return;
666         }
667
668         cerr << "Certificate is new\n";
669         certs[cert->fingerprint()] = cert;
670         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
671 }
672
673 void fides::auth_stats(const string &statement, int &self, int &trusted, int &all) {
674         self = trusted = all = 0;
675         vector<certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
676         for(size_t i = 0; i < matches.size(); ++i) {
677                 char code = matches[i]->statement[1];
678                 int diff = 0;
679                 if(code == '+')
680                         diff = 1;
681                 else if(code == '-')
682                         diff = -1;
683                 if(matches[i]->signer == &mykey)
684                         self += diff;
685                 if(matches[i]->signer->trust > 0)
686                         trusted += diff;
687                 all += diff;
688         }
689 }
690
691 bool fides::is_trusted(publickey *key) {
692         return key->trust > 0;
693 }
694
695 bool fides::is_distrusted(publickey *key) {
696         return key->trust < 0;
697 }
698
699 bool fides::is_allowed(const string &statement, publickey *key) {
700         int self, trusted, all;
701
702         if(key)
703                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
704         else
705                 auth_stats(statement, self, trusted, all);
706                 
707         if(self)
708                 return self > 0;
709         else if(trusted)
710                 return trusted > 0;
711         else
712                 return false;
713 }
714
715 bool fides::is_denied(const string &statement, publickey *key) {
716         int self, trusted, all;
717
718         if(key)
719                 auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
720         else
721                 auth_stats(statement, self, trusted, all);
722
723         if(self)
724                 return self < 0;
725         else if(trusted)
726                 return trusted < 0;
727         else
728                 return false;
729 }
730
731 void fides::sign(const string &statement) {
732         // Try to set "latest" to now, but ensure monoticity
733         struct timeval now;
734         gettimeofday(&now, 0);
735         if(timercmp(&latest, &now, >=)) {
736                 latest.tv_usec++;
737                 if(latest.tv_usec >= 1000000) {
738                         latest.tv_sec++;
739                         latest.tv_usec -= 1000000;
740                 }
741         } else {
742                 latest = now;
743         }
744
745         // Create a new certificate and merge it with our database
746         merge(new certificate(&mykey, latest, statement));
747 }
748
749 void fides::allow(const string &statement, publickey *key) {
750         string full = "a+ ";
751         if(key)
752                 full += hexencode(key->fingerprint()) + ' ';
753         full += statement;
754         sign(full);
755 }
756
757 void fides::dontcare(const string &statement, publickey *key) {
758         string full = "a0 ";
759         if(key)
760                 full += hexencode(key->fingerprint()) + ' ';
761         full += statement;
762         sign(full);
763 }
764
765 void fides::deny(const string &statement, publickey *key) {
766         string full = "a- ";
767         if(key)
768                 full += hexencode(key->fingerprint()) + ' ';
769         full += statement;
770         sign(full);
771 }
772