CernVM-FS  2.11.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  download::JobInfo download_whitelist(&whitelist_url,
225  false, probe_hosts, NULL);
226  retval_dl = download_manager_->Fetch(&download_whitelist);
227  if (retval_dl != download::kFailOk)
228  return kFailLoad;
229  plain_size_ = download_whitelist.destination_mem.pos;
230  if (plain_size_ == 0)
231  return kFailEmpty;
232  plain_buf_ =
233  reinterpret_cast<unsigned char *>(download_whitelist.destination_mem.data);
234 
235  retval_wl = ParseWhitelist(plain_buf_, plain_size_);
236  if (retval_wl != kFailOk)
237  return retval_wl;
238 
239  if (verification_flags_ & kFlagVerifyPkcs7) {
240  // Load the separate whitelist pkcs7 structure
241  const string whitelist_pkcs7_url =
242  base_url + string("cvmfswhitelist.pkcs7");
243  download::JobInfo download_whitelist_pkcs7(&whitelist_pkcs7_url, false,
244  probe_hosts, NULL);
245  retval_dl = download_manager_->Fetch(&download_whitelist_pkcs7);
246  if (retval_dl != download::kFailOk)
247  return kFailLoadPkcs7;
248  pkcs7_size_ = download_whitelist_pkcs7.destination_mem.pos;
249  if (pkcs7_size_ == 0)
250  return kFailEmptyPkcs7;
251  pkcs7_buf_ = reinterpret_cast<unsigned char *>
252  (download_whitelist_pkcs7.destination_mem.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) return true;
268  if (t_local.tm_year > t_whitelist.tm_year) return false;
269  if (t_local.tm_mon < t_whitelist.tm_mon) return true;
270  if (t_local.tm_mon > t_whitelist.tm_mon) return false;
271  if (t_local.tm_mday < t_whitelist.tm_mday) return true;
272  if (t_local.tm_mday > t_whitelist.tm_mday) return false;
273  if (t_local.tm_hour < t_whitelist.tm_hour) return true;
274  return false;
275 }
276 
277 
278 Failures Whitelist::ParseWhitelist(const unsigned char *whitelist,
279  const unsigned whitelist_size)
280 {
281  time_t local_timestamp = time(NULL);
282  string line;
283  unsigned payload_bytes = 0;
284  bool verify_pkcs7 = false;
285  bool verify_cachain = false;
286 
287  // Check timestamp (UTC), ignore issue date (legacy)
288  line = GetLineMem(reinterpret_cast<const char *>(whitelist), whitelist_size);
289  if (line.length() != 14) {
290  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
291  return kFailMalformed;
292  }
293  payload_bytes += 15;
294 
295  // Expiry date
296  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
297  whitelist_size-payload_bytes);
298  if (line.length() != 15) {
299  LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp format");
300  return kFailMalformed;
301  }
302  struct tm tm_wl;
303  memset(&tm_wl, 0, sizeof(struct tm));
304  tm_wl.tm_year = String2Int64(line.substr(1, 4))-1900;
305  tm_wl.tm_mon = String2Int64(line.substr(5, 2)) - 1;
306  tm_wl.tm_mday = String2Int64(line.substr(7, 2));
307  tm_wl.tm_hour = String2Int64(line.substr(9, 2));
308  tm_wl.tm_min = tm_wl.tm_sec = 0; // exact on hours level
309  time_t timestamp = timegm(&tm_wl);
311  "whitelist UTC expiry timestamp in localtime: %s",
312  StringifyTime(timestamp, false).c_str());
313  LogCvmfs(kLogSignature, kLogDebug, "local time: %s",
314  StringifyTime(local_timestamp, true).c_str());
315  // Makeshift solution to deal with whitelists valid after Y2038 on 32bit
316  // machines. Still unclear how glibc is going to treat the problem.
317  if (!IsBefore(local_timestamp, tm_wl)) {
319  "whitelist lifetime verification failed, expired");
320  return kFailExpired;
321  }
322  // if (timestamp < 0) {
323  // LogCvmfs(kLogSignature, kLogDebug, "invalid timestamp");
324  // return kFailMalformed;
325  // }
326  // if (local_timestamp > timestamp) {
327  // LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr,
328  // "whitelist lifetime verification failed, expired");
329  // return kFailExpired;
330  // }
331  expires_ = timestamp;
332  payload_bytes += 16;
333 
334  // Check repository name
335  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
336  whitelist_size-payload_bytes);
337  if ((fqrn_ != "") && ("N" + fqrn_ != line)) {
339  "repository name on the whitelist does not match "
340  "(found %s, expected %s)",
341  line.c_str(), fqrn_.c_str());
342  return kFailNameMismatch;
343  }
344  payload_bytes += line.length() + 1;
345 
346  // Check for PKCS7
347  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
348  whitelist_size-payload_bytes);
349  if (line == "Vpkcs7") {
350  LogCvmfs(kLogSignature, kLogDebug, "whitelist verification: pkcs#7");
351  verify_pkcs7 = true;
352  payload_bytes += line.length() + 1;
353  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
354  whitelist_size-payload_bytes);
355  }
356 
357  // Check for CA chain verification
358  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
359  whitelist_size-payload_bytes);
360  if (line == "Wcachain") {
362  "whitelist imposes ca chain verification of manifest signature");
363  verify_cachain = true;
364  payload_bytes += line.length() + 1;
365  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
366  whitelist_size-payload_bytes);
367  }
368 
369  do {
370  if (line == "--") break;
372  if (!this_hash.IsNull())
373  fingerprints_.push_back(this_hash);
374 
375  payload_bytes += line.length() + 1;
376  line = GetLineMem(reinterpret_cast<const char *>(whitelist)+payload_bytes,
377  whitelist_size-payload_bytes);
378  } while (payload_bytes < whitelist_size);
379 
380  verification_flags_ = verify_pkcs7 ? kFlagVerifyPkcs7 : kFlagVerifyRsa;
381  if (verify_cachain)
382  verification_flags_ |= kFlagVerifyCaChain;
383  return kFailOk;
384 }
385 
386 
387 void Whitelist::Reset() {
388  status_ = kStNone;
389  fingerprints_.clear();
390  expires_ = 0;
391  verification_flags_ = 0;
392  if (plain_buf_)
393  free(plain_buf_);
394  if (pkcs7_buf_)
395  free(pkcs7_buf_);
396  plain_buf_ = NULL;
397  pkcs7_buf_ = NULL;
398  plain_size_ = 0;
399  pkcs7_size_ = 0;
400 }
401 
402 
403 Whitelist::Whitelist(const string &fqrn,
404  download::DownloadManager *download_manager,
405  signature::SignatureManager *signature_manager) :
406  fqrn_(fqrn),
407  download_manager_(download_manager),
408  signature_manager_(signature_manager),
409  plain_buf_(NULL),
410  plain_size_(0),
411  pkcs7_buf_(NULL),
412  pkcs7_size_(0)
413 {
414  Reset();
415 }
416 
417 
419  fqrn_(other.fqrn_),
420  download_manager_(other.download_manager_),
421  signature_manager_(other.signature_manager_),
422  status_(other.status_),
423  fingerprints_(other.fingerprints_),
424  expires_(other.expires_),
425  verification_flags_(other.verification_flags_)
426 {
428 }
429 
430 
431 // Testing only
433  : download_manager_(NULL)
434  , signature_manager_(NULL)
435  , status_(kStNone)
436  , expires_(0)
437  , verification_flags_(0)
438  , plain_buf_(NULL)
439  , plain_size_(0)
440  , pkcs7_buf_(NULL)
441  , pkcs7_size_(0)
442 {
443 }
444 
446  if (&other == this)
447  return *this;
448 
449  Reset();
450  fqrn_ = other.fqrn_;
453  status_ = other.status_;
455  expires_ = other.expires_;
458 
459  return *this;
460 }
461 
462 
464  Reset();
465 }
466 
467 } // namespace whitelist
void HashString(const std::string &content, Any *any_digest)
Definition: hash.cc:268
#define LogCvmfs(source, mask,...)
Definition: logging.h:25
bool SignRsa(const unsigned char *buffer, const unsigned buffer_size, unsigned char **signature, unsigned *signature_size)
Definition: signature.cc:806
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:651
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
download::DownloadManager * download_manager_
Definition: whitelist.h:119
const whitelist::Whitelist * whitelist() const
Definition: repository.h:124
Whitelist & operator=(const Whitelist &other)
Definition: whitelist.cc:445
struct download::JobInfo::@4 destination_mem
std::string FingerprintCertificate(const shash::Algorithms hash_algorithm)
Definition: signature.cc:629
std::vector< shash::Any > fingerprints_
Definition: whitelist.h:123
signature::SignatureManager * signature_manager_
Definition: whitelist.h:120