GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
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 verifiying |
||
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 |
#include "cvmfs_config.h" |
||
15 |
#include "signature.h" |
||
16 |
|||
17 |
#include <openssl/evp.h> |
||
18 |
#include <openssl/pkcs7.h> |
||
19 |
#include <openssl/x509v3.h> |
||
20 |
|||
21 |
#include <cassert> |
||
22 |
#include <cctype> |
||
23 |
#include <cstdio> |
||
24 |
#include <cstdlib> |
||
25 |
#include <cstring> |
||
26 |
#include <string> |
||
27 |
#include <vector> |
||
28 |
|||
29 |
#include "compression.h" |
||
30 |
#include "duplex_ssl.h" |
||
31 |
#include "hash.h" |
||
32 |
#include "logging.h" |
||
33 |
#include "platform.h" |
||
34 |
#include "smalloc.h" |
||
35 |
#include "util/string.h" |
||
36 |
#include "util_concurrency.h" |
||
37 |
|||
38 |
using namespace std; // NOLINT |
||
39 |
|||
40 |
namespace signature { |
||
41 |
|||
42 |
const char *kDefaultPublicKey = "/etc/cvmfs/keys/cern.ch.pub"; |
||
43 |
|||
44 |
|||
45 |
static int CallbackCertVerify(int ok, X509_STORE_CTX *ctx) { |
||
46 |
LogCvmfs(kLogCvmfs, kLogDebug, "certificate chain verification: %d", ok); |
||
47 |
if (ok) return ok; |
||
48 |
|||
49 |
int error = X509_STORE_CTX_get_error(ctx); |
||
50 |
X509 *current_cert = X509_STORE_CTX_get_current_cert(ctx); |
||
51 |
string subject = "subject n/a"; |
||
52 |
if (current_cert) { |
||
53 |
char *buffer = NULL; |
||
54 |
buffer = X509_NAME_oneline(X509_get_subject_name(current_cert), NULL, 0); |
||
55 |
if (buffer) { |
||
56 |
subject = string(buffer); |
||
57 |
free(buffer); |
||
58 |
} |
||
59 |
} |
||
60 |
LogCvmfs(kLogCvmfs, kLogDebug | kLogSyslogErr, |
||
61 |
"certificate verification error: %s, error %s (%d)", |
||
62 |
subject.c_str(), X509_verify_cert_error_string(error), error); |
||
63 |
return ok; |
||
64 |
} |
||
65 |
|||
66 |
|||
67 |
151 |
SignatureManager::SignatureManager() { |
|
68 |
151 |
private_key_ = NULL; |
|
69 |
151 |
certificate_ = NULL; |
|
70 |
151 |
x509_store_ = NULL; |
|
71 |
151 |
x509_lookup_ = NULL; |
|
72 |
151 |
int retval = pthread_mutex_init(&lock_blacklist_, NULL); |
|
73 |
✗✓ | 151 |
assert(retval == 0); |
74 |
} |
||
75 |
|||
76 |
|||
77 |
141 |
void SignatureManager::InitX509Store() { |
|
78 |
✗✓ | 141 |
if (x509_store_) X509_STORE_free(x509_store_); |
79 |
141 |
x509_lookup_ = NULL; |
|
80 |
141 |
x509_store_ = X509_STORE_new(); |
|
81 |
✗✓ | 141 |
assert(x509_store_ != NULL); |
82 |
|||
83 |
unsigned long verify_flags = // NOLINT(runtime/int) |
||
84 |
X509_V_FLAG_CRL_CHECK | |
||
85 |
141 |
X509_V_FLAG_CRL_CHECK_ALL; |
|
86 |
#ifdef OPENSSL_API_INTERFACE_V09 |
||
87 |
X509_STORE_set_flags(x509_store_, verify_flags); |
||
88 |
#else |
||
89 |
int retval; |
||
90 |
141 |
X509_VERIFY_PARAM *param = X509_VERIFY_PARAM_new(); |
|
91 |
✗✓ | 141 |
assert(param != NULL); |
92 |
141 |
retval = X509_VERIFY_PARAM_set_flags(param, verify_flags); |
|
93 |
✗✓ | 141 |
assert(retval == 1); |
94 |
141 |
retval = X509_STORE_set1_param(x509_store_, param); |
|
95 |
✗✓ | 141 |
assert(retval == 1); |
96 |
141 |
X509_VERIFY_PARAM_free(param); |
|
97 |
#endif |
||
98 |
|||
99 |
141 |
x509_lookup_ = X509_STORE_add_lookup(x509_store_, X509_LOOKUP_hash_dir()); |
|
100 |
✗✓ | 141 |
assert(x509_lookup_ != NULL); |
101 |
|||
102 |
141 |
X509_STORE_set_verify_cb_func(x509_store_, CallbackCertVerify); |
|
103 |
141 |
} |
|
104 |
|||
105 |
|||
106 |
141 |
void SignatureManager::Init() { |
|
107 |
141 |
OpenSSL_add_all_algorithms(); |
|
108 |
141 |
InitX509Store(); |
|
109 |
141 |
} |
|
110 |
|||
111 |
|||
112 |
141 |
void SignatureManager::Fini() { |
|
113 |
✓✓ | 141 |
if (certificate_) X509_free(certificate_); |
114 |
141 |
certificate_ = NULL; |
|
115 |
✓✓ | 141 |
if (private_key_) EVP_PKEY_free(private_key_); |
116 |
141 |
private_key_ = NULL; |
|
117 |
✓✓ | 141 |
if (!public_keys_.empty()) { |
118 |
✓✓ | 152 |
for (unsigned i = 0; i < public_keys_.size(); ++i) |
119 |
76 |
RSA_free(public_keys_[i]); |
|
120 |
76 |
public_keys_.clear(); |
|
121 |
} |
||
122 |
// Lookup is freed automatically |
||
123 |
✓✗ | 141 |
if (x509_store_) X509_STORE_free(x509_store_); |
124 |
|||
125 |
141 |
EVP_cleanup(); |
|
126 |
|||
127 |
141 |
private_key_ = NULL; |
|
128 |
141 |
certificate_ = NULL; |
|
129 |
141 |
x509_store_ = NULL; |
|
130 |
141 |
x509_lookup_ = NULL; |
|
131 |
141 |
} |
|
132 |
|||
133 |
|||
134 |
/** |
||
135 |
* OpenSSL error strings. |
||
136 |
*/ |
||
137 |
string SignatureManager::GetCryptoError() { |
||
138 |
char buf[121]; |
||
139 |
string err; |
||
140 |
while (ERR_peek_error() != 0) { |
||
141 |
ERR_error_string(ERR_get_error(), buf); |
||
142 |
err += string(buf); |
||
143 |
} |
||
144 |
return err; |
||
145 |
} |
||
146 |
|||
147 |
|||
148 |
/** |
||
149 |
* @param[in] file_pem File name of the PEM key file |
||
150 |
* @param[in] password Password for the private key. |
||
151 |
* Password is not saved internally, but the private key is. |
||
152 |
* \return True on success, false otherwise |
||
153 |
*/ |
||
154 |
60 |
bool SignatureManager::LoadPrivateKeyPath(const string &file_pem, |
|
155 |
const string &password) |
||
156 |
{ |
||
157 |
bool result; |
||
158 |
60 |
FILE *fp = NULL; |
|
159 |
60 |
char *tmp = strdupa(password.c_str()); |
|
160 |
|||
161 |
✗✓ | 60 |
if ((fp = fopen(file_pem.c_str(), "r")) == NULL) |
162 |
return false; |
||
163 |
60 |
result = (private_key_ = PEM_read_PrivateKey(fp, NULL, NULL, tmp)) != NULL; |
|
164 |
60 |
fclose(fp); |
|
165 |
60 |
return result; |
|
166 |
} |
||
167 |
|||
168 |
|||
169 |
/** |
||
170 |
* Clears the memory storing the private key. |
||
171 |
*/ |
||
172 |
void SignatureManager::UnloadPrivateKey() { |
||
173 |
if (private_key_) EVP_PKEY_free(private_key_); |
||
174 |
private_key_ = NULL; |
||
175 |
} |
||
176 |
|||
177 |
|||
178 |
/** |
||
179 |
* Loads a certificate. This certificate is used for the following |
||
180 |
* signature verifications |
||
181 |
* |
||
182 |
* \return True on success, false otherwise |
||
183 |
*/ |
||
184 |
60 |
bool SignatureManager::LoadCertificatePath(const string &file_pem) { |
|
185 |
✗✓ | 60 |
if (certificate_) { |
186 |
X509_free(certificate_); |
||
187 |
certificate_ = NULL; |
||
188 |
} |
||
189 |
|||
190 |
bool result; |
||
191 |
60 |
char *nopwd = strdupa(""); |
|
192 |
FILE *fp; |
||
193 |
|||
194 |
✗✓ | 60 |
if ((fp = fopen(file_pem.c_str(), "r")) == NULL) |
195 |
return false; |
||
196 |
60 |
result = (certificate_ = PEM_read_X509_AUX(fp, NULL, NULL, nopwd)) != NULL; |
|
197 |
|||
198 |
✗✓✗✗ |
60 |
if (!result && certificate_) { |
199 |
X509_free(certificate_); |
||
200 |
certificate_ = NULL; |
||
201 |
} |
||
202 |
|||
203 |
60 |
fclose(fp); |
|
204 |
60 |
return result; |
|
205 |
} |
||
206 |
|||
207 |
|||
208 |
/** |
||
209 |
* See the function that loads the certificate from file. |
||
210 |
*/ |
||
211 |
32 |
bool SignatureManager::LoadCertificateMem(const unsigned char *buffer, |
|
212 |
const unsigned buffer_size) |
||
213 |
{ |
||
214 |
✓✓ | 32 |
if (certificate_) { |
215 |
4 |
X509_free(certificate_); |
|
216 |
4 |
certificate_ = NULL; |
|
217 |
} |
||
218 |
|||
219 |
bool result; |
||
220 |
32 |
char *nopwd = strdupa(""); |
|
221 |
|||
222 |
32 |
BIO *mem = BIO_new(BIO_s_mem()); |
|
223 |
✗✓ | 32 |
if (!mem) return false; |
224 |
✗✓ | 32 |
if (BIO_write(mem, buffer, buffer_size) <= 0) { |
225 |
BIO_free(mem); |
||
226 |
return false; |
||
227 |
} |
||
228 |
result = (certificate_ = PEM_read_bio_X509_AUX(mem, NULL, NULL, nopwd)) |
||
229 |
32 |
!= NULL; |
|
230 |
32 |
BIO_free(mem); |
|
231 |
|||
232 |
✗✓✗✗ |
32 |
if (!result && certificate_) { |
233 |
X509_free(certificate_); |
||
234 |
certificate_ = NULL; |
||
235 |
} |
||
236 |
|||
237 |
32 |
return result; |
|
238 |
} |
||
239 |
|||
240 |
|||
241 |
/** |
||
242 |
* Loads a list of public RSA keys separated by ":". |
||
243 |
*/ |
||
244 |
81 |
bool SignatureManager::LoadPublicRsaKeys(const string &path_list) { |
|
245 |
✗✓ | 81 |
if (!public_keys_.empty()) { |
246 |
for (unsigned i = 0; i < public_keys_.size(); ++i) |
||
247 |
RSA_free(public_keys_[i]); |
||
248 |
public_keys_.clear(); |
||
249 |
} |
||
250 |
|||
251 |
✓✓ | 81 |
if (path_list == "") |
252 |
3 |
return true; |
|
253 |
78 |
const vector<string> pem_files = SplitString(path_list, ':'); |
|
254 |
|||
255 |
78 |
char *nopwd = strdupa(""); |
|
256 |
FILE *fp; |
||
257 |
|||
258 |
✓✓ | 154 |
for (unsigned i = 0; i < pem_files.size(); ++i) { |
259 |
78 |
const char* pubkey_file = pem_files[i].c_str(); |
|
260 |
|||
261 |
// open public key file |
||
262 |
78 |
fp = fopen(pubkey_file, "r"); |
|
263 |
✓✓ | 78 |
if (fp == NULL) { |
264 |
LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, "failed to open " |
||
265 |
"public key '%s'", |
||
266 |
2 |
pubkey_file); |
|
267 |
2 |
return false; |
|
268 |
} |
||
269 |
|||
270 |
// load the public key from the file (and close it) |
||
271 |
76 |
EVP_PKEY *this_key = PEM_read_PUBKEY(fp, NULL, NULL, nopwd); |
|
272 |
76 |
fclose(fp); |
|
273 |
✗✓ | 76 |
if (this_key == NULL) { |
274 |
LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, "failed to load " |
||
275 |
"public key '%s'", |
||
276 |
pubkey_file); |
||
277 |
return false; |
||
278 |
} |
||
279 |
|||
280 |
// read the RSA key from the loaded public key |
||
281 |
76 |
RSA *key = EVP_PKEY_get1_RSA(this_key); |
|
282 |
76 |
EVP_PKEY_free(this_key); |
|
283 |
✗✓ | 76 |
if (key == NULL) { |
284 |
LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, "failed to read " |
||
285 |
"public key '%s'", |
||
286 |
pubkey_file); |
||
287 |
return false; |
||
288 |
} |
||
289 |
|||
290 |
// store the loaded public key |
||
291 |
76 |
public_keys_.push_back(key); |
|
292 |
} |
||
293 |
|||
294 |
76 |
return true; |
|
295 |
} |
||
296 |
|||
297 |
|||
298 |
std::string SignatureManager::GenerateKeyText(RSA *pubkey) { |
||
299 |
if (!pubkey) {return "";} |
||
300 |
|||
301 |
BIO *bp = BIO_new(BIO_s_mem()); |
||
302 |
if (bp == NULL) { |
||
303 |
LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, "Failed to allocate" |
||
304 |
" memory for pubkey"); |
||
305 |
return ""; |
||
306 |
} |
||
307 |
if (!PEM_write_bio_RSA_PUBKEY(bp, pubkey)) { |
||
308 |
LogCvmfs(kLogSignature, kLogDebug | kLogSyslogErr, "Failed to write" |
||
309 |
" pubkey to memory"); |
||
310 |
return ""; |
||
311 |
} |
||
312 |
char *bio_pubkey_text; |
||
313 |
long bytes = BIO_get_mem_data(bp, &bio_pubkey_text); // NOLINT |
||
314 |
std::string bio_pubkey_str(bio_pubkey_text, bytes); |
||
315 |
BIO_free(bp); |
||
316 |
|||
317 |
return bio_pubkey_str; |
||
318 |
} |
||
319 |
|||
320 |
|||
321 |
std::string SignatureManager::GetActivePubkeys() { |
||
322 |
std::string pubkeys; |
||
323 |
for (std::vector<RSA *>::const_iterator it = public_keys_.begin(); |
||
324 |
it != public_keys_.end(); |
||
325 |
it++) { |
||
326 |
pubkeys += GenerateKeyText(*it); |
||
327 |
} |
||
328 |
// NOTE: we do not add the pubkey of the certificate here, as it is |
||
329 |
// not used for the whitelist verification. |
||
330 |
return pubkeys; |
||
331 |
} |
||
332 |
|||
333 |
/** |
||
334 |
* Loads a list of blacklisted certificates (fingerprints) from a file. |
||
335 |
*/ |
||
336 |
9 |
bool SignatureManager::LoadBlacklist( |
|
337 |
const std::string &path_blacklist, |
||
338 |
bool append) |
||
339 |
{ |
||
340 |
9 |
MutexLockGuard lock_guard(&lock_blacklist_); |
|
341 |
LogCvmfs(kLogSignature, kLogDebug, "reading from blacklist %s", |
||
342 |
9 |
path_blacklist.c_str()); |
|
343 |
✓✗ | 9 |
if (!append) |
344 |
9 |
blacklist_.clear(); |
|
345 |
|||
346 |
char *buffer; |
||
347 |
unsigned buffer_size; |
||
348 |
✗✓ | 9 |
if (!CopyPath2Mem(path_blacklist, |
349 |
reinterpret_cast<unsigned char **>(&buffer), &buffer_size)) |
||
350 |
{ |
||
351 |
return false; |
||
352 |
} |
||
353 |
|||
354 |
9 |
unsigned num_bytes = 0; |
|
355 |
✗✓✓ | 18 |
while (num_bytes < buffer_size) { |
356 |
const string line = GetLineMem(buffer + num_bytes, |
||
357 |
9 |
buffer_size - num_bytes); |
|
358 |
9 |
blacklist_.push_back(line); |
|
359 |
9 |
num_bytes += line.length() + 1; |
|
360 |
} |
||
361 |
9 |
free(buffer); |
|
362 |
|||
363 |
9 |
return true; |
|
364 |
} |
||
365 |
|||
366 |
|||
367 |
72 |
vector<string> SignatureManager::GetBlacklist() { |
|
368 |
72 |
MutexLockGuard lock_guard(&lock_blacklist_); |
|
369 |
72 |
return blacklist_; |
|
370 |
} |
||
371 |
|||
372 |
|||
373 |
/** |
||
374 |
* Loads CA certificates CRLs from a ":" separated list of paths. |
||
375 |
* The information is used for proper X509 verification. |
||
376 |
* The format of the certificates and CRLs has to be OpenSSL hashed certs. |
||
377 |
* The path can be something like /etc/grid-security/certificates. |
||
378 |
* If path_list is empty, the default path is taken. |
||
379 |
*/ |
||
380 |
bool SignatureManager::LoadTrustedCaCrl(const string &path_list) { |
||
381 |
InitX509Store(); |
||
382 |
|||
383 |
/* TODO if (path_list == "") { |
||
384 |
return true; |
||
385 |
}*/ |
||
386 |
const vector<string> paths = SplitString(path_list, ':'); |
||
387 |
for (unsigned i = 0; i < paths.size(); ++i) { |
||
388 |
int retval = X509_LOOKUP_add_dir(x509_lookup_, paths[i].c_str(), |
||
389 |
X509_FILETYPE_PEM); |
||
390 |
if (!retval) |
||
391 |
return false; |
||
392 |
} |
||
393 |
return true; |
||
394 |
} |
||
395 |
|||
396 |
|||
397 |
/** |
||
398 |
* Returns cryptographic hash from DER encoded certificate, encoded the same way |
||
399 |
* OpenSSL does (01:AB:...). |
||
400 |
* Empty string on failure. |
||
401 |
*/ |
||
402 |
52 |
shash::Any SignatureManager::HashCertificate( |
|
403 |
const shash::Algorithms hash_algorithm) |
||
404 |
{ |
||
405 |
52 |
shash::Any result; |
|
406 |
✗✓ | 52 |
if (!certificate_) |
407 |
return result; |
||
408 |
|||
409 |
int buffer_size; |
||
410 |
52 |
unsigned char *buffer = NULL; |
|
411 |
|||
412 |
52 |
buffer_size = i2d_X509(certificate_, &buffer); |
|
413 |
✗✓ | 52 |
if (buffer_size < 0) |
414 |
return result; |
||
415 |
|||
416 |
52 |
result.algorithm = hash_algorithm; |
|
417 |
52 |
shash::HashMem(buffer, buffer_size, &result); |
|
418 |
52 |
free(buffer); |
|
419 |
|||
420 |
52 |
return result; |
|
421 |
} |
||
422 |
|||
423 |
|||
424 |
/** |
||
425 |
* Returns cryptographic hash from DER encoded certificate, encoded the same way |
||
426 |
* OpenSSL does (01:AB:...). |
||
427 |
* Empty string on failure. |
||
428 |
*/ |
||
429 |
20 |
string SignatureManager::FingerprintCertificate( |
|
430 |
const shash::Algorithms hash_algorithm) |
||
431 |
{ |
||
432 |
20 |
shash::Any hash = HashCertificate(hash_algorithm); |
|
433 |
✗✓ | 20 |
if (hash.IsNull()) |
434 |
return ""; |
||
435 |
|||
436 |
20 |
const string hash_str = hash.ToString(); |
|
437 |
20 |
string result; |
|
438 |
✓✓ | 820 |
for (unsigned i = 0; i < hash_str.length(); ++i) { |
439 |
✓✗ | 800 |
if (i < 2*shash::kDigestSizes[hash_algorithm]) { |
440 |
✓✓✓✓ |
800 |
if ((i > 0) && (i%2 == 0)) result += ":"; |
441 |
} |
||
442 |
800 |
result += toupper(hash_str[i]); |
|
443 |
} |
||
444 |
20 |
return result; |
|
445 |
} |
||
446 |
|||
447 |
|||
448 |
/** |
||
449 |
* Parses a fingerprint from the whitelist |
||
450 |
*/ |
||
451 |
42 |
shash::Any SignatureManager::MkFromFingerprint(const std::string &fingerprint) { |
|
452 |
42 |
string convert; |
|
453 |
✓✓ | 2323 |
for (unsigned i = 0; i < fingerprint.length(); ++i) { |
454 |
✓✓✓✗ ✗✓✓✓ |
2284 |
if ((fingerprint[i] == ' ') || (fingerprint[i] == '\t') || |
455 |
(fingerprint[i] == '#')) |
||
456 |
{ |
||
457 |
3 |
break; |
|
458 |
} |
||
459 |
✓✓ | 2281 |
if (fingerprint[i] != ':') |
460 |
1559 |
convert.push_back(tolower(fingerprint[i])); |
|
461 |
} |
||
462 |
|||
463 |
42 |
return shash::MkFromHexPtr(shash::HexPtr(convert)); |
|
464 |
} |
||
465 |
|||
466 |
|||
467 |
/** |
||
468 |
* \return Some human-readable information about the loaded certificate. |
||
469 |
*/ |
||
470 |
string SignatureManager::Whois() { |
||
471 |
if (!certificate_) return "No certificate loaded"; |
||
472 |
|||
473 |
string result; |
||
474 |
X509_NAME *subject = X509_get_subject_name(certificate_); |
||
475 |
X509_NAME *issuer = X509_get_issuer_name(certificate_); |
||
476 |
char *buffer = NULL; |
||
477 |
buffer = X509_NAME_oneline(subject, NULL, 0); |
||
478 |
if (buffer) { |
||
479 |
result = "Publisher: " + string(buffer); |
||
480 |
free(buffer); |
||
481 |
} |
||
482 |
buffer = X509_NAME_oneline(issuer, NULL, 0); |
||
483 |
if (buffer) { |
||
484 |
result += "\nCertificate issued by: " + string(buffer); |
||
485 |
free(buffer); |
||
486 |
} |
||
487 |
return result; |
||
488 |
} |
||
489 |
|||
490 |
|||
491 |
bool SignatureManager::WriteCertificateMem(unsigned char **buffer, |
||
492 |
unsigned *buffer_size) |
||
493 |
{ |
||
494 |
BIO *mem = BIO_new(BIO_s_mem()); |
||
495 |
if (!mem) return false; |
||
496 |
if (!PEM_write_bio_X509(mem, certificate_)) { |
||
497 |
BIO_free(mem); |
||
498 |
return false; |
||
499 |
} |
||
500 |
|||
501 |
void *bio_buffer; |
||
502 |
*buffer_size = BIO_get_mem_data(mem, &bio_buffer); |
||
503 |
*buffer = reinterpret_cast<unsigned char *>(smalloc(*buffer_size)); |
||
504 |
memcpy(*buffer, bio_buffer, *buffer_size); |
||
505 |
BIO_free(mem); |
||
506 |
return true; |
||
507 |
} |
||
508 |
|||
509 |
|||
510 |
/** |
||
511 |
* Checks, whether the loaded certificate and the loaded private key match. |
||
512 |
* |
||
513 |
* \return True, if private key and certificate match, false otherwise. |
||
514 |
*/ |
||
515 |
20 |
bool SignatureManager::KeysMatch() { |
|
516 |
✓✗✗✓ |
20 |
if (!certificate_ || !private_key_) |
517 |
return false; |
||
518 |
|||
519 |
20 |
bool result = false; |
|
520 |
const unsigned char *sign_me = reinterpret_cast<const unsigned char *> |
||
521 |
20 |
("sign me"); |
|
522 |
20 |
unsigned char *signature = NULL; |
|
523 |
unsigned signature_size; |
||
524 |
✓✗✓✗ ✓✗ |
20 |
if (Sign(sign_me, 7, &signature, &signature_size) && |
525 |
Verify(sign_me, 7, signature, signature_size)) |
||
526 |
{ |
||
527 |
20 |
result = true; |
|
528 |
} |
||
529 |
✓✗ | 20 |
if (signature) free(signature); |
530 |
20 |
return result; |
|
531 |
} |
||
532 |
|||
533 |
|||
534 |
/** |
||
535 |
* Verifies the currently loaded certificate against the trusted CA chain. |
||
536 |
*/ |
||
537 |
bool SignatureManager::VerifyCaChain() { |
||
538 |
if (!certificate_) |
||
539 |
return false; |
||
540 |
|||
541 |
X509_STORE_CTX *csc = NULL; |
||
542 |
csc = X509_STORE_CTX_new(); |
||
543 |
assert(csc); |
||
544 |
|||
545 |
X509_STORE_CTX_init(csc, x509_store_, certificate_, NULL); |
||
546 |
bool result = X509_verify_cert(csc) == 1; |
||
547 |
X509_STORE_CTX_free(csc); |
||
548 |
|||
549 |
return result; |
||
550 |
} |
||
551 |
|||
552 |
|||
553 |
/** |
||
554 |
* Signs a data block using the loaded private key. |
||
555 |
* |
||
556 |
* \return True on sucess, false otherwise |
||
557 |
*/ |
||
558 |
60 |
bool SignatureManager::Sign(const unsigned char *buffer, |
|
559 |
const unsigned buffer_size, |
||
560 |
unsigned char **signature, |
||
561 |
unsigned *signature_size) |
||
562 |
{ |
||
563 |
✗✓ | 60 |
if (!private_key_) { |
564 |
*signature_size = 0; |
||
565 |
*signature = NULL; |
||
566 |
return false; |
||
567 |
} |
||
568 |
|||
569 |
60 |
bool result = false; |
|
570 |
#ifdef OPENSSL_API_INTERFACE_V11 |
||
571 |
EVP_MD_CTX *ctx_ptr = EVP_MD_CTX_new(); |
||
572 |
#else |
||
573 |
EVP_MD_CTX ctx; |
||
574 |
60 |
EVP_MD_CTX_init(&ctx); |
|
575 |
60 |
EVP_MD_CTX *ctx_ptr = &ctx; |
|
576 |
#endif |
||
577 |
|||
578 |
*signature = reinterpret_cast<unsigned char *>( |
||
579 |
60 |
smalloc(EVP_PKEY_size(private_key_))); |
|
580 |
✓✗✓✗ ✓✗✓✗ |
60 |
if (EVP_SignInit(ctx_ptr, EVP_sha1()) && |
581 |
EVP_SignUpdate(ctx_ptr, buffer, buffer_size) && |
||
582 |
EVP_SignFinal(ctx_ptr, *signature, signature_size, private_key_)) |
||
583 |
{ |
||
584 |
60 |
result = true; |
|
585 |
} |
||
586 |
#ifdef OPENSSL_API_INTERFACE_V11 |
||
587 |
EVP_MD_CTX_free(ctx_ptr); |
||
588 |
#else |
||
589 |
60 |
EVP_MD_CTX_cleanup(&ctx); |
|
590 |
#endif |
||
591 |
✗✓ | 60 |
if (!result) { |
592 |
free(*signature); |
||
593 |
*signature_size = 0; |
||
594 |
*signature = NULL; |
||
595 |
} |
||
596 |
|||
597 |
60 |
return result; |
|
598 |
} |
||
599 |
|||
600 |
|||
601 |
/** |
||
602 |
* Veryfies a signature against loaded certificate. |
||
603 |
* |
||
604 |
* \return True if signature is valid, false on error or otherwise |
||
605 |
*/ |
||
606 |
52 |
bool SignatureManager::Verify(const unsigned char *buffer, |
|
607 |
const unsigned buffer_size, |
||
608 |
const unsigned char *signature, |
||
609 |
const unsigned signature_size) |
||
610 |
{ |
||
611 |
✗✓ | 52 |
if (!certificate_) return false; |
612 |
|||
613 |
52 |
bool result = false; |
|
614 |
#ifdef OPENSSL_API_INTERFACE_V11 |
||
615 |
EVP_MD_CTX *ctx_ptr = EVP_MD_CTX_new(); |
||
616 |
#else |
||
617 |
EVP_MD_CTX ctx; |
||
618 |
52 |
EVP_MD_CTX_init(&ctx); |
|
619 |
52 |
EVP_MD_CTX *ctx_ptr = &ctx; |
|
620 |
#endif |
||
621 |
|||
622 |
52 |
EVP_PKEY *pubkey = X509_get_pubkey(certificate_); |
|
623 |
✓✗✓✗ ✓✗✓✗ |
52 |
if (EVP_VerifyInit(ctx_ptr, EVP_sha1()) && |
624 |
EVP_VerifyUpdate(ctx_ptr, buffer, buffer_size) && |
||
625 |
#ifdef OPENSSL_API_INTERFACE_V09 |
||
626 |
EVP_VerifyFinal(ctx_ptr, |
||
627 |
const_cast<unsigned char *>(signature), signature_size, |
||
628 |
pubkey) |
||
629 |
#else |
||
630 |
EVP_VerifyFinal(ctx_ptr, signature, signature_size, pubkey) |
||
631 |
#endif |
||
632 |
) |
||
633 |
{ |
||
634 |
52 |
result = true; |
|
635 |
} |
||
636 |
✓✗ | 52 |
if (pubkey != NULL) |
637 |
52 |
EVP_PKEY_free(pubkey); |
|
638 |
#ifdef OPENSSL_API_INTERFACE_V11 |
||
639 |
EVP_MD_CTX_free(ctx_ptr); |
||
640 |
#else |
||
641 |
52 |
EVP_MD_CTX_cleanup(&ctx); |
|
642 |
#endif |
||
643 |
|||
644 |
52 |
return result; |
|
645 |
} |
||
646 |
|||
647 |
|||
648 |
/** |
||
649 |
* Veryfies a signature against all loaded public keys. |
||
650 |
* |
||
651 |
* \return True if signature is valid with any public key, false on error or otherwise |
||
652 |
*/ |
||
653 |
32 |
bool SignatureManager::VerifyRsa(const unsigned char *buffer, |
|
654 |
const unsigned buffer_size, |
||
655 |
const unsigned char *signature, |
||
656 |
const unsigned signature_size) |
||
657 |
{ |
||
658 |
✓✗ | 32 |
for (unsigned i = 0, s = public_keys_.size(); i < s; ++i) { |
659 |
✗✓ | 32 |
if (buffer_size > (unsigned)RSA_size(public_keys_[i])) |
660 |
continue; |
||
661 |
|||
662 |
32 |
unsigned char *to = (unsigned char *)smalloc(RSA_size(public_keys_[i])); |
|
663 |
32 |
unsigned char *from = (unsigned char *)smalloc(signature_size); |
|
664 |
32 |
memcpy(from, signature, signature_size); |
|
665 |
|||
666 |
int size = RSA_public_decrypt(signature_size, from, to, |
||
667 |
32 |
public_keys_[i], RSA_PKCS1_PADDING); |
|
668 |
32 |
free(from); |
|
669 |
✓✗✓✗ ✓✗ |
32 |
if ((size >= 0) && (unsigned(size) == buffer_size) && |
670 |
(memcmp(buffer, to, size) == 0)) |
||
671 |
{ |
||
672 |
32 |
free(to); |
|
673 |
32 |
return true; |
|
674 |
} |
||
675 |
|||
676 |
free(to); |
||
677 |
} |
||
678 |
|||
679 |
LogCvmfs(kLogSignature, kLogDebug, "VerifyRsa, no public key fits"); |
||
680 |
return false; |
||
681 |
} |
||
682 |
|||
683 |
|||
684 |
/** |
||
685 |
* Strips a signature from the letter (if exists) |
||
686 |
*/ |
||
687 |
64 |
void SignatureManager::CutLetter(const unsigned char *buffer, |
|
688 |
const unsigned buffer_size, |
||
689 |
const char separator, |
||
690 |
unsigned *letter_length, |
||
691 |
unsigned *pos_after_mark) |
||
692 |
{ |
||
693 |
64 |
unsigned pos = 0; |
|
694 |
64 |
*letter_length = *pos_after_mark = 0; |
|
695 |
9872 |
do { |
|
696 |
✗✓ | 9936 |
if (pos == buffer_size) { |
697 |
*pos_after_mark = pos; // Careful: pos_after_mark points out of buffer |
||
698 |
*letter_length = pos; |
||
699 |
break; |
||
700 |
} |
||
701 |
|||
702 |
✓✓✓✗ ✓✓✓✗ ✓✗ |
10537 |
if ((buffer[pos] == '\n') && (pos+4 <= buffer_size) && |
703 |
537 |
(buffer[pos+1] == separator) && (buffer[pos+2] == separator) && |
|
704 |
64 |
(buffer[pos+3] == '\n')) |
|
705 |
{ |
||
706 |
64 |
*letter_length = pos+1; |
|
707 |
64 |
pos += 4; |
|
708 |
64 |
break; |
|
709 |
} |
||
710 |
9872 |
pos++; |
|
711 |
} while (true); |
||
712 |
64 |
*pos_after_mark = pos; |
|
713 |
64 |
} |
|
714 |
|||
715 |
|||
716 |
/** |
||
717 |
* Checks a document of the form |
||
718 |
* <ASCII LINES> |
||
719 |
* -- |
||
720 |
* <hash> |
||
721 |
* <signature> |
||
722 |
*/ |
||
723 |
64 |
bool SignatureManager::VerifyLetter(const unsigned char *buffer, |
|
724 |
const unsigned buffer_size, |
||
725 |
const bool by_rsa) |
||
726 |
{ |
||
727 |
64 |
unsigned pos = 0; |
|
728 |
64 |
unsigned letter_length = 0; |
|
729 |
64 |
CutLetter(buffer, buffer_size, '-', &letter_length, &pos); |
|
730 |
✗✓ | 64 |
if (pos >= buffer_size) |
731 |
return false; |
||
732 |
|||
733 |
64 |
string hash_str = ""; |
|
734 |
64 |
unsigned hash_pos = pos; |
|
735 |
2560 |
do { |
|
736 |
✗✓ | 2624 |
if (pos == buffer_size) |
737 |
return false; |
||
738 |
✓✓ | 2624 |
if (buffer[pos] == '\n') { |
739 |
64 |
pos++; |
|
740 |
64 |
break; |
|
741 |
} |
||
742 |
2560 |
hash_str.push_back(buffer[pos++]); |
|
743 |
} while (true); |
||
744 |
64 |
shash::Any hash_printed = shash::MkFromHexPtr(shash::HexPtr(hash_str)); |
|
745 |
64 |
shash::Any hash_computed(hash_printed.algorithm); |
|
746 |
64 |
shash::HashMem(buffer, letter_length, &hash_computed); |
|
747 |
✗✓ | 64 |
if (hash_printed != hash_computed) |
748 |
return false; |
||
749 |
|||
750 |
✓✓ | 64 |
if (by_rsa) { |
751 |
return VerifyRsa(&buffer[hash_pos], hash_str.length(), |
||
752 |
32 |
&buffer[pos], buffer_size-pos); |
|
753 |
} else { |
||
754 |
return Verify(&buffer[hash_pos], hash_str.length(), |
||
755 |
32 |
&buffer[pos], buffer_size-pos); |
|
756 |
} |
||
757 |
} |
||
758 |
|||
759 |
|||
760 |
/** |
||
761 |
* Verifies a PKCS#7 binary content + signature structure |
||
762 |
* using the loaded trusted CAs/CRLs |
||
763 |
*/ |
||
764 |
bool SignatureManager::VerifyPkcs7(const unsigned char *buffer, |
||
765 |
const unsigned buffer_size, |
||
766 |
unsigned char **content, |
||
767 |
unsigned *content_size, |
||
768 |
vector<string> *alt_uris) |
||
769 |
{ |
||
770 |
*content = NULL; |
||
771 |
*content_size = 0; |
||
772 |
|||
773 |
BIO *bp_pkcs7 = BIO_new(BIO_s_mem()); |
||
774 |
if (!bp_pkcs7) return false; |
||
775 |
if (BIO_write(bp_pkcs7, buffer, buffer_size) <= 0) { |
||
776 |
BIO_free(bp_pkcs7); |
||
777 |
return false; |
||
778 |
} |
||
779 |
|||
780 |
PKCS7 *pkcs7 = NULL; |
||
781 |
pkcs7 = PEM_read_bio_PKCS7(bp_pkcs7, NULL, NULL, NULL); |
||
782 |
BIO_free(bp_pkcs7); |
||
783 |
if (!pkcs7) { |
||
784 |
LogCvmfs(kLogSignature, kLogDebug, "invalid pkcs#7 signature"); |
||
785 |
return false; |
||
786 |
} |
||
787 |
|||
788 |
BIO *bp_content = BIO_new(BIO_s_mem()); |
||
789 |
if (!bp_content) { |
||
790 |
PKCS7_free(pkcs7); |
||
791 |
return false; |
||
792 |
} |
||
793 |
|||
794 |
int flags = 0; |
||
795 |
STACK_OF(X509) *extra_signers = NULL; |
||
796 |
BIO *indata = NULL; |
||
797 |
bool result = PKCS7_verify(pkcs7, extra_signers, x509_store_, indata, |
||
798 |
bp_content, flags); |
||
799 |
if (result != 1) { |
||
800 |
BIO_free(bp_content); |
||
801 |
PKCS7_free(pkcs7); |
||
802 |
return false; |
||
803 |
} |
||
804 |
|||
805 |
BUF_MEM *bufmem_content; |
||
806 |
BIO_get_mem_ptr(bp_content, &bufmem_content); |
||
807 |
// BIO_free() leaves BUF_MEM alone |
||
808 |
(void) BIO_set_close(bp_content, BIO_NOCLOSE); |
||
809 |
BIO_free(bp_content); |
||
810 |
*content = reinterpret_cast<unsigned char *>(bufmem_content->data); |
||
811 |
*content_size = bufmem_content->length; |
||
812 |
free(bufmem_content); |
||
813 |
if (*content == NULL) { |
||
814 |
PKCS7_free(pkcs7); |
||
815 |
LogCvmfs(kLogSignature, kLogDebug, "empty pkcs#7 structure"); |
||
816 |
return false; |
||
817 |
} |
||
818 |
|||
819 |
// Extract signing certificates |
||
820 |
STACK_OF(X509) *signers = NULL; |
||
821 |
signers = PKCS7_get0_signers(pkcs7, NULL, 0); |
||
822 |
assert(signers); |
||
823 |
|||
824 |
// Extract alternative names |
||
825 |
for (int i = 0; i < sk_X509_num(signers); ++i) { |
||
826 |
X509* this_signer = sk_X509_value(signers, i); |
||
827 |
GENERAL_NAMES *subject_alt_names = NULL; |
||
828 |
subject_alt_names = reinterpret_cast<GENERAL_NAMES *>( |
||
829 |
X509_get_ext_d2i(this_signer, NID_subject_alt_name, NULL, NULL)); |
||
830 |
if (subject_alt_names != NULL) { |
||
831 |
for (int j = 0; j < sk_GENERAL_NAME_num(subject_alt_names); ++j) { |
||
832 |
GENERAL_NAME *this_name = sk_GENERAL_NAME_value(subject_alt_names, j); |
||
833 |
if (this_name->type != GEN_URI) |
||
834 |
continue; |
||
835 |
|||
836 |
const char *name_ptr = reinterpret_cast<const char *>( |
||
837 |
#ifdef OPENSSL_API_INTERFACE_V11 |
||
838 |
ASN1_STRING_get0_data(this_name->d.uniformResourceIdentifier)); |
||
839 |
#else |
||
840 |
ASN1_STRING_data(this_name->d.uniformResourceIdentifier)); |
||
841 |
#endif |
||
842 |
int name_len = |
||
843 |
ASN1_STRING_length(this_name->d.uniformResourceIdentifier); |
||
844 |
if (!name_ptr || (name_len <= 0)) |
||
845 |
continue; |
||
846 |
alt_uris->push_back(string(name_ptr, name_len)); |
||
847 |
} |
||
848 |
} |
||
849 |
} |
||
850 |
sk_X509_free(signers); |
||
851 |
PKCS7_free(pkcs7); |
||
852 |
return true; |
||
853 |
} |
||
854 |
|||
855 |
} // namespace signature |
Generated by: GCOVR (Version 4.1) |