Clean up the namespace.
[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 <list>
26
27 #include "fides.h"
28
29 #ifndef FIDES_DEBUG
30 #define FIDES_DEBUG false
31 #endif
32
33 #define debug if(FIDES_DEBUG)
34
35 using namespace std;
36
37 namespace Fides {
38         static regexp authexp("^a[+0-] ");
39         static regexp trustexp("^t[+0-] ");
40
41         /// Saves a certificate to a file.
42         //
43         /// @param cert      Certificate to save.
44         /// @param filename  File to save the certificate to.
45         void Manager::certificate_save(const Certificate *cert, const std::string &filename) const {
46                 ofstream file(filename.c_str());
47                 file << cert->to_string() << '\n';
48         }
49
50         /// Loads a certificate from a file.
51         //
52         /// @param filename  File to save the certificate to.
53         /// @return          The certificate.
54         Certificate *Manager::certificate_load(const std::string &filename) {
55                 ifstream file(filename.c_str());
56                 string data;
57                 getline(file, data);
58                 return certificate_from_string(data);
59         }
60
61         /// Loads a certificate from a string.
62         //
63         /// @param data  String containing the certificate in textual form.
64         /// @return      The certificate.
65         Certificate *Manager::certificate_from_string(const std::string &data) {
66                 size_t b, e;
67                 e = data.find(' ', 0);
68                 if(e == string::npos)
69                         throw exception("Invalid Certificate");
70                 string fingerprint = hexdecode(data.substr(0, e));
71                 const PublicKey *signer = find_key(fingerprint);
72                 if(!signer)
73                         throw exception("Unknown public key");
74                 b = e + 1;
75                 e = data.find('.', b);
76                 if(e == string::npos)
77                         throw exception("Invalid Certificate");
78                 struct timeval timestamp;
79                 timestamp.tv_sec = atol(data.c_str() + b);
80                 b = e + 1;
81                 timestamp.tv_usec = atol(data.c_str() + b);
82                 e = data.find(' ', b);
83                 if(e == string::npos)
84                         throw exception("Invalid Certificate");
85                 b = e + 1;
86                 e = data.find(' ', b);
87                 if(e == string::npos)
88                         throw exception("Invalid Certificate");
89                 string signature = b64decode(data.substr(b, e - b));
90                 b = e + 1;
91                 string statement = data.substr(b);
92
93                 return new Certificate(signer, timestamp, statement, signature);
94         }
95
96         /// \class Manager
97         ///
98         /// \brief Interaction with a Fides database.
99         ///
100         /// A Manager object manages a database of public keys and certificates.
101         /// New certificates can be created, certificates can be imported and exported,
102         /// and queries can be done on the database.
103
104
105         /// Creates a new handle on a Fides database.
106         //
107         /// Will load the private key, known public keys and certificates.
108         /// After that it will calculate the trust value of all keys.
109         ///
110         /// @param dir Directory where Fides stores the keys and certificates.
111         ///            If no directory is specified, the following environment variables
112         ///            are used, in the given order:
113         ///            - \$FIDES_HOME
114         ///            - \$HOME/.fides
115         ///            - \$PWD/.fides
116         Manager::Manager(const std::string &dir): homedir(dir) {
117                 debug cerr << "Fides initialising\n";
118
119                 // Set homedir to provided directory, or $FIDES_HOME, or $HOME/.fides, or as a last resort $PWD/.fides
120                 if(homedir.empty())
121                         homedir = getenv("FIDES_HOME") ?: "";
122                 if(homedir.empty()) {
123                         char cwd[PATH_MAX];
124                         homedir = getenv("HOME") ?: getcwd(cwd, sizeof cwd);
125                         homedir += "/.fides";
126                 }
127
128                 // Derived directories
129                 homedir += '/';
130                 certdir = homedir + "certs/";
131                 keydir = homedir + "keys/";
132                 obsoletedir = homedir + ".obsolete_certs/";
133
134                 // Ensure the homedir and its subdirectories exist
135                 mkdir(homedir.c_str(), 0700);
136                 mkdir(certdir.c_str(), 0700);
137                 mkdir(keydir.c_str(), 0700);
138                 mkdir(obsoletedir.c_str(), 0700);
139
140                 try {
141                         mykey.load_private(homedir + "priv");
142                         firstrun = false;
143                 } catch(exception &e) {
144                         cerr << "Fides generating keypair\n";
145                         mykey.generate();
146                         mykey.save_private(homedir + "priv");
147                         mykey.save(keydir + hexencode(mykey.fingerprint()));
148                         firstrun = true;
149                 }
150                 vector<string> files = dirlist(keydir);
151                 for(size_t i = 0; i < files.size(); ++i) {
152                         debug cerr << "Loading key " << files[i] << '\n';
153
154                         PublicKey *key = new PublicKey();
155                         key->load(keydir + files[i]);
156                         keys[hexdecode(files[i])] = key;
157                 }
158
159                 keys[mykey.fingerprint()] = &mykey;
160
161                 files = dirlist(certdir);
162                 for(size_t i = 0; i < files.size(); ++i) {
163                         debug cerr << "Loading Certificate " << files[i] << '\n';
164                         Certificate *cert = certificate_load(certdir + files[i]);
165                         if(false && !cert->validate()) {
166                                 cerr << "Bad Certificate in database: " << cert->to_string() << '\n';
167                                 continue;
168                         }
169                         certs[hexdecode(files[i])] = cert;
170                 }
171
172                 /// \TODO save and load this value
173                 latest.tv_sec = 0;
174                 latest.tv_usec = 0;
175
176                 update_trust();
177         }
178
179         Manager::~Manager() {
180                 debug cerr << "Fides exitting\n";
181                 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i)
182                         delete i->second;
183                 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
184                         if(i->second != &mykey)
185                                 delete i->second;
186         }
187
188         /// Checks the validaty of all certificates.
189         //
190         /// @return True if all known certificates are valid, false otherwise.
191         bool Manager::fsck() const {
192                 int errors = 0;
193
194                 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i) {
195                         if(!i->second->validate()) {
196                                 cerr << "Validation of Certificate failed: " << i->second->to_string() << '\n';
197                                 errors++;
198                         }
199                 }
200
201                 cerr << errors << " errors in " << certs.size() << " Certificates\n";
202                 return !errors;
203         }
204
205         /// Returns the base directory used by Fides.
206         //
207         /// @return The home directory.
208         string Manager::get_homedir() const {
209                 return homedir;
210         }
211
212         /// Tests whether this is the first time Fides has run and has generated new keys.
213         //
214         /// @return True if this is the first time, false otherwise.
215         bool Manager::is_firstrun() const {
216                 return firstrun;
217         }
218
219         /// Find the public key corresponding to a given fingerprint.
220         //
221         /// @param fingerprint String containing a fingerprint.
222         /// @return Pointer to the public key corresponding to the fingerprint, or NULL if it was not found.
223         PublicKey *Manager::find_key(const std::string &fingerprint) const {
224                 map<string, PublicKey *>::const_iterator i;
225                 i = keys.find(fingerprint);
226                 if(i != keys.end())
227                         return i->second;
228                 else
229                         return 0;
230         }
231
232         /// Find all certificates from a give public key and that match a regular expression.
233         //
234         /// @param signer Public key to match certificates to.
235         /// @param regex  Regular expression to match the statement of each certificate to.
236         /// @return A vector of certificates that match the criteria.
237         vector<const Certificate *> Manager::find_certificates(const PublicKey *signer, const std::string &regex) const {
238                 vector<const Certificate *> found;
239                 map<string, Certificate *>::const_iterator i;
240                 regexp regexp(regex);
241                 for(i = certs.begin(); i != certs.end(); ++i) {
242                         if(!i->second) {
243                                 cerr << "No Certificate for " << hexencode(i->first) << '\n';
244                                 continue;
245                         }
246                         if(i->second->signer == signer)
247                                 if(regexp.match(i->second->statement))
248                                         found.push_back(i->second);
249                 }
250                 return found;
251         }
252
253         /// Find all certificates that match a regular expression.
254         //
255         /// @param regex  Regular expression to match the statement of each certificate to.
256         /// @return A vector of certificates that match the criteria.
257         vector<const Certificate *> Manager::find_certificates(const std::string &regex) const {
258                 vector<const Certificate *> found;
259                 map<string, Certificate *>::const_iterator i;
260                 regexp regexp(regex);
261                 for(i = certs.begin(); i != certs.end(); ++i)
262                         if(regexp.match(i->second->statement))
263                                 found.push_back(i->second);
264                 return found;
265         }
266
267         /// Find all certificates from a give public key.
268         //
269         /// @param signer Public key to match certificates to.
270         /// @return A vector of certificates that match the criteria.
271         vector<const Certificate *> Manager::find_certificates(const PublicKey *signer) const {
272                 vector<const Certificate *> found;
273                 map<string, Certificate *>::const_iterator i;
274                 for(i = certs.begin(); i != certs.end(); ++i)
275                         if(i->second->signer == signer)
276                                 found.push_back(i->second);
277                 return found;
278         }
279
280         /// Import public keys and certificates from a stream.
281         //
282         /// @param in Stream to read from.
283         void Manager::import_all(std::istream &in) {
284                 string line, pem;
285                 bool is_pem = false;
286
287                 while(getline(in, line)) {
288                         if(line.empty())
289                                 continue;
290
291                         if(is_pem || !line.compare(0, 11, "-----BEGIN ")) {
292                                 pem += line + '\n';
293                                 if(!line.compare(0, 9, "-----END ")) {
294                                         PublicKey *key = new PublicKey();
295                                         key->from_string(pem);
296                                         debug cerr << "Imported key " << hexencode(key->fingerprint()) << '\n';
297                                         merge(key);
298                                         is_pem = false;
299                                 } else {
300                                         is_pem = true;
301                                 }
302                                 continue;
303                         }
304
305                         Certificate *cert = certificate_from_string(line);
306                         debug cerr << "Importing Certificate " << hexencode(cert->fingerprint()) << '\n';
307                         merge(cert);
308                 }
309         }
310
311         /// Export all public keys and certificates to a stream.
312         //
313         /// @param out Stream to write to.
314         void Manager::export_all(std::ostream &out) const {
315                 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
316                         out << i->second->to_string();
317                 for(map<string, Certificate *>::const_iterator i = certs.begin(); i != certs.end(); ++i)
318                         out << i->second->to_string() << '\n';
319         }
320
321         /// Trust a public key.
322         //
323         /// This creates a certificate that says that we trust the given public key.
324         /// If a key is trusted, then authorisation certificates from that key are taken into account
325         /// when calling functions such as Manager::is_allowed().
326         ///
327         /// @param key Public key to trust.
328         void Manager::trust(const PublicKey *key) {
329                 string full = "t+ " + hexencode(key->fingerprint());
330                 sign(full);
331         }
332
333         /// Distrust a public key.
334         //
335         /// This creates a certificate that says that we distrust the given public key.
336         /// If a key is distrusted, then authorisation certificates from that key are not taken into account
337         /// when calling functions such as Manager::is_allowed().
338         ///
339         /// @param key Public key to trust.
340         void Manager::distrust(const PublicKey *key) {
341                 string full = "t- " + hexencode(key->fingerprint());
342                 sign(full);
343         }
344
345         /// Don't care about a public key.
346         //
347         /// This creates a certificate that says that we neither trust nor distrust the given public key.
348         /// This key and certificates created by it are then treated as if we have never trusted nor distrusted this key.
349         ///
350         /// @param key Public key to trust.
351         void Manager::dctrust(const PublicKey *key) {
352                 string full = "t0 " + hexencode(key->fingerprint());
353                 sign(full);
354         }
355
356         /// Recalculate the trust value of all known public keys.
357         void Manager::update_trust() {
358                 // clear trust on all keys
359                 for(map<string, PublicKey *>::const_iterator i = keys.begin(); i != keys.end(); ++i)
360                         i->second->trust = 0;
361
362                 // Start by checking all trust certificates from ourself.
363                 // If another key is positively or negatively trusted, update its trust score
364                 // and add it to the the list of new keys to check.
365                 // Then add our own key to the list of already checked keys.
366                 // Then check all the trust certificates of those on the tocheck list, etc.
367                 // Already checked keys are never updated anymore (TODO: is that smart?)
368                 // Certificates of keys with a zero or negative trust score are not processed.
369
370                 set<PublicKey *> checked;
371                 set<PublicKey *> tocheck;
372                 set<PublicKey *> newkeys;
373                 set<PublicKey *>::iterator i;
374
375                 mykey.trust = 3;
376                 tocheck.insert(&mykey);
377
378                 while(tocheck.size()) {
379                         // add
380                         checked.insert(tocheck.begin(), tocheck.end());
381                         newkeys.clear();
382
383                         // loop over all keys whose certificates need to be checked
384
385                         for(i = tocheck.begin(); i != tocheck.end(); ++i) {
386                                 debug cerr << "Trust for key " << hexencode((*i)->fingerprint()) << " set to " << (*i)->trust << '\n';
387
388                                 // except if this key is not trusted
389
390                                 if((*i)->trust <= 0)
391                                         continue;
392
393                                 // find all non-zero trust certificates of this key
394
395                                 vector<const Certificate *> matches = find_certificates(*i, "^t[+-] ");
396
397                                 // update trust value of those keys
398
399                                 for(size_t j = 0; j < matches.size(); j++) {
400                                         PublicKey *other = find_key(hexdecode(matches[j]->statement.substr(3)));        
401
402                                         if(!other) {
403                                                 cerr << "Trust Certificate for unknown key: " << matches[j]->to_string() << '\n';
404                                                 continue;
405                                         }
406
407                                         // except for keys we already checked
408
409                                         if(checked.find(other) != checked.end()) {
410                                                 debug cerr << "Skipping trust Certificate for already checked key: " << matches[j]->to_string() << '\n';
411                                                 continue;
412                                         }
413
414                                         // update trust
415
416                                         if(matches[j]->statement[1] == '+')
417                                                 other->trust++;
418                                         else
419                                                 other->trust--;
420
421                                         newkeys.insert(other);
422                                 }
423                         }
424
425                         tocheck = newkeys;
426                 }       
427         }       
428
429         /// Merges a public key into the database.
430         //
431         /// @param key The public key to merge.
432         void Manager::merge(PublicKey *key) {
433                 if(keys.find(key->fingerprint()) != keys.end()) {
434                         debug cerr << "Key already known\n";
435                         return;
436                 }
437
438                 keys[key->fingerprint()] = key;
439                 key->save(keydir + hexencode(key->fingerprint()));
440         }
441
442         /// Merges a certificate into the database.
443         //
444         /// The database is searched to find if there are certificates from the same signer
445         /// with similar statements.
446         /// If the given certificate is similar to another one in our database,
447         /// then the certificate with the newer timestamp wins and will be allowed in the database,
448         /// the older certificate will be removed.
449         ///
450         /// @param cert The certificate to merge.
451         void Manager::merge(Certificate *cert) {
452                 // TODO: check if cert is already in database
453                 // TODO: check if cert obsoletes other certs
454
455                 // If we already know this certificate, drop it.
456                 if(certs.find(cert->fingerprint()) != certs.end()) {
457                         debug cerr << "Certificate already known\n";
458                         return;
459                 }
460
461                 // If the certificate does not validate, drop it.
462                 if(!cert->validate()) {
463                         // TODO: this should not happen, be wary of DoS attacks
464                         cerr << "Trying to merge invalid Certificate: " << cert->to_string() << '\n';
465                         return;
466                 }
467
468                 vector<const Certificate *> others;
469
470                 // Is this an authorisation cert?
471                 if(authexp.match(cert->statement)) {
472                         // Find certs identical except for the +/-/0
473                         // TODO: escape statement in regexp
474                         others = find_certificates(cert->signer, string("^a[+0-] ") + cert->statement.substr(3) + '$');
475                         if(others.size()) {
476                                 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
477                                         debug cerr << "Certificate is overruled by a newer Certificate\n";
478                                         return;
479                                 }
480                                 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
481                                         // TODO: this should not happen, be wary of DoS attacks
482                                         debug cerr << "Certificate has same timestamp as another timestamp!\n";
483                                         return;
484                                 }
485                                 debug cerr << "Certificate overrules an older Certificate!\n";
486                                 // save new cert first
487                                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
488                                 certs[cert->fingerprint()] = cert;
489
490                                 // delete old one
491                                 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
492                                 certs.erase(others[0]->fingerprint());
493                                 delete others[0];
494                                 return;
495                         }
496                 }
497
498                 // Is this a trust cert?
499                 // TODO: it's just the same as above!
500                 if(trustexp.match(cert->statement)) {
501                         // Find certs identical except for the +/-/0
502                         // TODO: escape statement in regexp
503                         others = find_certificates(cert->signer, string("^t[+0-] ") + cert->statement.substr(3) + '$');
504                         if(others.size()) {
505                                 if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
506                                         debug cerr << "Certificate is overruled by a newer Certificate\n";
507                                         return;
508                                 }
509                                 if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
510                                         // TODO: this should not happen, be wary of DoS attacks
511                                         debug cerr << "Certificate has same timestamp as another timestamp!\n";
512                                         return;
513                                 }
514                                 debug cerr << "Certificate overrules an older Certificate!\n";
515                                 // delete old one
516                                 rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
517                                 certs.erase(others[0]->fingerprint());
518                                 delete others[0];
519                                 certs[cert->fingerprint()] = cert;
520                                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
521                                 return;
522                         }
523                 }
524
525                 // Did somebody sign the exact same statement twice?
526                 // Could happen if there is a different, conflicting statement between this new and the corresponding old one.
527                 others = find_certificates(cert->signer, string("^") + cert->statement + '$');
528                 if(others.size()) {
529                         if(timercmp(&others[0]->timestamp, &cert->timestamp, >)) {
530                                 debug cerr << "Certificate is overruled by a newer Certificate\n";
531                                 return;
532                         }
533                         if(timercmp(&others[0]->timestamp, &cert->timestamp, ==)) {
534                                 // TODO: this should not happen, be wary of DoS attacks
535                                 debug cerr << "Certificate has same timestamp as another timestamp!\n";
536                                 return;
537                         }
538                         debug cerr << "Certificate overrules an older Certificate!\n";
539                         // delete old one
540                         rename((certdir + hexencode(others[0]->fingerprint())).c_str(), (obsoletedir + hexencode(others[0]->fingerprint())).c_str());
541                         certs.erase(others[0]->fingerprint());
542                         delete others[0];
543                         certs[cert->fingerprint()] = cert;
544                         certificate_save(cert, certdir + hexencode(cert->fingerprint()));
545                         return;
546                 }
547
548                 debug cerr << "Certificate is new\n";
549                 certs[cert->fingerprint()] = cert;
550                 certificate_save(cert, certdir + hexencode(cert->fingerprint()));
551         }
552
553         /// Calculates whether a statement is allowed or denied.
554         //
555         /// @param statement The statement to calculate the authorisation values for.
556         /// @param self      Will be set to 1 if we allow the statement,
557         ///                  0 if we neither allowed nor denied it,
558         ///                  or -1 if we denied it.
559         /// @param trusted   Will be positive if the majority of the trusted public keys
560         ///                  gave a positive authorisation, 0 if there is a tie,
561         ///                  or negative if the majority gave a negative authorisation.
562         /// @param all       Same as trusted but for all public keys.
563         void Manager::auth_stats(const std::string &statement, int &self, int &trusted, int &all) const {
564                 self = trusted = all = 0;
565                 vector<const Certificate *> matches = find_certificates(string("^a[+0-] ") + statement + '$');
566                 for(size_t i = 0; i < matches.size(); ++i) {
567                         char code = matches[i]->statement[1];
568                         int diff = 0;
569                         if(code == '+')
570                                 diff = 1;
571                         else if(code == '-')
572                                 diff = -1;
573                         if(matches[i]->signer == &mykey)
574                                 self += diff;
575                         if(matches[i]->signer->trust > 0)
576                                 trusted += diff;
577                         all += diff;
578                 }
579         }
580
581         /// Tests whether the given public key is trusted.
582         //
583         /// @param key The public key to test.
584         /// @return True if the key is explicitly trusted, false otherwise.
585         bool Manager::is_trusted(const PublicKey *key) const {
586                 return key->trust > 0;
587         }
588
589         /// Tests whether the given public key is distrusted.
590         //
591         /// @param key The public key to test.
592         /// @return True if the key is explicitly distrusted, false otherwise.
593         bool Manager::is_distrusted(const PublicKey *key) const {
594                 return key->trust < 0;
595         }
596
597         /// Tests whether the given statement is allowed.
598         //
599         /// @param statement The statement to test.
600         /// @param key       The public key to test.
601         /// @return True if the statement is allowed for the given key, false otherwise.
602         bool Manager::is_allowed(const std::string &statement, const PublicKey *key) const {
603                 int self, trusted, all;
604
605                 if(key)
606                         auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
607                 else
608                         auth_stats(statement, self, trusted, all);
609                         
610                 if(self)
611                         return self > 0;
612                 else if(trusted)
613                         return trusted > 0;
614                 else
615                         return false;
616         }
617
618         /// Tests whether the given statement is denied.
619         //
620         /// @param statement The statement to test.
621         /// @param key       The public key to test.
622         /// @return True if the statement is denied for the given key, false otherwise.
623         bool Manager::is_denied(const std::string &statement, const PublicKey *key) const {
624                 int self, trusted, all;
625
626                 if(key)
627                         auth_stats(hexencode(key->fingerprint()) + " " + statement, self, trusted, all);
628                 else
629                         auth_stats(statement, self, trusted, all);
630
631                 if(self)
632                         return self < 0;
633                 else if(trusted)
634                         return trusted < 0;
635                 else
636                         return false;
637         }
638
639         /// Creates a certificate for the given statement.
640         //
641         /// @param statement The statement to create a certificate for.
642         void Manager::sign(const std::string &statement) {
643                 // Try to set "latest" to now, but ensure monoticity
644                 struct timeval now;
645                 gettimeofday(&now, 0);
646                 if(timercmp(&latest, &now, >=)) {
647                         latest.tv_usec++;
648                         if(latest.tv_usec >= 1000000) {
649                                 latest.tv_sec++;
650                                 latest.tv_usec -= 1000000;
651                         }
652                 } else {
653                         latest = now;
654                 }
655
656                 // Create a new certificate and merge it with our database
657                 merge(new Certificate(&mykey, latest, statement));
658         }
659
660         void Manager::allow(const std::string &statement, const PublicKey *key) {
661                 string full = "a+ ";
662                 if(key)
663                         full += hexencode(key->fingerprint()) + ' ';
664                 full += statement;
665                 sign(full);
666         }
667
668         void Manager::dontcare(const std::string &statement, const PublicKey *key) {
669                 string full = "a0 ";
670                 if(key)
671                         full += hexencode(key->fingerprint()) + ' ';
672                 full += statement;
673                 sign(full);
674         }
675
676         void Manager::deny(const std::string &statement, const PublicKey *key) {
677                 string full = "a- ";
678                 if(key)
679                         full += hexencode(key->fingerprint()) + ' ';
680                 full += statement;
681                 sign(full);
682         }
683 }