GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/signing_tool.cc
Date: 2024-04-21 02:33:16
Exec Total Coverage
Lines: 0 152 0.0%
Branches: 0 290 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System
3 */
4
5 #include "signing_tool.h"
6
7 #include <string>
8
9 #include "manifest.h"
10 #include "object_fetcher.h"
11 #include "reflog.h"
12 #include "server_tool.h"
13 #include "upload.h"
14 #include "util/exception.h"
15 #include "util/pointer.h"
16
17 namespace {
18
19 typedef HttpObjectFetcher<> ObjectFetcher;
20
21 } // namespace
22
23 SigningTool::SigningTool(ServerTool *server_tool) : server_tool_(server_tool) {}
24
25 SigningTool::~SigningTool() {}
26
27 SigningTool::Result SigningTool::Run(
28 const std::string &manifest_path, const std::string &repo_url,
29 const std::string &spooler_definition, const std::string &temp_dir,
30 const std::string &certificate, const std::string &priv_key,
31 const std::string &repo_name, const std::string &pwd,
32 const std::string &meta_info, const std::string &reflog_chksum_path,
33 const std::string &proxy,
34 const bool garbage_collectable, const bool bootstrap_shortcuts,
35 const bool return_early, const std::vector<shash::Any> reflog_catalogs) {
36 shash::Any reflog_hash;
37 if (reflog_chksum_path != "") {
38 if (!manifest::Reflog::ReadChecksum(reflog_chksum_path, &reflog_hash)) {
39 LogCvmfs(kLogCvmfs, kLogStderr, "Could not read reflog checksum");
40 return kReflogChecksumMissing;
41 }
42 }
43
44 UniquePtr<upload::Spooler> spooler;
45 UniquePtr<manifest::Manifest> manifest;
46
47 if (!DirectoryExists(temp_dir)) {
48 LogCvmfs(kLogCvmfs, kLogStderr, "%s does not exist", temp_dir.c_str());
49 return kError;
50 }
51
52 // prepare global manager modules
53 const bool follow_redirects = false;
54 if (!server_tool_->InitDownloadManager(follow_redirects, proxy) ||
55 !server_tool_->InitSigningSignatureManager(certificate, priv_key, pwd)) {
56 LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection");
57 return kInitError;
58 }
59
60 // init the download helper
61 ObjectFetcher object_fetcher(repo_name, repo_url, temp_dir,
62 server_tool_->download_manager(),
63 server_tool_->signature_manager());
64
65 // Load Manifest
66 manifest = manifest::Manifest::LoadFile(manifest_path);
67 if (!manifest.IsValid()) {
68 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to parse manifest");
69 return kError;
70 }
71
72 // reflog_chksum_path wasn't given, the reflog checksum can possibly be
73 // obtained from the manifest
74 if (reflog_chksum_path.empty()) {
75 reflog_hash = manifest->reflog_hash();
76 }
77
78 // Connect to the spooler
79 const upload::SpoolerDefinition sd(spooler_definition,
80 manifest->GetHashAlgorithm());
81 spooler = upload::Spooler::Construct(sd);
82 if (!spooler.IsValid()) {
83 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to setup upload spooler");
84 return kInitError;
85 }
86
87 UniquePtr<manifest::Reflog> reflog;
88 if (!reflog_hash.IsNull()) {
89 reflog = server_tool_->FetchReflog(&object_fetcher, repo_name, reflog_hash);
90 if (!reflog.IsValid()) {
91 LogCvmfs(kLogCvmfs, kLogStderr, "reflog missing");
92 return kReflogMissing;
93 }
94 } else {
95 LogCvmfs(kLogCvmfs, kLogVerboseMsg, "no reflog (ignoring)");
96 if (spooler->Peek(".cvmfsreflog")) {
97 LogCvmfs(kLogCvmfs, kLogStderr,
98 "no reflog hash specified but reflog is present");
99 return kError;
100 }
101 }
102
103 // From here on things are potentially put in backend storage
104
105 // Register callback for retrieving the certificate hash
106 upload::Spooler::CallbackPtr callback =
107 spooler->RegisterListener(&SigningTool::CertificateUploadCallback, this);
108
109 // Safe certificate (and wait for the upload through a Future)
110 spooler->ProcessCertificate(certificate);
111 const shash::Any certificate_hash = certificate_hash_.Get();
112 spooler->UnregisterListener(callback);
113
114 if (certificate_hash.IsNull()) {
115 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to upload certificate");
116 return kError;
117 }
118
119 // Safe repository meta info file
120 shash::Any metainfo_hash = manifest->meta_info();
121 if (!meta_info.empty()) {
122 upload::Spooler::CallbackPtr callback =
123 spooler->RegisterListener(&SigningTool::MetainfoUploadCallback, this);
124 spooler->ProcessMetainfo(meta_info);
125 metainfo_hash = metainfo_hash_.Get();
126 spooler->UnregisterListener(callback);
127
128 if (metainfo_hash.IsNull()) {
129 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to upload meta info");
130 return kError;
131 }
132 }
133
134 // Update Reflog database
135 if (reflog.IsValid()) {
136 reflog->BeginTransaction();
137
138 if (!reflog->AddCatalog(manifest->catalog_hash())) {
139 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to add catalog to Reflog");
140 return kError;
141 }
142
143 if (!reflog->AddCertificate(certificate_hash)) {
144 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to add certificate to Reflog");
145 return kError;
146 }
147
148 if (!manifest->history().IsNull()) {
149 if (!reflog->AddHistory(manifest->history())) {
150 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to add history to Reflog");
151 return kError;
152 }
153 }
154
155 if (!metainfo_hash.IsNull()) {
156 if (!reflog->AddMetainfo(metainfo_hash)) {
157 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to add meta info to Reflog");
158 return kError;
159 }
160 }
161
162 // Callers of SigningTool may provide a list of additional catalogs that
163 // need to be added to reflog (e. g. for later garbage collection)
164 std::vector<shash::Any>::const_iterator i = reflog_catalogs.begin();
165 std::vector<shash::Any>::const_iterator iend = reflog_catalogs.end();
166 for (; i != iend; ++i) {
167 if (!reflog->AddCatalog(*i)) {
168 LogCvmfs(kLogCvmfs, kLogStderr,
169 "Failed to add additional catalog %s to Reflog",
170 (*i).ToString().c_str());
171 return kError;
172 }
173 }
174
175 reflog->CommitTransaction();
176
177 // upload Reflog database
178 reflog->DropDatabaseFileOwnership();
179 const std::string reflog_db_file = reflog->database_file();
180 reflog.Destroy();
181 spooler->UploadReflog(reflog_db_file);
182 spooler->WaitForUpload();
183 reflog_hash.algorithm = manifest->GetHashAlgorithm();
184 manifest::Reflog::HashDatabase(reflog_db_file, &reflog_hash);
185 unlink(reflog_db_file.c_str());
186 if (spooler->GetNumberOfErrors()) {
187 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to upload Reflog (errors: %d)",
188 spooler->GetNumberOfErrors());
189 return kError;
190 }
191 if (!reflog_chksum_path.empty())
192 manifest::Reflog::WriteChecksum(reflog_chksum_path, reflog_hash);
193 }
194
195 // Don't activate new manifest, just make sure all its references are uploaded
196 // and entered into the reflog
197 if (return_early) {
198 return kSuccess;
199 }
200
201 // Update manifest
202 manifest->set_certificate(certificate_hash);
203 manifest->set_repository_name(repo_name);
204 manifest->set_publish_timestamp(time(NULL));
205 manifest->set_garbage_collectability(garbage_collectable);
206 manifest->set_has_alt_catalog_path(bootstrap_shortcuts);
207 if (!metainfo_hash.IsNull()) {
208 manifest->set_meta_info(metainfo_hash);
209 }
210 if (!reflog_hash.IsNull()) {
211 manifest->set_reflog_hash(reflog_hash);
212 }
213
214 std::string signed_manifest = manifest->ExportString();
215 shash::Any published_hash(manifest->GetHashAlgorithm());
216 shash::HashMem(
217 reinterpret_cast<const unsigned char *>(signed_manifest.data()),
218 signed_manifest.length(), &published_hash);
219 signed_manifest += "--\n" + published_hash.ToString() + "\n";
220
221 // Create alternative bootstrapping symlinks for VOMS secured repos
222 if (manifest->has_alt_catalog_path()) {
223 const bool success =
224 spooler->PlaceBootstrappingShortcut(manifest->certificate()) &&
225 spooler->PlaceBootstrappingShortcut(manifest->catalog_hash()) &&
226 (manifest->history().IsNull() ||
227 spooler->PlaceBootstrappingShortcut(manifest->history())) &&
228 (metainfo_hash.IsNull() ||
229 spooler->PlaceBootstrappingShortcut(metainfo_hash));
230
231 if (!success) {
232 LogCvmfs(kLogCvmfs, kLogStderr,
233 "failed to place VOMS bootstrapping "
234 "symlinks");
235 return kError;
236 }
237 }
238
239 // Sign manifest
240 unsigned char *sig;
241 unsigned sig_size;
242 const bool manifest_was_signed = server_tool_->signature_manager()->Sign(
243 reinterpret_cast<const unsigned char *>(published_hash.ToString().data()),
244 published_hash.GetHexSize(), &sig, &sig_size);
245 if (!manifest_was_signed) {
246 PANIC(kLogStderr, "Failed to sign manifest");
247 }
248
249 // Write new manifest
250 signed_manifest += std::string(reinterpret_cast<char *>(sig), sig_size);
251 free(sig);
252 if (!SafeWriteToFile(signed_manifest, manifest_path, 0664)) {
253 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to write manifest (errno: %d)",
254 errno);
255 return kError;
256 }
257
258 // Upload manifest
259 spooler->UploadManifest(manifest_path);
260 spooler->WaitForUpload();
261 unlink(manifest_path.c_str());
262 if (spooler->GetNumberOfErrors()) {
263 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to commit manifest (errors: %d)",
264 spooler->GetNumberOfErrors());
265 return kError;
266 }
267
268 return kSuccess;
269 }
270
271 void SigningTool::CertificateUploadCallback(
272 const upload::SpoolerResult &result) {
273 shash::Any certificate_hash;
274 if (result.return_code == 0) {
275 certificate_hash = result.content_hash;
276 } else {
277 LogCvmfs(kLogCvmfs, kLogStderr,
278 "Failed to upload certificate "
279 "(retcode: %d)",
280 result.return_code);
281 }
282 certificate_hash_.Set(certificate_hash);
283 }
284
285 void SigningTool::MetainfoUploadCallback(const upload::SpoolerResult &result) {
286 shash::Any metainfo_hash;
287 if (result.return_code == 0) {
288 metainfo_hash = result.content_hash;
289 } else {
290 LogCvmfs(kLogCvmfs, kLogStderr, "Failed to upload meta info (retcode: %d)",
291 result.return_code);
292 }
293 metainfo_hash_.Set(metainfo_hash);
294 }
295