]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/ssl/certificate_db.cc
6 #include "ssl/certificate_db.h"
26 #define HERE "(ssl_crtd) " << __FILE__ << ':' << __LINE__ << ": "
28 Ssl::Lock::Lock(std::string
const &aFilename
) :
31 hFile(INVALID_HANDLE_VALUE
)
38 bool Ssl::Lock::locked() const
41 return hFile
!= INVALID_HANDLE_VALUE
;
47 void Ssl::Lock::lock()
51 hFile
= CreateFile(TEXT(filename
.c_str()), GENERIC_READ
, 0, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
52 if (hFile
== INVALID_HANDLE_VALUE
)
54 fd
= open(filename
.c_str(), 0);
57 throw std::runtime_error("Failed to open file " + filename
);
61 if (!LockFile(hFile
, 0, 0, 1, 0))
63 if (flock(fd
, LOCK_EX
) != 0)
65 throw std::runtime_error("Failed to get a lock of " + filename
);
68 void Ssl::Lock::unlock()
71 if (hFile
!= INVALID_HANDLE_VALUE
) {
72 UnlockFile(hFile
, 0, 0, 1, 0);
74 hFile
= INVALID_HANDLE_VALUE
;
84 throw std::runtime_error("Lock is already unlocked for " + filename
);
93 Ssl::Locker::Locker(Lock
&aLock
, const char *aFileName
, int aLineNo
):
94 weLocked(false), lock(aLock
), fileName(aFileName
), lineNo(aLineNo
)
102 Ssl::Locker::~Locker()
108 Ssl::CertificateDb::Row::Row()
111 row
= new char *[width
+ 1];
112 for (size_t i
= 0; i
< width
+ 1; i
++)
116 Ssl::CertificateDb::Row::~Row()
119 for (size_t i
= 0; i
< width
+ 1; i
++) {
126 void Ssl::CertificateDb::Row::reset()
131 void Ssl::CertificateDb::Row::setValue(size_t cell
, char const * value
)
133 assert(cell
< width
);
138 row
[cell
] = static_cast<char *>(malloc(sizeof(char) * (strlen(value
) + 1)));
139 memcpy(row
[cell
], value
, sizeof(char) * (strlen(value
) + 1));
144 char ** Ssl::CertificateDb::Row::getRow()
149 unsigned long Ssl::CertificateDb::index_serial_hash(const char **a
)
151 const char *n
= a
[Ssl::CertificateDb::cnlSerial
];
152 while (*n
== '0') n
++;
153 return lh_strhash(n
);
156 int Ssl::CertificateDb::index_serial_cmp(const char **a
, const char **b
)
159 for (aa
= a
[Ssl::CertificateDb::cnlSerial
]; *aa
== '0'; aa
++);
160 for (bb
= b
[Ssl::CertificateDb::cnlSerial
]; *bb
== '0'; bb
++);
161 return strcmp(aa
, bb
);
164 unsigned long Ssl::CertificateDb::index_name_hash(const char **a
)
166 return(lh_strhash(a
[Ssl::CertificateDb::cnlName
]));
169 int Ssl::CertificateDb::index_name_cmp(const char **a
, const char **b
)
171 return(strcmp(a
[Ssl::CertificateDb::cnlName
], b
[CertificateDb::cnlName
]));
174 const std::string
Ssl::CertificateDb::db_file("index.txt");
175 const std::string
Ssl::CertificateDb::cert_dir("certs");
176 const std::string
Ssl::CertificateDb::size_file("size");
178 Ssl::CertificateDb::CertificateDb(std::string
const & aDb_path
, size_t aMax_db_size
, size_t aFs_block_size
)
180 db_full(aDb_path
+ "/" + db_file
),
181 cert_full(aDb_path
+ "/" + cert_dir
),
182 size_full(aDb_path
+ "/" + size_file
),
184 max_db_size(aMax_db_size
),
185 fs_block_size(aFs_block_size
),
187 enabled_disk_store(true)
189 if (db_path
.empty() && !max_db_size
)
190 enabled_disk_store
= false;
191 else if ((db_path
.empty() && max_db_size
) || (!db_path
.empty() && !max_db_size
))
192 throw std::runtime_error("ssl_crtd is missing the required parameter. There should be -s and -M parameters together.");
195 bool Ssl::CertificateDb::find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
197 const Locker
locker(dbLock
, Here
);
199 return pure_find(host_name
, cert
, pkey
);
202 bool Ssl::CertificateDb::purgeCert(std::string
const & key
)
204 const Locker
locker(dbLock
, Here
);
209 if (!deleteByHostname(key
))
216 bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
, std::string
const & useName
)
218 const Locker
locker(dbLock
, Here
);
220 if (!db
|| !cert
|| !pkey
)
223 ASN1_INTEGER
* ai
= X509_get_serialNumber(cert
.get());
224 std::string serial_string
;
225 Ssl::BIGNUM_Pointer
serial(ASN1_INTEGER_to_BN(ai
, NULL
));
227 TidyPointer
<char, tidyFree
> hex_bn(BN_bn2hex(serial
.get()));
228 serial_string
= std::string(hex_bn
.get());
230 row
.setValue(cnlSerial
, serial_string
.c_str());
231 char ** rrow
= TXT_DB_get_by_index(db
.get(), cnlSerial
, row
.getRow());
232 // We are creating certificates with unique serial number. If the serial
233 // number found in the database, means that the certificate already exist
239 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
240 if (pure_find(useName
.empty() ? subject
.get() : useName
, cert
, pkey
))
244 // check db size while trying to minimize calls to size()
245 while (size() > max_db_size
) {
246 if (deleteInvalidCertificate())
247 continue; // try to find another invalid certificate if needed
249 // there are no more invalid ones, but there must be valid certificates
251 if (!deleteOldestCertificate())
252 return false; // errors prevented us from freeing enough space
253 } while (size() > max_db_size
);
257 row
.setValue(cnlType
, "V");
258 ASN1_UTCTIME
* tm
= X509_get_notAfter(cert
.get());
259 row
.setValue(cnlExp_date
, std::string(reinterpret_cast<char *>(tm
->data
), tm
->length
).c_str());
260 row
.setValue(cnlFile
, "unknown");
261 if (!useName
.empty())
262 row
.setValue(cnlName
, useName
.c_str());
264 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
265 row
.setValue(cnlName
, subject
.get());
268 if (!TXT_DB_insert(db
.get(), row
.getRow()))
272 std::string
filename(cert_full
+ "/" + serial_string
+ ".pem");
273 if (!writeCertAndPrivateKeyToFile(cert
, pkey
, filename
.c_str()))
281 void Ssl::CertificateDb::create(std::string
const & db_path
)
284 throw std::runtime_error("Path to db is empty");
285 std::string
db_full(db_path
+ "/" + db_file
);
286 std::string
cert_full(db_path
+ "/" + cert_dir
);
287 std::string
size_full(db_path
+ "/" + size_file
);
290 if (mkdir(db_path
.c_str()))
292 if (mkdir(db_path
.c_str(), 0777))
294 throw std::runtime_error("Cannot create " + db_path
);
297 if (mkdir(cert_full
.c_str()))
299 if (mkdir(cert_full
.c_str(), 0777))
301 throw std::runtime_error("Cannot create " + cert_full
);
303 std::ofstream
size(size_full
.c_str());
307 throw std::runtime_error("Cannot open " + size_full
+ " to open");
308 std::ofstream
db(db_full
.c_str());
310 throw std::runtime_error("Cannot open " + db_full
+ " to open");
313 void Ssl::CertificateDb::check(std::string
const & db_path
, size_t max_db_size
)
315 CertificateDb
db(db_path
, max_db_size
, 0);
319 bool Ssl::CertificateDb::pure_find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
325 row
.setValue(cnlName
, host_name
.c_str());
327 char **rrow
= TXT_DB_get_by_index(db
.get(), cnlName
, row
.getRow());
331 if (!sslDateIsInTheFuture(rrow
[cnlExp_date
])) {
332 deleteByHostname(rrow
[cnlName
]);
336 // read cert and pkey from file.
337 std::string
filename(cert_full
+ "/" + rrow
[cnlSerial
] + ".pem");
338 readCertAndPrivateKeyFromFiles(cert
, pkey
, filename
.c_str(), NULL
);
344 size_t Ssl::CertificateDb::size() const
349 void Ssl::CertificateDb::addSize(std::string
const & filename
)
351 writeSize(readSize() + getFileSize(filename
));
354 void Ssl::CertificateDb::subSize(std::string
const & filename
)
356 writeSize(readSize() - getFileSize(filename
));
359 size_t Ssl::CertificateDb::readSize() const
361 std::ifstream
size_file(size_full
.c_str());
362 if (!size_file
&& enabled_disk_store
)
363 throw std::runtime_error("cannot open for reading: " + size_full
);
365 if (!(size_file
>> db_size
))
366 throw std::runtime_error("error while reading " + size_full
);
370 void Ssl::CertificateDb::writeSize(size_t db_size
)
372 std::ofstream
size_file(size_full
.c_str());
373 if (!size_file
&& enabled_disk_store
)
374 throw std::runtime_error("cannot write \"" + size_full
+ "\" file");
375 size_file
<< db_size
;
378 size_t Ssl::CertificateDb::getFileSize(std::string
const & filename
)
380 std::ifstream
file(filename
.c_str(), std::ios::binary
);
381 file
.seekg(0, std::ios_base::end
);
382 size_t file_size
= file
.tellg();
383 return ((file_size
+ fs_block_size
- 1) / fs_block_size
) * fs_block_size
;
386 void Ssl::CertificateDb::load()
388 // Load db from file.
389 Ssl::BIO_Pointer
in(BIO_new(BIO_s_file()));
390 if (!in
|| BIO_read_filename(in
.get(), db_full
.c_str()) <= 0)
391 throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path
+ ". To initialize, run \"ssl_crtd -c -s " + db_path
+ "\".");
393 bool corrupt
= false;
394 Ssl::TXT_DB_Pointer
temp_db(TXT_DB_read(in
.get(), cnlNumber
));
398 // Create indexes in db.
399 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
400 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial
), LHASH_COMP_FN(index_serial
)))
403 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name
), LHASH_COMP_FN(index_name
)))
406 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial_hash
), LHASH_COMP_FN(index_serial_cmp
)))
409 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name_hash
), LHASH_COMP_FN(index_name_cmp
)))
414 throw std::runtime_error("The SSL certificate database " + db_path
+ " is corrupted. Please rebuild");
416 db
.reset(temp_db
.release());
419 void Ssl::CertificateDb::save()
422 throw std::runtime_error("The certificates database is not loaded");;
424 // To save the db to file, create a new BIO with BIO file methods.
425 Ssl::BIO_Pointer
out(BIO_new(BIO_s_file()));
426 if (!out
|| !BIO_write_filename(out
.get(), const_cast<char *>(db_full
.c_str())))
427 throw std::runtime_error("Failed to initialize " + db_full
+ " file for writing");;
429 if (TXT_DB_write(out
.get(), db
.get()) < 0)
430 throw std::runtime_error("Failed to write " + db_full
+ " file");
433 // Normally defined in defines.h file
434 #define countof(arr) (sizeof(arr)/sizeof(*arr))
435 void Ssl::CertificateDb::deleteRow(const char **row
, int rowIndex
)
437 const std::string
filename(cert_full
+ "/" + row
[cnlSerial
] + ".pem");
438 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
439 sk_OPENSSL_PSTRING_delete(db
.get()->data
, rowIndex
);
441 sk_delete(db
.get()->data
, rowIndex
);
444 const Columns db_indexes
[]={cnlSerial
, cnlName
};
445 for (unsigned int i
= 0; i
< countof(db_indexes
); i
++) {
446 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
447 if (LHASH_OF(OPENSSL_STRING
) *fieldIndex
= db
.get()->index
[db_indexes
[i
]])
448 lh_OPENSSL_STRING_delete(fieldIndex
, (char **)row
);
450 if (LHASH
*fieldIndex
= db
.get()->index
[db_indexes
[i
]])
451 lh_delete(fieldIndex
, row
);
456 int ret
= remove(filename
.c_str());
457 if (ret
< 0 && errno
!= ENOENT
)
458 throw std::runtime_error("Failed to remove certficate file " + filename
+ " from db");
461 bool Ssl::CertificateDb::deleteInvalidCertificate()
466 bool removed_one
= false;
467 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
468 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
469 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
471 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
472 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
475 if (!sslDateIsInTheFuture(current_row
[cnlExp_date
])) {
476 deleteRow(current_row
, i
);
487 bool Ssl::CertificateDb::deleteOldestCertificate()
492 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
493 if (sk_OPENSSL_PSTRING_num(db
.get()->data
) == 0)
495 if (sk_num(db
.get()->data
) == 0)
499 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
500 const char **row
= (const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, 0);
502 const char **row
= (const char **)sk_value(db
.get()->data
, 0);
510 bool Ssl::CertificateDb::deleteByHostname(std::string
const & host
)
515 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
516 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
517 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
519 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
520 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
522 if (host
== current_row
[cnlName
]) {
523 deleteRow(current_row
, i
);
530 bool Ssl::CertificateDb::IsEnabledDiskStore() const
532 return enabled_disk_store
;