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