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