GCC Code Coverage Report
Directory: cvmfs/ Exec Total Coverage
File: cvmfs/swissknife_letter.cc Lines: 0 127 0.0 %
Date: 2019-02-03 02:48:13 Branches: 0 90 0.0 %

Line Branch Exec Source
1
/**
2
 * This file is part of the CernVM File System
3
 *
4
 * This tool signs a CernVM-FS manifest with an X.509 certificate.
5
 */
6
7
#include "cvmfs_config.h"
8
#include "swissknife_letter.h"
9
10
#include <inttypes.h>
11
#include <termios.h>
12
13
#include <cassert>
14
15
#include "download.h"
16
#include "hash.h"
17
#include "letter.h"
18
#include "signature.h"
19
#include "util/string.h"
20
#include "whitelist.h"
21
22
using namespace std;  // NOLINT
23
24
25
static void ReadStdinBytes(unsigned char *buf, const uint16_t num_bytes) {
26
  int read_chunk;
27
  unsigned read_all = 0;
28
29
  do {
30
    if ((read_chunk = read(0, buf+read_all, num_bytes-read_all)) <= 0)
31
      break;
32
    read_all += read_chunk;
33
  } while (read_all < num_bytes);
34
35
  if (read_chunk == 0) exit(0);
36
  assert(read_all == num_bytes);
37
}
38
39
40
static void WriteStdoutBytes(const unsigned char *buf,
41
                             const uint16_t num_bytes)
42
{
43
  int wrote_chunk;
44
  unsigned wrote_all = 0;
45
46
  do {
47
    if ((wrote_chunk = write(1, buf+wrote_all, num_bytes-wrote_all)) <= 0)
48
      break;
49
    wrote_all += wrote_chunk;
50
  } while (wrote_all < num_bytes);
51
52
  assert(wrote_all == num_bytes);
53
}
54
55
56
static uint16_t ReadErlang(unsigned char *buf) {
57
  int len;
58
59
  ReadStdinBytes(buf, 2);
60
  len = (buf[0] << 8) | buf[1];
61
  if (len > 0)
62
    ReadStdinBytes(buf, len);
63
  return len;
64
}
65
66
67
static void WriteErlang(const unsigned char *buf, int len) {
68
  unsigned char li;
69
70
  li = (len >> 8) & 0xff;
71
  WriteStdoutBytes(&li, 1);
72
  li = len & 0xff;
73
  WriteStdoutBytes(&li, 1);
74
75
  WriteStdoutBytes(buf, len);
76
}
77
78
79
int swissknife::CommandLetter::Main(const swissknife::ArgumentList &args) {
80
  bool verify = false;
81
  if (args.find('v') != args.end()) verify = true;
82
  if ((args.find('s') != args.end()) && verify) {
83
    LogCvmfs(kLogCvmfs, kLogStderr,
84
             "invalid option combination (sign + verify)");
85
    return 1;
86
  }
87
88
  bool erlang = false;
89
  string repository_url;
90
  string certificate_path;
91
  string certificate_password;
92
  shash::Algorithms hash_algorithm = shash::kSha1;
93
  uint64_t max_age = kDefaultMaxAge;
94
  if (verify) {
95
    repository_url = *args.find('r')->second;
96
    max_age = String2Uint64(*args.find('m')->second);
97
    if (args.find('e') != args.end()) erlang = true;
98
  } else {
99
    certificate_path = *args.find('c')->second;
100
    if (args.find('p') != args.end())
101
      certificate_password = *args.find('p')->second;
102
    if (args.find('a') != args.end()) {
103
      hash_algorithm = shash::ParseHashAlgorithm(*args.find('a')->second);
104
      if (hash_algorithm == shash::kAny) {
105
        LogCvmfs(kLogCvmfs, kLogStderr, "unknown hash algorithm");
106
        return 1;
107
      }
108
    }
109
  }
110
  string fqrn;
111
  string text;
112
  string key_path;
113
  string cacrl_path;
114
  fqrn = *args.find('f')->second;
115
  key_path = *args.find('k')->second;
116
  if (args.find('t') != args.end()) text = *args.find('t')->second;
117
  if (args.find('z') != args.end()) cacrl_path = *args.find('z')->second;
118
119
  whitelist::Failures retval_wl;
120
  letter::Failures retval_ltr;
121
122
  if (verify) {
123
    if (!InitVerifyingSignatureManager(key_path, cacrl_path)) {
124
      return 2;
125
    }
126
127
    const bool     follow_redirects = false;
128
    const unsigned max_pool_handles = 2;
129
    if (!this->InitDownloadManager(follow_redirects, max_pool_handles)) {
130
      LogCvmfs(kLogCvmfs, kLogStderr, "failed to init repo connection");
131
      return 2;
132
    }
133
134
    whitelist::Whitelist whitelist(fqrn, download_manager(),
135
                                   signature_manager());
136
    retval_wl = whitelist.Load(repository_url);
137
    if (retval_wl != whitelist::kFailOk) {
138
      LogCvmfs(kLogCvmfs, kLogStderr, "failed to load whitelist (%d): %s",
139
               retval_wl, whitelist::Code2Ascii(retval_wl));
140
      return 2;
141
    }
142
143
    if (erlang) {
144
      const char *ready = "ready";
145
      WriteErlang(reinterpret_cast<const unsigned char *>(ready), 5);
146
    }
147
148
    char exit_code = 0;
149
    do {
150
      if (erlang) {
151
        unsigned char buf[65000];
152
        int length = ReadErlang(buf);
153
        text = string(reinterpret_cast<char *>(buf), length);
154
      } else {
155
        if (text == "") {
156
          char c;
157
          int num_read;
158
          while ((num_read = read(0, &c, 1)) == 1) {
159
            if (c == '\n')
160
              break;
161
            text.push_back(c);
162
          }
163
          if (num_read != 1) return exit_code;
164
        }
165
      }
166
167
      if ((time(NULL) + 3600*24*3) > whitelist.expires()) {
168
        LogCvmfs(kLogCvmfs, kLogStderr, "reloading whitelist");
169
        whitelist::Whitelist refresh(fqrn, download_manager(),
170
                                     signature_manager());
171
        retval_wl = refresh.Load(repository_url);
172
        if (retval_wl == whitelist::kFailOk)
173
          whitelist = refresh;
174
      }
175
176
      string message;
177
      string cert;
178
      letter::Letter letter(fqrn, text, signature_manager());
179
      retval_ltr = letter.Verify(max_age, &message, &cert);
180
      if (retval_ltr != letter::kFailOk) {
181
        exit_code = 3;
182
        LogCvmfs(kLogCvmfs, kLogStderr, "%s", letter::Code2Ascii(retval_ltr));
183
      } else {
184
        if (whitelist.IsExpired()) {
185
          exit_code = 4;
186
          LogCvmfs(kLogCvmfs, kLogStderr, "whitelist expired");
187
        } else {
188
          retval_wl = whitelist.VerifyLoadedCertificate();
189
          if (retval_wl == whitelist::kFailOk) {
190
            exit_code = 0;
191
          } else {
192
            exit_code = 5;
193
            LogCvmfs(kLogCvmfs, kLogStderr, "%s",
194
                     whitelist::Code2Ascii(retval_wl));
195
          }
196
        }
197
      }
198
199
      if (erlang) {
200
        if ((exit_code == 0) && (message.length() > 60000))
201
          exit_code = 6;
202
        WriteErlang(reinterpret_cast<unsigned char *>(&exit_code), 1);
203
        if (exit_code == 0)
204
          WriteErlang(reinterpret_cast<const unsigned char *>(message.data()),
205
                      message.length());
206
      } else {
207
        if (exit_code == 0)
208
          LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s",
209
                   message.c_str());
210
      }
211
      text = "";
212
    } while (erlang);
213
214
    return exit_code;
215
  }
216
217
  if (!InitSigningSignatureManager(certificate_path,
218
                                   key_path,
219
                                   certificate_password)) {
220
    return 2;
221
  }
222
223
  if (text == "") {
224
    char c;
225
    while (read(0, &c, 1) == 1) {
226
      if (c == '\n')
227
        break;
228
      text.push_back(c);
229
    }
230
  }
231
232
  letter::Letter text_letter(fqrn, text, signature_manager());
233
  LogCvmfs(kLogCvmfs, kLogStdout, "%s",
234
           text_letter.Sign(hash_algorithm).c_str());
235
236
  return 0;
237
}