]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/certificate_db.cc
6 #include "ssl/certificate_db.h"
23 Ssl::FileLocker::FileLocker(std::string
const & filename
)
27 hFile
= CreateFile(TEXT(filename
.c_str()), GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
28 if (hFile
!= INVALID_HANDLE_VALUE
)
29 LockFile(hFile
, 0, 0, 1, 0);
31 fd
= open(filename
.c_str(), 0);
37 Ssl::FileLocker::~FileLocker()
40 if (hFile
!= INVALID_HANDLE_VALUE
) {
41 UnlockFile(hFile
, 0, 0, 1, 0);
52 Ssl::CertificateDb::Row::Row()
55 row
= new char *[width
+ 1];
56 for (size_t i
= 0; i
< width
+ 1; i
++)
60 Ssl::CertificateDb::Row::~Row()
63 for (size_t i
= 0; i
< width
+ 1; i
++) {
70 void Ssl::CertificateDb::Row::reset()
75 void Ssl::CertificateDb::Row::setValue(size_t cell
, char const * value
)
82 row
[cell
] = static_cast<char *>(malloc(sizeof(char) * (strlen(value
) + 1)));
83 memcpy(row
[cell
], value
, sizeof(char) * (strlen(value
) + 1));
88 char ** Ssl::CertificateDb::Row::getRow()
93 unsigned long Ssl::CertificateDb::index_serial_hash(const char **a
)
95 const char *n
= a
[Ssl::CertificateDb::cnlSerial
];
96 while (*n
== '0') n
++;
100 int Ssl::CertificateDb::index_serial_cmp(const char **a
, const char **b
)
103 for (aa
= a
[Ssl::CertificateDb::cnlSerial
]; *aa
== '0'; aa
++);
104 for (bb
= b
[Ssl::CertificateDb::cnlSerial
]; *bb
== '0'; bb
++);
105 return strcmp(aa
, bb
);
108 unsigned long Ssl::CertificateDb::index_name_hash(const char **a
)
110 return(lh_strhash(a
[Ssl::CertificateDb::cnlName
]));
113 int Ssl::CertificateDb::index_name_cmp(const char **a
, const char **b
)
115 return(strcmp(a
[Ssl::CertificateDb::cnlName
], b
[CertificateDb::cnlName
]));
118 const std::string
Ssl::CertificateDb::serial_file("serial");
119 const std::string
Ssl::CertificateDb::db_file("index.txt");
120 const std::string
Ssl::CertificateDb::cert_dir("certs");
121 const std::string
Ssl::CertificateDb::size_file("size");
122 const size_t Ssl::CertificateDb::min_db_size(4096);
124 Ssl::CertificateDb::CertificateDb(std::string
const & aDb_path
, size_t aMax_db_size
, size_t aFs_block_size
)
126 serial_full(aDb_path
+ "/" + serial_file
),
127 db_full(aDb_path
+ "/" + db_file
),
128 cert_full(aDb_path
+ "/" + cert_dir
),
129 size_full(aDb_path
+ "/" + size_file
),
131 max_db_size(aMax_db_size
),
132 fs_block_size(aFs_block_size
),
133 enabled_disk_store(true)
135 if (db_path
.empty() && !max_db_size
)
136 enabled_disk_store
= false;
137 else if ((db_path
.empty() && max_db_size
) || (!db_path
.empty() && !max_db_size
))
138 throw std::runtime_error("ssl_crtd is missing the required parameter. There should be -s and -M parameters together.");
143 bool Ssl::CertificateDb::find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
145 FileLocker
db_locker(db_full
);
147 return pure_find(host_name
, cert
, pkey
);
150 bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
152 FileLocker
db_locker(db_full
);
154 if (!db
|| !cert
|| !pkey
|| min_db_size
> max_db_size
)
157 ASN1_INTEGER
* ai
= X509_get_serialNumber(cert
.get());
158 std::string serial_string
;
159 Ssl::BIGNUM_Pointer
serial(ASN1_INTEGER_to_BN(ai
, NULL
));
161 TidyPointer
<char, tidyFree
> hex_bn(BN_bn2hex(serial
.get()));
162 serial_string
= std::string(hex_bn
.get());
164 row
.setValue(cnlSerial
, serial_string
.c_str());
165 char ** rrow
= TXT_DB_get_by_index(db
.get(), cnlSerial
, row
.getRow());
170 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
171 if (pure_find(subject
.get(), cert
, pkey
))
175 while (max_db_size
< size()) {
176 if (!deleteInvalidCertificate())
180 while (max_db_size
< size()) {
181 deleteOldestCertificate();
184 row
.setValue(cnlType
, "V");
185 ASN1_UTCTIME
* tm
= X509_get_notAfter(cert
.get());
186 row
.setValue(cnlExp_date
, std::string(reinterpret_cast<char *>(tm
->data
), tm
->length
).c_str());
187 row
.setValue(cnlFile
, "unknown");
189 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
190 row
.setValue(cnlName
, subject
.get());
193 if (!TXT_DB_insert(db
.get(), row
.getRow()))
197 std::string
filename(cert_full
+ "/" + serial_string
+ ".pem");
198 FileLocker
cert_locker(filename
);
199 if (!writeCertAndPrivateKeyToFile(cert
, pkey
, filename
.c_str()))
207 BIGNUM
* Ssl::CertificateDb::getCurrentSerialNumber()
209 FileLocker
serial_locker(serial_full
);
210 // load serial number from file.
211 Ssl::BIO_Pointer
file(BIO_new(BIO_s_file()));
215 if (BIO_rw_filename(file
.get(), const_cast<char *>(serial_full
.c_str())) <= 0)
218 Ssl::ASN1_INT_Pointer
serial_ai(ASN1_INTEGER_new());
223 if (!a2i_ASN1_INTEGER(file
.get(), serial_ai
.get(), buffer
, sizeof(buffer
)))
226 Ssl::BIGNUM_Pointer
serial(ASN1_INTEGER_to_BN(serial_ai
.get(), NULL
));
231 // increase serial number.
232 Ssl::BIGNUM_Pointer
increased_serial(BN_dup(serial
.get()));
233 if (!increased_serial
)
236 BN_add_word(increased_serial
.get(), 1);
238 // save increased serial number.
239 if (BIO_seek(file
.get(), 0))
242 Ssl::ASN1_INT_Pointer
increased_serial_ai(BN_to_ASN1_INTEGER(increased_serial
.get(), NULL
));
243 if (!increased_serial_ai
)
246 i2a_ASN1_INTEGER(file
.get(), increased_serial_ai
.get());
247 BIO_puts(file
.get(),"\n");
249 return serial
.release();
252 void Ssl::CertificateDb::create(std::string
const & db_path
, int serial
)
255 throw std::runtime_error("Path to db is empty");
256 std::string
serial_full(db_path
+ "/" + serial_file
);
257 std::string
db_full(db_path
+ "/" + db_file
);
258 std::string
cert_full(db_path
+ "/" + cert_dir
);
259 std::string
size_full(db_path
+ "/" + size_file
);
262 if (mkdir(db_path
.c_str()))
264 if (mkdir(db_path
.c_str(), 0777))
266 throw std::runtime_error("Cannot create " + db_path
);
269 if (mkdir(cert_full
.c_str()))
271 if (mkdir(cert_full
.c_str(), 0777))
273 throw std::runtime_error("Cannot create " + cert_full
);
275 Ssl::ASN1_INT_Pointer
i(ASN1_INTEGER_new());
276 ASN1_INTEGER_set(i
.get(), serial
);
278 Ssl::BIO_Pointer
file(BIO_new(BIO_s_file()));
280 throw std::runtime_error("SSL error");
282 if (BIO_write_filename(file
.get(), const_cast<char *>(serial_full
.c_str())) <= 0)
283 throw std::runtime_error("Cannot open " + cert_full
+ " to open");
285 i2a_ASN1_INTEGER(file
.get(), i
.get());
287 std::ofstream
size(size_full
.c_str());
291 throw std::runtime_error("Cannot open " + size_full
+ " to open");
292 std::ofstream
db(db_full
.c_str());
294 throw std::runtime_error("Cannot open " + db_full
+ " to open");
297 void Ssl::CertificateDb::check(std::string
const & db_path
, size_t max_db_size
)
299 CertificateDb
db(db_path
, max_db_size
, 0);
302 std::string
Ssl::CertificateDb::getSNString() const
304 FileLocker
serial_locker(serial_full
);
305 std::ifstream
file(serial_full
.c_str());
313 bool Ssl::CertificateDb::pure_find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
319 row
.setValue(cnlName
, host_name
.c_str());
321 char **rrow
= TXT_DB_get_by_index(db
.get(), cnlName
, row
.getRow());
325 if (!sslDateIsInTheFuture(rrow
[cnlExp_date
])) {
326 deleteByHostname(rrow
[cnlName
]);
330 // read cert and pkey from file.
331 std::string
filename(cert_full
+ "/" + rrow
[cnlSerial
] + ".pem");
332 FileLocker
cert_locker(filename
);
333 readCertAndPrivateKeyFromFiles(cert
, pkey
, filename
.c_str(), NULL
);
339 size_t Ssl::CertificateDb::size() const
341 FileLocker
size_locker(size_full
);
345 void Ssl::CertificateDb::addSize(std::string
const & filename
)
347 FileLocker
size_locker(size_full
);
348 writeSize(readSize() + getFileSize(filename
));
351 void Ssl::CertificateDb::subSize(std::string
const & filename
)
353 FileLocker
size_locker(size_full
);
354 writeSize(readSize() - getFileSize(filename
));
357 size_t Ssl::CertificateDb::readSize() const
360 std::ifstream
size_file(size_full
.c_str());
361 if (!size_file
&& enabled_disk_store
)
362 throw std::runtime_error("cannot read \"" + size_full
+ "\" file");
363 size_file
>> db_size
;
367 void Ssl::CertificateDb::writeSize(size_t db_size
)
369 std::ofstream
size_file(size_full
.c_str());
370 if (!size_file
&& enabled_disk_store
)
371 throw std::runtime_error("cannot write \"" + size_full
+ "\" file");
372 size_file
<< db_size
;
375 size_t Ssl::CertificateDb::getFileSize(std::string
const & filename
)
377 std::ifstream
file(filename
.c_str(), std::ios::binary
);
378 file
.seekg(0, std::ios_base::end
);
379 size_t file_size
= file
.tellg();
380 return ((file_size
+ fs_block_size
- 1) / fs_block_size
) * fs_block_size
;
383 void Ssl::CertificateDb::load()
385 // Load db from file.
386 Ssl::BIO_Pointer
in(BIO_new(BIO_s_file()));
387 if (!in
|| BIO_read_filename(in
.get(), db_full
.c_str()) <= 0)
388 throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path
+ ". To initialize, run \"ssl_crtd -c -s " + db_path
+ "\".");
390 bool corrupt
= false;
391 Ssl::TXT_DB_Pointer
temp_db(TXT_DB_read(in
.get(), cnlNumber
));
395 // Create indexes in db.
396 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
397 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial
), LHASH_COMP_FN(index_serial
)))
400 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name
), LHASH_COMP_FN(index_name
)))
403 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial_hash
), LHASH_COMP_FN(index_serial_cmp
)))
406 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name_hash
), LHASH_COMP_FN(index_name_cmp
)))
411 throw std::runtime_error("The SSL certificate database " + db_path
+ " is corrupted. Please rebuild");
413 db
.reset(temp_db
.release());
416 void Ssl::CertificateDb::save()
419 throw std::runtime_error("The certificates database is not loaded");;
421 // To save the db to file, create a new BIO with BIO file methods.
422 Ssl::BIO_Pointer
out(BIO_new(BIO_s_file()));
423 if (!out
|| !BIO_write_filename(out
.get(), const_cast<char *>(db_full
.c_str())))
424 throw std::runtime_error("Failed to initialize " + db_full
+ " file for writing");;
426 if (TXT_DB_write(out
.get(), db
.get()) < 0)
427 throw std::runtime_error("Failed to write " + db_full
+ " file");
430 bool Ssl::CertificateDb::deleteInvalidCertificate()
435 bool removed_one
= false;
436 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
437 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
438 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
440 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
441 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
444 if (!sslDateIsInTheFuture(current_row
[cnlExp_date
])) {
445 std::string
filename(cert_full
+ "/" + current_row
[cnlSerial
] + ".pem");
446 FileLocker
cert_locker(filename
);
447 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
448 sk_OPENSSL_PSTRING_delete(db
.get()->data
, i
);
450 sk_delete(db
.get()->data
, i
);
453 remove(filename
.c_str());
464 bool Ssl::CertificateDb::deleteOldestCertificate()
469 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
470 if (sk_OPENSSL_PSTRING_num(db
.get()->data
) == 0)
472 if (sk_num(db
.get()->data
) == 0)
476 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
477 const char **row
= (const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, 0);
479 const char **row
= (const char **)sk_value(db
.get()->data
, 0);
481 std::string
filename(cert_full
+ "/" + row
[cnlSerial
] + ".pem");
482 FileLocker
cert_locker(filename
);
484 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
485 sk_OPENSSL_PSTRING_delete(db
.get()->data
, 0);
487 sk_delete(db
.get()->data
, 0);
491 remove(filename
.c_str());
496 bool Ssl::CertificateDb::deleteByHostname(std::string
const & host
)
501 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
502 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
503 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
505 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
506 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
508 if (host
== current_row
[cnlName
]) {
509 std::string
filename(cert_full
+ "/" + current_row
[cnlSerial
] + ".pem");
510 FileLocker
cert_locker(filename
);
511 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
512 sk_OPENSSL_PSTRING_delete(db
.get()->data
, i
);
514 sk_delete(db
.get()->data
, i
);
517 remove(filename
.c_str());
524 bool Ssl::CertificateDb::IsEnabledDiskStore() const
526 return enabled_disk_store
;