GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/signing_tool.cc Lines: 1 129 0.8 %
Date: 2019-02-03 02:48:13 Branches: 2 94 2.1 %

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

45
}