GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/manifest_fetch.cc Lines: 66 84 78.6 %
Date: 2019-02-03 02:48:13 Branches: 34 53 64.2 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System.
3
 */
4
5
#include "manifest_fetch.h"
6
7
#include <string>
8
#include <vector>
9
10
#include <cassert>
11
12
#include "download.h"
13
#include "hash.h"
14
#include "manifest.h"
15
#include "signature.h"
16
#include "whitelist.h"
17
18
using namespace std;  // NOLINT
19
20
namespace manifest {
21
22
/**
23
 * Downloads and verifies the manifest, the certificate, and the whitelist.
24
 * If base_url is empty, uses the probe_hosts feature from download manager.
25
 */
26
39
static Failures DoFetch(
27
  const std::string &base_url,
28
  const std::string &repository_name,
29
  const uint64_t minimum_timestamp,
30
  const shash::Any *base_catalog,
31
  signature::SignatureManager *signature_manager,
32
  download::DownloadManager *download_manager,
33
  ManifestEnsemble *ensemble)
34
{
35
39
  assert(ensemble);
36
39
  const bool probe_hosts = base_url == "";
37
39
  Failures result = kFailUnknown;
38
  bool retval_b;
39
  download::Failures retval_dl;
40
  whitelist::Failures retval_wl;
41
  whitelist::Whitelist whitelist(repository_name,
42
                                 download_manager,
43
39
                                 signature_manager);
44
45
39
  const string manifest_url = base_url + string("/.cvmfspublished");
46
39
  download::JobInfo download_manifest(&manifest_url, false, probe_hosts, NULL);
47
39
  shash::Any certificate_hash;
48
39
  string certificate_url = base_url + "/";  // rest is in manifest
49
  download::JobInfo download_certificate(&certificate_url, true, probe_hosts,
50
39
                                         &certificate_hash);
51
52
39
  retval_dl = download_manager->Fetch(&download_manifest);
53
39
  if (retval_dl != download::kFailOk) {
54
    LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogWarn,
55
             "failed to download repository manifest (%d - %s)",
56
3
             retval_dl, download::Code2Ascii(retval_dl));
57
3
    return kFailLoad;
58
  }
59
60
  // Load Manifest
61
  ensemble->raw_manifest_buf =
62
36
    reinterpret_cast<unsigned char *>(download_manifest.destination_mem.data);
63
36
  ensemble->raw_manifest_size = download_manifest.destination_mem.pos;
64
  ensemble->manifest =
65
    manifest::Manifest::LoadMem(ensemble->raw_manifest_buf,
66
36
                                ensemble->raw_manifest_size);
67
36
  if (!ensemble->manifest)
68
    return kFailIncomplete;
69
70
  // Basic manifest sanity check
71
36
  if (ensemble->manifest->repository_name() != repository_name) {
72
    LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
73
             "repository name does not match (found %s, expected %s)",
74
             ensemble->manifest->repository_name().c_str(),
75
2
             repository_name.c_str());
76
2
    result = kFailNameMismatch;
77
2
    goto cleanup;
78
  }
79

34
  if (ensemble->manifest->root_path() != shash::Md5(shash::AsciiPtr(""))) {
80
    result = kFailRootMismatch;
81
    goto cleanup;
82
  }
83
34
  if (ensemble->manifest->publish_timestamp() < minimum_timestamp) {
84
    result = kFailOutdated;
85
    goto cleanup;
86
  }
87
88
  // Quick way out: hash matches base catalog
89

34
  if (base_catalog && (ensemble->manifest->catalog_hash() == *base_catalog))
90
2
    return kFailOk;
91
92
  // Load certificate
93
32
  certificate_hash = ensemble->manifest->certificate();
94
32
  ensemble->FetchCertificate(certificate_hash);
95
32
  if (!ensemble->cert_buf) {
96
32
    certificate_url += ensemble->manifest->MakeCertificatePath();
97
32
    retval_dl = download_manager->Fetch(&download_certificate);
98
32
    if (retval_dl != download::kFailOk) {
99
      result = kFailLoad;
100
        goto cleanup;
101
    }
102
    ensemble->cert_buf = reinterpret_cast<unsigned char *>(
103
32
      download_certificate.destination_mem.data);
104
32
    ensemble->cert_size = download_certificate.destination_mem.pos;
105
  }
106
  retval_b = signature_manager->LoadCertificateMem(ensemble->cert_buf,
107
32
                                                   ensemble->cert_size);
108
32
  if (!retval_b) {
109
    result = kFailBadCertificate;
110
    goto cleanup;
111
  }
112
113
  // Verify manifest
114
  retval_b = signature_manager->VerifyLetter(ensemble->raw_manifest_buf,
115
                                             ensemble->raw_manifest_size,
116
32
                                             false);
117
32
  if (!retval_b) {
118
    LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
119
             "failed to verify repository manifest");
120
    result = kFailBadSignature;
121
    goto cleanup;
122
  }
123
124
  // Load whitelist and verify
125
32
  retval_wl = whitelist.Load(base_url);
126
32
  if (retval_wl != whitelist::kFailOk) {
127
    LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
128
             "whitelist verification failed (%d): %s",
129
             retval_wl, whitelist::Code2Ascii(retval_wl));
130
    result = kFailBadWhitelist;
131
    goto cleanup;
132
  }
133
134
32
  retval_wl = whitelist.VerifyLoadedCertificate();
135
32
  if (retval_wl != whitelist::kFailOk) {
136
    LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr,
137
             "failed to verify repository signature against whitelist (%d): %s",
138
6
             retval_wl, whitelist::Code2Ascii(retval_wl));
139
6
    result = kFailInvalidCertificate;
140
6
    goto cleanup;
141
  }
142
143
  whitelist.CopyBuffers(&ensemble->whitelist_size,
144
                        &ensemble->whitelist_buf,
145
                        &ensemble->whitelist_pkcs7_size,
146
26
                        &ensemble->whitelist_pkcs7_buf);
147
148
26
  return kFailOk;
149
150
 cleanup:
151
8
  delete ensemble->manifest;
152
8
  ensemble->manifest = NULL;
153
8
  if (ensemble->raw_manifest_buf) free(ensemble->raw_manifest_buf);
154
8
  if (ensemble->cert_buf) free(ensemble->cert_buf);
155
8
  if (ensemble->whitelist_buf) free(ensemble->whitelist_buf);
156
8
  if (ensemble->whitelist_pkcs7_buf) free(ensemble->whitelist_pkcs7_buf);
157
8
  ensemble->raw_manifest_buf = NULL;
158
8
  ensemble->cert_buf = NULL;
159
8
  ensemble->whitelist_buf = NULL;
160
8
  ensemble->whitelist_pkcs7_buf = NULL;
161
8
  ensemble->raw_manifest_size = 0;
162
8
  ensemble->cert_size = 0;
163
8
  ensemble->whitelist_size = 0;
164
8
  ensemble->whitelist_pkcs7_size = 0;
165
8
  return result;
166
}
167
168
/**
169
 * If the whitelist or the manifest are corrupted, fail-over once to another
170
 * stratum 1 if more than a single stratum 1 is known.
171
 */
172
39
Failures Fetch(const std::string &base_url, const std::string &repository_name,
173
               const uint64_t minimum_timestamp, const shash::Any *base_catalog,
174
               signature::SignatureManager *signature_manager,
175
               download::DownloadManager *download_manager,
176
               ManifestEnsemble *ensemble)
177
{
178
  Failures result =
179
    DoFetch(base_url, repository_name, minimum_timestamp, base_catalog,
180
39
            signature_manager, download_manager, ensemble);
181


39
  if ((result != kFailOk) && (result != kFailLoad) &&
182
      (download_manager->num_hosts() > 1))
183
  {
184
    LogCvmfs(kLogCache, kLogDebug | kLogSyslogWarn,
185
             "failed to fetch manifest (%d - %s), trying another stratum 1",
186
             result, Code2Ascii(result));
187
    download_manager->SwitchHost();
188
    result =
189
      DoFetch(base_url, repository_name, minimum_timestamp, base_catalog,
190
              signature_manager, download_manager, ensemble);
191
  }
192
39
  return result;
193
}
194
195
}  // namespace manifest