CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
whitelist.cc
Go to the documentation of this file.
1 
5 #include "cvmfs_config.h"
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, unsigned char **pkcs7_buf)
30  const
31 {
32  *plain_size = plain_size_;
33  *pkcs7_size = pkcs7_size_;
34  *plain_buf = NULL;
35  *pkcs7_buf = NULL;
36  if (plain_size_ > 0) {
37  *plain_buf = reinterpret_cast<unsigned char *>(smalloc(plain_size_));
38  memcpy(*plain_buf, plain_buf_, plain_size_);
39  }
40  if (pkcs7_size_ > 0) {
41  *pkcs7_buf = reinterpret_cast<unsigned char *>(smalloc(pkcs7_size_));
42  memcpy(*pkcs7_buf, pkcs7_buf_, pkcs7_size_);
43  }
44 }
45 
46 
47 std::string Whitelist::CreateString(
48  const std::string &fqrn,
49  int validity_days,
50  shash::Algorithms hash_algorithm,
51  signature::SignatureManager *signature_manager)
52 {
53  std::string to_sign =
54  WhitelistTimestamp(time(NULL)) + "\n" +
55  "E" + WhitelistTimestamp(time(NULL) + validity_days * 24 * 3600) + "\n" +
56  "N" + fqrn + "\n" +
57  signature_manager->FingerprintCertificate(hash_algorithm) + "\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()), hash_str.length(),
68  &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) return "";
79  return std::string(reinterpret_cast<char *>(plain_buf_), plain_size_);
80 }
81 
82 
83 time_t Whitelist::expires() const {
84  assert(status_ == kStAvailable);
85  return expires_;
86 }
87 
88 
89 bool Whitelist::IsExpired() const {
90  assert(status_ == kStAvailable);
91  return time(NULL) > expires_;
92 }
93 
94 
95 Failures Whitelist::VerifyLoadedCertificate() const {
96  assert(status_ == kStAvailable);
97 
98  vector<string> blacklist = signature_manager_->GetBlacklist();
99  for (unsigned i = 0; i < blacklist.size(); ++i) {
100  shash::Any this_hash =
102  if (this_hash.IsNull())
103  continue;
104 
106  if (this_hash == signature_manager_->HashCertificate(algorithm))
107  return kFailBlacklisted;
108  }
109 
110  for (unsigned i = 0; i < fingerprints_.size(); ++i) {
111  shash::Algorithms algorithm = fingerprints_[i].algorithm;
112  if (signature_manager_->HashCertificate(algorithm) == fingerprints_[i]) {
113  if (verification_flags_ & kFlagVerifyCaChain) {
114  bool retval = signature_manager_->VerifyCaChain();
115  if (!retval)
116  return kFailBadCaChain;
117  }
118  return kFailOk;
119  }
120  }
121  return kFailNotListed;
122 }
123 
124 
129 Failures Whitelist::VerifyWhitelist() {
130  bool retval_b;
131  whitelist::Failures retval_wl;
132 
133  assert(verification_flags_ != 0);
134 
135  if (verification_flags_ & kFlagVerifyRsa) {
136  retval_b = signature_manager_->VerifyLetter(plain_buf_, plain_size_, true);
137  if (!retval_b) {
138  LogCvmfs(kLogCvmfs, kLogDebug, "failed to verify repository whitelist");
139  return kFailBadSignature;
140  }
141  }
142 
143  if (verification_flags_ & kFlagVerifyPkcs7) {
144  unsigned char *extracted_whitelist;
145  unsigned extracted_whitelist_size;
146  vector<string> alt_uris;
147  retval_b =
148  signature_manager_->VerifyPkcs7(pkcs7_buf_, pkcs7_size_,
149  &extracted_whitelist,
150  &extracted_whitelist_size,
151  &alt_uris);
152  if (!retval_b) {
154  "failed to verify repository whitelist (pkcs#7): %s",
155  signature_manager_->GetCryptoError().c_str());
156  return kFailBadPkcs7;
157  }
158 
159  // Check for subject alternative name matching the repository name
160  bool found_uri = false;
161  for (unsigned i = 0; i < alt_uris.size(); ++i) {
162  LogCvmfs(kLogSignature, kLogDebug, "found pkcs#7 signer uri %s",
163  alt_uris[i].c_str());
164  if (alt_uris[i] == "cvmfs:" + fqrn_) {
165  found_uri = true;
166  break;
167  }
168  }
169  if (!found_uri) {
171  "failed to find whitelist signer with SAN/URI cvmfs:%s",
172  fqrn_.c_str());
173  free(extracted_whitelist);
174  return kFailBadSignaturePkcs7;
175  }
176 
177  // Check once again the extracted whitelist
178  Reset();
179  LogCvmfs(kLogCvmfs, kLogDebug, "Extracted pkcs#7 whitelist:\n%s",
180  string(reinterpret_cast<char *>(extracted_whitelist),
181  extracted_whitelist_size).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 =
243  base_url + string("cvmfswhitelist.pkcs7");
244  cvmfs::MemSink pkcs7_memsink;
245  download::JobInfo download_whitelist_pkcs7(&whitelist_pkcs7_url, false,
246  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) return true;
270  if (t_local.tm_year > t_whitelist.tm_year) return false;
271  if (t_local.tm_mon < t_whitelist.tm_mon) return true;
272  if (t_local.tm_mon > t_whitelist.tm_mon) return false;
273  if (t_local.tm_mday < t_whitelist.tm_mday) return true;
274  if (t_local.tm_mday > t_whitelist.tm_mday) return false;
275  if (t_local.tm_hour < t_whitelist.tm_hour) return true;
276  return false;
277 }
278 
279 
280 Failures Whitelist::ParseWhitelist(const unsigned char *whitelist,
281  const unsigned whitelist_size)
282 {
283  time_t local_timestamp = time(NULL);
284  string line;
285  unsigned payload_bytes = 0;
286  bool verify_pkcs7 = false;
287  bool verify_cachain = false;
288 
289  // Check timestamp (UTC), ignore issue date (legacy)
290  line = GetLineMem(reinterpret_cast<const char *>(whitelist), whitelist_size);
291  if (line.length() != 14) {
292  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
293  return kFailMalformed;
294  }
295  payload_bytes += 15;
296 
297  // Expiry date
298  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
299  whitelist_size-payload_bytes);
300  if (line.length() != 15) {
301  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
302  return kFailMalformed;
303  }
304  struct tm tm_wl;
305  memset(&tm_wl, 0, sizeof(struct tm));
306  tm_wl.tm_year = String2Int64(line.substr(1, 4))-1900;
307  tm_wl.tm_mon = String2Int64(line.substr(5, 2)) - 1;
308  tm_wl.tm_mday = String2Int64(line.substr(7, 2));
309  tm_wl.tm_hour = String2Int64(line.substr(9, 2));
310  tm_wl.tm_min = tm_wl.tm_sec = 0; // exact on hours level
311  time_t timestamp = timegm(&tm_wl);
313  "whitelist UTC expiry timestamp in localtime: %s",
314  StringifyTime(timestamp, false).c_str());
315  LogCvmfs(kLogSignature, kLogDebug, "local time: %s",
316  StringifyTime(local_timestamp, true).c_str());
317  // Makeshift solution to deal with whitelists valid after Y2038 on 32bit
318  // machines. Still unclear how glibc is going to treat the problem.
319  if (!IsBefore(local_timestamp, tm_wl)) {
321  "whitelist lifetime verification failed, expired");
322  return kFailExpired;
323  }
324  // if (timestamp < 0) {
325  // LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp");
326  // return kFailMalformed;
327  // }
328  // if (local_timestamp > timestamp) {
329  // LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr,
330  // "whitelist lifetime verification failed, expired");
331  // return kFailExpired;
332  // }
333  expires_ = timestamp;
334  payload_bytes += 16;
335 
336  // Check repository name
337  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
338  whitelist_size-payload_bytes);
339  if ((fqrn_ != "") && ("N" + fqrn_ != line)) {
341  "repository name on the whitelist does not match "
342  "(found %s, expected %s)",
343  line.c_str(), fqrn_.c_str());
344  return kFailNameMismatch;
345  }
346  payload_bytes += line.length() + 1;
347 
348  // Check for PKCS7
349  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
350  whitelist_size-payload_bytes);
351  if (line == "Vpkcs7") {
352  LogCvmfs(kLogSignature, kLogDebug, "whitelist verification: pkcs#7");
353  verify_pkcs7 = true;
354  payload_bytes += line.length() + 1;
355  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
356  whitelist_size-payload_bytes);
357  }
358 
359  // Check for CA chain verification
360  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
361  whitelist_size-payload_bytes);
362  if (line == "Wcachain") {
364  "whitelist imposes ca chain verification of manifest signature");
365  verify_cachain = true;
366  payload_bytes += line.length() + 1;
367  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
368  whitelist_size-payload_bytes);
369  }
370 
371  do {
372  if (line == "--") break;
374  if (!this_hash.IsNull())
375  fingerprints_.push_back(this_hash);
376 
377  payload_bytes += line.length() + 1;
378  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
379  whitelist_size-payload_bytes);
380  } while (payload_bytes < whitelist_size);
381 
382  verification_flags_ = verify_pkcs7 ? kFlagVerifyPkcs7 : kFlagVerifyRsa;
383  if (verify_cachain)
384  verification_flags_ |= kFlagVerifyCaChain;
385  return kFailOk;
386 }
387 
388 
389 void Whitelist::Reset() {
390  status_ = kStNone;
391  fingerprints_.clear();
392  expires_ = 0;
393  verification_flags_ = 0;
394  if (plain_buf_)
395  free(plain_buf_);
396  if (pkcs7_buf_)
397  free(pkcs7_buf_);
398  plain_buf_ = NULL;
399  pkcs7_buf_ = NULL;
400  plain_size_ = 0;
401  pkcs7_size_ = 0;
402 }
403 
404 
405 Whitelist::Whitelist(const string &fqrn,
406  download::DownloadManager *download_manager,
407  signature::SignatureManager *signature_manager) :
408  fqrn_(fqrn),
409  download_manager_(download_manager),
410  signature_manager_(signature_manager),
411  plain_buf_(NULL),
412  plain_size_(0),
413  pkcs7_buf_(NULL),
414  pkcs7_size_(0)
415 {
416  Reset();
417 }
418 
419 
421  fqrn_(other.fqrn_),
422  download_manager_(other.download_manager_),
423  signature_manager_(other.signature_manager_),
424  status_(other.status_),
425  fingerprints_(other.fingerprints_),
426  expires_(other.expires_),
427  verification_flags_(other.verification_flags_)
428 {
430 }
431 
432 
433 // Testing only
435  : download_manager_(NULL)
436  , signature_manager_(NULL)
437  , status_(kStNone)
438  , expires_(0)
439  , verification_flags_(0)
440  , plain_buf_(NULL)
441  , plain_size_(0)
442  , pkcs7_buf_(NULL)
443  , pkcs7_size_(0)
444 {
445 }
446 
448  if (&other == this)
449  return *this;
450 
451  Reset();
452  fqrn_ = other.fqrn_;
455  status_ = other.status_;
457  expires_ = other.expires_;
460 
461  return *this;
462 }
463 
464 
466  Reset();
467 }
468 
469 } // namespace whitelist
void HashString(const std::string &content, Any *any_digest)
Definition: hash.cc:268
bool SignRsa(const unsigned char *buffer, const unsigned buffer_size, unsigned char **signature, unsigned *signature_size)
Definition: signature.cc:817
unsigned plain_size_
Definition: whitelist.h:127
bool IsNull() const
Definition: hash.h:383
string GetLineMem(const char *text, const int text_size)
Definition: string.cc:380
static shash::Any MkFromFingerprint(const std::string &fingerprint)
Definition: signature.cc:662
unsigned char * data()
Definition: sink_mem.h:122
unsigned pkcs7_size_
Definition: whitelist.h:129
std::string ToString(const bool with_suffix=false) const
Definition: hash.h:249
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:125
string StringifyTime(const time_t seconds, const bool utc)
Definition: string.cc:105
unsigned char * plain_buf_
Definition: whitelist.h:126
char algorithm
Algorithms
Definition: hash.h:41
int64_t String2Int64(const string &value)
Definition: string.cc:222
std::string WhitelistTimestamp(time_t when)
Definition: string.cc:169
std::string fqrn_
Definition: whitelist.h:118
void Release()
Definition: sink.h:58
download::DownloadManager * download_manager_
Definition: whitelist.h:119
const whitelist::Whitelist * whitelist() const
Definition: repository.h:124
size_t pos()
Definition: sink_mem.h:121
Whitelist & operator=(const Whitelist &other)
Definition: whitelist.cc:447
std::string FingerprintCertificate(const shash::Algorithms hash_algorithm)
Definition: signature.cc:640
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:528