]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
bbc27441 AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
f7f3304a | 9 | #include "squid.h" |
95d2589c | 10 | #include "ssl/gadgets.h" |
cb4f4424 | 11 | |
5107d2c4 | 12 | #include <openssl/asn1.h> |
a594dbfa CT |
13 | #if HAVE_OPENSSL_X509V3_H |
14 | #include <openssl/x509v3.h> | |
15 | #endif | |
95d2589c | 16 | |
95d2589c CT |
17 | EVP_PKEY * Ssl::createSslPrivateKey() |
18 | { | |
19 | Ssl::EVP_PKEY_Pointer pkey(EVP_PKEY_new()); | |
20 | ||
21 | if (!pkey) | |
22 | return NULL; | |
23 | ||
2a268a06 CT |
24 | BIGNUM_Pointer bn(BN_new()); |
25 | if (!bn) | |
26 | return NULL; | |
27 | ||
28 | if (!BN_set_word(bn.get(), RSA_F4)) | |
29 | return NULL; | |
30 | ||
31 | Ssl::RSA_Pointer rsa(RSA_new()); | |
32 | if (!rsa) | |
33 | return NULL; | |
ac756c8c | 34 | |
2a268a06 CT |
35 | int num = 2048; // Maybe use 4096 RSA keys, or better make it configurable? |
36 | if (!RSA_generate_key_ex(rsa.get(), num, bn.get(), NULL)) | |
37 | return NULL; | |
95d2589c CT |
38 | |
39 | if (!rsa) | |
40 | return NULL; | |
41 | ||
42 | if (!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get()))) | |
43 | return NULL; | |
44 | ||
45 | rsa.release(); | |
46 | return pkey.release(); | |
47 | } | |
48 | ||
95d2589c CT |
49 | /** |
50 | \ingroup ServerProtocolSSLInternal | |
51 | * Set serial random serial number or set random serial number. | |
52 | */ | |
53 | static bool setSerialNumber(ASN1_INTEGER *ai, BIGNUM const* serial) | |
54 | { | |
55 | if (!ai) | |
56 | return false; | |
57 | Ssl::BIGNUM_Pointer bn(BN_new()); | |
58 | if (serial) { | |
59 | bn.reset(BN_dup(serial)); | |
60 | } else { | |
61 | if (!bn) | |
62 | return false; | |
63 | ||
64 | if (!BN_pseudo_rand(bn.get(), 64, 0, 0)) | |
65 | return false; | |
66 | } | |
67 | ||
68 | if (ai && !BN_to_ASN1_INTEGER(bn.get(), ai)) | |
69 | return false; | |
70 | return true; | |
71 | } | |
72 | ||
f97700a0 | 73 | bool Ssl::writeCertAndPrivateKeyToMemory(Security::CertPointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite) |
95d2589c CT |
74 | { |
75 | bufferToWrite.clear(); | |
76 | if (!pkey || !cert) | |
77 | return false; | |
78 | BIO_Pointer bio(BIO_new(BIO_s_mem())); | |
79 | if (!bio) | |
80 | return false; | |
81 | ||
82 | if (!PEM_write_bio_X509 (bio.get(), cert.get())) | |
83 | return false; | |
84 | ||
85 | if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL)) | |
86 | return false; | |
87 | ||
88 | char *ptr = NULL; | |
89 | long len = BIO_get_mem_data(bio.get(), &ptr); | |
90 | if (!ptr) | |
91 | return false; | |
92 | ||
93 | bufferToWrite = std::string(ptr, len); | |
94 | return true; | |
95 | } | |
96 | ||
f97700a0 | 97 | bool Ssl::appendCertToMemory(Security::CertPointer const & cert, std::string & bufferToWrite) |
9a90aace CT |
98 | { |
99 | if (!cert) | |
100 | return false; | |
101 | ||
102 | BIO_Pointer bio(BIO_new(BIO_s_mem())); | |
103 | if (!bio) | |
104 | return false; | |
105 | ||
106 | if (!PEM_write_bio_X509 (bio.get(), cert.get())) | |
107 | return false; | |
108 | ||
109 | char *ptr = NULL; | |
110 | long len = BIO_get_mem_data(bio.get(), &ptr); | |
111 | if (!ptr) | |
112 | return false; | |
113 | ||
87f237a9 | 114 | if (!bufferToWrite.empty()) |
9a90aace CT |
115 | bufferToWrite.append(" "); // add a space... |
116 | ||
117 | bufferToWrite.append(ptr, len); | |
118 | return true; | |
119 | } | |
120 | ||
f97700a0 | 121 | bool Ssl::readCertAndPrivateKeyFromMemory(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * bufferToRead) |
95d2589c CT |
122 | { |
123 | Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem())); | |
124 | BIO_puts(bio.get(), bufferToRead); | |
125 | ||
126 | X509 * certPtr = NULL; | |
35b3559c | 127 | cert.resetWithoutLocking(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0)); |
95d2589c CT |
128 | if (!cert) |
129 | return false; | |
130 | ||
131 | EVP_PKEY * pkeyPtr = NULL; | |
35b3559c | 132 | pkey.resetWithoutLocking(PEM_read_bio_PrivateKey(bio.get(), &pkeyPtr, 0, 0)); |
95d2589c CT |
133 | if (!pkey) |
134 | return false; | |
135 | ||
136 | return true; | |
137 | } | |
138 | ||
f97700a0 | 139 | bool Ssl::readCertFromMemory(Security::CertPointer & cert, char const * bufferToRead) |
9a90aace CT |
140 | { |
141 | Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem())); | |
142 | BIO_puts(bio.get(), bufferToRead); | |
143 | ||
144 | X509 * certPtr = NULL; | |
35b3559c | 145 | cert.resetWithoutLocking(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0)); |
9a90aace CT |
146 | if (!cert) |
147 | return false; | |
148 | ||
149 | return true; | |
150 | } | |
151 | ||
bf94620c CT |
152 | // According to RFC 5280 (Section A.1), the common name length in a certificate |
153 | // can be at most 64 characters | |
154 | static const size_t MaxCnLen = 64; | |
155 | ||
fb2178bb | 156 | // Replace certs common name with the given |
f97700a0 | 157 | static bool replaceCommonName(Security::CertPointer & cert, std::string const &rawCn) |
fb2178bb | 158 | { |
4a962df3 AR |
159 | std::string cn = rawCn; |
160 | ||
bf94620c CT |
161 | if (cn.length() > MaxCnLen) { |
162 | // In the case the length od CN is more than the maximum supported size | |
163 | // try to use the first upper level domain. | |
164 | size_t pos = 0; | |
165 | do { | |
166 | pos = cn.find('.', pos + 1); | |
87f237a9 | 167 | } while (pos != std::string::npos && (cn.length() - pos + 2) > MaxCnLen); |
bf94620c CT |
168 | |
169 | // If no short domain found or this domain is a toplevel domain | |
170 | // we failed to find a good cn name. | |
171 | if (pos == std::string::npos || cn.find('.', pos + 1) == std::string::npos) | |
172 | return false; | |
173 | ||
4a962df3 | 174 | std::string fixedCn(1, '*'); |
bf94620c | 175 | fixedCn.append(cn.c_str() + pos); |
4a962df3 | 176 | cn = fixedCn; |
bf94620c CT |
177 | } |
178 | ||
4a962df3 AR |
179 | // Assume [] surround an IPv6 address and strip them because browsers such |
180 | // as Firefox, Chromium, and Safari prefer bare IPv6 addresses in CNs. | |
181 | if (cn.length() > 2 && *cn.begin() == '[' && *cn.rbegin() == ']') | |
182 | cn = cn.substr(1, cn.size()-2); | |
183 | ||
fb2178bb CT |
184 | X509_NAME *name = X509_get_subject_name(cert.get()); |
185 | if (!name) | |
186 | return false; | |
187 | // Remove the CN part: | |
188 | int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1); | |
aebe6888 CT |
189 | if (loc >=0) { |
190 | X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc); | |
191 | X509_NAME_delete_entry(name, loc); | |
192 | X509_NAME_ENTRY_free(tmp); | |
193 | } | |
fb2178bb CT |
194 | |
195 | // Add a new CN | |
196 | return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC, | |
4a962df3 | 197 | (unsigned char *)(cn.c_str()), -1, -1, 0); |
fb2178bb CT |
198 | } |
199 | ||
aebe6888 CT |
200 | const char *Ssl::CertSignAlgorithmStr[] = { |
201 | "signTrusted", | |
87f237a9 | 202 | "signUntrusted", |
aebe6888 CT |
203 | "signSelf", |
204 | NULL | |
205 | }; | |
206 | ||
fb2178bb CT |
207 | const char *Ssl::CertAdaptAlgorithmStr[] = { |
208 | "setValidAfter", | |
209 | "setValidBefore", | |
210 | "setCommonName", | |
211 | NULL | |
212 | }; | |
213 | ||
a0b971d5 | 214 | Ssl::CertificateProperties::CertificateProperties(): |
f53969cc SM |
215 | setValidAfter(false), |
216 | setValidBefore(false), | |
217 | setCommonName(false), | |
218 | signAlgorithm(Ssl::algSignEnd), | |
219 | signHash(NULL) | |
aebe6888 CT |
220 | {} |
221 | ||
5107d2c4 CT |
222 | static void |
223 | printX509Signature(const Security::CertPointer &cert, std::string &out) | |
224 | { | |
70cfe22f | 225 | const ASN1_BIT_STRING *sig = Ssl::X509_get_signature(cert); |
5107d2c4 CT |
226 | if (sig && sig->data) { |
227 | const unsigned char *s = sig->data; | |
228 | for (int i = 0; i < sig->length; ++i) { | |
229 | char hex[3]; | |
230 | snprintf(hex, sizeof(hex), "%02x", s[i]); | |
231 | out.append(hex); | |
232 | } | |
233 | } | |
234 | } | |
235 | ||
236 | std::string & | |
237 | Ssl::OnDiskCertificateDbKey(const Ssl::CertificateProperties &properties) | |
06997a38 CT |
238 | { |
239 | static std::string certKey; | |
7fc31d31 | 240 | certKey.clear(); |
06997a38 | 241 | certKey.reserve(4096); |
5107d2c4 CT |
242 | if (properties.mimicCert.get()) |
243 | printX509Signature(properties.mimicCert, certKey); | |
06997a38 | 244 | |
7fc31d31 AR |
245 | if (certKey.empty()) { |
246 | certKey.append("/CN=", 4); | |
5107d2c4 | 247 | certKey.append(properties.commonName); |
7fc31d31 | 248 | } |
06997a38 | 249 | |
5107d2c4 | 250 | if (properties.setValidAfter) |
06997a38 CT |
251 | certKey.append("+SetValidAfter=on", 17); |
252 | ||
5107d2c4 | 253 | if (properties.setValidBefore) |
06997a38 CT |
254 | certKey.append("+SetValidBefore=on", 18); |
255 | ||
5107d2c4 | 256 | if (properties.setCommonName) { |
06997a38 | 257 | certKey.append("+SetCommonName=", 15); |
5107d2c4 | 258 | certKey.append(properties.commonName); |
06997a38 CT |
259 | } |
260 | ||
5107d2c4 | 261 | if (properties.signAlgorithm != Ssl::algSignEnd) { |
06997a38 | 262 | certKey.append("+Sign=", 6); |
5107d2c4 | 263 | certKey.append(certSignAlgorithm(properties.signAlgorithm)); |
06997a38 CT |
264 | } |
265 | ||
5107d2c4 | 266 | if (properties.signHash != NULL) { |
3c26b00a | 267 | certKey.append("+SignHash=", 10); |
5107d2c4 | 268 | certKey.append(EVP_MD_name(properties.signHash)); |
3c26b00a CT |
269 | } |
270 | ||
06997a38 CT |
271 | return certKey; |
272 | } | |
273 | ||
5f1318b1 CT |
274 | /// Check if mimicCert certificate has the Authority Key Identifier extension |
275 | /// and if yes add the extension to cert certificate with the same fields if | |
5ff0af8e | 276 | /// possible. If the issuerCert certificate does not have the Subject Key |
5f1318b1 CT |
277 | /// Identifier extension (required to build the keyIdentifier field of |
278 | /// AuthorityKeyIdentifier) then the authorityCertIssuer and | |
279 | /// authorityCertSerialNumber fields added. | |
280 | static bool | |
281 | mimicAuthorityKeyId(Security::CertPointer &cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert) | |
282 | { | |
283 | if (!mimicCert.get() || !issuerCert.get()) | |
284 | return false; | |
285 | ||
286 | Ssl::AUTHORITY_KEYID_Pointer akid((AUTHORITY_KEYID *)X509_get_ext_d2i(mimicCert.get(), NID_authority_key_identifier, nullptr, nullptr)); | |
287 | ||
288 | bool addKeyId = false, addIssuer = false; | |
289 | if (akid.get()) { | |
290 | addKeyId = (akid.get()->keyid != nullptr); | |
291 | addIssuer = (akid.get()->issuer && akid.get()->serial); | |
292 | } | |
293 | ||
294 | if (!addKeyId && !addIssuer) | |
295 | return false; // No need to add AuthorityKeyIdentifier | |
296 | ||
297 | Ssl::ASN1_OCTET_STRING_Pointer issuerKeyId; | |
298 | if (addKeyId) { | |
299 | X509_EXTENSION *ext; | |
300 | // Check if the issuer has the Subject Key Identifier extension | |
301 | const int indx = X509_get_ext_by_NID(issuerCert.get(), NID_subject_key_identifier, -1); | |
302 | if (indx >= 0 && (ext = X509_get_ext(issuerCert.get(), indx))) { | |
303 | issuerKeyId.reset((ASN1_OCTET_STRING *)X509V3_EXT_d2i(ext)); | |
304 | } | |
305 | } | |
306 | ||
307 | Ssl::X509_NAME_Pointer issuerName; | |
308 | Ssl::ASN1_INT_Pointer issuerSerial; | |
309 | if (issuerKeyId.get() == nullptr || addIssuer) { | |
310 | issuerName.reset(X509_NAME_dup(X509_get_issuer_name(issuerCert.get()))); | |
2a268a06 | 311 | issuerSerial.reset(ASN1_INTEGER_dup(X509_get_serialNumber(issuerCert.get()))); |
5f1318b1 CT |
312 | } |
313 | ||
314 | Ssl::AUTHORITY_KEYID_Pointer theAuthKeyId(AUTHORITY_KEYID_new()); | |
315 | if (!theAuthKeyId.get()) | |
316 | return false; | |
317 | theAuthKeyId.get()->keyid = issuerKeyId.release(); | |
318 | if (issuerName && issuerSerial) { | |
319 | Ssl::GENERAL_NAME_STACK_Pointer genNames(sk_GENERAL_NAME_new_null()); | |
320 | if (genNames.get()) { | |
321 | if (GENERAL_NAME *aname = GENERAL_NAME_new()) { | |
322 | sk_GENERAL_NAME_push(genNames.get(), aname); | |
323 | aname->type = GEN_DIRNAME; | |
324 | aname->d.dirn = issuerName.release(); | |
325 | theAuthKeyId.get()->issuer = genNames.release(); | |
326 | theAuthKeyId.get()->serial = issuerSerial.release(); | |
327 | } | |
328 | } | |
329 | } | |
330 | ||
331 | // The Authority Key Identifier extension should include KeyId or/and both | |
332 | /// issuer name and issuer serial | |
333 | if (!theAuthKeyId.get()->keyid && (!theAuthKeyId.get()->issuer || !theAuthKeyId.get()->serial)) | |
334 | return false; | |
335 | ||
336 | const X509V3_EXT_METHOD *method = X509V3_EXT_get_nid(NID_authority_key_identifier); | |
337 | if (!method) | |
338 | return false; | |
339 | ||
340 | unsigned char *ext_der = NULL; | |
341 | int ext_len = ASN1_item_i2d((ASN1_VALUE *)theAuthKeyId.get(), &ext_der, ASN1_ITEM_ptr(method->it)); | |
2a268a06 | 342 | Ssl::ASN1_OCTET_STRING_Pointer extOct(ASN1_OCTET_STRING_new()); |
5f1318b1 CT |
343 | extOct.get()->data = ext_der; |
344 | extOct.get()->length = ext_len; | |
345 | Ssl::X509_EXTENSION_Pointer extAuthKeyId(X509_EXTENSION_create_by_NID(NULL, NID_authority_key_identifier, 0, extOct.get())); | |
346 | if (!extAuthKeyId.get()) | |
347 | return false; | |
348 | ||
349 | extOct.release(); | |
350 | if (!X509_add_ext(cert.get(), extAuthKeyId.get(), -1)) | |
351 | return false; | |
352 | ||
353 | return true; | |
354 | } | |
355 | ||
5c80eab2 CT |
356 | /// Copy certificate extensions from cert to mimicCert. |
357 | /// Returns the number of extensions copied. | |
bee6354e CT |
358 | // Currently only extensions which are reported by the users that required are |
359 | // mimicked. More safe to mimic extensions would be added here if users request | |
360 | // them. | |
5c80eab2 | 361 | static int |
5f1318b1 | 362 | mimicExtensions(Security::CertPointer & cert, Security::CertPointer const &mimicCert, Security::CertPointer const &issuerCert) |
bee6354e CT |
363 | { |
364 | static int extensions[]= { | |
365 | NID_key_usage, | |
dcc9214e | 366 | NID_ext_key_usage, |
bee6354e CT |
367 | NID_basic_constraints, |
368 | 0 | |
369 | }; | |
370 | ||
789bf810 CT |
371 | // key usage bit names |
372 | enum { | |
373 | DigitalSignature, | |
374 | NonRepudiation, | |
375 | KeyEncipherment, // NSS requires for RSA but not EC | |
376 | DataEncipherment, | |
377 | KeyAgreement, | |
378 | KeyCertificateSign, | |
379 | CRLSign, | |
380 | EncipherOnly, | |
381 | DecipherOnly | |
382 | }; | |
383 | ||
17e98f24 AJ |
384 | #if HAVE_LIBCRYPTO_EVP_PKEY_GET0_RSA |
385 | EVP_PKEY *certKey = X509_get_pubkey(mimicCert.get()); | |
386 | const bool rsaPkey = (EVP_PKEY_get0_RSA(certKey) != nullptr); | |
387 | #else | |
2a268a06 CT |
388 | const int mimicAlgo = OBJ_obj2nid(mimicCert.get()->cert_info->key->algor->algorithm); |
389 | const bool rsaPkey = (mimicAlgo == NID_rsaEncryption); | |
2a268a06 CT |
390 | #endif |
391 | ||
5c80eab2 | 392 | int added = 0; |
bee6354e CT |
393 | int nid; |
394 | for (int i = 0; (nid = extensions[i]) != 0; ++i) { | |
395 | const int pos = X509_get_ext_by_NID(mimicCert.get(), nid, -1); | |
789bf810 CT |
396 | if (X509_EXTENSION *ext = X509_get_ext(mimicCert.get(), pos)) { |
397 | // Mimic extension exactly. | |
5c80eab2 CT |
398 | if (X509_add_ext(cert.get(), ext, -1)) |
399 | ++added; | |
2a268a06 | 400 | if (nid == NID_key_usage && !rsaPkey) { |
789bf810 CT |
401 | // NSS does not requre the KeyEncipherment flag on EC keys |
402 | // but it does require it for RSA keys. Since ssl-bump | |
403 | // substitutes RSA keys for EC ones, we need to ensure that | |
404 | // that the more stringent requirements are met. | |
405 | ||
406 | const int p = X509_get_ext_by_NID(cert.get(), NID_key_usage, -1); | |
407 | if ((ext = X509_get_ext(cert.get(), p)) != NULL) { | |
408 | ASN1_BIT_STRING *keyusage = (ASN1_BIT_STRING *)X509V3_EXT_d2i(ext); | |
409 | ASN1_BIT_STRING_set_bit(keyusage, KeyEncipherment, 1); | |
e061c75e CT |
410 | |
411 | //Build the ASN1_OCTET_STRING | |
412 | const X509V3_EXT_METHOD *method = X509V3_EXT_get(ext); | |
413 | assert(method && method->it); | |
414 | unsigned char *ext_der = NULL; | |
415 | int ext_len = ASN1_item_i2d((ASN1_VALUE *)keyusage, | |
f53969cc | 416 | &ext_der, |
e061c75e CT |
417 | (const ASN1_ITEM *)ASN1_ITEM_ptr(method->it)); |
418 | ||
2a268a06 | 419 | ASN1_OCTET_STRING *ext_oct = ASN1_OCTET_STRING_new(); |
e061c75e CT |
420 | ext_oct->data = ext_der; |
421 | ext_oct->length = ext_len; | |
422 | X509_EXTENSION_set_data(ext, ext_oct); | |
423 | ||
2a268a06 | 424 | ASN1_OCTET_STRING_free(ext_oct); |
789bf810 CT |
425 | ASN1_BIT_STRING_free(keyusage); |
426 | } | |
427 | } | |
428 | } | |
bee6354e CT |
429 | } |
430 | ||
5f1318b1 CT |
431 | if (mimicAuthorityKeyId(cert, mimicCert, issuerCert)) |
432 | ++added; | |
433 | ||
bee6354e CT |
434 | // We could also restrict mimicking of the CA extension to CA:FALSE |
435 | // because Squid does not generate valid fake CA certificates. | |
5c80eab2 CT |
436 | |
437 | return added; | |
bee6354e CT |
438 | } |
439 | ||
add2db46 CT |
440 | /// Adds a new subjectAltName extension contining Subject CN or returns false |
441 | /// expects the caller to check for the existing subjectAltName extension | |
442 | static bool | |
443 | addAltNameWithSubjectCn(Security::CertPointer &cert) | |
444 | { | |
445 | X509_NAME *name = X509_get_subject_name(cert.get()); | |
446 | if (!name) | |
447 | return false; | |
448 | ||
449 | const int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1); | |
450 | if (loc < 0) | |
451 | return false; | |
452 | ||
453 | ASN1_STRING *cn_data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(name, loc)); | |
454 | if (!cn_data) | |
455 | return false; | |
456 | ||
457 | char dnsName[1024]; // DNS names are limited to 256 characters | |
458 | const int res = snprintf(dnsName, sizeof(dnsName), "DNS:%*s", cn_data->length, cn_data->data); | |
459 | if (res <= 0 || res >= static_cast<int>(sizeof(dnsName))) | |
460 | return false; | |
461 | ||
462 | X509_EXTENSION *ext = X509V3_EXT_conf_nid(NULL, NULL, NID_subject_alt_name, dnsName); | |
463 | if (!ext) | |
464 | return false; | |
465 | ||
466 | const bool result = X509_add_ext(cert.get(), ext, -1); | |
467 | ||
468 | X509_EXTENSION_free(ext); | |
469 | return result; | |
470 | } | |
471 | ||
f97700a0 | 472 | static bool buildCertificate(Security::CertPointer & cert, Ssl::CertificateProperties const &properties) |
87f237a9 | 473 | { |
9a90aace CT |
474 | // not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name() |
475 | // returns a pointer to the existing subject name. Nothing to clean here. | |
aebe6888 | 476 | if (properties.mimicCert.get()) { |
0d57adf9 CT |
477 | // Leave subject empty if we cannot extract it from true cert. |
478 | if (X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) { | |
87f237a9 | 479 | // X509_set_subject_name will call X509_dup for name |
5367d845 CT |
480 | X509_set_subject_name(cert.get(), name); |
481 | } | |
aebe6888 CT |
482 | } |
483 | ||
484 | if (properties.setCommonName || !properties.mimicCert.get()) { | |
485 | // In this case the CN of the certificate given by the user | |
bf94620c | 486 | // Ignore errors: it is better to make a certificate with no CN |
051da40c | 487 | // than to quit ssl-crtd helper because we cannot make a certificate. |
bf94620c CT |
488 | // Most errors are caused by user input such as huge domain names. |
489 | (void)replaceCommonName(cert, properties.commonName); | |
fb2178bb | 490 | } |
9a90aace | 491 | |
87f237a9 | 492 | // We should get caCert notBefore and notAfter fields and do not allow |
9a90aace CT |
493 | // notBefore/notAfter values from certToMimic before/after notBefore/notAfter |
494 | // fields from caCert. | |
87f237a9 | 495 | // Currently there is not any way in openssl tollkit to compare two ASN1_TIME |
9a90aace | 496 | // objects. |
fb2178bb | 497 | ASN1_TIME *aTime = NULL; |
aebe6888 CT |
498 | if (!properties.setValidBefore && properties.mimicCert.get()) |
499 | aTime = X509_get_notBefore(properties.mimicCert.get()); | |
500 | if (!aTime && properties.signWithX509.get()) | |
501 | aTime = X509_get_notBefore(properties.signWithX509.get()); | |
fb2178bb CT |
502 | |
503 | if (aTime) { | |
9a90aace CT |
504 | if (!X509_set_notBefore(cert.get(), aTime)) |
505 | return false; | |
87f237a9 | 506 | } else if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60)) |
9a90aace CT |
507 | return false; |
508 | ||
fb2178bb | 509 | aTime = NULL; |
aebe6888 CT |
510 | if (!properties.setValidAfter && properties.mimicCert.get()) |
511 | aTime = X509_get_notAfter(properties.mimicCert.get()); | |
512 | if (!aTime && properties.signWithX509.get()) | |
513 | aTime = X509_get_notAfter(properties.signWithX509.get()); | |
fb2178bb | 514 | if (aTime) { |
9a90aace | 515 | if (!X509_set_notAfter(cert.get(), aTime)) |
b0137cd1 | 516 | return false; |
9a90aace | 517 | } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3)) |
b0137cd1 | 518 | return false; |
9a90aace | 519 | |
add2db46 CT |
520 | int addedExtensions = 0; |
521 | bool useCommonNameAsAltName = true; | |
ad88633d | 522 | // mimic the alias and possibly subjectAltName |
aebe6888 CT |
523 | if (properties.mimicCert.get()) { |
524 | unsigned char *alStr; | |
525 | int alLen; | |
526 | alStr = X509_alias_get0(properties.mimicCert.get(), &alLen); | |
527 | if (alStr) { | |
528 | X509_alias_set1(cert.get(), alStr, alLen); | |
529 | } | |
530 | ||
ad88633d AR |
531 | // Mimic subjectAltName unless we used a configured CN: browsers reject |
532 | // certificates with CN unrelated to subjectAltNames. | |
3b7ed55f | 533 | if (!properties.setCommonName) { |
add2db46 | 534 | int pos = X509_get_ext_by_NID(properties.mimicCert.get(), NID_subject_alt_name, -1); |
87f237a9 | 535 | X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos); |
4a874fbd | 536 | if (ext) { |
5c80eab2 CT |
537 | if (X509_add_ext(cert.get(), ext, -1)) |
538 | ++addedExtensions; | |
4a874fbd | 539 | } |
add2db46 CT |
540 | // We want to mimic the server-sent subjectAltName, not enhance it. |
541 | useCommonNameAsAltName = false; | |
3b7ed55f | 542 | } |
bee6354e | 543 | |
5f1318b1 | 544 | addedExtensions += mimicExtensions(cert, properties.mimicCert, properties.signWithX509); |
aebe6888 | 545 | } |
9a90aace | 546 | |
add2db46 CT |
547 | if (useCommonNameAsAltName && addAltNameWithSubjectCn(cert)) |
548 | ++addedExtensions; | |
549 | ||
550 | // According to RFC 5280, using extensions requires v3 certificate. | |
551 | if (addedExtensions) | |
552 | X509_set_version(cert.get(), 2); // value 2 means v3 | |
553 | ||
9a90aace CT |
554 | return true; |
555 | } | |
556 | ||
f97700a0 | 557 | static bool generateFakeSslCertificate(Security::CertPointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial) |
9a90aace | 558 | { |
aebe6888 | 559 | Ssl::EVP_PKEY_Pointer pkey; |
95588170 CT |
560 | // Use signing certificates private key as generated certificate private key |
561 | if (properties.signWithPkey.get()) | |
562 | pkey.resetAndLock(properties.signWithPkey.get()); | |
563 | else // if not exist generate one | |
35b3559c | 564 | pkey.resetWithoutLocking(Ssl::createSslPrivateKey()); |
95588170 | 565 | |
9a90aace CT |
566 | if (!pkey) |
567 | return false; | |
568 | ||
f97700a0 | 569 | Security::CertPointer cert(X509_new()); |
9a90aace CT |
570 | if (!cert) |
571 | return false; | |
572 | ||
573 | // Set pub key and serial given by the caller | |
574 | if (!X509_set_pubkey(cert.get(), pkey.get())) | |
575 | return false; | |
a0b971d5 | 576 | if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial.get())) |
9a90aace CT |
577 | return false; |
578 | ||
aebe6888 CT |
579 | // Fill the certificate with the required properties |
580 | if (!buildCertificate(cert, properties)) | |
9a90aace CT |
581 | return false; |
582 | ||
aebe6888 | 583 | int ret = 0; |
9a90aace | 584 | // Set issuer name, from CA or our subject name for self signed cert |
aebe6888 CT |
585 | if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithX509.get()) |
586 | ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(properties.signWithX509.get())); | |
587 | else // Self signed certificate, set issuer to self | |
588 | ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(cert.get())); | |
589 | if (!ret) | |
9a90aace CT |
590 | return false; |
591 | ||
3c26b00a CT |
592 | const EVP_MD *hash = properties.signHash ? properties.signHash : EVP_get_digestbyname(SQUID_SSL_SIGN_HASH_IF_NONE); |
593 | assert(hash); | |
9a90aace | 594 | /*Now sign the request */ |
aebe6888 | 595 | if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithPkey.get()) |
3c26b00a | 596 | ret = X509_sign(cert.get(), properties.signWithPkey.get(), hash); |
9a90aace | 597 | else //else sign with self key (self signed request) |
3c26b00a | 598 | ret = X509_sign(cert.get(), pkey.get(), hash); |
9a90aace CT |
599 | |
600 | if (!ret) | |
601 | return false; | |
602 | ||
35b3559c AJ |
603 | certToStore = std::move(cert); |
604 | pkeyToStore = std::move(pkey); | |
9a90aace CT |
605 | return true; |
606 | } | |
607 | ||
4ece76b2 CT |
608 | static BIGNUM *createCertSerial(unsigned char *md, unsigned int n) |
609 | { | |
87f237a9 | 610 | |
4ece76b2 CT |
611 | assert(n == 20); //for sha1 n is 20 (for md5 n is 16) |
612 | ||
613 | BIGNUM *serial = NULL; | |
614 | serial = BN_bin2bn(md, n, NULL); | |
615 | ||
616 | // if the serial is "0" set it to '1' | |
b9bb9562 | 617 | if (BN_is_zero(serial) == true) |
4ece76b2 CT |
618 | BN_one(serial); |
619 | ||
c5bd24d6 CT |
620 | // serial size does not exceed 20 bytes |
621 | assert(BN_num_bits(serial) <= 160); | |
622 | ||
4ece76b2 CT |
623 | // According the RFC 5280, serial is an 20 bytes ASN.1 INTEGER (a signed big integer) |
624 | // and the maximum value for X.509 certificate serial number is 2^159-1 and | |
625 | // the minimum 0. If the first bit of the serial is '1' ( eg 2^160-1), | |
626 | // will result to a negative integer. | |
627 | // To handle this, if the produced serial is greater than 2^159-1 | |
628 | // truncate the last bit | |
629 | if (BN_is_bit_set(serial, 159)) | |
630 | BN_clear_bit(serial, 159); | |
631 | ||
632 | return serial; | |
633 | } | |
634 | ||
a0b971d5 CT |
635 | /// Return the SHA1 digest of the DER encoded version of the certificate |
636 | /// stored in a BIGNUM | |
f97700a0 | 637 | static BIGNUM *x509Digest(Security::CertPointer const & cert) |
a0b971d5 CT |
638 | { |
639 | unsigned int n; | |
640 | unsigned char md[EVP_MAX_MD_SIZE]; | |
641 | ||
642 | if (!X509_digest(cert.get(),EVP_sha1(),md,&n)) | |
643 | return NULL; | |
644 | ||
4ece76b2 CT |
645 | return createCertSerial(md, n); |
646 | } | |
647 | ||
f97700a0 | 648 | static BIGNUM *x509Pubkeydigest(Security::CertPointer const & cert) |
4ece76b2 CT |
649 | { |
650 | unsigned int n; | |
651 | unsigned char md[EVP_MAX_MD_SIZE]; | |
652 | ||
653 | if (!X509_pubkey_digest(cert.get(),EVP_sha1(),md,&n)) | |
654 | return NULL; | |
a0b971d5 | 655 | |
4ece76b2 | 656 | return createCertSerial(md, n); |
a0b971d5 CT |
657 | } |
658 | ||
87f237a9 A |
659 | /// Generate a unique serial number based on a Ssl::CertificateProperties object |
660 | /// for a new generated certificate | |
a0b971d5 CT |
661 | static bool createSerial(Ssl::BIGNUM_Pointer &serial, Ssl::CertificateProperties const &properties) |
662 | { | |
663 | Ssl::EVP_PKEY_Pointer fakePkey; | |
f97700a0 | 664 | Security::CertPointer fakeCert; |
a0b971d5 | 665 | |
4ece76b2 | 666 | serial.reset(x509Pubkeydigest(properties.signWithX509)); |
5cc307f3 CT |
667 | if (!serial.get()) { |
668 | serial.reset(BN_new()); | |
0e62e0d6 | 669 | BN_zero(serial.get()); |
5cc307f3 | 670 | } |
4ece76b2 | 671 | |
a0b971d5 CT |
672 | if (!generateFakeSslCertificate(fakeCert, fakePkey, properties, serial)) |
673 | return false; | |
674 | ||
675 | // The x509Fingerprint return an SHA1 hash. | |
676 | // both SHA1 hash and maximum serial number size are 20 bytes. | |
4ece76b2 | 677 | BIGNUM *r = x509Digest(fakeCert); |
a0b971d5 CT |
678 | if (!r) |
679 | return false; | |
680 | ||
a0b971d5 CT |
681 | serial.reset(r); |
682 | return true; | |
683 | } | |
684 | ||
f97700a0 | 685 | bool Ssl::generateSslCertificate(Security::CertPointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties) |
a0b971d5 CT |
686 | { |
687 | Ssl::BIGNUM_Pointer serial; | |
688 | ||
689 | if (!createSerial(serial, properties)) | |
690 | return false; | |
691 | ||
692 | return generateFakeSslCertificate(certToStore, pkeyToStore, properties, serial); | |
693 | } | |
694 | ||
5107d2c4 CT |
695 | bool |
696 | Ssl::OpenCertsFileForReading(Ssl::BIO_Pointer &bio, const char *filename) | |
95d2589c | 697 | { |
5107d2c4 | 698 | bio.reset(BIO_new(BIO_s_file())); |
95d2589c | 699 | if (!bio) |
5107d2c4 CT |
700 | return false; |
701 | if (!BIO_read_filename(bio.get(), filename)) | |
702 | return false; | |
703 | return true; | |
95d2589c CT |
704 | } |
705 | ||
5107d2c4 CT |
706 | bool |
707 | Ssl::ReadX509Certificate(Ssl::BIO_Pointer &bio, Security::CertPointer & cert) | |
708 | { | |
709 | assert(bio); | |
710 | if (X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL)) { | |
711 | cert.resetWithoutLocking(certificate); | |
712 | return true; | |
713 | } | |
714 | return false; | |
715 | } | |
716 | ||
717 | bool | |
718 | Ssl::ReadPrivateKey(Ssl::BIO_Pointer &bio, Ssl::EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback) | |
719 | { | |
720 | assert(bio); | |
721 | if (EVP_PKEY *akey = PEM_read_bio_PrivateKey(bio.get(), NULL, passwd_callback, NULL)) { | |
722 | pkey.resetWithoutLocking(akey); | |
723 | return true; | |
724 | } | |
725 | return false; | |
726 | } | |
727 | ||
728 | void | |
729 | Ssl::ReadPrivateKeyFromFile(char const * keyFilename, Ssl::EVP_PKEY_Pointer &pkey, pem_password_cb *passwd_callback) | |
95d2589c CT |
730 | { |
731 | if (!keyFilename) | |
5107d2c4 CT |
732 | return; |
733 | Ssl::BIO_Pointer bio; | |
734 | if (!OpenCertsFileForReading(bio, keyFilename)) | |
735 | return; | |
736 | ReadPrivateKey(bio, pkey, passwd_callback); | |
737 | } | |
738 | ||
739 | bool | |
740 | Ssl::OpenCertsFileForWriting(Ssl::BIO_Pointer &bio, const char *filename) | |
741 | { | |
742 | bio.reset(BIO_new(BIO_s_file())); | |
95d2589c | 743 | if (!bio) |
5107d2c4 CT |
744 | return false; |
745 | if (!BIO_write_filename(bio.get(), const_cast<char *>(filename))) | |
746 | return false; | |
747 | return true; | |
95d2589c CT |
748 | } |
749 | ||
5107d2c4 CT |
750 | bool |
751 | Ssl::WriteX509Certificate(Ssl::BIO_Pointer &bio, const Security::CertPointer & cert) | |
95d2589c | 752 | { |
5107d2c4 CT |
753 | if (!cert || !bio) |
754 | return false; | |
755 | if (!PEM_write_bio_X509(bio.get(), cert.get())) | |
756 | return false; | |
757 | return true; | |
758 | } | |
759 | ||
760 | bool | |
761 | Ssl::WritePrivateKey(Ssl::BIO_Pointer &bio, const Ssl::EVP_PKEY_Pointer &pkey) | |
762 | { | |
763 | if (!pkey || !bio) | |
764 | return false; | |
765 | if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL)) | |
766 | return false; | |
767 | return true; | |
95d2589c CT |
768 | } |
769 | ||
770 | bool Ssl::sslDateIsInTheFuture(char const * date) | |
771 | { | |
772 | ASN1_UTCTIME tm; | |
773 | tm.flags = 0; | |
774 | tm.type = 23; | |
775 | tm.data = (unsigned char *)date; | |
776 | tm.length = strlen(date); | |
777 | ||
778 | return (X509_cmp_current_time(&tm) > 0); | |
779 | } | |
e7bcc25f CT |
780 | |
781 | /// Print the time represented by a ASN1_TIME struct to a string using GeneralizedTime format | |
782 | static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen) | |
783 | { | |
87f237a9 | 784 | // ASN1_Time holds time to UTCTime or GeneralizedTime form. |
e7bcc25f CT |
785 | // UTCTime has the form YYMMDDHHMMSS[Z | [+|-]offset] |
786 | // GeneralizedTime has the form YYYYMMDDHHMMSS[Z | [+|-] offset] | |
787 | ||
788 | // length should have space for data plus 2 extra bytes for the two extra year fields | |
789 | // plus the '\0' char. | |
790 | if ((aTime->length + 3) > bufLen) | |
791 | return false; | |
792 | ||
793 | char *str; | |
794 | if (aTime->type == V_ASN1_UTCTIME) { | |
795 | if (aTime->data[0] > '5') { // RFC 2459, section 4.1.2.5.1 | |
796 | buf[0] = '1'; | |
797 | buf[1] = '9'; | |
798 | } else { | |
799 | buf[0] = '2'; | |
800 | buf[1] = '0'; | |
801 | } | |
802 | str = buf +2; | |
87f237a9 | 803 | } else // if (aTime->type == V_ASN1_GENERALIZEDTIME) |
e7bcc25f CT |
804 | str = buf; |
805 | ||
806 | memcpy(str, aTime->data, aTime->length); | |
807 | str[aTime->length] = '\0'; | |
808 | return true; | |
809 | } | |
810 | ||
811 | static int asn1time_cmp(ASN1_TIME *asnTime1, ASN1_TIME *asnTime2) | |
812 | { | |
813 | char strTime1[64], strTime2[64]; | |
814 | if (!asn1timeToGeneralizedTimeStr(asnTime1, strTime1, sizeof(strTime1))) | |
815 | return -1; | |
816 | if (!asn1timeToGeneralizedTimeStr(asnTime2, strTime2, sizeof(strTime2))) | |
817 | return -1; | |
87f237a9 | 818 | |
e7bcc25f CT |
819 | return strcmp(strTime1, strTime2); |
820 | } | |
821 | ||
4ece76b2 | 822 | bool Ssl::certificateMatchesProperties(X509 *cert, CertificateProperties const &properties) |
e7bcc25f | 823 | { |
4ece76b2 CT |
824 | assert(cert); |
825 | ||
826 | // For non self-signed certificates we have to check if the signing certificate changed | |
827 | if (properties.signAlgorithm != Ssl::algSignSelf) { | |
f4e4d4d6 | 828 | assert(properties.signWithX509.get()); |
4ece76b2 CT |
829 | if (X509_check_issued(properties.signWithX509.get(), cert) != X509_V_OK) |
830 | return false; | |
831 | } | |
87f237a9 | 832 | |
4ece76b2 CT |
833 | X509 *cert2 = properties.mimicCert.get(); |
834 | // If there is not certificate to mimic stop here | |
835 | if (!cert2) | |
836 | return true; | |
837 | ||
838 | if (!properties.setCommonName) { | |
839 | X509_NAME *cert1_name = X509_get_subject_name(cert); | |
840 | X509_NAME *cert2_name = X509_get_subject_name(cert2); | |
841 | if (X509_NAME_cmp(cert1_name, cert2_name) != 0) | |
842 | return false; | |
87f237a9 A |
843 | } else if (properties.commonName != CommonHostName(cert)) |
844 | return false; | |
845 | ||
4ece76b2 CT |
846 | if (!properties.setValidBefore) { |
847 | ASN1_TIME *aTime = X509_get_notBefore(cert); | |
848 | ASN1_TIME *bTime = X509_get_notBefore(cert2); | |
849 | if (asn1time_cmp(aTime, bTime) != 0) | |
850 | return false; | |
0efa4d01 CT |
851 | } else if (X509_cmp_current_time(X509_get_notBefore(cert)) >= 0) { |
852 | // notBefore does not exist (=0) or it is in the future (>0) | |
853 | return false; | |
4ece76b2 | 854 | } |
e7bcc25f | 855 | |
4ece76b2 CT |
856 | if (!properties.setValidAfter) { |
857 | ASN1_TIME *aTime = X509_get_notAfter(cert); | |
858 | ASN1_TIME *bTime = X509_get_notAfter(cert2); | |
859 | if (asn1time_cmp(aTime, bTime) != 0) | |
860 | return false; | |
0efa4d01 CT |
861 | } else if (X509_cmp_current_time(X509_get_notAfter(cert)) <= 0) { |
862 | // notAfter does not exist (0) or it is in the past (<0) | |
863 | return false; | |
4ece76b2 | 864 | } |
0efa4d01 | 865 | |
e7bcc25f CT |
866 | char *alStr1; |
867 | int alLen; | |
4ece76b2 | 868 | alStr1 = (char *)X509_alias_get0(cert, &alLen); |
e7bcc25f CT |
869 | char *alStr2 = (char *)X509_alias_get0(cert2, &alLen); |
870 | if ((!alStr1 && alStr2) || (alStr1 && !alStr2) || | |
87f237a9 | 871 | (alStr1 && alStr2 && strcmp(alStr1, alStr2)) != 0) |
e7bcc25f | 872 | return false; |
87f237a9 | 873 | |
e7bcc25f CT |
874 | // Compare subjectAltName extension |
875 | STACK_OF(GENERAL_NAME) * cert1_altnames; | |
4ece76b2 | 876 | cert1_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); |
e7bcc25f CT |
877 | STACK_OF(GENERAL_NAME) * cert2_altnames; |
878 | cert2_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert2, NID_subject_alt_name, NULL, NULL); | |
879 | bool match = true; | |
880 | if (cert1_altnames) { | |
881 | int numalts = sk_GENERAL_NAME_num(cert1_altnames); | |
a38ec4b1 | 882 | for (int i = 0; match && i < numalts; ++i) { |
2a268a06 | 883 | GENERAL_NAME *aName = sk_GENERAL_NAME_value(cert1_altnames, i); |
e7bcc25f CT |
884 | match = sk_GENERAL_NAME_find(cert2_altnames, aName); |
885 | } | |
87f237a9 | 886 | } else if (cert2_altnames) |
e7bcc25f | 887 | match = false; |
87f237a9 | 888 | |
e7bcc25f CT |
889 | sk_GENERAL_NAME_pop_free(cert1_altnames, GENERAL_NAME_free); |
890 | sk_GENERAL_NAME_pop_free(cert2_altnames, GENERAL_NAME_free); | |
891 | ||
892 | return match; | |
893 | } | |
0efa4d01 CT |
894 | |
895 | static const char *getSubjectEntry(X509 *x509, int nid) | |
896 | { | |
897 | static char name[1024] = ""; // stores common name (CN) | |
898 | ||
899 | if (!x509) | |
900 | return NULL; | |
901 | ||
902 | // TODO: What if the entry is a UTF8String? See X509_NAME_get_index_by_NID(3ssl). | |
903 | const int nameLen = X509_NAME_get_text_by_NID( | |
87f237a9 A |
904 | X509_get_subject_name(x509), |
905 | nid, name, sizeof(name)); | |
0efa4d01 CT |
906 | |
907 | if (nameLen > 0) | |
908 | return name; | |
909 | ||
910 | return NULL; | |
911 | } | |
912 | ||
913 | const char *Ssl::CommonHostName(X509 *x509) | |
914 | { | |
915 | return getSubjectEntry(x509, NID_commonName); | |
916 | } | |
917 | ||
918 | const char *Ssl::getOrganization(X509 *x509) | |
919 | { | |
920 | return getSubjectEntry(x509, NID_organizationName); | |
921 | } | |
922 | ||
5107d2c4 CT |
923 | bool |
924 | Ssl::CertificatesCmp(const Security::CertPointer &cert1, const Security::CertPointer &cert2) | |
925 | { | |
926 | if (!cert1 || ! cert2) | |
927 | return false; | |
928 | ||
929 | int cert1Len; | |
930 | unsigned char *cert1Asn = NULL; | |
931 | cert1Len = ASN1_item_i2d((ASN1_VALUE *)cert1.get(), &cert1Asn, ASN1_ITEM_rptr(X509)); | |
932 | ||
933 | int cert2Len; | |
934 | unsigned char *cert2Asn = NULL; | |
935 | cert2Len = ASN1_item_i2d((ASN1_VALUE *)cert2.get(), &cert2Asn, ASN1_ITEM_rptr(X509)); | |
936 | ||
937 | if (cert1Len != cert2Len) | |
938 | return false; | |
939 | ||
940 | bool ret = (memcmp(cert1Asn, cert2Asn, cert1Len) == 0); | |
941 | ||
942 | OPENSSL_free(cert1Asn); | |
943 | OPENSSL_free(cert2Asn); | |
944 | ||
945 | return ret; | |
946 | } | |
70cfe22f AJ |
947 | |
948 | const ASN1_BIT_STRING * | |
949 | Ssl::X509_get_signature(const Security::CertPointer &cert) | |
950 | { | |
951 | #if HAVE_LIBCRYPTO_X509_GET0_SIGNATURE | |
952 | #if SQUID_USE_CONST_X509_GET0_SIGNATURE_ARGS | |
953 | const ASN1_BIT_STRING *sig = nullptr; | |
954 | const X509_ALGOR *sig_alg = nullptr; | |
955 | #else | |
956 | ASN1_BIT_STRING *sig = nullptr; | |
957 | X509_ALGOR *sig_alg = nullptr; | |
958 | #endif | |
959 | X509_get0_signature(&sig, &sig_alg, cert.get()); | |
960 | return sig; | |
961 | #else | |
962 | return cert->signature; | |
963 | #endif | |
964 | } | |
62b9cc19 | 965 |