CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
whitelist.cc
Go to the documentation of this file.
1 
6 #include "whitelist.h"
7 
8 #include <algorithm>
9 #include <cassert>
10 #include <cstring>
11 #include <ctime>
12 
13 #include "crypto/signature.h"
14 #include "network/download.h"
15 #include "util/logging.h"
16 #include "util/smalloc.h"
17 #include "util/string.h"
18 
19 using namespace std; // NOLINT
20 
21 namespace whitelist {
22 
23 const int Whitelist::kFlagVerifyRsa = 0x01;
24 const int Whitelist::kFlagVerifyPkcs7 = 0x02;
25 const int Whitelist::kFlagVerifyCaChain = 0x04;
26 
27 
28 void Whitelist::CopyBuffers(unsigned *plain_size, unsigned char **plain_buf,
29  unsigned *pkcs7_size,
30  unsigned char **pkcs7_buf) const {
31  *plain_size = plain_size_;
32  *pkcs7_size = pkcs7_size_;
33  *plain_buf = NULL;
34  *pkcs7_buf = NULL;
35  if (plain_size_ > 0) {
36  *plain_buf = reinterpret_cast<unsigned char *>(smalloc(plain_size_));
37  memcpy(*plain_buf, plain_buf_, plain_size_);
38  }
39  if (pkcs7_size_ > 0) {
40  *pkcs7_buf = reinterpret_cast<unsigned char *>(smalloc(pkcs7_size_));
41  memcpy(*pkcs7_buf, pkcs7_buf_, pkcs7_size_);
42  }
43 }
44 
45 
46 std::string Whitelist::CreateString(
47  const std::string &fqrn,
48  int validity_days,
49  shash::Algorithms hash_algorithm,
50  signature::SignatureManager *signature_manager) {
51  const std::string to_sign =
52  WhitelistTimestamp(time(NULL)) + "\n" + "E" +
53  WhitelistTimestamp(time(NULL) + validity_days * 24 * 3600) + "\n" + "N" +
54  fqrn + "\n" + signature_manager->FingerprintCertificate(hash_algorithm) +
55  "\n";
56  shash::Any hash(hash_algorithm);
57  shash::HashString(to_sign, &hash);
58  std::string hash_str = hash.ToString();
59 
60  std::string whitelist(to_sign);
61  whitelist += "--\n" + hash_str + "\n";
62  unsigned char *signature;
63  unsigned signature_size;
64  const bool retval = signature_manager->SignRsa(
65  reinterpret_cast<const unsigned char *>(hash_str.data()),
66  hash_str.length(), &signature, &signature_size);
67  assert(retval);
68  whitelist += std::string(reinterpret_cast<char *>(signature), signature_size);
69  free(signature);
70 
71  return whitelist;
72 }
73 
74 
75 std::string Whitelist::ExportString() const {
76  if (plain_buf_ == NULL)
77  return "";
78  return std::string(reinterpret_cast<char *>(plain_buf_), plain_size_);
79 }
80 
81 
82 time_t Whitelist::expires() const {
83  assert(status_ == kStAvailable);
84  return expires_;
85 }
86 
87 
88 bool Whitelist::IsExpired() const {
89  assert(status_ == kStAvailable);
90  return time(NULL) > expires_;
91 }
92 
93 
94 Failures Whitelist::VerifyLoadedCertificate() const {
95  assert(status_ == kStAvailable);
96 
97  vector<string> blacklist = signature_manager_->GetBlacklist();
98  for (unsigned i = 0; i < blacklist.size(); ++i) {
99  const shash::Any this_hash =
101  if (this_hash.IsNull())
102  continue;
103 
104  const shash::Algorithms algorithm = this_hash.algorithm;
105  if (this_hash == signature_manager_->HashCertificate(algorithm))
106  return kFailBlacklisted;
107  }
108 
109  for (unsigned i = 0; i < fingerprints_.size(); ++i) {
110  const shash::Algorithms algorithm = fingerprints_[i].algorithm;
111  if (signature_manager_->HashCertificate(algorithm) == fingerprints_[i]) {
112  if (verification_flags_ & kFlagVerifyCaChain) {
113  const bool retval = signature_manager_->VerifyCaChain();
114  if (!retval)
115  return kFailBadCaChain;
116  }
117  return kFailOk;
118  }
119  }
120  return kFailNotListed;
121 }
122 
123 
128 Failures Whitelist::VerifyWhitelist() {
129  bool retval_b;
130  whitelist::Failures retval_wl;
131 
132  assert(verification_flags_ != 0);
133 
134  if (verification_flags_ & kFlagVerifyRsa) {
135  retval_b = signature_manager_->VerifyLetter(plain_buf_, plain_size_, true);
136  if (!retval_b) {
137  LogCvmfs(kLogCvmfs, kLogDebug, "failed to verify repository whitelist");
138  return kFailBadSignature;
139  }
140  }
141 
142  if (verification_flags_ & kFlagVerifyPkcs7) {
143  unsigned char *extracted_whitelist;
144  unsigned extracted_whitelist_size;
145  vector<string> alt_uris;
146  retval_b = signature_manager_->VerifyPkcs7(
147  pkcs7_buf_, pkcs7_size_, &extracted_whitelist,
148  &extracted_whitelist_size, &alt_uris);
149  if (!retval_b) {
151  "failed to verify repository whitelist (pkcs#7): %s",
152  signature_manager_->GetCryptoError().c_str());
153  return kFailBadPkcs7;
154  }
155 
156  // Check for subject alternative name matching the repository name
157  bool found_uri = false;
158  for (unsigned i = 0; i < alt_uris.size(); ++i) {
159  LogCvmfs(kLogSignature, kLogDebug, "found pkcs#7 signer uri %s",
160  alt_uris[i].c_str());
161  if (alt_uris[i] == "cvmfs:" + fqrn_) {
162  found_uri = true;
163  break;
164  }
165  }
166  if (!found_uri) {
168  "failed to find whitelist signer with SAN/URI cvmfs:%s",
169  fqrn_.c_str());
170  free(extracted_whitelist);
171  return kFailBadSignaturePkcs7;
172  }
173 
174  // Check once again the extracted whitelist
175  Reset();
176  LogCvmfs(kLogCvmfs, kLogDebug, "Extracted pkcs#7 whitelist:\n%s",
177  string(reinterpret_cast<char *>(extracted_whitelist),
178  extracted_whitelist_size)
179  .c_str());
180  retval_wl = ParseWhitelist(extracted_whitelist, extracted_whitelist_size);
181  if (retval_wl != kFailOk) {
183  "failed to verify repository certificate against pkcs#7 "
184  "whitelist");
185  return kFailMalformedPkcs7;
186  }
187  }
188 
189  status_ = kStAvailable;
190  return kFailOk;
191 }
192 
193 
194 Failures Whitelist::LoadMem(const std::string &whitelist) {
195  Failures retval_wl;
196 
197  Reset();
198 
199  plain_size_ = whitelist.length();
200  plain_buf_ = reinterpret_cast<unsigned char *>(smalloc(plain_size_));
201  memcpy(plain_buf_, whitelist.data(), plain_size_);
202 
203  retval_wl = ParseWhitelist(plain_buf_, plain_size_);
204  if (retval_wl != kFailOk)
205  return retval_wl;
206  // TODO(jblomer): PKCS7 verification unsupported when loading from memory
207  if (verification_flags_ & kFlagVerifyPkcs7)
208  return kFailLoadPkcs7;
209 
210  return VerifyWhitelist();
211 }
212 
213 
214 Failures Whitelist::LoadUrl(const std::string &base_url) {
215  const bool probe_hosts = base_url == "";
216  download::Failures retval_dl;
217  Failures retval_wl;
218 
219  Reset();
220 
221  const string whitelist_url = base_url + string("/.cvmfswhitelist");
222  cvmfs::MemSink whitelist_memsink;
223  download::JobInfo download_whitelist(&whitelist_url, false, probe_hosts, NULL,
224  &whitelist_memsink);
225  retval_dl = download_manager_->Fetch(&download_whitelist);
226  if (retval_dl != download::kFailOk)
227  return kFailLoad;
228  plain_size_ = whitelist_memsink.pos();
229  if (plain_size_ == 0)
230  return kFailEmpty;
231  whitelist_memsink.Release();
232  plain_buf_ = whitelist_memsink.data();
233 
234  retval_wl = ParseWhitelist(plain_buf_, plain_size_);
235  if (retval_wl != kFailOk)
236  return retval_wl;
237 
238  if (verification_flags_ & kFlagVerifyPkcs7) {
239  // Load the separate whitelist pkcs7 structure
240  const string whitelist_pkcs7_url = base_url
241  + string("cvmfswhitelist.pkcs7");
242  cvmfs::MemSink pkcs7_memsink;
243  download::JobInfo download_whitelist_pkcs7(
244  &whitelist_pkcs7_url, false, probe_hosts, NULL, &pkcs7_memsink);
245  retval_dl = download_manager_->Fetch(&download_whitelist_pkcs7);
246  if (retval_dl != download::kFailOk)
247  return kFailLoadPkcs7;
248  pkcs7_size_ = pkcs7_memsink.pos();
249  if (pkcs7_size_ == 0)
250  return kFailEmptyPkcs7;
251  pkcs7_memsink.Release();
252  pkcs7_buf_ = pkcs7_memsink.data();
253  }
254 
255  return VerifyWhitelist();
256 }
257 
258 
263 bool Whitelist::IsBefore(time_t now, const struct tm &t_whitelist) {
264  struct tm t_local;
265  if (gmtime_r(&now, &t_local) == NULL)
266  return false;
267  if (t_local.tm_year < t_whitelist.tm_year)
268  return true;
269  if (t_local.tm_year > t_whitelist.tm_year)
270  return false;
271  if (t_local.tm_mon < t_whitelist.tm_mon)
272  return true;
273  if (t_local.tm_mon > t_whitelist.tm_mon)
274  return false;
275  if (t_local.tm_mday < t_whitelist.tm_mday)
276  return true;
277  if (t_local.tm_mday > t_whitelist.tm_mday)
278  return false;
279  if (t_local.tm_hour < t_whitelist.tm_hour)
280  return true;
281  return false;
282 }
283 
284 
285 Failures Whitelist::ParseWhitelist(const unsigned char *whitelist,
286  const unsigned whitelist_size) {
287  const time_t local_timestamp = time(NULL);
288  string line;
289  unsigned payload_bytes = 0;
290  bool verify_pkcs7 = false;
291  bool verify_cachain = false;
292 
293  // Check timestamp (UTC), ignore issue date (legacy)
294  line = GetLineMem(reinterpret_cast<const char *>(whitelist), whitelist_size);
295  if (line.length() != 14) {
296  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
297  return kFailMalformed;
298  }
299  payload_bytes += 15;
300 
301  // Expiry date
302  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
303  whitelist_size - payload_bytes);
304  if (line.length() != 15) {
305  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
306  return kFailMalformed;
307  }
308  struct tm tm_wl;
309  memset(&tm_wl, 0, sizeof(struct tm));
310  tm_wl.tm_year = String2Int64(line.substr(1, 4)) - 1900;
311  tm_wl.tm_mon = String2Int64(line.substr(5, 2)) - 1;
312  tm_wl.tm_mday = String2Int64(line.substr(7, 2));
313  tm_wl.tm_hour = String2Int64(line.substr(9, 2));
314  tm_wl.tm_min = tm_wl.tm_sec = 0; // exact on hours level
315  const time_t timestamp = timegm(&tm_wl);
317  "whitelist UTC expiry timestamp in localtime: %s",
318  StringifyTime(timestamp, false).c_str());
319  LogCvmfs(kLogSignature, kLogDebug, "local time: %s",
320  StringifyTime(local_timestamp, true).c_str());
321  // Makeshift solution to deal with whitelists valid after Y2038 on 32bit
322  // machines. Still unclear how glibc is going to treat the problem.
323  if (!IsBefore(local_timestamp, tm_wl)) {
325  "whitelist lifetime verification failed, expired");
326  return kFailExpired;
327  }
328  // if (timestamp < 0) {
329  // LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp");
330  // return kFailMalformed;
331  // }
332  // if (local_timestamp > timestamp) {
333  // LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr,
334  // "whitelist lifetime verification failed, expired");
335  // return kFailExpired;
336  // }
337  expires_ = timestamp;
338  payload_bytes += 16;
339 
340  // Check repository name
341  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
342  whitelist_size - payload_bytes);
343  if ((fqrn_ != "") && ("N" + fqrn_ != line)) {
345  "repository name on the whitelist does not match "
346  "(found %s, expected %s)",
347  line.c_str(), fqrn_.c_str());
348  return kFailNameMismatch;
349  }
350  payload_bytes += line.length() + 1;
351 
352  // Check for PKCS7
353  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
354  whitelist_size - payload_bytes);
355  if (line == "Vpkcs7") {
356  LogCvmfs(kLogSignature, kLogDebug, "whitelist verification: pkcs#7");
357  verify_pkcs7 = true;
358  payload_bytes += line.length() + 1;
359  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
360  whitelist_size - payload_bytes);
361  }
362 
363  // Check for CA chain verification
364  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
365  whitelist_size - payload_bytes);
366  if (line == "Wcachain") {
368  "whitelist imposes ca chain verification of manifest signature");
369  verify_cachain = true;
370  payload_bytes += line.length() + 1;
371  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
372  whitelist_size - payload_bytes);
373  }
374 
375  do {
376  if (line == "--")
377  break;
378  const shash::Any this_hash =
380  if (!this_hash.IsNull())
381  fingerprints_.push_back(this_hash);
382 
383  payload_bytes += line.length() + 1;
384  line = GetLineMem(reinterpret_cast<const char *>(whitelist) + payload_bytes,
385  whitelist_size - payload_bytes);
386  } while (payload_bytes < whitelist_size);
387 
388  verification_flags_ = verify_pkcs7 ? kFlagVerifyPkcs7 : kFlagVerifyRsa;
389  if (verify_cachain)
390  verification_flags_ |= kFlagVerifyCaChain;
391  return kFailOk;
392 }
393 
394 
395 void Whitelist::Reset() {
396  status_ = kStNone;
397  fingerprints_.clear();
398  expires_ = 0;
399  verification_flags_ = 0;
400  if (plain_buf_)
401  free(plain_buf_);
402  if (pkcs7_buf_)
403  free(pkcs7_buf_);
404  plain_buf_ = NULL;
405  pkcs7_buf_ = NULL;
406  plain_size_ = 0;
407  pkcs7_size_ = 0;
408 }
409 
410 
411 Whitelist::Whitelist(const string &fqrn,
412  download::DownloadManager *download_manager,
413  signature::SignatureManager *signature_manager)
414  : fqrn_(fqrn)
415  , download_manager_(download_manager)
416  , signature_manager_(signature_manager)
417  , plain_buf_(NULL)
418  , plain_size_(0)
419  , pkcs7_buf_(NULL)
420  , pkcs7_size_(0) {
421  Reset();
422 }
423 
424 
426  : fqrn_(other.fqrn_)
427  , download_manager_(other.download_manager_)
428  , signature_manager_(other.signature_manager_)
429  , status_(other.status_)
430  , fingerprints_(other.fingerprints_)
431  , expires_(other.expires_)
432  , verification_flags_(other.verification_flags_) {
434 }
435 
436 
437 // Testing only
439  : download_manager_(NULL)
440  , signature_manager_(NULL)
441  , status_(kStNone)
442  , expires_(0)
443  , verification_flags_(0)
444  , plain_buf_(NULL)
445  , plain_size_(0)
446  , pkcs7_buf_(NULL)
447  , pkcs7_size_(0) { }
448 
450  if (&other == this)
451  return *this;
452 
453  Reset();
454  fqrn_ = other.fqrn_;
457  status_ = other.status_;
459  expires_ = other.expires_;
462 
463  return *this;
464 }
465 
466 
468 
469 } // namespace whitelist
void HashString(const std::string &content, Any *any_digest)
Definition: hash.cc:267
bool SignRsa(const unsigned char *buffer, const unsigned buffer_size, unsigned char **signature, unsigned *signature_size)
Definition: signature.cc:827
unsigned plain_size_
Definition: whitelist.h:127
bool IsNull() const
Definition: hash.h:371
string GetLineMem(const char *text, const int text_size)
Definition: string.cc:415
static shash::Any MkFromFingerprint(const std::string &fingerprint)
Definition: signature.cc:674
unsigned char * data()
Definition: sink_mem.h:115
unsigned pkcs7_size_
Definition: whitelist.h:129
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:241
unsigned char * pkcs7_buf_
Definition: whitelist.h:128
void CopyBuffers(unsigned *plain_size, unsigned char **plain_buf, unsigned *pkcs7_size, unsigned char **pkcs7_buf) const
Definition: whitelist.cc:28
assert((mem||(size==0))&&"Out Of Memory")
Algorithms algorithm
Definition: hash.h:122
string StringifyTime(const time_t seconds, const bool utc)
Definition: string.cc:104
unsigned char * plain_buf_
Definition: whitelist.h:126
char algorithm
Algorithms
Definition: hash.h:41
int64_t String2Int64(const string &value)
Definition: string.cc:234
std::string WhitelistTimestamp(time_t when)
Definition: string.cc:183
std::string fqrn_
Definition: whitelist.h:118
void Release()
Definition: sink.h:59
download::DownloadManager * download_manager_
Definition: whitelist.h:119
const whitelist::Whitelist * whitelist() const
Definition: repository.h:124
size_t pos()
Definition: sink_mem.h:114
Whitelist & operator=(const Whitelist &other)
Definition: whitelist.cc:449
std::string FingerprintCertificate(const shash::Algorithms hash_algorithm)
Definition: signature.cc:652
std::vector< shash::Any > fingerprints_
Definition: whitelist.h:123
signature::SignatureManager * signature_manager_
Definition: whitelist.h:120
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:545