]> git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/gadgets.cc
certificateMatchesProperties segfault
[thirdparty/squid.git] / src / ssl / gadgets.cc
1 /*
2 * $Id$
3 */
4
5 #include "squid.h"
6 #include "ssl/gadgets.h"
7 #if HAVE_OPENSSL_X509V3_H
8 #include <openssl/x509v3.h>
9 #endif
10
11 EVP_PKEY * Ssl::createSslPrivateKey()
12 {
13 Ssl::EVP_PKEY_Pointer pkey(EVP_PKEY_new());
14
15 if (!pkey)
16 return NULL;
17
18 Ssl::RSA_Pointer rsa(RSA_generate_key(1024, RSA_F4, NULL, NULL));
19
20 if (!rsa)
21 return NULL;
22
23 if (!EVP_PKEY_assign_RSA(pkey.get(), (rsa.get())))
24 return NULL;
25
26 rsa.release();
27 return pkey.release();
28 }
29
30 /**
31 \ingroup ServerProtocolSSLInternal
32 * Set serial random serial number or set random serial number.
33 */
34 static bool setSerialNumber(ASN1_INTEGER *ai, BIGNUM const* serial)
35 {
36 if (!ai)
37 return false;
38 Ssl::BIGNUM_Pointer bn(BN_new());
39 if (serial) {
40 bn.reset(BN_dup(serial));
41 } else {
42 if (!bn)
43 return false;
44
45 if (!BN_pseudo_rand(bn.get(), 64, 0, 0))
46 return false;
47 }
48
49 if (ai && !BN_to_ASN1_INTEGER(bn.get(), ai))
50 return false;
51 return true;
52 }
53
54 bool Ssl::writeCertAndPrivateKeyToMemory(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, std::string & bufferToWrite)
55 {
56 bufferToWrite.clear();
57 if (!pkey || !cert)
58 return false;
59 BIO_Pointer bio(BIO_new(BIO_s_mem()));
60 if (!bio)
61 return false;
62
63 if (!PEM_write_bio_X509 (bio.get(), cert.get()))
64 return false;
65
66 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
67 return false;
68
69 char *ptr = NULL;
70 long len = BIO_get_mem_data(bio.get(), &ptr);
71 if (!ptr)
72 return false;
73
74 bufferToWrite = std::string(ptr, len);
75 return true;
76 }
77
78 bool Ssl::appendCertToMemory(Ssl::X509_Pointer const & cert, std::string & bufferToWrite)
79 {
80 if (!cert)
81 return false;
82
83 BIO_Pointer bio(BIO_new(BIO_s_mem()));
84 if (!bio)
85 return false;
86
87 if (!PEM_write_bio_X509 (bio.get(), cert.get()))
88 return false;
89
90 char *ptr = NULL;
91 long len = BIO_get_mem_data(bio.get(), &ptr);
92 if (!ptr)
93 return false;
94
95 if (!bufferToWrite.empty())
96 bufferToWrite.append(" "); // add a space...
97
98 bufferToWrite.append(ptr, len);
99 return true;
100 }
101
102 bool Ssl::writeCertAndPrivateKeyToFile(Ssl::X509_Pointer const & cert, Ssl::EVP_PKEY_Pointer const & pkey, char const * filename)
103 {
104 if (!pkey || !cert)
105 return false;
106
107 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
108 if (!bio)
109 return false;
110 if (!BIO_write_filename(bio.get(), const_cast<char *>(filename)))
111 return false;
112
113 if (!PEM_write_bio_X509(bio.get(), cert.get()))
114 return false;
115
116 if (!PEM_write_bio_PrivateKey(bio.get(), pkey.get(), NULL, NULL, 0, NULL, NULL))
117 return false;
118
119 return true;
120 }
121
122 bool Ssl::readCertAndPrivateKeyFromMemory(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * bufferToRead)
123 {
124 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
125 BIO_puts(bio.get(), bufferToRead);
126
127 X509 * certPtr = NULL;
128 cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0));
129 if (!cert)
130 return false;
131
132 EVP_PKEY * pkeyPtr = NULL;
133 pkey.reset(PEM_read_bio_PrivateKey(bio.get(), &pkeyPtr, 0, 0));
134 if (!pkey)
135 return false;
136
137 return true;
138 }
139
140 bool Ssl::readCertFromMemory(X509_Pointer & cert, char const * bufferToRead)
141 {
142 Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
143 BIO_puts(bio.get(), bufferToRead);
144
145 X509 * certPtr = NULL;
146 cert.reset(PEM_read_bio_X509(bio.get(), &certPtr, 0, 0));
147 if (!cert)
148 return false;
149
150 return true;
151 }
152
153 // According to RFC 5280 (Section A.1), the common name length in a certificate
154 // can be at most 64 characters
155 static const size_t MaxCnLen = 64;
156
157 // Replace certs common name with the given
158 static bool replaceCommonName(Ssl::X509_Pointer & cert, std::string const &cn)
159 {
160 std::string fixedCn;
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);
167 } while(pos != std::string::npos && (cn.length() - pos + 2) > MaxCnLen);
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
174 fixedCn.append(1,'*');
175 fixedCn.append(cn.c_str() + pos);
176 }
177
178 X509_NAME *name = X509_get_subject_name(cert.get());
179 if (!name)
180 return false;
181 // Remove the CN part:
182 int loc = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
183 if (loc >=0) {
184 X509_NAME_ENTRY *tmp = X509_NAME_get_entry(name, loc);
185 X509_NAME_delete_entry(name, loc);
186 X509_NAME_ENTRY_free(tmp);
187 }
188
189 // Add a new CN
190 return X509_NAME_add_entry_by_NID(name, NID_commonName, MBSTRING_ASC,
191 (unsigned char *)(fixedCn.empty() ? cn.c_str() : fixedCn.c_str()), -1, -1, 0);
192 }
193
194 const char *Ssl::CertSignAlgorithmStr[] = {
195 "signTrusted",
196 "signUntrusted",
197 "signSelf",
198 NULL
199 };
200
201 const char *Ssl::CertAdaptAlgorithmStr[] = {
202 "setValidAfter",
203 "setValidBefore",
204 "setCommonName",
205 NULL
206 };
207
208 Ssl::CertificateProperties::CertificateProperties():
209 setValidAfter(false),
210 setValidBefore(false),
211 setCommonName(false),
212 signAlgorithm(Ssl::algSignEnd)
213 {}
214
215 std::string & Ssl::CertificateProperties::dbKey() const
216 {
217 static std::string certKey;
218 certKey.clear();
219 certKey.reserve(4096);
220 if (mimicCert.get()) {
221 char buf[1024];
222 certKey.append(X509_NAME_oneline(X509_get_subject_name(mimicCert.get()), buf, sizeof(buf)));
223 }
224
225 if (certKey.empty()) {
226 certKey.append("/CN=", 4);
227 certKey.append(commonName);
228 }
229
230 if (setValidAfter)
231 certKey.append("+SetValidAfter=on", 17);
232
233 if (setValidBefore)
234 certKey.append("+SetValidBefore=on", 18);
235
236 if (setCommonName) {
237 certKey.append("+SetCommonName=", 15);
238 certKey.append(commonName);
239 }
240
241 if (signAlgorithm != Ssl::algSignEnd) {
242 certKey.append("+Sign=", 6);
243 certKey.append(certSignAlgorithm(signAlgorithm));
244 }
245
246 return certKey;
247 }
248
249 static bool buildCertificate(Ssl::X509_Pointer & cert, Ssl::CertificateProperties const &properties)
250 {
251 // not an Ssl::X509_NAME_Pointer because X509_REQ_get_subject_name()
252 // returns a pointer to the existing subject name. Nothing to clean here.
253 if (properties.mimicCert.get()) {
254 // Leave subject empty if we cannot extract it from true cert.
255 if (X509_NAME *name = X509_get_subject_name(properties.mimicCert.get())) {
256 // X509_set_subject_name will call X509_dup for name
257 X509_set_subject_name(cert.get(), name);
258 }
259 }
260
261 if (properties.setCommonName || !properties.mimicCert.get()) {
262 // In this case the CN of the certificate given by the user
263 // Ignore errors: it is better to make a certificate with no CN
264 // than to quit ssl_crtd because we cannot make a certificate.
265 // Most errors are caused by user input such as huge domain names.
266 (void)replaceCommonName(cert, properties.commonName);
267 }
268
269 // We should get caCert notBefore and notAfter fields and do not allow
270 // notBefore/notAfter values from certToMimic before/after notBefore/notAfter
271 // fields from caCert.
272 // Currently there is not any way in openssl tollkit to compare two ASN1_TIME
273 // objects.
274 ASN1_TIME *aTime = NULL;
275 if (!properties.setValidBefore && properties.mimicCert.get())
276 aTime = X509_get_notBefore(properties.mimicCert.get());
277 if (!aTime && properties.signWithX509.get())
278 aTime = X509_get_notBefore(properties.signWithX509.get());
279
280 if (aTime) {
281 if (!X509_set_notBefore(cert.get(), aTime))
282 return false;
283 }
284 else if (!X509_gmtime_adj(X509_get_notBefore(cert.get()), (-2)*24*60*60))
285 return false;
286
287 aTime = NULL;
288 if (!properties.setValidAfter && properties.mimicCert.get())
289 aTime = X509_get_notAfter(properties.mimicCert.get());
290 if (!aTime && properties.signWithX509.get())
291 aTime = X509_get_notAfter(properties.signWithX509.get());
292 if (aTime) {
293 if (!X509_set_notAfter(cert.get(), aTime))
294 return false;
295 } else if (!X509_gmtime_adj(X509_get_notAfter(cert.get()), 60*60*24*356*3))
296 return false;
297
298 // mimic the alias and possibly subjectAltName
299 if (properties.mimicCert.get()) {
300 unsigned char *alStr;
301 int alLen;
302 alStr = X509_alias_get0(properties.mimicCert.get(), &alLen);
303 if (alStr) {
304 X509_alias_set1(cert.get(), alStr, alLen);
305 }
306
307 // Mimic subjectAltName unless we used a configured CN: browsers reject
308 // certificates with CN unrelated to subjectAltNames.
309 if (!properties.setCommonName) {
310 int pos=X509_get_ext_by_NID (properties.mimicCert.get(), OBJ_sn2nid("subjectAltName"), -1);
311 X509_EXTENSION *ext=X509_get_ext(properties.mimicCert.get(), pos);
312 if (ext)
313 X509_add_ext(cert.get(), ext, -1);
314 }
315 }
316
317 return true;
318 }
319
320 static bool generateFakeSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties, Ssl::BIGNUM_Pointer const &serial)
321 {
322 Ssl::EVP_PKEY_Pointer pkey;
323 // Use signing certificates private key as generated certificate private key
324 if (properties.signWithPkey.get())
325 pkey.resetAndLock(properties.signWithPkey.get());
326 else // if not exist generate one
327 pkey.reset(Ssl::createSslPrivateKey());
328
329 if (!pkey)
330 return false;
331
332 Ssl::X509_Pointer cert(X509_new());
333 if (!cert)
334 return false;
335
336 // Set pub key and serial given by the caller
337 if (!X509_set_pubkey(cert.get(), pkey.get()))
338 return false;
339 if (!setSerialNumber(X509_get_serialNumber(cert.get()), serial.get()))
340 return false;
341
342 // Fill the certificate with the required properties
343 if (!buildCertificate(cert, properties))
344 return false;
345
346 int ret = 0;
347 // Set issuer name, from CA or our subject name for self signed cert
348 if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithX509.get())
349 ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(properties.signWithX509.get()));
350 else // Self signed certificate, set issuer to self
351 ret = X509_set_issuer_name(cert.get(), X509_get_subject_name(cert.get()));
352 if (!ret)
353 return false;
354
355 /*Now sign the request */
356 if (properties.signAlgorithm != Ssl::algSignSelf && properties.signWithPkey.get())
357 ret = X509_sign(cert.get(), properties.signWithPkey.get(), EVP_sha1());
358 else //else sign with self key (self signed request)
359 ret = X509_sign(cert.get(), pkey.get(), EVP_sha1());
360
361 if (!ret)
362 return false;
363
364 certToStore.reset(cert.release());
365 pkeyToStore.reset(pkey.release());
366 return true;
367 }
368
369 static BIGNUM *createCertSerial(unsigned char *md, unsigned int n)
370 {
371
372 assert(n == 20); //for sha1 n is 20 (for md5 n is 16)
373
374 BIGNUM *serial = NULL;
375 serial = BN_bin2bn(md, n, NULL);
376
377 // if the serial is "0" set it to '1'
378 if (BN_is_zero(serial))
379 BN_one(serial);
380
381 // serial size does not exceed 20 bytes
382 assert(BN_num_bits(serial) <= 160);
383
384 // According the RFC 5280, serial is an 20 bytes ASN.1 INTEGER (a signed big integer)
385 // and the maximum value for X.509 certificate serial number is 2^159-1 and
386 // the minimum 0. If the first bit of the serial is '1' ( eg 2^160-1),
387 // will result to a negative integer.
388 // To handle this, if the produced serial is greater than 2^159-1
389 // truncate the last bit
390 if (BN_is_bit_set(serial, 159))
391 BN_clear_bit(serial, 159);
392
393 return serial;
394 }
395
396 /// Return the SHA1 digest of the DER encoded version of the certificate
397 /// stored in a BIGNUM
398 static BIGNUM *x509Digest(Ssl::X509_Pointer const & cert)
399 {
400 unsigned int n;
401 unsigned char md[EVP_MAX_MD_SIZE];
402
403 if (!X509_digest(cert.get(),EVP_sha1(),md,&n))
404 return NULL;
405
406 return createCertSerial(md, n);
407 }
408
409 static BIGNUM *x509Pubkeydigest(Ssl::X509_Pointer const & cert)
410 {
411 unsigned int n;
412 unsigned char md[EVP_MAX_MD_SIZE];
413
414 if (!X509_pubkey_digest(cert.get(),EVP_sha1(),md,&n))
415 return NULL;
416
417 return createCertSerial(md, n);
418 }
419
420 /// Generate a unique serial number based on a Ssl::CertificateProperties object
421 /// for a new generated certificate
422 static bool createSerial(Ssl::BIGNUM_Pointer &serial, Ssl::CertificateProperties const &properties)
423 {
424 Ssl::EVP_PKEY_Pointer fakePkey;
425 Ssl::X509_Pointer fakeCert;
426
427 serial.reset(x509Pubkeydigest(properties.signWithX509));
428 if (!serial.get()) {
429 serial.reset(BN_new());
430 BN_is_zero(serial.get());
431 }
432
433 if (!generateFakeSslCertificate(fakeCert, fakePkey, properties, serial))
434 return false;
435
436 // The x509Fingerprint return an SHA1 hash.
437 // both SHA1 hash and maximum serial number size are 20 bytes.
438 BIGNUM *r = x509Digest(fakeCert);
439 if (!r)
440 return false;
441
442 serial.reset(r);
443 return true;
444 }
445
446 bool Ssl::generateSslCertificate(Ssl::X509_Pointer & certToStore, Ssl::EVP_PKEY_Pointer & pkeyToStore, Ssl::CertificateProperties const &properties)
447 {
448 Ssl::BIGNUM_Pointer serial;
449
450 if (!createSerial(serial, properties))
451 return false;
452
453 return generateFakeSslCertificate(certToStore, pkeyToStore, properties, serial);
454 }
455
456 /**
457 \ingroup ServerProtocolSSLInternal
458 * Read certificate from file.
459 */
460 static X509 * readSslX509Certificate(char const * certFilename)
461 {
462 if (!certFilename)
463 return NULL;
464 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
465 if (!bio)
466 return NULL;
467 if (!BIO_read_filename(bio.get(), certFilename))
468 return NULL;
469 X509 *certificate = PEM_read_bio_X509(bio.get(), NULL, NULL, NULL);
470 return certificate;
471 }
472
473 EVP_PKEY * Ssl::readSslPrivateKey(char const * keyFilename, pem_password_cb *passwd_callback)
474 {
475 if (!keyFilename)
476 return NULL;
477 Ssl::BIO_Pointer bio(BIO_new(BIO_s_file_internal()));
478 if (!bio)
479 return NULL;
480 if (!BIO_read_filename(bio.get(), keyFilename))
481 return NULL;
482 EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio.get(), NULL, passwd_callback, NULL);
483 return pkey;
484 }
485
486 void Ssl::readCertAndPrivateKeyFromFiles(Ssl::X509_Pointer & cert, Ssl::EVP_PKEY_Pointer & pkey, char const * certFilename, char const * keyFilename)
487 {
488 if (keyFilename == NULL)
489 keyFilename = certFilename;
490 pkey.reset(readSslPrivateKey(keyFilename));
491 cert.reset(readSslX509Certificate(certFilename));
492 if (!pkey || !cert || !X509_check_private_key(cert.get(), pkey.get())) {
493 pkey.reset(NULL);
494 cert.reset(NULL);
495 }
496 }
497
498 bool Ssl::sslDateIsInTheFuture(char const * date)
499 {
500 ASN1_UTCTIME tm;
501 tm.flags = 0;
502 tm.type = 23;
503 tm.data = (unsigned char *)date;
504 tm.length = strlen(date);
505
506 return (X509_cmp_current_time(&tm) > 0);
507 }
508
509 /// Print the time represented by a ASN1_TIME struct to a string using GeneralizedTime format
510 static bool asn1timeToGeneralizedTimeStr(ASN1_TIME *aTime, char *buf, int bufLen)
511 {
512 // ASN1_Time holds time to UTCTime or GeneralizedTime form.
513 // UTCTime has the form YYMMDDHHMMSS[Z | [+|-]offset]
514 // GeneralizedTime has the form YYYYMMDDHHMMSS[Z | [+|-] offset]
515
516 // length should have space for data plus 2 extra bytes for the two extra year fields
517 // plus the '\0' char.
518 if ((aTime->length + 3) > bufLen)
519 return false;
520
521 char *str;
522 if (aTime->type == V_ASN1_UTCTIME) {
523 if (aTime->data[0] > '5') { // RFC 2459, section 4.1.2.5.1
524 buf[0] = '1';
525 buf[1] = '9';
526 } else {
527 buf[0] = '2';
528 buf[1] = '0';
529 }
530 str = buf +2;
531 }
532 else // if (aTime->type == V_ASN1_GENERALIZEDTIME)
533 str = buf;
534
535 memcpy(str, aTime->data, aTime->length);
536 str[aTime->length] = '\0';
537 return true;
538 }
539
540 static int asn1time_cmp(ASN1_TIME *asnTime1, ASN1_TIME *asnTime2)
541 {
542 char strTime1[64], strTime2[64];
543 if (!asn1timeToGeneralizedTimeStr(asnTime1, strTime1, sizeof(strTime1)))
544 return -1;
545 if (!asn1timeToGeneralizedTimeStr(asnTime2, strTime2, sizeof(strTime2)))
546 return -1;
547
548 return strcmp(strTime1, strTime2);
549 }
550
551 bool Ssl::certificateMatchesProperties(X509 *cert, CertificateProperties const &properties)
552 {
553 assert(cert);
554
555 // For non self-signed certificates we have to check if the signing certificate changed
556 if (properties.signAlgorithm != Ssl::algSignSelf) {
557 assert(properties.signWithX509.get());
558 if (X509_check_issued(properties.signWithX509.get(), cert) != X509_V_OK)
559 return false;
560 }
561
562 X509 *cert2 = properties.mimicCert.get();
563 // If there is not certificate to mimic stop here
564 if (!cert2)
565 return true;
566
567 if (!properties.setCommonName) {
568 X509_NAME *cert1_name = X509_get_subject_name(cert);
569 X509_NAME *cert2_name = X509_get_subject_name(cert2);
570 if (X509_NAME_cmp(cert1_name, cert2_name) != 0)
571 return false;
572 }
573 /* else {
574 if (properties.commonName != Ssl::CommonHostName(cert))
575 return false;
576 This function normaly called to verify a cached certificate matches the
577 specifications given by properties parameter.
578 The cached certificate retrieved from the cache using a key which has
579 as part the properties.commonName. This is enough to assume that the
580 cached cert has in its subject the properties.commonName as cn field.
581 }
582 */
583
584 if (!properties.setValidBefore) {
585 ASN1_TIME *aTime = X509_get_notBefore(cert);
586 ASN1_TIME *bTime = X509_get_notBefore(cert2);
587 if (asn1time_cmp(aTime, bTime) != 0)
588 return false;
589 }
590
591 if (!properties.setValidAfter) {
592 ASN1_TIME *aTime = X509_get_notAfter(cert);
593 ASN1_TIME *bTime = X509_get_notAfter(cert2);
594 if (asn1time_cmp(aTime, bTime) != 0)
595 return false;
596 }
597
598 char *alStr1;
599 int alLen;
600 alStr1 = (char *)X509_alias_get0(cert, &alLen);
601 char *alStr2 = (char *)X509_alias_get0(cert2, &alLen);
602 if ((!alStr1 && alStr2) || (alStr1 && !alStr2) ||
603 (alStr1 && alStr2 && strcmp(alStr1, alStr2)) != 0)
604 return false;
605
606 // Compare subjectAltName extension
607 STACK_OF(GENERAL_NAME) * cert1_altnames;
608 cert1_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
609 STACK_OF(GENERAL_NAME) * cert2_altnames;
610 cert2_altnames = (STACK_OF(GENERAL_NAME)*)X509_get_ext_d2i(cert2, NID_subject_alt_name, NULL, NULL);
611 bool match = true;
612 if (cert1_altnames) {
613 int numalts = sk_GENERAL_NAME_num(cert1_altnames);
614 for (int i = 0; match && i < numalts; i++) {
615 const GENERAL_NAME *aName = sk_GENERAL_NAME_value(cert1_altnames, i);
616 match = sk_GENERAL_NAME_find(cert2_altnames, aName);
617 }
618 }
619 else if (cert2_altnames)
620 match = false;
621
622 sk_GENERAL_NAME_pop_free(cert1_altnames, GENERAL_NAME_free);
623 sk_GENERAL_NAME_pop_free(cert2_altnames, GENERAL_NAME_free);
624
625 return match;
626 }