GCC Code Coverage Report


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