]>
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::serial_file("serial");
175 const std::string
Ssl::CertificateDb::db_file("index.txt");
176 const std::string
Ssl::CertificateDb::cert_dir("certs");
177 const std::string
Ssl::CertificateDb::size_file("size");
178 const size_t Ssl::CertificateDb::min_db_size(4096);
180 Ssl::CertificateDb::CertificateDb(std::string
const & aDb_path
, size_t aMax_db_size
, size_t aFs_block_size
)
182 serial_full(aDb_path
+ "/" + serial_file
),
183 db_full(aDb_path
+ "/" + db_file
),
184 cert_full(aDb_path
+ "/" + cert_dir
),
185 size_full(aDb_path
+ "/" + size_file
),
187 max_db_size(aMax_db_size
),
188 fs_block_size(aFs_block_size
),
190 dbSerialLock(serial_full
),
191 enabled_disk_store(true)
193 if (db_path
.empty() && !max_db_size
)
194 enabled_disk_store
= false;
195 else if ((db_path
.empty() && max_db_size
) || (!db_path
.empty() && !max_db_size
))
196 throw std::runtime_error("ssl_crtd is missing the required parameter. There should be -s and -M parameters together.");
199 bool Ssl::CertificateDb::find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
201 const Locker
locker(dbLock
, Here
);
203 return pure_find(host_name
, cert
, pkey
);
206 bool Ssl::CertificateDb::addCertAndPrivateKey(Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
208 const Locker
locker(dbLock
, Here
);
210 if (!db
|| !cert
|| !pkey
|| min_db_size
> max_db_size
)
213 ASN1_INTEGER
* ai
= X509_get_serialNumber(cert
.get());
214 std::string serial_string
;
215 Ssl::BIGNUM_Pointer
serial(ASN1_INTEGER_to_BN(ai
, NULL
));
217 TidyPointer
<char, tidyFree
> hex_bn(BN_bn2hex(serial
.get()));
218 serial_string
= std::string(hex_bn
.get());
220 row
.setValue(cnlSerial
, serial_string
.c_str());
221 char ** rrow
= TXT_DB_get_by_index(db
.get(), cnlSerial
, row
.getRow());
226 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
227 if (pure_find(subject
.get(), cert
, pkey
))
231 while (max_db_size
< size()) {
232 if (!deleteInvalidCertificate())
236 while (max_db_size
< size()) {
237 deleteOldestCertificate();
240 row
.setValue(cnlType
, "V");
241 ASN1_UTCTIME
* tm
= X509_get_notAfter(cert
.get());
242 row
.setValue(cnlExp_date
, std::string(reinterpret_cast<char *>(tm
->data
), tm
->length
).c_str());
243 row
.setValue(cnlFile
, "unknown");
245 TidyPointer
<char, tidyFree
> subject(X509_NAME_oneline(X509_get_subject_name(cert
.get()), NULL
, 0));
246 row
.setValue(cnlName
, subject
.get());
249 if (!TXT_DB_insert(db
.get(), row
.getRow()))
253 std::string
filename(cert_full
+ "/" + serial_string
+ ".pem");
254 if (!writeCertAndPrivateKeyToFile(cert
, pkey
, filename
.c_str()))
262 BIGNUM
* Ssl::CertificateDb::getCurrentSerialNumber()
264 const Locker
locker(dbSerialLock
, Here
);
265 // load serial number from file.
266 Ssl::BIO_Pointer
file(BIO_new(BIO_s_file()));
270 if (BIO_rw_filename(file
.get(), const_cast<char *>(serial_full
.c_str())) <= 0)
273 Ssl::ASN1_INT_Pointer
serial_ai(ASN1_INTEGER_new());
278 if (!a2i_ASN1_INTEGER(file
.get(), serial_ai
.get(), buffer
, sizeof(buffer
)))
281 Ssl::BIGNUM_Pointer
serial(ASN1_INTEGER_to_BN(serial_ai
.get(), NULL
));
286 // increase serial number.
287 Ssl::BIGNUM_Pointer
increased_serial(BN_dup(serial
.get()));
288 if (!increased_serial
)
291 BN_add_word(increased_serial
.get(), 1);
293 // save increased serial number.
294 if (BIO_seek(file
.get(), 0))
297 Ssl::ASN1_INT_Pointer
increased_serial_ai(BN_to_ASN1_INTEGER(increased_serial
.get(), NULL
));
298 if (!increased_serial_ai
)
301 i2a_ASN1_INTEGER(file
.get(), increased_serial_ai
.get());
302 BIO_puts(file
.get(),"\n");
304 return serial
.release();
307 void Ssl::CertificateDb::create(std::string
const & db_path
, int serial
)
310 throw std::runtime_error("Path to db is empty");
311 std::string
serial_full(db_path
+ "/" + serial_file
);
312 std::string
db_full(db_path
+ "/" + db_file
);
313 std::string
cert_full(db_path
+ "/" + cert_dir
);
314 std::string
size_full(db_path
+ "/" + size_file
);
317 if (mkdir(db_path
.c_str()))
319 if (mkdir(db_path
.c_str(), 0777))
321 throw std::runtime_error("Cannot create " + db_path
);
324 if (mkdir(cert_full
.c_str()))
326 if (mkdir(cert_full
.c_str(), 0777))
328 throw std::runtime_error("Cannot create " + cert_full
);
330 Ssl::ASN1_INT_Pointer
i(ASN1_INTEGER_new());
331 ASN1_INTEGER_set(i
.get(), serial
);
333 Ssl::BIO_Pointer
file(BIO_new(BIO_s_file()));
335 throw std::runtime_error("SSL error");
337 if (BIO_write_filename(file
.get(), const_cast<char *>(serial_full
.c_str())) <= 0)
338 throw std::runtime_error("Cannot open " + cert_full
+ " to open");
340 i2a_ASN1_INTEGER(file
.get(), i
.get());
342 std::ofstream
size(size_full
.c_str());
346 throw std::runtime_error("Cannot open " + size_full
+ " to open");
347 std::ofstream
db(db_full
.c_str());
349 throw std::runtime_error("Cannot open " + db_full
+ " to open");
352 void Ssl::CertificateDb::check(std::string
const & db_path
, size_t max_db_size
)
354 CertificateDb
db(db_path
, max_db_size
, 0);
358 std::string
Ssl::CertificateDb::getSNString() const
360 const Locker
locker(dbSerialLock
, Here
);
361 std::ifstream
file(serial_full
.c_str());
369 bool Ssl::CertificateDb::pure_find(std::string
const & host_name
, Ssl::X509_Pointer
& cert
, Ssl::EVP_PKEY_Pointer
& pkey
)
375 row
.setValue(cnlName
, host_name
.c_str());
377 char **rrow
= TXT_DB_get_by_index(db
.get(), cnlName
, row
.getRow());
381 if (!sslDateIsInTheFuture(rrow
[cnlExp_date
])) {
382 deleteByHostname(rrow
[cnlName
]);
386 // read cert and pkey from file.
387 std::string
filename(cert_full
+ "/" + rrow
[cnlSerial
] + ".pem");
388 readCertAndPrivateKeyFromFiles(cert
, pkey
, filename
.c_str(), NULL
);
394 size_t Ssl::CertificateDb::size() const
399 void Ssl::CertificateDb::addSize(std::string
const & filename
)
401 writeSize(readSize() + getFileSize(filename
));
404 void Ssl::CertificateDb::subSize(std::string
const & filename
)
406 writeSize(readSize() - getFileSize(filename
));
409 size_t Ssl::CertificateDb::readSize() const
412 std::ifstream
size_file(size_full
.c_str());
413 if (!size_file
&& enabled_disk_store
)
414 throw std::runtime_error("cannot read \"" + size_full
+ "\" file");
415 size_file
>> db_size
;
419 void Ssl::CertificateDb::writeSize(size_t db_size
)
421 std::ofstream
size_file(size_full
.c_str());
422 if (!size_file
&& enabled_disk_store
)
423 throw std::runtime_error("cannot write \"" + size_full
+ "\" file");
424 size_file
<< db_size
;
427 size_t Ssl::CertificateDb::getFileSize(std::string
const & filename
)
429 std::ifstream
file(filename
.c_str(), std::ios::binary
);
430 file
.seekg(0, std::ios_base::end
);
431 size_t file_size
= file
.tellg();
432 return ((file_size
+ fs_block_size
- 1) / fs_block_size
) * fs_block_size
;
435 void Ssl::CertificateDb::load()
437 // Load db from file.
438 Ssl::BIO_Pointer
in(BIO_new(BIO_s_file()));
439 if (!in
|| BIO_read_filename(in
.get(), db_full
.c_str()) <= 0)
440 throw std::runtime_error("Uninitialized SSL certificate database directory: " + db_path
+ ". To initialize, run \"ssl_crtd -c -s " + db_path
+ "\".");
442 bool corrupt
= false;
443 Ssl::TXT_DB_Pointer
temp_db(TXT_DB_read(in
.get(), cnlNumber
));
447 // Create indexes in db.
448 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
449 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial
), LHASH_COMP_FN(index_serial
)))
452 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name
), LHASH_COMP_FN(index_name
)))
455 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlSerial
, NULL
, LHASH_HASH_FN(index_serial_hash
), LHASH_COMP_FN(index_serial_cmp
)))
458 if (!corrupt
&& !TXT_DB_create_index(temp_db
.get(), cnlName
, NULL
, LHASH_HASH_FN(index_name_hash
), LHASH_COMP_FN(index_name_cmp
)))
463 throw std::runtime_error("The SSL certificate database " + db_path
+ " is corrupted. Please rebuild");
465 db
.reset(temp_db
.release());
468 void Ssl::CertificateDb::save()
471 throw std::runtime_error("The certificates database is not loaded");;
473 // To save the db to file, create a new BIO with BIO file methods.
474 Ssl::BIO_Pointer
out(BIO_new(BIO_s_file()));
475 if (!out
|| !BIO_write_filename(out
.get(), const_cast<char *>(db_full
.c_str())))
476 throw std::runtime_error("Failed to initialize " + db_full
+ " file for writing");;
478 if (TXT_DB_write(out
.get(), db
.get()) < 0)
479 throw std::runtime_error("Failed to write " + db_full
+ " file");
482 // Normally defined in defines.h file
483 #define countof(arr) (sizeof(arr)/sizeof(*arr))
484 void Ssl::CertificateDb::deleteRow(const char **row
, int rowIndex
)
486 const std::string
filename(cert_full
+ "/" + row
[cnlSerial
] + ".pem");
487 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
488 sk_OPENSSL_PSTRING_delete(db
.get()->data
, rowIndex
);
490 sk_delete(db
.get()->data
, rowIndex
);
493 const Columns db_indexes
[]={cnlSerial
, cnlName
};
494 for (unsigned int i
= 0; i
< countof(db_indexes
); i
++) {
495 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
496 if (LHASH_OF(OPENSSL_STRING
) *fieldIndex
= db
.get()->index
[db_indexes
[i
]])
497 lh_OPENSSL_STRING_delete(fieldIndex
, (char **)row
);
499 if (LHASH
*fieldIndex
= db
.get()->index
[db_indexes
[i
]])
500 lh_delete(fieldIndex
, row
);
505 int ret
= remove(filename
.c_str());
507 throw std::runtime_error("Failed to remove certficate file " + filename
+ " from db");
510 bool Ssl::CertificateDb::deleteInvalidCertificate()
515 bool removed_one
= false;
516 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
517 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
518 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
520 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
521 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
524 if (!sslDateIsInTheFuture(current_row
[cnlExp_date
])) {
525 deleteRow(current_row
, i
);
536 bool Ssl::CertificateDb::deleteOldestCertificate()
541 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
542 if (sk_OPENSSL_PSTRING_num(db
.get()->data
) == 0)
544 if (sk_num(db
.get()->data
) == 0)
548 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
549 const char **row
= (const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, 0);
551 const char **row
= (const char **)sk_value(db
.get()->data
, 0);
559 bool Ssl::CertificateDb::deleteByHostname(std::string
const & host
)
564 #if OPENSSL_VERSION_NUMBER >= 0x1000004fL
565 for (int i
= 0; i
< sk_OPENSSL_PSTRING_num(db
.get()->data
); i
++) {
566 const char ** current_row
= ((const char **)sk_OPENSSL_PSTRING_value(db
.get()->data
, i
));
568 for (int i
= 0; i
< sk_num(db
.get()->data
); i
++) {
569 const char ** current_row
= ((const char **)sk_value(db
.get()->data
, i
));
571 if (host
== current_row
[cnlName
]) {
572 deleteRow(current_row
, i
);
579 bool Ssl::CertificateDb::IsEnabledDiskStore() const
581 return enabled_disk_store
;