Directory: | cvmfs/ |
---|---|
File: | cvmfs/crypto/signature.cc |
Date: | 2025-06-22 02:36:02 |
Exec | Total | Coverage | |
---|---|---|---|
Lines: | 391 | 581 | 67.3% |
Branches: | 213 | 592 | 36.0% |
Line | Branch | Exec | Source |
---|---|---|---|
1 | /** | ||
2 | * This file is part of the CernVM File System. | ||
3 | * | ||
4 | * This is a wrapper around OpenSSL's libcrypto. It supports | ||
5 | * signing of data with an X.509 certificate and verifying | ||
6 | * a signature against a certificate. The certificates can act only as key | ||
7 | * store, in which case there is no verification against the CA chain. | ||
8 | * | ||
9 | * It also supports verification of plain RSA signatures (for the whitelist). | ||
10 | * | ||
11 | * We work exclusively with PEM formatted files (= Base64-encoded DER files). | ||
12 | */ | ||
13 | |||
14 | |||
15 | #include "crypto/signature.h" | ||
16 | |||
17 | #include <openssl/bn.h> | ||
18 | #include <openssl/evp.h> | ||
19 | #include <openssl/pkcs7.h> | ||
20 | #include <openssl/x509v3.h> | ||
21 | |||
22 | #include <cassert> | ||
23 | #include <cctype> | ||
24 | #include <cstdio> | ||
25 | #include <cstdlib> | ||
26 | #include <cstring> | ||
27 | #include <string> | ||
28 | #include <vector> | ||
29 | |||
30 | #include "crypto/hash.h" | ||
31 | #include "crypto/openssl_version.h" | ||
32 | #include "util/concurrency.h" | ||
33 | #include "util/logging.h" | ||
34 | #include "util/platform.h" | ||
35 | #include "util/posix.h" | ||
36 | #include "util/prng.h" | ||
37 | #include "util/smalloc.h" | ||
38 | #include "util/string.h" | ||
39 | |||
40 | using namespace std; // NOLINT | ||
41 | |||
42 | namespace signature { | ||
43 | |||
44 | const char *kDefaultPublicKey = "/etc/cvmfs/keys/cern.ch/cern-it4.pub"; | ||
45 | |||
46 | |||
47 | ✗ | static int CallbackCertVerify(int ok, X509_STORE_CTX *ctx) { | |
48 | ✗ | LogCvmfs(kLogCvmfs, kLogDebug, "certificate chain verification: %d", ok); | |
49 | ✗ | if (ok) | |
50 | ✗ | return ok; | |
51 | |||
52 | ✗ | const int error = X509_STORE_CTX_get_error(ctx); | |
53 | ✗ | X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); | |
54 | ✗ | string subject = "subject n/a"; | |
55 | ✗ | if (current_cert) { | |
56 | ✗ | char *buffer = NULL; | |
57 | ✗ | buffer = X509_NAME_oneline(X509_get_subject_name(current_cert), NULL, 0); | |
58 | ✗ | if (buffer) { | |
59 | ✗ | subject = string(buffer); | |
60 | ✗ | free(buffer); | |
61 | } | ||
62 | } | ||
63 | ✗ | LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, | |
64 | "certificate verification error: %s, error %s (%d)", subject.c_str(), | ||
65 | X509_verify_cert_error_string(error), error); | ||
66 | ✗ | return ok; | |
67 | } | ||
68 | |||
69 | |||
70 | 514 | SignatureManager::SignatureManager() { | |
71 | 514 | private_key_ = NULL; | |
72 | 514 | private_master_key_ = NULL; | |
73 | 514 | certificate_ = NULL; | |
74 | 514 | x509_store_ = NULL; | |
75 | 514 | x509_lookup_ = NULL; | |
76 | 514 | const int retval = pthread_mutex_init(&lock_blacklist_, NULL); | |
77 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 514 times.
|
514 | assert(retval == 0); |
78 | 514 | } | |
79 | |||
80 | |||
81 | 494 | void SignatureManager::InitX509Store() { | |
82 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | if (x509_store_) |
83 | ✗ | X509_STORE_free(x509_store_); | |
84 | 494 | x509_lookup_ = NULL; | |
85 | 494 | x509_store_ = X509_STORE_new(); | |
86 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | assert(x509_store_ != NULL); |
87 | |||
88 | 494 | const unsigned long verify_flags = // NOLINT(runtime/int) | |
89 | X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL; | ||
90 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
91 | X509_STORE_set_flags(x509_store_, verify_flags); | ||
92 | #else | ||
93 | int retval; | ||
94 | 494 | X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); | |
95 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | assert(param != NULL); |
96 | 494 | retval = X509_VERIFY_PARAM_set_flags(param, verify_flags); | |
97 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | assert(retval == 1); |
98 | 494 | retval = X509_STORE_set1_param(x509_store_, param); | |
99 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | assert(retval == 1); |
100 | 494 | X509_VERIFY_PARAM_free(param); | |
101 | #endif | ||
102 | |||
103 | 494 | x509_lookup_ = X509_STORE_add_lookup(x509_store_, X509_LOOKUP_hash_dir()); | |
104 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 494 times.
|
494 | assert(x509_lookup_ != NULL); |
105 | |||
106 | 494 | X509_STORE_set_verify_cb_func(x509_store_, CallbackCertVerify); | |
107 | 494 | } | |
108 | |||
109 | |||
110 | 494 | void SignatureManager::Init() { | |
111 | 494 | OpenSSL_add_all_algorithms(); | |
112 | 494 | InitX509Store(); | |
113 | 494 | } | |
114 | |||
115 | |||
116 | 494 | void SignatureManager::Fini() { | |
117 | 494 | UnloadCertificate(); | |
118 | 494 | UnloadPrivateKey(); | |
119 | 494 | UnloadPrivateMasterKey(); | |
120 | 494 | UnloadPublicRsaKeys(); | |
121 | // Lookup is freed automatically | ||
122 |
1/2✓ Branch 0 taken 494 times.
✗ Branch 1 not taken.
|
494 | if (x509_store_) |
123 | 494 | X509_STORE_free(x509_store_); | |
124 | |||
125 | 494 | EVP_cleanup(); | |
126 | |||
127 | 494 | private_key_ = NULL; | |
128 | 494 | private_master_key_ = NULL; | |
129 | 494 | certificate_ = NULL; | |
130 | 494 | x509_store_ = NULL; | |
131 | 494 | x509_lookup_ = NULL; | |
132 | 494 | } | |
133 | |||
134 | |||
135 | /** | ||
136 | * OpenSSL error strings. | ||
137 | */ | ||
138 | ✗ | string SignatureManager::GetCryptoError() { | |
139 | char buf[121]; | ||
140 | ✗ | string err; | |
141 | ✗ | while (ERR_peek_error() != 0) { | |
142 | ✗ | ERR_error_string(ERR_get_error(), buf); | |
143 | ✗ | err += string(buf); | |
144 | } | ||
145 | ✗ | return err; | |
146 | } | ||
147 | |||
148 | |||
149 | /** | ||
150 | * @param[in] file_pem File name of the PEM key file | ||
151 | * @param[in] password Password for the private key. | ||
152 | * Password is not saved internally, but the private key is. | ||
153 | * \return True on success, false otherwise | ||
154 | */ | ||
155 | 164 | bool SignatureManager::LoadPrivateMasterKeyPath(const string &file_pem) { | |
156 | 164 | UnloadPrivateMasterKey(); | |
157 | FILE *fp; | ||
158 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 164 times.
|
164 | if ((fp = fopen(file_pem.c_str(), "r")) == NULL) |
159 | ✗ | return false; | |
160 | 164 | private_master_key_ = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL); | |
161 | 164 | fclose(fp); | |
162 | 164 | return (private_master_key_ != NULL); | |
163 | } | ||
164 | |||
165 | 4 | bool SignatureManager::LoadPrivateMasterKeyMem(const string &key) { | |
166 | 4 | UnloadPrivateMasterKey(); | |
167 | 4 | BIO *bp = BIO_new(BIO_s_mem()); | |
168 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(bp != NULL); |
169 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
4 | if (BIO_write(bp, key.data(), key.size()) <= 0) { |
170 | ✗ | BIO_free(bp); | |
171 | ✗ | return false; | |
172 | } | ||
173 | 4 | private_master_key_ = PEM_read_bio_RSAPrivateKey(bp, NULL, NULL, NULL); | |
174 | 4 | BIO_free(bp); | |
175 | 4 | return (private_master_key_ != NULL); | |
176 | } | ||
177 | |||
178 | |||
179 | /** | ||
180 | * @param[in] file_pem File name of the PEM key file | ||
181 | * @param[in] password Password for the private key. | ||
182 | * Password is not saved internally, but the private key is. | ||
183 | * \return True on success, false otherwise | ||
184 | */ | ||
185 | 256 | bool SignatureManager::LoadPrivateKeyPath(const string &file_pem, | |
186 | const string &password) { | ||
187 | 256 | UnloadPrivateKey(); | |
188 | bool result; | ||
189 | 256 | FILE *fp = NULL; | |
190 | 256 | char *tmp = strdupa(password.c_str()); | |
191 | |||
192 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 256 times.
|
256 | if ((fp = fopen(file_pem.c_str(), "r")) == NULL) |
193 | ✗ | return false; | |
194 | 256 | result = (private_key_ = PEM_read_PrivateKey(fp, NULL, NULL, tmp)) != NULL; | |
195 | 256 | fclose(fp); | |
196 | 256 | return result; | |
197 | } | ||
198 | |||
199 | 4 | bool SignatureManager::LoadPrivateKeyMem(const std::string &key) { | |
200 | 4 | UnloadPrivateKey(); | |
201 | 4 | BIO *bp = BIO_new(BIO_s_mem()); | |
202 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | assert(bp != NULL); |
203 |
1/2✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
4 | if (BIO_write(bp, key.data(), key.size()) <= 0) { |
204 | ✗ | BIO_free(bp); | |
205 | ✗ | return false; | |
206 | } | ||
207 | 4 | private_key_ = PEM_read_bio_PrivateKey(bp, NULL, NULL, NULL); | |
208 | 4 | BIO_free(bp); | |
209 | 4 | return (private_key_ != NULL); | |
210 | } | ||
211 | |||
212 | |||
213 | /** | ||
214 | * Clears the memory storing the private key. | ||
215 | */ | ||
216 | 768 | void SignatureManager::UnloadPrivateKey() { | |
217 |
2/2✓ Branch 0 taken 274 times.
✓ Branch 1 taken 494 times.
|
768 | if (private_key_) |
218 | 274 | EVP_PKEY_free(private_key_); | |
219 | 768 | private_key_ = NULL; | |
220 | 768 | } | |
221 | |||
222 | |||
223 | 508 | void SignatureManager::UnloadCertificate() { | |
224 |
2/2✓ Branch 0 taken 339 times.
✓ Branch 1 taken 169 times.
|
508 | if (certificate_) |
225 | 339 | X509_free(certificate_); | |
226 | 508 | certificate_ = NULL; | |
227 | 508 | } | |
228 | |||
229 | |||
230 | /** | ||
231 | * Clears the memory storing the private RSA master key (whitelist signing). | ||
232 | */ | ||
233 | 676 | void SignatureManager::UnloadPrivateMasterKey() { | |
234 |
2/2✓ Branch 0 taken 182 times.
✓ Branch 1 taken 494 times.
|
676 | if (private_master_key_) |
235 | 182 | RSA_free(private_master_key_); | |
236 | 676 | private_master_key_ = NULL; | |
237 | 676 | } | |
238 | |||
239 | |||
240 | /** | ||
241 | * Loads a certificate. This certificate is used for the following | ||
242 | * signature verifications | ||
243 | * | ||
244 | * \return True on success, false otherwise | ||
245 | */ | ||
246 | 252 | bool SignatureManager::LoadCertificatePath(const string &file_pem) { | |
247 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
|
252 | if (certificate_) { |
248 | ✗ | X509_free(certificate_); | |
249 | ✗ | certificate_ = NULL; | |
250 | } | ||
251 | |||
252 | bool result; | ||
253 | 252 | char *nopwd = strdupa(""); | |
254 | FILE *fp; | ||
255 | |||
256 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 252 times.
|
252 | if ((fp = fopen(file_pem.c_str(), "r")) == NULL) |
257 | ✗ | return false; | |
258 | 252 | result = (certificate_ = PEM_read_X509_AUX(fp, NULL, NULL, nopwd)) != NULL; | |
259 | |||
260 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 252 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
252 | if (!result && certificate_) { |
261 | ✗ | X509_free(certificate_); | |
262 | ✗ | certificate_ = NULL; | |
263 | } | ||
264 | |||
265 | 252 | fclose(fp); | |
266 | 252 | return result; | |
267 | } | ||
268 | |||
269 | |||
270 | /** | ||
271 | * See the function that loads the certificate from file. | ||
272 | */ | ||
273 | 107 | bool SignatureManager::LoadCertificateMem(const unsigned char *buffer, | |
274 | const unsigned buffer_size) { | ||
275 |
2/2✓ Branch 0 taken 34 times.
✓ Branch 1 taken 73 times.
|
107 | if (certificate_) { |
276 | 34 | X509_free(certificate_); | |
277 | 34 | certificate_ = NULL; | |
278 | } | ||
279 | |||
280 | bool result; | ||
281 | 107 | char *nopwd = strdupa(""); | |
282 | |||
283 | 107 | BIO *mem = BIO_new(BIO_s_mem()); | |
284 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 107 times.
|
107 | if (!mem) |
285 | ✗ | return false; | |
286 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 107 times.
|
107 | if (BIO_write(mem, buffer, buffer_size) <= 0) { |
287 | ✗ | BIO_free(mem); | |
288 | ✗ | return false; | |
289 | } | ||
290 | 107 | result = (certificate_ = PEM_read_bio_X509_AUX(mem, NULL, NULL, nopwd)) | |
291 | != NULL; | ||
292 | 107 | BIO_free(mem); | |
293 | |||
294 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 107 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
107 | if (!result && certificate_) { |
295 | ✗ | X509_free(certificate_); | |
296 | ✗ | certificate_ = NULL; | |
297 | } | ||
298 | |||
299 | 107 | return result; | |
300 | } | ||
301 | |||
302 | |||
303 | /** | ||
304 | * Loads a list of public RSA keys separated by ":". | ||
305 | */ | ||
306 | 228 | bool SignatureManager::LoadPublicRsaKeys(const string &path_list) { | |
307 |
1/2✓ Branch 1 taken 228 times.
✗ Branch 2 not taken.
|
228 | UnloadPublicRsaKeys(); |
308 | |||
309 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 214 times.
|
228 | if (path_list == "") |
310 | 14 | return true; | |
311 |
1/2✓ Branch 1 taken 214 times.
✗ Branch 2 not taken.
|
214 | const vector<string> pem_files = SplitString(path_list, ':'); |
312 | |||
313 | 214 | char *nopwd = strdupa(""); | |
314 | FILE *fp; | ||
315 | |||
316 |
2/2✓ Branch 1 taken 214 times.
✓ Branch 2 taken 210 times.
|
424 | for (unsigned i = 0; i < pem_files.size(); ++i) { |
317 | 214 | const char *pubkey_file = pem_files[i].c_str(); | |
318 | |||
319 | // open public key file | ||
320 |
1/2✓ Branch 1 taken 214 times.
✗ Branch 2 not taken.
|
214 | fp = fopen(pubkey_file, "r"); |
321 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 210 times.
|
214 | if (fp == NULL) { |
322 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, |
323 | "failed to open " | ||
324 | "public key '%s'", | ||
325 | pubkey_file); | ||
326 | 4 | return false; | |
327 | } | ||
328 | |||
329 | // load the public key from the file (and close it) | ||
330 |
1/2✓ Branch 1 taken 210 times.
✗ Branch 2 not taken.
|
210 | EVP_PKEY *this_key = PEM_read_PUBKEY(fp, NULL, NULL, nopwd); |
331 |
1/2✓ Branch 1 taken 210 times.
✗ Branch 2 not taken.
|
210 | fclose(fp); |
332 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
|
210 | if (this_key == NULL) { |
333 | ✗ | LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, | |
334 | "failed to load " | ||
335 | "public key '%s'", | ||
336 | pubkey_file); | ||
337 | ✗ | return false; | |
338 | } | ||
339 | |||
340 | // read the RSA key from the loaded public key | ||
341 |
1/2✓ Branch 1 taken 210 times.
✗ Branch 2 not taken.
|
210 | RSA *key = EVP_PKEY_get1_RSA(this_key); |
342 |
1/2✓ Branch 1 taken 210 times.
✗ Branch 2 not taken.
|
210 | EVP_PKEY_free(this_key); |
343 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 210 times.
|
210 | if (key == NULL) { |
344 | ✗ | LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, | |
345 | "failed to read " | ||
346 | "public key '%s'", | ||
347 | pubkey_file); | ||
348 | ✗ | return false; | |
349 | } | ||
350 | |||
351 | // store the loaded public key | ||
352 |
1/2✓ Branch 1 taken 210 times.
✗ Branch 2 not taken.
|
210 | public_keys_.push_back(key); |
353 | } | ||
354 | |||
355 | 210 | return true; | |
356 | 214 | } | |
357 | |||
358 | |||
359 | 736 | void SignatureManager::UnloadPublicRsaKeys() { | |
360 |
2/2✓ Branch 1 taken 224 times.
✓ Branch 2 taken 736 times.
|
960 | for (unsigned i = 0; i < public_keys_.size(); ++i) |
361 | 224 | RSA_free(public_keys_[i]); | |
362 | 736 | public_keys_.clear(); | |
363 | 736 | } | |
364 | |||
365 | |||
366 | 4 | std::string SignatureManager::GenerateKeyText(RSA *pubkey) const { | |
367 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (!pubkey) { |
368 | ✗ | return ""; | |
369 | } | ||
370 | |||
371 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4 times.
✗ Branch 5 not taken.
|
4 | BIO *bp = BIO_new(BIO_s_mem()); |
372 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (bp == NULL) { |
373 | ✗ | LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, | |
374 | "Failed to allocate" | ||
375 | " memory for pubkey"); | ||
376 | ✗ | return ""; | |
377 | } | ||
378 |
2/4✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 4 times.
|
4 | if (!PEM_write_bio_RSA_PUBKEY(bp, pubkey)) { |
379 | ✗ | LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, | |
380 | "Failed to write" | ||
381 | " pubkey to memory"); | ||
382 | ✗ | return ""; | |
383 | } | ||
384 | char *bio_pubkey_text; | ||
385 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | long bytes = BIO_get_mem_data(bp, &bio_pubkey_text); // NOLINT |
386 |
1/2✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
|
4 | std::string bio_pubkey_str(bio_pubkey_text, bytes); |
387 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | BIO_free(bp); |
388 | |||
389 | 4 | return bio_pubkey_str; | |
390 | 4 | } | |
391 | |||
392 | |||
393 | 4 | std::string SignatureManager::GetActivePubkeys() const { | |
394 | 4 | std::string pubkeys; | |
395 | 4 | for (std::vector<RSA *>::const_iterator it = public_keys_.begin(); | |
396 |
2/2✓ Branch 2 taken 4 times.
✓ Branch 3 taken 4 times.
|
8 | it != public_keys_.end(); |
397 | 4 | it++) { | |
398 |
2/4✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
|
4 | pubkeys += GenerateKeyText(*it); |
399 | } | ||
400 | // NOTE: we do not add the pubkey of the certificate here, as it is | ||
401 | // not used for the whitelist verification. | ||
402 | 4 | return pubkeys; | |
403 | } | ||
404 | |||
405 | ✗ | std::vector<std::string> SignatureManager::GetActivePubkeysAsVector() const { | |
406 | ✗ | std::vector<std::string> pubkeys; | |
407 | ✗ | for (std::vector<RSA *>::const_iterator it = public_keys_.begin(); | |
408 | ✗ | it != public_keys_.end(); | |
409 | ✗ | it++) { | |
410 | ✗ | pubkeys.push_back(GenerateKeyText(*it)); | |
411 | } | ||
412 | // NOTE: we do not add the pubkey of the certificate here, as it is | ||
413 | // not used for the whitelist verification. | ||
414 | ✗ | return pubkeys; | |
415 | } | ||
416 | |||
417 | 8 | std::string SignatureManager::GetCertificate() const { | |
418 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!certificate_) |
419 | ✗ | return ""; | |
420 | |||
421 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | BIO *bp = BIO_new(BIO_s_mem()); |
422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bp != NULL); |
423 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | const bool rvb = PEM_write_bio_X509(bp, certificate_); |
424 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(rvb); |
425 | char *bio_crt_text; | ||
426 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | long bytes = BIO_get_mem_data(bp, &bio_crt_text); // NOLINT |
427 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bytes > 0); |
428 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | std::string bio_crt_str(bio_crt_text, bytes); |
429 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | BIO_free(bp); |
430 | 8 | return bio_crt_str; | |
431 | 8 | } | |
432 | |||
433 | |||
434 | 8 | std::string SignatureManager::GetPrivateKey() { | |
435 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!private_key_) |
436 | ✗ | return ""; | |
437 | |||
438 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | BIO *bp = BIO_new(BIO_s_mem()); |
439 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bp != NULL); |
440 | const bool rvb = | ||
441 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | PEM_write_bio_PrivateKey(bp, private_key_, NULL, NULL, 0, 0, NULL); |
442 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(rvb); |
443 | char *bio_privkey_text; | ||
444 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | long bytes = BIO_get_mem_data(bp, &bio_privkey_text); // NOLINT |
445 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bytes > 0); |
446 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | std::string bio_privkey_str(bio_privkey_text, bytes); |
447 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | BIO_free(bp); |
448 | 8 | return bio_privkey_str; | |
449 | 8 | } | |
450 | |||
451 | |||
452 | 8 | std::string SignatureManager::GetPrivateMasterKey() { | |
453 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if (!private_master_key_) |
454 | ✗ | return ""; | |
455 | |||
456 |
2/4✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
|
8 | BIO *bp = BIO_new(BIO_s_mem()); |
457 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bp != NULL); |
458 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | const bool rvb = PEM_write_bio_RSAPrivateKey(bp, private_master_key_, NULL, |
459 | 8 | NULL, 0, 0, NULL); | |
460 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(rvb); |
461 | char *bio_master_privkey_text; | ||
462 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | long bytes = BIO_get_mem_data(bp, &bio_master_privkey_text); // NOLINT |
463 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | assert(bytes > 0); |
464 |
1/2✓ Branch 2 taken 8 times.
✗ Branch 3 not taken.
|
8 | std::string bio_master_privkey_str(bio_master_privkey_text, bytes); |
465 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | BIO_free(bp); |
466 | 8 | return bio_master_privkey_str; | |
467 | 8 | } | |
468 | |||
469 | 28 | RSA *SignatureManager::GenerateRsaKeyPair() { | |
470 | 28 | RSA *rsa = NULL; | |
471 | 28 | BIGNUM *bn = BN_new(); | |
472 | 28 | int retval = BN_set_word(bn, RSA_F4); | |
473 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | assert(retval == 1); |
474 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
475 | rsa = RSA_generate_key(2048, RSA_F4, NULL, NULL); | ||
476 | assert(rsa != NULL); | ||
477 | #else | ||
478 | 28 | rsa = RSA_new(); | |
479 | 28 | retval = RSA_generate_key_ex(rsa, 2048, bn, NULL); | |
480 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | assert(retval == 1); |
481 | #endif | ||
482 | 28 | BN_free(bn); | |
483 | 28 | return rsa; | |
484 | } | ||
485 | |||
486 | |||
487 | /** | ||
488 | * Creates the RSA master key pair for whitelist signing | ||
489 | */ | ||
490 | 14 | void SignatureManager::GenerateMasterKeyPair() { | |
491 | 14 | UnloadPrivateMasterKey(); | |
492 | 14 | UnloadPublicRsaKeys(); | |
493 | |||
494 | 14 | RSA *rsa = GenerateRsaKeyPair(); | |
495 | 14 | private_master_key_ = RSAPrivateKey_dup(rsa); | |
496 |
1/2✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
|
14 | public_keys_.push_back(RSAPublicKey_dup(rsa)); |
497 | 14 | RSA_free(rsa); | |
498 | 14 | } | |
499 | |||
500 | /** | ||
501 | * Creates a new RSA key pair (private key) and a self-signed certificate | ||
502 | */ | ||
503 | 14 | void SignatureManager::GenerateCertificate(const std::string &cn) { | |
504 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | UnloadPrivateKey(); |
505 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | UnloadCertificate(); |
506 | int retval; | ||
507 | |||
508 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | RSA *rsa = GenerateRsaKeyPair(); |
509 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | private_key_ = EVP_PKEY_new(); |
510 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | retval = EVP_PKEY_set1_RSA(private_key_, RSAPrivateKey_dup(rsa)); |
511 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 1); |
512 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | EVP_PKEY *pkey = EVP_PKEY_new(); |
513 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | retval = EVP_PKEY_set1_RSA(pkey, rsa); |
514 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 1); |
515 | |||
516 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | certificate_ = X509_new(); |
517 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_set_version(certificate_, 2L); |
518 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_set_pubkey(certificate_, pkey); |
519 | |||
520 | 14 | Prng prng; | |
521 | 14 | prng.InitLocaltime(); | |
522 | 14 | unsigned long rnd_serial_no = prng.Next(uint64_t(1) + uint32_t(-1)); // NOLINT | |
523 | 14 | rnd_serial_no = rnd_serial_no | |
524 | 14 | | uint64_t(prng.Next(uint64_t(1) + uint32_t(-1))) << 32; | |
525 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | ASN1_INTEGER_set(X509_get_serialNumber(certificate_), rnd_serial_no); |
526 | |||
527 | // valid as of now | ||
528 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_gmtime_adj( |
529 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | reinterpret_cast<ASN1_TIME *>(X509_get_notBefore(certificate_)), 0); |
530 | // valid for 1 year (validity range is unused) | ||
531 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_gmtime_adj( |
532 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | reinterpret_cast<ASN1_TIME *>(X509_get_notAfter(certificate_)), |
533 | 3600 * 24 * 365); | ||
534 | |||
535 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_NAME *name = X509_get_subject_name(certificate_); |
536 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
537 | X509_NAME_add_entry_by_txt( | ||
538 | name, "CN", MBSTRING_ASC, | ||
539 | const_cast<unsigned char *>( | ||
540 | reinterpret_cast<const unsigned char *>(cn.c_str())), | ||
541 | -1, -1, 0); | ||
542 | #else | ||
543 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | X509_NAME_add_entry_by_txt( |
544 | name, "CN", MBSTRING_ASC, | ||
545 | 14 | reinterpret_cast<const unsigned char *>(cn.c_str()), -1, -1, 0); | |
546 | #endif | ||
547 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | retval = X509_set_issuer_name(certificate_, name); |
548 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval == 1); |
549 | |||
550 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
551 | retval = X509_sign(certificate_, pkey, EVP_sha1()); | ||
552 | #else | ||
553 |
2/4✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 14 times.
✗ Branch 5 not taken.
|
14 | retval = X509_sign(certificate_, pkey, EVP_sha256()); |
554 | #endif | ||
555 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
14 | EVP_PKEY_free(pkey); |
556 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
14 | assert(retval > 0); |
557 | 14 | } | |
558 | |||
559 | /** | ||
560 | * Loads a list of blacklisted certificates (fingerprints) from a file. | ||
561 | */ | ||
562 | 12 | bool SignatureManager::LoadBlacklist(const std::string &path_blacklist, | |
563 | bool append) { | ||
564 | 12 | const MutexLockGuard lock_guard(&lock_blacklist_); | |
565 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | LogCvmfs(kLogSignature, kLogDebug, "reading from blacklist %s", |
566 | path_blacklist.c_str()); | ||
567 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (!append) |
568 | 12 | blacklist_.clear(); | |
569 | |||
570 |
1/2✓ Branch 2 taken 12 times.
✗ Branch 3 not taken.
|
12 | const int fd = open(path_blacklist.c_str(), O_RDONLY); |
571 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (fd < 0) |
572 | ✗ | return false; | |
573 | 12 | std::string blacklist_buffer; | |
574 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | const bool retval = SafeReadToString(fd, &blacklist_buffer); |
575 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | close(fd); |
576 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | if (!retval) |
577 | ✗ | return false; | |
578 | |||
579 | 12 | unsigned num_bytes = 0; | |
580 |
2/2✓ Branch 1 taken 12 times.
✓ Branch 2 taken 12 times.
|
24 | while (num_bytes < blacklist_buffer.size()) { |
581 | 24 | const string line = GetLineMem(blacklist_buffer.data() + num_bytes, | |
582 |
1/2✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
|
12 | blacklist_buffer.size() - num_bytes); |
583 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | blacklist_.push_back(line); |
584 | 12 | num_bytes += line.length() + 1; | |
585 | 12 | } | |
586 | |||
587 | 12 | return true; | |
588 | 12 | } | |
589 | |||
590 | |||
591 | 193 | vector<string> SignatureManager::GetBlacklist() { | |
592 | 193 | const MutexLockGuard lock_guard(&lock_blacklist_); | |
593 |
1/2✓ Branch 1 taken 193 times.
✗ Branch 2 not taken.
|
386 | return blacklist_; |
594 | 193 | } | |
595 | |||
596 | |||
597 | /** | ||
598 | * Loads CA certificates CRLs from a ":" separated list of paths. | ||
599 | * The information is used for proper X509 verification. | ||
600 | * The format of the certificates and CRLs has to be OpenSSL hashed certs. | ||
601 | * The path can be something like /etc/grid-security/certificates. | ||
602 | * If path_list is empty, the default path is taken. | ||
603 | */ | ||
604 | ✗ | bool SignatureManager::LoadTrustedCaCrl(const string &path_list) { | |
605 | ✗ | InitX509Store(); | |
606 | |||
607 | /* TODO if (path_list == "") { | ||
608 | return true; | ||
609 | }*/ | ||
610 | ✗ | const vector<string> paths = SplitString(path_list, ':'); | |
611 | ✗ | for (unsigned i = 0; i < paths.size(); ++i) { | |
612 | const int retval = | ||
613 | ✗ | X509_LOOKUP_add_dir(x509_lookup_, paths[i].c_str(), X509_FILETYPE_PEM); | |
614 | ✗ | if (!retval) | |
615 | ✗ | return false; | |
616 | } | ||
617 | ✗ | return true; | |
618 | } | ||
619 | |||
620 | |||
621 | /** | ||
622 | * Returns cryptographic hash from DER encoded certificate, encoded the same way | ||
623 | * OpenSSL does (01:AB:...). | ||
624 | * Empty string on failure. | ||
625 | */ | ||
626 | 185 | shash::Any SignatureManager::HashCertificate( | |
627 | const shash::Algorithms hash_algorithm) { | ||
628 |
1/2✓ Branch 1 taken 185 times.
✗ Branch 2 not taken.
|
185 | shash::Any result; |
629 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 185 times.
|
185 | if (!certificate_) |
630 | ✗ | return result; | |
631 | |||
632 | int buffer_size; | ||
633 | 185 | unsigned char *buffer = NULL; | |
634 | |||
635 |
1/2✓ Branch 1 taken 185 times.
✗ Branch 2 not taken.
|
185 | buffer_size = i2d_X509(certificate_, &buffer); |
636 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 185 times.
|
185 | if (buffer_size < 0) |
637 | ✗ | return result; | |
638 | |||
639 | 185 | result.algorithm = hash_algorithm; | |
640 |
1/2✓ Branch 1 taken 185 times.
✗ Branch 2 not taken.
|
185 | shash::HashMem(buffer, buffer_size, &result); |
641 | 185 | free(buffer); | |
642 | |||
643 | 185 | return result; | |
644 | } | ||
645 | |||
646 | |||
647 | /** | ||
648 | * Returns cryptographic hash from DER encoded certificate, encoded the same way | ||
649 | * OpenSSL does (01:AB:...). | ||
650 | * Empty string on failure. | ||
651 | */ | ||
652 | 86 | string SignatureManager::FingerprintCertificate( | |
653 | const shash::Algorithms hash_algorithm) { | ||
654 |
1/2✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
|
86 | const shash::Any hash = HashCertificate(hash_algorithm); |
655 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 86 times.
|
86 | if (hash.IsNull()) |
656 | ✗ | return ""; | |
657 | |||
658 |
1/2✓ Branch 1 taken 86 times.
✗ Branch 2 not taken.
|
86 | const string hash_str = hash.ToString(); |
659 | 86 | string result; | |
660 |
2/2✓ Branch 1 taken 3494 times.
✓ Branch 2 taken 86 times.
|
3580 | for (unsigned i = 0; i < hash_str.length(); ++i) { |
661 |
2/2✓ Branch 0 taken 3440 times.
✓ Branch 1 taken 54 times.
|
3494 | if (i < 2 * shash::kDigestSizes[hash_algorithm]) { |
662 |
4/4✓ Branch 0 taken 3354 times.
✓ Branch 1 taken 86 times.
✓ Branch 2 taken 1634 times.
✓ Branch 3 taken 1720 times.
|
3440 | if ((i > 0) && (i % 2 == 0)) |
663 |
1/2✓ Branch 1 taken 1634 times.
✗ Branch 2 not taken.
|
1634 | result += ":"; |
664 | } | ||
665 |
1/2✓ Branch 2 taken 3494 times.
✗ Branch 3 not taken.
|
3494 | result += toupper(hash_str[i]); |
666 | } | ||
667 | 86 | return result; | |
668 | 86 | } | |
669 | |||
670 | |||
671 | /** | ||
672 | * Parses a fingerprint from the whitelist | ||
673 | */ | ||
674 | 117 | shash::Any SignatureManager::MkFromFingerprint(const std::string &fingerprint) { | |
675 | 117 | string convert; | |
676 |
2/2✓ Branch 1 taken 6641 times.
✓ Branch 2 taken 113 times.
|
6754 | for (unsigned i = 0; i < fingerprint.length(); ++i) { |
677 |
1/2✓ Branch 2 taken 6637 times.
✗ Branch 3 not taken.
|
13278 | if ((fingerprint[i] == ' ') || (fingerprint[i] == '\t') |
678 |
5/6✓ Branch 0 taken 6637 times.
✓ Branch 1 taken 4 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6637 times.
✓ Branch 5 taken 4 times.
✓ Branch 6 taken 6637 times.
|
13278 | || (fingerprint[i] == '#')) { |
679 | 4 | break; | |
680 | } | ||
681 |
2/2✓ Branch 1 taken 4528 times.
✓ Branch 2 taken 2109 times.
|
6637 | if (fingerprint[i] != ':') |
682 |
1/2✓ Branch 2 taken 4528 times.
✗ Branch 3 not taken.
|
4528 | convert.push_back(tolower(fingerprint[i])); |
683 | } | ||
684 | |||
685 |
1/2✓ Branch 2 taken 117 times.
✗ Branch 3 not taken.
|
234 | return shash::MkFromHexPtr(shash::HexPtr(convert)); |
686 | 117 | } | |
687 | |||
688 | |||
689 | /** | ||
690 | * \return Some human-readable information about the loaded certificate. | ||
691 | */ | ||
692 | ✗ | string SignatureManager::Whois() { | |
693 | ✗ | if (!certificate_) | |
694 | ✗ | return "No certificate loaded"; | |
695 | |||
696 | ✗ | string result; | |
697 | ✗ | X509_NAME *subject = X509_get_subject_name(certificate_); | |
698 | ✗ | X509_NAME *issuer = X509_get_issuer_name(certificate_); | |
699 | ✗ | char *buffer = NULL; | |
700 | ✗ | buffer = X509_NAME_oneline(subject, NULL, 0); | |
701 | ✗ | if (buffer) { | |
702 | ✗ | result = "Publisher: " + string(buffer); | |
703 | ✗ | free(buffer); | |
704 | } | ||
705 | ✗ | buffer = X509_NAME_oneline(issuer, NULL, 0); | |
706 | ✗ | if (buffer) { | |
707 | ✗ | result += "\nCertificate issued by: " + string(buffer); | |
708 | ✗ | free(buffer); | |
709 | } | ||
710 | ✗ | return result; | |
711 | } | ||
712 | |||
713 | |||
714 | ✗ | bool SignatureManager::WriteCertificateMem(unsigned char **buffer, | |
715 | unsigned *buffer_size) { | ||
716 | ✗ | BIO *mem = BIO_new(BIO_s_mem()); | |
717 | ✗ | if (!mem) | |
718 | ✗ | return false; | |
719 | ✗ | if (!PEM_write_bio_X509(mem, certificate_)) { | |
720 | ✗ | BIO_free(mem); | |
721 | ✗ | return false; | |
722 | } | ||
723 | |||
724 | void *bio_buffer; | ||
725 | ✗ | *buffer_size = BIO_get_mem_data(mem, &bio_buffer); | |
726 | ✗ | *buffer = reinterpret_cast<unsigned char *>(smalloc(*buffer_size)); | |
727 | ✗ | memcpy(*buffer, bio_buffer, *buffer_size); | |
728 | ✗ | BIO_free(mem); | |
729 | ✗ | return true; | |
730 | } | ||
731 | |||
732 | |||
733 | /** | ||
734 | * Checks, whether the loaded certificate and the loaded private key match. | ||
735 | * | ||
736 | * \return True, if private key and certificate match, false otherwise. | ||
737 | */ | ||
738 | 108 | bool SignatureManager::KeysMatch() { | |
739 |
2/4✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 108 times.
|
108 | if (!certificate_ || !private_key_) |
740 | ✗ | return false; | |
741 | |||
742 | 108 | bool result = false; | |
743 | 108 | const unsigned char *sign_me = reinterpret_cast<const unsigned char *>( | |
744 | "sign me"); | ||
745 | 108 | unsigned char *signature = NULL; | |
746 | unsigned signature_size; | ||
747 |
1/2✓ Branch 1 taken 108 times.
✗ Branch 2 not taken.
|
108 | if (Sign(sign_me, 7, &signature, &signature_size) |
748 |
4/8✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 108 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 108 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 108 times.
✗ Branch 8 not taken.
|
108 | && Verify(sign_me, 7, signature, signature_size)) { |
749 | 108 | result = true; | |
750 | } | ||
751 |
1/2✓ Branch 0 taken 108 times.
✗ Branch 1 not taken.
|
108 | if (signature) |
752 | 108 | free(signature); | |
753 | 108 | return result; | |
754 | } | ||
755 | |||
756 | |||
757 | /** | ||
758 | * Verifies the currently loaded certificate against the trusted CA chain. | ||
759 | */ | ||
760 | ✗ | bool SignatureManager::VerifyCaChain() { | |
761 | ✗ | if (!certificate_) | |
762 | ✗ | return false; | |
763 | |||
764 | ✗ | X509_STORE_CTX *csc = NULL; | |
765 | ✗ | csc = X509_STORE_CTX_new(); | |
766 | ✗ | assert(csc); | |
767 | |||
768 | ✗ | X509_STORE_CTX_init(csc, x509_store_, certificate_, NULL); | |
769 | ✗ | const bool result = X509_verify_cert(csc) == 1; | |
770 | ✗ | X509_STORE_CTX_free(csc); | |
771 | |||
772 | ✗ | return result; | |
773 | } | ||
774 | |||
775 | |||
776 | /** | ||
777 | * Signs a data block using the loaded private key. | ||
778 | * | ||
779 | * \return True on success, false otherwise | ||
780 | */ | ||
781 | 292 | bool SignatureManager::Sign(const unsigned char *buffer, | |
782 | const unsigned buffer_size, | ||
783 | unsigned char **signature, | ||
784 | unsigned *signature_size) { | ||
785 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 292 times.
|
292 | if (!private_key_) { |
786 | ✗ | *signature_size = 0; | |
787 | ✗ | *signature = NULL; | |
788 | ✗ | return false; | |
789 | } | ||
790 | |||
791 | 292 | bool result = false; | |
792 | #ifdef OPENSSL_API_INTERFACE_V11 | ||
793 | 292 | EVP_MD_CTX *ctx_ptr = EVP_MD_CTX_new(); | |
794 | #else | ||
795 | EVP_MD_CTX ctx; | ||
796 | EVP_MD_CTX_init(&ctx); | ||
797 | EVP_MD_CTX *ctx_ptr = &ctx; | ||
798 | #endif | ||
799 | |||
800 | 292 | *signature = reinterpret_cast<unsigned char *>( | |
801 | 292 | smalloc(EVP_PKEY_size(private_key_))); | |
802 | 292 | if (EVP_SignInit(ctx_ptr, EVP_sha1()) | |
803 |
1/2✓ Branch 1 taken 292 times.
✗ Branch 2 not taken.
|
292 | && EVP_SignUpdate(ctx_ptr, buffer, buffer_size) |
804 |
3/6✓ Branch 0 taken 292 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 292 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 292 times.
✗ Branch 6 not taken.
|
584 | && EVP_SignFinal(ctx_ptr, *signature, signature_size, private_key_)) { |
805 | 292 | result = true; | |
806 | } | ||
807 | #ifdef OPENSSL_API_INTERFACE_V11 | ||
808 | 292 | EVP_MD_CTX_free(ctx_ptr); | |
809 | #else | ||
810 | EVP_MD_CTX_cleanup(&ctx); | ||
811 | #endif | ||
812 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 292 times.
|
292 | if (!result) { |
813 | ✗ | free(*signature); | |
814 | ✗ | *signature_size = 0; | |
815 | ✗ | *signature = NULL; | |
816 | } | ||
817 | |||
818 | 292 | return result; | |
819 | } | ||
820 | |||
821 | |||
822 | /** | ||
823 | * Signs a data block using the loaded private master key. | ||
824 | * | ||
825 | * \return True on success, false otherwise | ||
826 | */ | ||
827 | 102 | bool SignatureManager::SignRsa(const unsigned char *buffer, | |
828 | const unsigned buffer_size, | ||
829 | unsigned char **signature, | ||
830 | unsigned *signature_size) { | ||
831 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
|
102 | if (!private_master_key_) { |
832 | ✗ | *signature_size = 0; | |
833 | ✗ | *signature = NULL; | |
834 | ✗ | return false; | |
835 | } | ||
836 | |||
837 | 102 | unsigned char *to = (unsigned char *)smalloc(RSA_size(private_master_key_)); | |
838 | 102 | unsigned char *from = (unsigned char *)smalloc(buffer_size); | |
839 | 102 | memcpy(from, buffer, buffer_size); | |
840 | |||
841 | 102 | const int size = RSA_private_encrypt(buffer_size, from, to, | |
842 | private_master_key_, RSA_PKCS1_PADDING); | ||
843 | 102 | free(from); | |
844 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 102 times.
|
102 | if (size < 0) { |
845 | ✗ | *signature_size = 0; | |
846 | ✗ | *signature = NULL; | |
847 | ✗ | return false; | |
848 | } | ||
849 | 102 | *signature = to; | |
850 | 102 | *signature_size = size; | |
851 | 102 | return true; | |
852 | } | ||
853 | |||
854 | |||
855 | /** | ||
856 | * Verifies a signature against loaded certificate. | ||
857 | * | ||
858 | * \return True if signature is valid, false on error or otherwise | ||
859 | */ | ||
860 | 219 | bool SignatureManager::Verify(const unsigned char *buffer, | |
861 | const unsigned buffer_size, | ||
862 | const unsigned char *signature, | ||
863 | const unsigned signature_size) { | ||
864 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 219 times.
|
219 | if (!certificate_) |
865 | ✗ | return false; | |
866 | |||
867 | 219 | bool result = false; | |
868 | #ifdef OPENSSL_API_INTERFACE_V11 | ||
869 | 219 | EVP_MD_CTX *ctx_ptr = EVP_MD_CTX_new(); | |
870 | #else | ||
871 | EVP_MD_CTX ctx; | ||
872 | EVP_MD_CTX_init(&ctx); | ||
873 | EVP_MD_CTX *ctx_ptr = &ctx; | ||
874 | #endif | ||
875 | |||
876 | 219 | EVP_PKEY *pubkey = X509_get_pubkey(certificate_); | |
877 | 219 | if (EVP_VerifyInit(ctx_ptr, EVP_sha1()) | |
878 |
3/6✓ Branch 0 taken 219 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 219 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 219 times.
✗ Branch 6 not taken.
|
438 | && EVP_VerifyUpdate(ctx_ptr, buffer, buffer_size) && |
879 | #ifdef OPENSSL_API_INTERFACE_V09 | ||
880 | EVP_VerifyFinal(ctx_ptr, const_cast<unsigned char *>(signature), | ||
881 | signature_size, pubkey) | ||
882 | #else | ||
883 |
1/2✓ Branch 1 taken 219 times.
✗ Branch 2 not taken.
|
219 | EVP_VerifyFinal(ctx_ptr, signature, signature_size, pubkey) |
884 | #endif | ||
885 | ) { | ||
886 | 219 | result = true; | |
887 | } | ||
888 |
1/2✓ Branch 0 taken 219 times.
✗ Branch 1 not taken.
|
219 | if (pubkey != NULL) |
889 | 219 | EVP_PKEY_free(pubkey); | |
890 | #ifdef OPENSSL_API_INTERFACE_V11 | ||
891 | 219 | EVP_MD_CTX_free(ctx_ptr); | |
892 | #else | ||
893 | EVP_MD_CTX_cleanup(&ctx); | ||
894 | #endif | ||
895 | |||
896 | 219 | return result; | |
897 | } | ||
898 | |||
899 | |||
900 | /** | ||
901 | * Verifies a signature against all loaded public keys. | ||
902 | * | ||
903 | * \return True if signature is valid with any public key, false on error or | ||
904 | * otherwise | ||
905 | */ | ||
906 | 119 | bool SignatureManager::VerifyRsa(const unsigned char *buffer, | |
907 | const unsigned buffer_size, | ||
908 | const unsigned char *signature, | ||
909 | const unsigned signature_size) { | ||
910 |
1/2✓ Branch 1 taken 119 times.
✗ Branch 2 not taken.
|
119 | for (unsigned i = 0, s = public_keys_.size(); i < s; ++i) { |
911 |
1/2✗ Branch 2 not taken.
✓ Branch 3 taken 119 times.
|
119 | if (buffer_size > (unsigned)RSA_size(public_keys_[i])) |
912 | ✗ | continue; | |
913 | |||
914 | 119 | unsigned char *to = (unsigned char *)smalloc(RSA_size(public_keys_[i])); | |
915 | 119 | unsigned char *from = (unsigned char *)smalloc(signature_size); | |
916 | 119 | memcpy(from, signature, signature_size); | |
917 | |||
918 | 119 | const int size = RSA_public_decrypt(signature_size, from, to, | |
919 | 119 | public_keys_[i], RSA_PKCS1_PADDING); | |
920 | 119 | free(from); | |
921 |
2/4✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 119 times.
✗ Branch 3 not taken.
|
119 | if ((size >= 0) && (unsigned(size) == buffer_size) |
922 |
1/2✓ Branch 0 taken 119 times.
✗ Branch 1 not taken.
|
119 | && (memcmp(buffer, to, size) == 0)) { |
923 | 119 | free(to); | |
924 | 119 | return true; | |
925 | } | ||
926 | |||
927 | ✗ | free(to); | |
928 | } | ||
929 | |||
930 | ✗ | LogCvmfs(kLogSignature, kLogDebug, "VerifyRsa, no public key fits"); | |
931 | ✗ | return false; | |
932 | } | ||
933 | |||
934 | |||
935 | /** | ||
936 | * Strips a signature from the letter (if exists) | ||
937 | */ | ||
938 | 202 | void SignatureManager::CutLetter(const unsigned char *buffer, | |
939 | const unsigned buffer_size, | ||
940 | const char separator, | ||
941 | unsigned *letter_length, | ||
942 | unsigned *pos_after_mark) { | ||
943 | 202 | unsigned pos = 0; | |
944 | 202 | *letter_length = *pos_after_mark = 0; | |
945 | do { | ||
946 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30874 times.
|
30874 | if (pos == buffer_size) { |
947 | ✗ | *pos_after_mark = pos; // Careful: pos_after_mark points out of buffer | |
948 | ✗ | *letter_length = pos; | |
949 | ✗ | break; | |
950 | } | ||
951 | |||
952 |
3/4✓ Branch 0 taken 1459 times.
✓ Branch 1 taken 29415 times.
✓ Branch 2 taken 1459 times.
✗ Branch 3 not taken.
|
30874 | if ((buffer[pos] == '\n') && (pos + 4 <= buffer_size) |
953 |
3/4✓ Branch 0 taken 202 times.
✓ Branch 1 taken 1257 times.
✓ Branch 2 taken 202 times.
✗ Branch 3 not taken.
|
1459 | && (buffer[pos + 1] == separator) && (buffer[pos + 2] == separator) |
954 |
1/2✓ Branch 0 taken 202 times.
✗ Branch 1 not taken.
|
202 | && (buffer[pos + 3] == '\n')) { |
955 | 202 | *letter_length = pos + 1; | |
956 | 202 | pos += 4; | |
957 | 202 | break; | |
958 | } | ||
959 | 30672 | pos++; | |
960 | } while (true); | ||
961 | 202 | *pos_after_mark = pos; | |
962 | 202 | } | |
963 | |||
964 | |||
965 | /** | ||
966 | * Checks a document of the form | ||
967 | * <ASCII LINES> | ||
968 | * -- | ||
969 | * <hash> | ||
970 | * <signature> | ||
971 | */ | ||
972 | 202 | bool SignatureManager::VerifyLetter(const unsigned char *buffer, | |
973 | const unsigned buffer_size, | ||
974 | const bool by_rsa) { | ||
975 | 202 | unsigned pos = 0; | |
976 | 202 | unsigned letter_length = 0; | |
977 |
1/2✓ Branch 1 taken 202 times.
✗ Branch 2 not taken.
|
202 | CutLetter(buffer, buffer_size, '-', &letter_length, &pos); |
978 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 202 times.
|
202 | if (pos >= buffer_size) |
979 | ✗ | return false; | |
980 | |||
981 |
1/2✓ Branch 2 taken 202 times.
✗ Branch 3 not taken.
|
202 | string hash_str = ""; |
982 | 202 | const unsigned hash_pos = pos; | |
983 | do { | ||
984 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8318 times.
|
8318 | if (pos == buffer_size) |
985 | ✗ | return false; | |
986 |
2/2✓ Branch 0 taken 202 times.
✓ Branch 1 taken 8116 times.
|
8318 | if (buffer[pos] == '\n') { |
987 | 202 | pos++; | |
988 | 202 | break; | |
989 | } | ||
990 |
1/2✓ Branch 1 taken 8116 times.
✗ Branch 2 not taken.
|
8116 | hash_str.push_back(buffer[pos++]); |
991 | } while (true); | ||
992 |
1/2✓ Branch 2 taken 202 times.
✗ Branch 3 not taken.
|
202 | const shash::Any hash_printed = shash::MkFromHexPtr(shash::HexPtr(hash_str)); |
993 |
1/2✓ Branch 1 taken 202 times.
✗ Branch 2 not taken.
|
202 | shash::Any hash_computed(hash_printed.algorithm); |
994 |
1/2✓ Branch 1 taken 202 times.
✗ Branch 2 not taken.
|
202 | shash::HashMem(buffer, letter_length, &hash_computed); |
995 |
2/4✓ Branch 1 taken 202 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 202 times.
|
202 | if (hash_printed != hash_computed) |
996 | ✗ | return false; | |
997 | |||
998 |
2/2✓ Branch 0 taken 103 times.
✓ Branch 1 taken 99 times.
|
202 | if (by_rsa) { |
999 |
1/2✓ Branch 2 taken 103 times.
✗ Branch 3 not taken.
|
103 | return VerifyRsa(&buffer[hash_pos], hash_str.length(), &buffer[pos], |
1000 | 103 | buffer_size - pos); | |
1001 | } else { | ||
1002 |
1/2✓ Branch 2 taken 99 times.
✗ Branch 3 not taken.
|
99 | return Verify(&buffer[hash_pos], hash_str.length(), &buffer[pos], |
1003 | 99 | buffer_size - pos); | |
1004 | } | ||
1005 | 202 | } | |
1006 | |||
1007 | |||
1008 | /** | ||
1009 | * Verifies a PKCS#7 binary content + signature structure | ||
1010 | * using the loaded trusted CAs/CRLs | ||
1011 | */ | ||
1012 | ✗ | bool SignatureManager::VerifyPkcs7(const unsigned char *buffer, | |
1013 | const unsigned buffer_size, | ||
1014 | unsigned char **content, | ||
1015 | unsigned *content_size, | ||
1016 | vector<string> *alt_uris) { | ||
1017 | ✗ | *content = NULL; | |
1018 | ✗ | *content_size = 0; | |
1019 | |||
1020 | ✗ | BIO *bp_pkcs7 = BIO_new(BIO_s_mem()); | |
1021 | ✗ | if (!bp_pkcs7) | |
1022 | ✗ | return false; | |
1023 | ✗ | if (BIO_write(bp_pkcs7, buffer, buffer_size) <= 0) { | |
1024 | ✗ | BIO_free(bp_pkcs7); | |
1025 | ✗ | return false; | |
1026 | } | ||
1027 | |||
1028 | ✗ | PKCS7 *pkcs7 = NULL; | |
1029 | ✗ | pkcs7 = PEM_read_bio_PKCS7(bp_pkcs7, NULL, NULL, NULL); | |
1030 | ✗ | BIO_free(bp_pkcs7); | |
1031 | ✗ | if (!pkcs7) { | |
1032 | ✗ | LogCvmfs(kLogSignature, kLogDebug, "invalid pkcs#7 signature"); | |
1033 | ✗ | return false; | |
1034 | } | ||
1035 | |||
1036 | ✗ | BIO *bp_content = BIO_new(BIO_s_mem()); | |
1037 | ✗ | if (!bp_content) { | |
1038 | ✗ | PKCS7_free(pkcs7); | |
1039 | ✗ | return false; | |
1040 | } | ||
1041 | |||
1042 | ✗ | const int flags = 0; | |
1043 | ✗ | STACK_OF(X509) *extra_signers = NULL; | |
1044 | ✗ | BIO *indata = NULL; | |
1045 | ✗ | const bool result = PKCS7_verify(pkcs7, extra_signers, x509_store_, indata, | |
1046 | ✗ | bp_content, flags); | |
1047 | ✗ | if (result != 1) { | |
1048 | ✗ | BIO_free(bp_content); | |
1049 | ✗ | PKCS7_free(pkcs7); | |
1050 | ✗ | return false; | |
1051 | } | ||
1052 | |||
1053 | BUF_MEM *bufmem_content; | ||
1054 | ✗ | BIO_get_mem_ptr(bp_content, &bufmem_content); | |
1055 | // BIO_free() leaves BUF_MEM alone | ||
1056 | ✗ | (void)BIO_set_close(bp_content, BIO_NOCLOSE); | |
1057 | ✗ | BIO_free(bp_content); | |
1058 | ✗ | *content = reinterpret_cast<unsigned char *>(bufmem_content->data); | |
1059 | ✗ | *content_size = bufmem_content->length; | |
1060 | ✗ | free(bufmem_content); | |
1061 | ✗ | if (*content == NULL) { | |
1062 | ✗ | PKCS7_free(pkcs7); | |
1063 | ✗ | LogCvmfs(kLogSignature, kLogDebug, "empty pkcs#7 structure"); | |
1064 | ✗ | return false; | |
1065 | } | ||
1066 | |||
1067 | // Extract signing certificates | ||
1068 | ✗ | STACK_OF(X509) *signers = NULL; | |
1069 | ✗ | signers = PKCS7_get0_signers(pkcs7, NULL, 0); | |
1070 | ✗ | assert(signers); | |
1071 | |||
1072 | // Extract alternative names | ||
1073 | ✗ | for (int i = 0; i < sk_X509_num(signers); ++i) { | |
1074 | ✗ | X509 *this_signer = sk_X509_value(signers, i); | |
1075 | ✗ | GENERAL_NAMES *subject_alt_names = NULL; | |
1076 | subject_alt_names = reinterpret_cast<GENERAL_NAMES *>( | ||
1077 | ✗ | X509_get_ext_d2i(this_signer, NID_subject_alt_name, NULL, NULL)); | |
1078 | ✗ | if (subject_alt_names != NULL) { | |
1079 | ✗ | for (int j = 0; j < sk_GENERAL_NAME_num(subject_alt_names); ++j) { | |
1080 | ✗ | GENERAL_NAME *this_name = sk_GENERAL_NAME_value(subject_alt_names, j); | |
1081 | ✗ | if (this_name->type != GEN_URI) | |
1082 | ✗ | continue; | |
1083 | |||
1084 | const char *name_ptr = reinterpret_cast<const char *>( | ||
1085 | #ifdef OPENSSL_API_INTERFACE_V11 | ||
1086 | ✗ | ASN1_STRING_get0_data(this_name->d.uniformResourceIdentifier)); | |
1087 | #else | ||
1088 | ASN1_STRING_data(this_name->d.uniformResourceIdentifier)); | ||
1089 | #endif | ||
1090 | const int name_len = | ||
1091 | ✗ | ASN1_STRING_length(this_name->d.uniformResourceIdentifier); | |
1092 | ✗ | if (!name_ptr || (name_len <= 0)) | |
1093 | ✗ | continue; | |
1094 | ✗ | alt_uris->push_back(string(name_ptr, name_len)); | |
1095 | } | ||
1096 | } | ||
1097 | } | ||
1098 | ✗ | sk_X509_free(signers); | |
1099 | ✗ | PKCS7_free(pkcs7); | |
1100 | ✗ | return true; | |
1101 | } | ||
1102 | |||
1103 | } // namespace signature | ||
1104 |