CernVM-FS  2.12.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
signing_tool.cc
Go to the documentation of this file.
1 
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 
20 
21 } // namespace
22 
23 SigningTool::SigningTool(ServerTool *server_tool) : server_tool_(server_tool) {}
24 
26 
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");
41  }
42  }
43 
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,
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 
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")) {
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)) {
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());
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) {
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 
272  const upload::SpoolerResult &result) {
273  shash::Any certificate_hash;
274  if (result.return_code == 0) {
275  certificate_hash = result.content_hash;
276  } else {
278  "Failed to upload certificate "
279  "(retcode: %d)",
280  result.return_code);
281  }
282  certificate_hash_.Set(certificate_hash);
283 }
284 
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 }
int return_code
the return value of the spooler operation
Future< shash::Any > metainfo_hash_
Definition: signing_tool.h:55
std::string database_file() const
Definition: reflog.cc:337
bool IsNull() const
Definition: hash.h:383
const manifest::Manifest * manifest() const
Definition: repository.h:125
bool AddHistory(const shash::Any &history)
Definition: reflog.cc:134
Result Run(const std::string &manifest_path, const std::string &repo_url, const std::string &spooler_definition, const std::string &temp_dir, const std::string &certificate="", const std::string &priv_key="", const std::string &repo_name="", const std::string &pwd="", const std::string &meta_info="", const std::string &reflog_chksum_path="", const std::string &proxy="", const bool garbage_collectable=false, const bool bootstrap_shortcuts=false, const bool return_early=false, const std::vector< shash::Any > reflog_catalogs=std::vector< shash::Any >())
Definition: signing_tool.cc:27
HttpObjectFetcher ObjectFetcher
static bool ReadChecksum(const std::string &path, shash::Any *checksum)
Definition: reflog.cc:47
#define PANIC(...)
Definition: exception.h:29
manifest::Reflog * FetchReflog(ObjectFetcherT *object_fetcher, const std::string &repo_name, const shash::Any &reflog_hash)
void CertificateUploadCallback(const upload::SpoolerResult &result)
bool SafeWriteToFile(const std::string &content, const std::string &path, int mode)
Definition: posix.cc:2084
Algorithms algorithm
Definition: hash.h:125
void Set(const T &object)
Definition: future.h:53
bool AddCatalog(const shash::Any &catalog)
Definition: reflog.cc:128
signature::SignatureManager * signature_manager() const
Definition: server_tool.cc:106
T & Get()
Definition: future.h:66
download::DownloadManager * download_manager() const
Definition: server_tool.cc:101
bool AddMetainfo(const shash::Any &metainfo)
Definition: reflog.cc:140
bool Sign(const unsigned char *buffer, const unsigned buffer_size, unsigned char **signature, unsigned *signature_size)
Definition: signature.cc:769
Future< shash::Any > certificate_hash_
Definition: signing_tool.h:54
void BeginTransaction()
Definition: reflog.cc:295
static void HashDatabase(const std::string &database_path, shash::Any *hash_reflog)
Definition: reflog.cc:322
void DropDatabaseFileOwnership()
Definition: reflog.cc:313
ServerTool * server_tool_
Definition: signing_tool.h:53
bool DirectoryExists(const std::string &path)
Definition: posix.cc:813
bool AddCertificate(const shash::Any &certificate)
Definition: reflog.cc:121
void HashMem(const unsigned char *buffer, const unsigned buffer_size, Any *any_digest)
Definition: hash.cc:255
virtual ~SigningTool()
Definition: signing_tool.cc:25
void CommitTransaction()
Definition: reflog.cc:301
std::string meta_info() const
Definition: repository.h:128
const int kLogVerboseMsg
static bool WriteChecksum(const std::string &path, const shash::Any &value)
Definition: reflog.cc:64
bool InitDownloadManager(const bool follow_redirects, const std::string &proxy, const unsigned max_pool_handles=1)
Definition: server_tool.cc:17
void MetainfoUploadCallback(const upload::SpoolerResult &result)
static Manifest * LoadFile(const std::string &path)
Definition: manifest.cc:92
bool InitSigningSignatureManager(const std::string &certificate_path, const std::string &private_key_path, const std::string &private_key_password)
Definition: server_tool.cc:62
SigningTool(ServerTool *server_tool)
Definition: signing_tool.cc:23
CVMFS_EXPORT void LogCvmfs(const LogSource source, const int mask, const char *format,...)
Definition: logging.cc:528