]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
ef57eb7b | 2 | * Copyright (C) 1996-2016 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/certificate_db.h" |
074d6a40 AJ |
11 | |
12 | #include <cerrno> | |
95d2589c | 13 | #include <fstream> |
95d2589c | 14 | #include <stdexcept> |
95d2589c CT |
15 | #if HAVE_SYS_STAT_H |
16 | #include <sys/stat.h> | |
17 | #endif | |
18 | #if HAVE_SYS_FILE_H | |
19 | #include <sys/file.h> | |
20 | #endif | |
21 | #if HAVE_FCNTL_H | |
22 | #include <fcntl.h> | |
23 | #endif | |
24 | ||
5b3d088d CT |
25 | #define HERE "(ssl_crtd) " << __FILE__ << ':' << __LINE__ << ": " |
26 | ||
27 | Ssl::Lock::Lock(std::string const &aFilename) : | |
f53969cc | 28 | filename(aFilename), |
7aa9bb3e | 29 | #if _SQUID_WINDOWS_ |
f53969cc | 30 | hFile(INVALID_HANDLE_VALUE) |
5b3d088d | 31 | #else |
f53969cc | 32 | fd(-1) |
5b3d088d CT |
33 | #endif |
34 | { | |
35 | } | |
36 | ||
37 | bool Ssl::Lock::locked() const | |
95d2589c | 38 | { |
7aa9bb3e | 39 | #if _SQUID_WINDOWS_ |
5b3d088d CT |
40 | return hFile != INVALID_HANDLE_VALUE; |
41 | #else | |
42 | return fd != -1; | |
43 | #endif | |
44 | } | |
45 | ||
46 | void Ssl::Lock::lock() | |
47 | { | |
48 | ||
7aa9bb3e | 49 | #if _SQUID_WINDOWS_ |
95d2589c | 50 | hFile = CreateFile(TEXT(filename.c_str()), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); |
5b3d088d | 51 | if (hFile == INVALID_HANDLE_VALUE) |
95d2589c | 52 | #else |
e071de0c | 53 | fd = open(filename.c_str(), O_RDWR); |
5b3d088d | 54 | if (fd == -1) |
95d2589c | 55 | #endif |
5b3d088d CT |
56 | throw std::runtime_error("Failed to open file " + filename); |
57 | ||
7aa9bb3e | 58 | #if _SQUID_WINDOWS_ |
5b3d088d | 59 | if (!LockFile(hFile, 0, 0, 1, 0)) |
e5c56e0a | 60 | #elif _SQUID_SOLARIS_ |
e071de0c | 61 | if (lockf(fd, F_LOCK, 0) != 0) |
e5c56e0a AJ |
62 | #else |
63 | if (flock(fd, LOCK_EX) != 0) | |
5b3d088d CT |
64 | #endif |
65 | throw std::runtime_error("Failed to get a lock of " + filename); | |
95d2589c CT |
66 | } |
67 | ||
5b3d088d | 68 | void Ssl::Lock::unlock() |
e29ccb57 | 69 | { |
7aa9bb3e | 70 | #if _SQUID_WINDOWS_ |
95d2589c CT |
71 | if (hFile != INVALID_HANDLE_VALUE) { |
72 | UnlockFile(hFile, 0, 0, 1, 0); | |
73 | CloseHandle(hFile); | |
5b3d088d | 74 | hFile = INVALID_HANDLE_VALUE; |
95d2589c CT |
75 | } |
76 | #else | |
77 | if (fd != -1) { | |
e5c56e0a | 78 | #if _SQUID_SOLARIS_ |
e071de0c | 79 | lockf(fd, F_ULOCK, 0); |
e5c56e0a | 80 | #else |
0485c6e1 | 81 | flock(fd, LOCK_UN); |
e5c56e0a | 82 | #endif |
95d2589c | 83 | close(fd); |
5b3d088d | 84 | fd = -1; |
95d2589c CT |
85 | } |
86 | #endif | |
5b3d088d CT |
87 | else |
88 | throw std::runtime_error("Lock is already unlocked for " + filename); | |
89 | } | |
90 | ||
91 | Ssl::Lock::~Lock() | |
92 | { | |
93 | if (locked()) | |
94 | unlock(); | |
95 | } | |
96 | ||
e29ccb57 | 97 | Ssl::Locker::Locker(Lock &aLock, const char *aFileName, int aLineNo): |
f53969cc | 98 | weLocked(false), lock(aLock), fileName(aFileName), lineNo(aLineNo) |
5b3d088d CT |
99 | { |
100 | if (!lock.locked()) { | |
101 | lock.lock(); | |
102 | weLocked = true; | |
103 | } | |
104 | } | |
105 | ||
106 | Ssl::Locker::~Locker() | |
107 | { | |
108 | if (weLocked) | |
109 | lock.unlock(); | |
95d2589c CT |
110 | } |
111 | ||
112 | Ssl::CertificateDb::Row::Row() | |
f53969cc | 113 | : width(cnlNumber) |
95d2589c | 114 | { |
f385ac29 | 115 | row = (char **)OPENSSL_malloc(sizeof(char *) * (width + 1)); |
d7ae3534 | 116 | for (size_t i = 0; i < width + 1; ++i) |
95d2589c CT |
117 | row[i] = NULL; |
118 | } | |
119 | ||
f385ac29 CT |
120 | Ssl::CertificateDb::Row::Row(char **aRow, size_t aWidth): width(aWidth) |
121 | { | |
122 | row = aRow; | |
123 | } | |
124 | ||
95d2589c CT |
125 | Ssl::CertificateDb::Row::~Row() |
126 | { | |
f385ac29 CT |
127 | if (!row) |
128 | return; | |
129 | ||
130 | void *max; | |
131 | if ((max = (void *)row[width]) != NULL) { | |
132 | // It is an openSSL allocated row. The TXT_DB_read function stores the | |
133 | // index and row items one one memory segment. The row[width] points | |
134 | // to the end of buffer. We have to check for items in the array which | |
135 | // are not stored in this segment. These items should released. | |
d7ae3534 | 136 | for (size_t i = 0; i < width + 1; ++i) { |
f385ac29 CT |
137 | if (((row[i] < (char *)row) || (row[i] > max)) && (row[i] != NULL)) |
138 | OPENSSL_free(row[i]); | |
139 | } | |
140 | } else { | |
141 | for (size_t i = 0; i < width + 1; ++i) { | |
142 | if (row[i]) | |
143 | OPENSSL_free(row[i]); | |
95d2589c | 144 | } |
95d2589c | 145 | } |
f385ac29 | 146 | OPENSSL_free(row); |
95d2589c CT |
147 | } |
148 | ||
149 | void Ssl::CertificateDb::Row::reset() | |
150 | { | |
151 | row = NULL; | |
152 | } | |
153 | ||
154 | void Ssl::CertificateDb::Row::setValue(size_t cell, char const * value) | |
155 | { | |
156 | assert(cell < width); | |
157 | if (row[cell]) { | |
158 | free(row[cell]); | |
159 | } | |
160 | if (value) { | |
f385ac29 | 161 | row[cell] = static_cast<char *>(OPENSSL_malloc(sizeof(char) * (strlen(value) + 1))); |
95d2589c CT |
162 | memcpy(row[cell], value, sizeof(char) * (strlen(value) + 1)); |
163 | } else | |
164 | row[cell] = NULL; | |
165 | } | |
166 | ||
167 | char ** Ssl::CertificateDb::Row::getRow() | |
168 | { | |
169 | return row; | |
170 | } | |
171 | ||
f385ac29 CT |
172 | void Ssl::CertificateDb::sq_TXT_DB_delete(TXT_DB *db, const char **row) |
173 | { | |
174 | if (!db) | |
175 | return; | |
176 | ||
fee5325b | 177 | #if SQUID_SSLTXTDB_PSTRINGDATA |
f385ac29 | 178 | for (int i = 0; i < sk_OPENSSL_PSTRING_num(db->data); ++i) { |
19179f7c CT |
179 | #if SQUID_STACKOF_PSTRINGDATA_HACK |
180 | const char ** current_row = ((const char **)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, db->data), i)); | |
181 | #else | |
f385ac29 | 182 | const char ** current_row = ((const char **)sk_OPENSSL_PSTRING_value(db->data, i)); |
19179f7c | 183 | #endif |
f385ac29 CT |
184 | #else |
185 | for (int i = 0; i < sk_num(db->data); ++i) { | |
186 | const char ** current_row = ((const char **)sk_value(db->data, i)); | |
187 | #endif | |
188 | if (current_row == row) { | |
189 | sq_TXT_DB_delete_row(db, i); | |
190 | return; | |
191 | } | |
192 | } | |
193 | } | |
194 | ||
195 | #define countof(arr) (sizeof(arr)/sizeof(*arr)) | |
d74740bd | 196 | void Ssl::CertificateDb::sq_TXT_DB_delete_row(TXT_DB *db, int idx) { |
f385ac29 | 197 | char **rrow; |
fee5325b | 198 | #if SQUID_SSLTXTDB_PSTRINGDATA |
f385ac29 CT |
199 | rrow = (char **)sk_OPENSSL_PSTRING_delete(db->data, idx); |
200 | #else | |
201 | rrow = (char **)sk_delete(db->data, idx); | |
202 | #endif | |
203 | ||
204 | if (!rrow) | |
205 | return; | |
206 | ||
207 | Row row(rrow, cnlNumber); // row wrapper used to free the rrow | |
208 | ||
f53969cc | 209 | const Columns db_indexes[]= {cnlSerial, cnlName}; |
f385ac29 CT |
210 | for (unsigned int i = 0; i < countof(db_indexes); ++i) { |
211 | void *data = NULL; | |
fee5325b | 212 | #if SQUID_SSLTXTDB_PSTRINGDATA |
f385ac29 CT |
213 | if (LHASH_OF(OPENSSL_STRING) *fieldIndex = db->index[db_indexes[i]]) |
214 | data = lh_OPENSSL_STRING_delete(fieldIndex, rrow); | |
215 | #else | |
216 | if (LHASH *fieldIndex = db->index[db_indexes[i]]) | |
217 | data = lh_delete(fieldIndex, rrow); | |
218 | #endif | |
219 | if (data) | |
220 | assert(data == rrow); | |
221 | } | |
222 | } | |
223 | ||
d74740bd | 224 | unsigned long Ssl::CertificateDb::index_serial_hash(const char **a) { |
95d2589c | 225 | const char *n = a[Ssl::CertificateDb::cnlSerial]; |
d7ae3534 FC |
226 | while (*n == '0') |
227 | ++n; | |
95d2589c CT |
228 | return lh_strhash(n); |
229 | } | |
230 | ||
d74740bd | 231 | int Ssl::CertificateDb::index_serial_cmp(const char **a, const char **b) { |
95d2589c | 232 | const char *aa, *bb; |
d7ae3534 FC |
233 | for (aa = a[Ssl::CertificateDb::cnlSerial]; *aa == '0'; ++aa); |
234 | for (bb = b[Ssl::CertificateDb::cnlSerial]; *bb == '0'; ++bb); | |
95d2589c CT |
235 | return strcmp(aa, bb); |
236 | } | |
237 | ||
d74740bd | 238 | unsigned long Ssl::CertificateDb::index_name_hash(const char **a) { |
95d2589c CT |
239 | return(lh_strhash(a[Ssl::CertificateDb::cnlName])); |
240 | } | |
241 | ||
d74740bd | 242 | int Ssl::CertificateDb::index_name_cmp(const char **a, const char **b) { |
95d2589c CT |
243 | return(strcmp(a[Ssl::CertificateDb::cnlName], b[CertificateDb::cnlName])); |
244 | } | |
245 | ||
95d2589c CT |
246 | const std::string Ssl::CertificateDb::db_file("index.txt"); |
247 | const std::string Ssl::CertificateDb::cert_dir("certs"); | |
248 | const std::string Ssl::CertificateDb::size_file("size"); | |
95d2589c CT |
249 | |
250 | Ssl::CertificateDb::CertificateDb(std::string const & aDb_path, size_t aMax_db_size, size_t aFs_block_size) | |
f53969cc SM |
251 | : db_path(aDb_path), |
252 | db_full(aDb_path + "/" + db_file), | |
253 | cert_full(aDb_path + "/" + cert_dir), | |
254 | size_full(aDb_path + "/" + size_file), | |
255 | db(NULL), | |
256 | max_db_size(aMax_db_size), | |
257 | fs_block_size((aFs_block_size ? aFs_block_size : 2048)), | |
258 | dbLock(db_full), | |
259 | enabled_disk_store(true) { | |
95d2589c CT |
260 | if (db_path.empty() && !max_db_size) |
261 | enabled_disk_store = false; | |
262 | else if ((db_path.empty() && max_db_size) || (!db_path.empty() && !max_db_size)) | |
263 | throw std::runtime_error("ssl_crtd is missing the required parameter. There should be -s and -M parameters together."); | |
95d2589c CT |
264 | } |
265 | ||
f97700a0 | 266 | bool Ssl::CertificateDb::find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey) { |
5b3d088d | 267 | const Locker locker(dbLock, Here); |
95d2589c CT |
268 | load(); |
269 | return pure_find(host_name, cert, pkey); | |
270 | } | |
271 | ||
d74740bd | 272 | bool Ssl::CertificateDb::purgeCert(std::string const & key) { |
4ece76b2 CT |
273 | const Locker locker(dbLock, Here); |
274 | load(); | |
275 | if (!db) | |
276 | return false; | |
277 | ||
278 | if (!deleteByHostname(key)) | |
279 | return false; | |
280 | ||
281 | save(); | |
282 | return true; | |
283 | } | |
284 | ||
f97700a0 | 285 | bool Ssl::CertificateDb::addCertAndPrivateKey(Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey, std::string const & useName) { |
5b3d088d | 286 | const Locker locker(dbLock, Here); |
95d2589c | 287 | load(); |
08e152fe | 288 | if (!db || !cert || !pkey) |
95d2589c CT |
289 | return false; |
290 | Row row; | |
291 | ASN1_INTEGER * ai = X509_get_serialNumber(cert.get()); | |
292 | std::string serial_string; | |
293 | Ssl::BIGNUM_Pointer serial(ASN1_INTEGER_to_BN(ai, NULL)); | |
294 | { | |
295 | TidyPointer<char, tidyFree> hex_bn(BN_bn2hex(serial.get())); | |
296 | serial_string = std::string(hex_bn.get()); | |
297 | } | |
298 | row.setValue(cnlSerial, serial_string.c_str()); | |
299 | char ** rrow = TXT_DB_get_by_index(db.get(), cnlSerial, row.getRow()); | |
7a957a93 AR |
300 | // We are creating certificates with unique serial numbers. If the serial |
301 | // number is found in the database, the same certificate is already stored. | |
f385ac29 CT |
302 | if (rrow != NULL) { |
303 | // TODO: check if the stored row is valid. | |
a0b971d5 | 304 | return true; |
f385ac29 | 305 | } |
95d2589c CT |
306 | |
307 | { | |
308 | TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0)); | |
f97700a0 | 309 | Security::CertPointer findCert; |
f385ac29 CT |
310 | Ssl::EVP_PKEY_Pointer findPkey; |
311 | if (pure_find(useName.empty() ? subject.get() : useName, findCert, findPkey)) { | |
312 | // Replace with database certificate | |
313 | cert.reset(findCert.release()); | |
314 | pkey.reset(findPkey.release()); | |
95d2589c | 315 | return true; |
f385ac29 | 316 | } |
a42222c5 | 317 | // pure_find may fail because the entry is expired, or because the |
f385ac29 CT |
318 | // certs file is corrupted. Remove any entry with given hostname |
319 | deleteByHostname(useName.empty() ? subject.get() : useName); | |
95d2589c | 320 | } |
95d2589c | 321 | |
08e152fe | 322 | // check db size while trying to minimize calls to size() |
541e6ab5 CT |
323 | size_t dbSize = size(); |
324 | if ((dbSize == 0 && hasRows()) || | |
4ae148ef SM |
325 | (dbSize > 0 && !hasRows()) || |
326 | (dbSize > 10 * max_db_size)) { | |
541e6ab5 CT |
327 | // Invalid database size, rebuild |
328 | dbSize = rebuildSize(); | |
329 | } | |
330 | while (dbSize > max_db_size && deleteInvalidCertificate()) { | |
331 | dbSize = size(); // get the current database size | |
332 | // and try to find another invalid certificate if needed | |
333 | } | |
334 | // there are no more invalid ones, but there must be valid certificates | |
4ae148ef | 335 | while (dbSize > max_db_size) { |
541e6ab5 CT |
336 | if (!deleteOldestCertificate()) { |
337 | rebuildSize(); // No certificates in database.Update the size file. | |
338 | save(); // Some entries may have been removed. Update the index file. | |
339 | return false; // errors prevented us from freeing enough space | |
340 | } | |
341 | dbSize = size(); // get the current database size | |
95d2589c CT |
342 | } |
343 | ||
344 | row.setValue(cnlType, "V"); | |
345 | ASN1_UTCTIME * tm = X509_get_notAfter(cert.get()); | |
346 | row.setValue(cnlExp_date, std::string(reinterpret_cast<char *>(tm->data), tm->length).c_str()); | |
347 | row.setValue(cnlFile, "unknown"); | |
fb2178bb CT |
348 | if (!useName.empty()) |
349 | row.setValue(cnlName, useName.c_str()); | |
350 | else { | |
95d2589c CT |
351 | TidyPointer<char, tidyFree> subject(X509_NAME_oneline(X509_get_subject_name(cert.get()), NULL, 0)); |
352 | row.setValue(cnlName, subject.get()); | |
353 | } | |
354 | ||
f385ac29 CT |
355 | if (!TXT_DB_insert(db.get(), row.getRow())) { |
356 | // failed to add index (???) but we may have already modified | |
357 | // the database so save before exit | |
358 | save(); | |
95d2589c | 359 | return false; |
f385ac29 CT |
360 | } |
361 | rrow = row.getRow(); | |
95d2589c | 362 | row.reset(); |
f385ac29 | 363 | |
95d2589c | 364 | std::string filename(cert_full + "/" + serial_string + ".pem"); |
f385ac29 CT |
365 | if (!writeCertAndPrivateKeyToFile(cert, pkey, filename.c_str())) { |
366 | //remove row from txt_db and save | |
367 | sq_TXT_DB_delete(db.get(), (const char **)rrow); | |
368 | save(); | |
95d2589c | 369 | return false; |
f385ac29 | 370 | } |
95d2589c CT |
371 | addSize(filename); |
372 | ||
373 | save(); | |
374 | return true; | |
375 | } | |
376 | ||
d74740bd | 377 | void Ssl::CertificateDb::create(std::string const & db_path) { |
95d2589c CT |
378 | if (db_path == "") |
379 | throw std::runtime_error("Path to db is empty"); | |
95d2589c CT |
380 | std::string db_full(db_path + "/" + db_file); |
381 | std::string cert_full(db_path + "/" + cert_dir); | |
382 | std::string size_full(db_path + "/" + size_file); | |
383 | ||
95d2589c | 384 | if (mkdir(db_path.c_str(), 0777)) |
95d2589c CT |
385 | throw std::runtime_error("Cannot create " + db_path); |
386 | ||
95d2589c | 387 | if (mkdir(cert_full.c_str(), 0777)) |
95d2589c CT |
388 | throw std::runtime_error("Cannot create " + cert_full); |
389 | ||
95d2589c CT |
390 | std::ofstream size(size_full.c_str()); |
391 | if (size) | |
392 | size << 0; | |
393 | else | |
394 | throw std::runtime_error("Cannot open " + size_full + " to open"); | |
395 | std::ofstream db(db_full.c_str()); | |
396 | if (!db) | |
397 | throw std::runtime_error("Cannot open " + db_full + " to open"); | |
398 | } | |
399 | ||
a066059b CT |
400 | void Ssl::CertificateDb::check(std::string const & db_path, size_t max_db_size, size_t fs_block_size) { |
401 | CertificateDb db(db_path, max_db_size, fs_block_size); | |
5b3d088d | 402 | db.load(); |
a066059b CT |
403 | |
404 | // Call readSize to force rebuild size file in the case it is corrupted | |
405 | (void)db.readSize(); | |
406 | } | |
407 | ||
408 | size_t Ssl::CertificateDb::rebuildSize() | |
409 | { | |
410 | size_t dbSize = 0; | |
411 | #if SQUID_SSLTXTDB_PSTRINGDATA | |
412 | for (int i = 0; i < sk_OPENSSL_PSTRING_num(db.get()->data); ++i) { | |
413 | #if SQUID_STACKOF_PSTRINGDATA_HACK | |
414 | const char ** current_row = ((const char **)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, db.get()->data), i)); | |
415 | #else | |
416 | const char ** current_row = ((const char **)sk_OPENSSL_PSTRING_value(db.get()->data, i)); | |
417 | #endif | |
418 | #else | |
419 | for (int i = 0; i < sk_num(db.get()->data); ++i) { | |
420 | const char ** current_row = ((const char **)sk_value(db.get()->data, i)); | |
421 | #endif | |
422 | const std::string filename(cert_full + "/" + current_row[cnlSerial] + ".pem"); | |
423 | const size_t fSize = getFileSize(filename); | |
f53969cc | 424 | dbSize += fSize; |
a066059b CT |
425 | } |
426 | writeSize(dbSize); | |
427 | return dbSize; | |
95d2589c CT |
428 | } |
429 | ||
f97700a0 | 430 | bool Ssl::CertificateDb::pure_find(std::string const & host_name, Security::CertPointer & cert, Ssl::EVP_PKEY_Pointer & pkey) { |
95d2589c CT |
431 | if (!db) |
432 | return false; | |
433 | ||
434 | Row row; | |
435 | row.setValue(cnlName, host_name.c_str()); | |
436 | ||
437 | char **rrow = TXT_DB_get_by_index(db.get(), cnlName, row.getRow()); | |
438 | if (rrow == NULL) | |
439 | return false; | |
440 | ||
f385ac29 | 441 | if (!sslDateIsInTheFuture(rrow[cnlExp_date])) |
95d2589c | 442 | return false; |
95d2589c CT |
443 | |
444 | // read cert and pkey from file. | |
445 | std::string filename(cert_full + "/" + rrow[cnlSerial] + ".pem"); | |
95d2589c CT |
446 | readCertAndPrivateKeyFromFiles(cert, pkey, filename.c_str(), NULL); |
447 | if (!cert || !pkey) | |
448 | return false; | |
449 | return true; | |
450 | } | |
451 | ||
a066059b | 452 | size_t Ssl::CertificateDb::size() { |
95d2589c CT |
453 | return readSize(); |
454 | } | |
455 | ||
d74740bd | 456 | void Ssl::CertificateDb::addSize(std::string const & filename) { |
a066059b CT |
457 | // readSize will rebuild 'size' file if missing or it is corrupted |
458 | size_t dbSize = readSize(); | |
459 | dbSize += getFileSize(filename); | |
460 | writeSize(dbSize); | |
95d2589c CT |
461 | } |
462 | ||
d74740bd | 463 | void Ssl::CertificateDb::subSize(std::string const & filename) { |
a066059b CT |
464 | // readSize will rebuild 'size' file if missing or it is corrupted |
465 | size_t dbSize = readSize(); | |
541e6ab5 CT |
466 | const size_t fileSize = getFileSize(filename); |
467 | dbSize = dbSize > fileSize ? dbSize - fileSize : 0; | |
a066059b | 468 | writeSize(dbSize); |
95d2589c CT |
469 | } |
470 | ||
a066059b | 471 | size_t Ssl::CertificateDb::readSize() { |
f318b8e9 | 472 | std::ifstream ifstr(size_full.c_str()); |
08e152fe | 473 | size_t db_size = 0; |
a066059b CT |
474 | if (!ifstr || !(ifstr >> db_size)) |
475 | return rebuildSize(); | |
95d2589c CT |
476 | return db_size; |
477 | } | |
478 | ||
d74740bd | 479 | void Ssl::CertificateDb::writeSize(size_t db_size) { |
f318b8e9 | 480 | std::ofstream ofstr(size_full.c_str()); |
a066059b | 481 | if (!ofstr) |
95d2589c | 482 | throw std::runtime_error("cannot write \"" + size_full + "\" file"); |
f318b8e9 | 483 | ofstr << db_size; |
95d2589c CT |
484 | } |
485 | ||
d74740bd | 486 | size_t Ssl::CertificateDb::getFileSize(std::string const & filename) { |
95d2589c | 487 | std::ifstream file(filename.c_str(), std::ios::binary); |
a066059b CT |
488 | if (!file) |
489 | return 0; | |
95d2589c | 490 | file.seekg(0, std::ios_base::end); |
541e6ab5 CT |
491 | const std::streampos file_size = file.tellg(); |
492 | if (file_size < 0) | |
493 | return 0; | |
494 | return ((static_cast<size_t>(file_size) + fs_block_size - 1) / fs_block_size) * fs_block_size; | |
95d2589c CT |
495 | } |
496 | ||
d74740bd | 497 | void Ssl::CertificateDb::load() { |
95d2589c CT |
498 | // Load db from file. |
499 | Ssl::BIO_Pointer in(BIO_new(BIO_s_file())); | |
500 | if (!in || BIO_read_filename(in.get(), db_full.c_str()) <= 0) | |
501 | throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path + ". To initialize, run \"ssl_crtd -c -s " + db_path + "\"."); | |
502 | ||
503 | bool corrupt = false; | |
504 | Ssl::TXT_DB_Pointer temp_db(TXT_DB_read(in.get(), cnlNumber)); | |
505 | if (!temp_db) | |
506 | corrupt = true; | |
507 | ||
508 | // Create indexes in db. | |
509 | if (!corrupt && !TXT_DB_create_index(temp_db.get(), cnlSerial, NULL, LHASH_HASH_FN(index_serial_hash), LHASH_COMP_FN(index_serial_cmp))) | |
510 | corrupt = true; | |
511 | ||
512 | if (!corrupt && !TXT_DB_create_index(temp_db.get(), cnlName, NULL, LHASH_HASH_FN(index_name_hash), LHASH_COMP_FN(index_name_cmp))) | |
513 | corrupt = true; | |
514 | ||
515 | if (corrupt) | |
6ad16fa2 | 516 | throw std::runtime_error("The SSL certificate database " + db_path + " is corrupted. Please rebuild"); |
95d2589c CT |
517 | |
518 | db.reset(temp_db.release()); | |
519 | } | |
520 | ||
d74740bd | 521 | void Ssl::CertificateDb::save() { |
95d2589c CT |
522 | if (!db) |
523 | throw std::runtime_error("The certificates database is not loaded");; | |
524 | ||
525 | // To save the db to file, create a new BIO with BIO file methods. | |
526 | Ssl::BIO_Pointer out(BIO_new(BIO_s_file())); | |
527 | if (!out || !BIO_write_filename(out.get(), const_cast<char *>(db_full.c_str()))) | |
528 | throw std::runtime_error("Failed to initialize " + db_full + " file for writing");; | |
529 | ||
530 | if (TXT_DB_write(out.get(), db.get()) < 0) | |
531 | throw std::runtime_error("Failed to write " + db_full + " file"); | |
532 | } | |
533 | ||
1bf6c6e7 | 534 | // Normally defined in defines.h file |
d74740bd | 535 | void Ssl::CertificateDb::deleteRow(const char **row, int rowIndex) { |
1bf6c6e7 | 536 | const std::string filename(cert_full + "/" + row[cnlSerial] + ".pem"); |
f385ac29 | 537 | sq_TXT_DB_delete_row(db.get(), rowIndex); |
16fea83b | 538 | |
1bf6c6e7 CT |
539 | subSize(filename); |
540 | int ret = remove(filename.c_str()); | |
ccfc0ee9 | 541 | if (ret < 0 && errno != ENOENT) |
1bf6c6e7 CT |
542 | throw std::runtime_error("Failed to remove certficate file " + filename + " from db"); |
543 | } | |
544 | ||
d74740bd | 545 | bool Ssl::CertificateDb::deleteInvalidCertificate() { |
95d2589c CT |
546 | if (!db) |
547 | return false; | |
548 | ||
549 | bool removed_one = false; | |
fee5325b | 550 | #if SQUID_SSLTXTDB_PSTRINGDATA |
d7ae3534 | 551 | for (int i = 0; i < sk_OPENSSL_PSTRING_num(db.get()->data); ++i) { |
19179f7c CT |
552 | #if SQUID_STACKOF_PSTRINGDATA_HACK |
553 | const char ** current_row = ((const char **)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, db.get()->data), i)); | |
554 | #else | |
3eee6040 | 555 | const char ** current_row = ((const char **)sk_OPENSSL_PSTRING_value(db.get()->data, i)); |
19179f7c | 556 | #endif |
3eee6040 | 557 | #else |
d7ae3534 | 558 | for (int i = 0; i < sk_num(db.get()->data); ++i) { |
95d2589c | 559 | const char ** current_row = ((const char **)sk_value(db.get()->data, i)); |
3eee6040 | 560 | #endif |
95d2589c CT |
561 | |
562 | if (!sslDateIsInTheFuture(current_row[cnlExp_date])) { | |
1bf6c6e7 | 563 | deleteRow(current_row, i); |
95d2589c CT |
564 | removed_one = true; |
565 | break; | |
566 | } | |
567 | } | |
568 | ||
569 | if (!removed_one) | |
570 | return false; | |
571 | return true; | |
572 | } | |
573 | ||
541e6ab5 CT |
574 | bool Ssl::CertificateDb::deleteOldestCertificate() |
575 | { | |
576 | if (!hasRows()) | |
95d2589c CT |
577 | return false; |
578 | ||
fee5325b | 579 | #if SQUID_SSLTXTDB_PSTRINGDATA |
19179f7c CT |
580 | #if SQUID_STACKOF_PSTRINGDATA_HACK |
581 | const char **row = ((const char **)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, db.get()->data), 0)); | |
582 | #else | |
3eee6040 | 583 | const char **row = (const char **)sk_OPENSSL_PSTRING_value(db.get()->data, 0); |
19179f7c | 584 | #endif |
3eee6040 CT |
585 | #else |
586 | const char **row = (const char **)sk_value(db.get()->data, 0); | |
587 | #endif | |
3eee6040 | 588 | |
1bf6c6e7 | 589 | deleteRow(row, 0); |
95d2589c CT |
590 | |
591 | return true; | |
592 | } | |
593 | ||
d74740bd | 594 | bool Ssl::CertificateDb::deleteByHostname(std::string const & host) { |
95d2589c CT |
595 | if (!db) |
596 | return false; | |
597 | ||
fee5325b | 598 | #if SQUID_SSLTXTDB_PSTRINGDATA |
d7ae3534 | 599 | for (int i = 0; i < sk_OPENSSL_PSTRING_num(db.get()->data); ++i) { |
19179f7c CT |
600 | #if SQUID_STACKOF_PSTRINGDATA_HACK |
601 | const char ** current_row = ((const char **)sk_value(CHECKED_STACK_OF(OPENSSL_PSTRING, db.get()->data), i)); | |
602 | #else | |
3eee6040 | 603 | const char ** current_row = ((const char **)sk_OPENSSL_PSTRING_value(db.get()->data, i)); |
19179f7c | 604 | #endif |
3eee6040 | 605 | #else |
d7ae3534 | 606 | for (int i = 0; i < sk_num(db.get()->data); ++i) { |
95d2589c | 607 | const char ** current_row = ((const char **)sk_value(db.get()->data, i)); |
3eee6040 | 608 | #endif |
95d2589c | 609 | if (host == current_row[cnlName]) { |
1bf6c6e7 | 610 | deleteRow(current_row, i); |
95d2589c CT |
611 | return true; |
612 | } | |
613 | } | |
614 | return false; | |
615 | } | |
616 | ||
541e6ab5 CT |
617 | bool Ssl::CertificateDb::hasRows() const |
618 | { | |
619 | if (!db) | |
620 | return false; | |
621 | ||
622 | #if SQUID_SSLTXTDB_PSTRINGDATA | |
623 | if (sk_OPENSSL_PSTRING_num(db.get()->data) == 0) | |
624 | #else | |
625 | if (sk_num(db.get()->data) == 0) | |
626 | #endif | |
627 | return false; | |
628 | return true; | |
629 | } | |
630 | ||
d74740bd | 631 | bool Ssl::CertificateDb::IsEnabledDiskStore() const { |
95d2589c CT |
632 | return enabled_disk_store; |
633 | } | |
f53969cc | 634 |