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