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