/* Copyright (C) 2003 Frédéric Giudicelli (contact_nos@yahoo.com). All rights reserved. This product includes cryptographic software written by Eric Young (eay@cryptsoft.com) This program is released under the GPL with the additional exemption that compiling, linking, and/or using OpenSSL is allowed. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ // CA_Handler.cpp: implementation of the CA_Handler class. // ////////////////////////////////////////////////////////////////////// #include "CA_Handler.h" #include "svintl.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CA_Handler::CA_Handler(const mString & EntityName, int CaType) { m_EntityName = EntityName; m_DbConn = NULL; m_CaType = CaType; } CA_Handler::~CA_Handler() { if(m_DbConn) delete m_DbConn; Reset(); } bool CA_Handler::CreateTables(SQL_Connection * DbConn) { SQL sql(DbConn); long i; char * CommonCreates[] = {CA_HANDLER_CREATE_1, CA_HANDLER_CREATE_2, CA_HANDLER_CREATE_3, NULL}; //We execute each request for(i=0; CommonCreates[i]; i++) { if(!sql.Execute(CommonCreates[i])) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } return true; } PKI_CERT * CA_Handler::get_CaCert() { if(!*this) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return NULL; } return &m_CaCert; } bool CA_Handler::Create(const PKI_CERT & cert, const InternalCaKey & privkey) { Reset(); m_CaKey = cert.GetPrivateKey(); if(!m_CaKey) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(!m_InternCa.set_caKey(privkey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!m_InternCa.set_caCert(cert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_InternCaBody.set_caType(m_CaType); m_InternCaBody.set_caSerial(1); m_InternCaBody.set_isOK(); m_InternCa.set_isOK(); //Flush new datas into database if(!Flush()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Load datas if(!Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool CA_Handler::Flush() { SQL sql(m_DbConn); mString req; mString ca_pem; if(!m_InternCaBody || !m_InternCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } //Encrypt the body and generate signature if(!m_InternCaBody.to_SignEncrypt(m_InternCa.get_cryptedBody(), m_CaKey.GetRsaKey(), m_CaKey.GetRsaKey(), EVP_sha1(), EVP_des_ede3_cbc())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Convert to PEM if(!m_InternCa.to_PEM(ca_pem)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CleanMemory(); return false; } //Save some memory CleanMemory(); if(req.sprintf(CA_HANDLER_INSERT_CA, m_CaType, ca_pem.c_str()) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool CA_Handler::Load() { Reset(); long NumRows; if(!m_DbConn) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } //We load the CA datas SQL sql(m_DbConn); mString req; mString datas; if(req.sprintf(CA_HANDLER_GET_CA, m_CaType) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } // It can be loaded latter if(!NumRows) { return true; } if(!sql.Value(0, "datas", datas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //We convert the datas from the PEM blob if(!m_InternCa.from_PEM(datas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } //Setting the CA cert if( !(m_CaCert = m_InternCa.get_caCert()) ) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Setting the CA key if(!INTERNAL_CA_KEY_load(m_InternCa.get_caKey(), m_CaKey, m_Engine)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Associate private key and cert if(!m_CaCert.SetPrivateKey(m_CaKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!m_InternCaBody.from_SignEncrypt(m_InternCa.get_cryptedBody(), m_CaKey.GetRsaKey(), m_CaKey.GetRsaKey())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Now we verify that the two ca_types match if((int)m_InternCaBody.get_caType() != m_CaType) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } return true; } void CA_Handler::Reset() { CaLock.EnterCS(); m_InternCaBody.Clear(); m_InternCa.Clear(); CaLock.LeaveCS(); } void CA_Handler::set_ENGINE(ENGINE *e) { CaLock.EnterCS(); m_Engine = e; CaLock.LeaveCS(); } ENGINE * CA_Handler::get_ENGINE() { return m_Engine; } bool CA_Handler::Sign(const PKI_CSR & csr, PKI_CERT & ResultCert, const mString & uid, int Days, const HashTable_String *Exts, bool check_sig) { InternalCaCert currCert; time_t currTime; X509_REQ * reqcsr; char * strDn; mString dn; time_t begin_date; time_t end_date; unsigned long serial; mString strUID; mString strDnHash; SQL sql(m_DbConn); mString req; long NumRows; mString signature; time_t startTime; if(!*this) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } //Search if we have a certificate with same DN ! reqcsr = csr.GetX509_REQ(); time_gmt(&currTime); strDn = X509_NAME_oneline(reqcsr->req_info->subject, NULL, 0); if(!strDn) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } currCert.set_dn(strDn); dn = sql.FormatString(strDn); free(strDn); if(!dn.size()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(uid.size()) { strUID = sql.FormatString(uid); if(!strUID.size()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } if(!NewPKIStore::X509_NAMEtoHash(reqcsr->req_info->subject, strDnHash)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(req.sprintf(CA_HANDLER_GET_CERT_UNIQUE, strDnHash.c_str(), CERT_STATE_VALID, currTime, m_CaType) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } CaLock.EnterCS(); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } if(NumRows) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CERT_ALREADY_SIGNED_DN); CaLock.LeaveCS(); return false; } serial = m_InternCaBody.get_caSerial(); //We sign the request if(!m_CaCert.SignCSR(ResultCert, csr, Exts, Days, serial, "sha1", check_sig)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } //Preparing signature begin_date = ResultCert.GetStartDate(); end_date = ResultCert.GetEndDate(); currCert.set_caType(m_CaType); currCert.set_serial(serial); currCert.set_state(CERT_STATE_VALID); currCert.set_uid(uid); currCert.set_beginDate(begin_date); currCert.set_endDate(end_date); currCert.set_revDate(0); if(!currCert.set_cert(ResultCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } if(!get_InternalCertSig(currCert, signature)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } //Ok let's do the insertion in database if(req.sprintf(CA_HANDLER_INSERT_CERT, m_CaType, serial, CERT_STATE_VALID, strDnHash.c_str(), dn.c_str(), strUID.c_str(), begin_date, end_date, ResultCert.GetCertPEM().c_str(), signature.c_str()) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); CaLock.LeaveCS(); return false; } dn = ""; if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); CaLock.LeaveCS(); return false; } //Increment serial serial++; m_InternCaBody.set_caSerial(serial); Flush(); // Every 100000 certificates we request the table to be optimized if( (serial % 100000) == 0 ) { NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimizing certificates table...")); time(&startTime); if(!sql.OptimizeTable(CA_HANDLER_CERTS_TABLE)) { req = ""; ERR_to_mstring(req); NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to optimize certificates table - Reason: %s"), req.c_str()); ERR_clear_error(); } else { NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimized certificates table in %ld secondes"), time(NULL) - startTime); } } CaLock.LeaveCS(); return true; } CA_Handler::operator int() { if(!m_InternCaBody || !m_CaCert || !m_CaKey || !m_DbConn) return 0; else return 1; } void CA_Handler::CleanMemory() { if(!m_InternCa) return; CaLock.EnterCS(); m_InternCa.get_cryptedBody().Clear(); CaLock.LeaveCS(); } bool CA_Handler::get_Certs(mVector & Certs, CERT_STATE state, long index, long num) { if(!*this) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } InternalCaCert res; long NumRows; SQL sql(m_DbConn); mString req; long i; if(state) { if(req.sprintf(CA_HANDLER_GET_CERTS_BY_STATE, state, m_CaType) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } } else { if(req.sprintf(CA_HANDLER_GET_CERTS, m_CaType) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } } if(num) { req += " LIMIT "; req += index; req += ","; req += num; } req += ";"; if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } for(i=0; i & Crls, long index, long num) { if(!*this) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } long NumRows; SQL sql(m_DbConn); mString req; long i; int v_index; if(req.sprintf(CA_HANDLER_GET_CRLS, m_CaType) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } if(num) { req += " LIMIT "; req += index; req += ","; req += num; } req += ";"; if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } v_index = 0; for(i=0; iValue(i, "ca_type", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_caType(strdatas.c_ulng()); if(!sql->Value(i, "serial", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_serial(strdatas.c_ulng()); if(!sql->Value(i, "state", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_state(strdatas.c_ulng()); if(!sql->Value(i, "dn", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_dn(strdatas); if(!sql->Value(i, "uid", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_uid(strdatas); if(!sql->Value(i, "begin_date", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_beginDate(strdatas.c_ulng()); if(!sql->Value(i, "end_date", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_endDate(strdatas.c_ulng()); if(!sql->Value(i, "rev_date", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert.set_revDate(strdatas.c_ulng()); if(!sql->Value(i, "cert", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!cert.get_cert().SetCert(strdatas.c_str())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //now we verify the signature of the thing ! if(!sql->Value(i, "signature", strdatas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!verify_InternalCertSig(cert, strdatas.c_str())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool CA_Handler::GEN_PRIVATE_KEY_load(const GenPrivateKey & KeyGen, PKI_RSA & Key, InternalCaKey &InternalKey, ENGINE * e) { mString keyid; int rsalen; switch(KeyGen.get_type()) { case GEN_PRIVATE_KEY_TYPE_ENGINE: keyid = KeyGen.get_keyid(); if(!keyid.size()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!Key.SetKey(keyid, e)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!InternalKey.set_type(INTERNAL_CA_KEY_TYPE_ENGINE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } InternalKey.set_keyid(keyid); break; case GEN_PRIVATE_KEY_TYPE_KEYLEN: rsalen = (int)KeyGen.get_keylen(); if(!rsalen) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!Key.GenerateKey(rsalen, e)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!InternalKey.set_type(INTERNAL_CA_KEY_TYPE_KEY)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!InternalKey.set_privkey(Key.GetRSA())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } break; default: NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; break; } return true; } bool CA_Handler::INTERNAL_CA_KEY_load(const InternalCaKey & ca_key, PKI_RSA & key, ENGINE * e) { switch(ca_key.get_type()) { case INTERNAL_CA_KEY_TYPE_KEY: if(!key.SetKey(ca_key.get_privkey())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } break; case INTERNAL_CA_KEY_TYPE_ENGINE: if(!key.SetKey(ca_key.get_keyid(), e)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } break; default: NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } return true; } bool CA_Handler::SetDbConn(const SQL_Connection *DbConn) { if(m_DbConn) { delete m_DbConn; m_DbConn = NULL; } try { m_DbConn = DbConn->Clone(); } catch(ExceptionNewPKI e) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!m_DbConn) { NEWPKIerr(PKI_ERROR_TXT, ERROR_MALLOC); return false; } return true; } bool CA_Handler::GetLastCrl(PKI_CRL & crl) { bool res; if(!*this) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } CaLock.EnterCS(); if(m_InternCaBody.get_lastcrl()) { if(!(crl = m_InternCaBody.get_lastcrl())) res = false; else res = true; } else { res = false; } CaLock.LeaveCS(); return res; }