GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_letter.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 0 128 0.0%
Branches: 0 92 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 "crypto/hash.h"
16 #include "crypto/signature.h"
17 #include "letter.h"
18 #include "network/download.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 fqrn = *args.find('f')->second;
114 key_path = *args.find('k')->second;
115 if (args.find('t') != args.end()) text = *args.find('t')->second;
116
117 whitelist::Failures retval_wl;
118 letter::Failures retval_ltr;
119
120 if (verify) {
121 if (!InitVerifyingSignatureManager(key_path)) {
122 return 2;
123 }
124
125 const bool follow_redirects = false;
126 const unsigned max_pool_handles = 2;
127 const string proxy =
128 (args.find('@') != args.end()) ? *args.find('@')->second : "";
129 if (!this->InitDownloadManager(follow_redirects, proxy, 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.LoadUrl(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.LoadUrl(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 }
238