GCC Code Coverage Report


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