/* 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 */ // Entity_CA.cpp: implementation of the Entity_CA class. // ////////////////////////////////////////////////////////////////////// #include "Entity_CA.h" #include "NewPKIStore.h" #include "svintl.h" #include ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Entity_CA::Entity_CA(ENTITY_CONSTRUCTOR_PARAMETERS): Entity(ENTITY_CONSTRUCTOR_PARAM_PASSTHRU, &myConf, &CaPublicationStore), CaPublicationStore(EntityName, e) { m_InternalCa = NULL; m_PlugCA = NULL; try { m_InternalCa = new CA_Handler(EntityName, 0); } catch(ExceptionNewPKI e) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); throw ExceptionNewPKI(); } m_InternalCa->set_ENGINE(m_Engine); hThreadGenerateCRL.Create(ThreadGenerateCRL, this); hThreadRepublish.Create(ThreadRepublish, this); } Entity_CA::~Entity_CA() { m_Jobs.StopAll(); hThreadGenerateCRL.Stop(); hThreadRepublish.Stop(); if(m_InternalCa) delete m_InternalCa; if(m_PlugCA) delete m_PlugCA; } bool Entity_CA::Create(const EntityCreationDatas & Params, AdminResponseBody & response) { if(!Params) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(Params.get_type() != ENTITY_TYPE_CA) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(!Params.get_entityKey()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } //We create the database if(!Common_Create(Params.get_entityKey(), NULL)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!CA_Handler::CreateTables(m_DbConn)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Destroy(); return false; } if(!m_InternalCa->SetDbConn(m_DbConn)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Destroy(); return false; } if(!response.get_creEntity().set_type(ENTITY_TYPE_CA)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Destroy(); return false; } if(!response.get_creEntity().set_entityPubKey(m_EntityKey.GetPublicKey())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_MALLOC); Destroy(); return false; } return true; } bool Entity_CA::Load() { if(!Common_Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!IsFullyInit()) { return true; } if(!m_InternalCa->SetDbConn(m_DbConn)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Start the conf synch if(!m_Jobs.StartConfSync()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Load the CA if(!m_InternalCa->Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!hThreadGenerateCRL.Start()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } if(!hThreadRepublish.Start()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } return true; } bool Entity_CA::Init(const EntitySignatureResp & init_datas) { if(IsFullyInit()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!init_datas) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(init_datas.get_body().get_type() != ENTITY_TYPE_CA) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(!Common_Init(init_datas.get_body().get_entitycert(), init_datas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::LoginUser(UserHandle & hUser, int & UserType) { if(!AclValidator.CanUserPerform(hUser.GetUserCert(), ACL_TYPE_AUTHENTICATE_ON_ENTITY)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } UserType = USER_TYPE_CA; return true; } void Entity_CA::LogoutUser(const UserHandle & hUser) { } bool Entity_CA::ParseNewConf() { mString LibraryPath; //Load the extensions from the conf m_Exts.Lock(); if(!m_Exts.From_EXTENSION_VALUE(myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_exts())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); m_Exts.Unlock(); return false; } m_Exts.Unlock(); //Load the CRL extensions from the conf m_CrlExts.Lock(); if(!m_CrlExts.From_EXTENSION_VALUE(myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_crlexts())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); m_CrlExts.Unlock(); return false; } m_CrlExts.Unlock(); //Load the CRL validity len m_crldays = myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_crlvaliditydays(); m_crlhours = myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_crlvalidityhours(); //Load the plug options from the conf m_PlugOptions.Lock(); if(!m_PlugOptions.From_PLUG_OPTION(myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_plugca().get_options())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); m_PlugOptions.Unlock(); return false; } m_PlugOptions.Unlock(); LibraryPath = myConf.get_conf().get_body().get_caConf().CA_CONF_PTR.get_plugca().get_librarypath(); //Do we need to update the PlugCA ? if(m_PlugCA) { // Has the lib name changed ? if(!(LibraryPath == m_PlugCA->GetLibraryPath()) || !LibraryPath.size()) { delete m_PlugCA; m_PlugCA = NULL; } } //Need to load a new ca plug if(LibraryPath.size()) { try { m_PlugCA = new PlugCA(LibraryPath); } catch(ExceptionNewPKI e) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!m_PlugCA) { NEWPKIerr(PKI_ERROR_TXT, ERROR_MALLOC); return false; } } // Validate the plug options if(m_PlugCA && !m_PlugCA->ValidateOptions(m_PlugOptions)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::Upgrade(const char * Version) { return true; } void Entity_CA::GetACL_List(mVector & acl_list) { int i; static ACL_TYPE list_acls[] = { ACL_TYPE_VIEW_LOGS, ACL_TYPE_CREATE_ROOT_CA, ACL_TYPE_SEND_ADMIN_MAIL, ACL_TYPE_CREATE_CHILD_CA, ACL_TYPE_CA_SIGN_CERT, ACL_TYPE_CA_REVOKE_CERT, ACL_TYPE_CA_GENERATE_CRL, (ACL_TYPE)0 }; for(i=0; list_acls[i]; i++) { acl_list.push_back(list_acls[i]); } } bool Entity_CA::ParseAdminCommand(AdminResponseBody & response, const PKI_CERT & ClientCert, const AdminRequest & AdminRequest) { //We only accept SSLv3 connection if(!ClientCert) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); ERR_to_ADMIN_RESPONSE(response); return false; } return Private_ParseAdminCommand(true, this, LogsType, response, ClientCert, AdminRequest, GetUserHandle()); } bool Entity_CA::Private_ParseAdminCommand(bool ExecuteCmd, Entity * me_this, mVector & mLogsType, AdminResponseBody & response, const PKI_CERT & ClientCert, const AdminRequest & AdminRequest, UserHandle & hUser) { mString str; char * Dn; if(AdminRequest && AdminRequest.get_body().get_type() == ADMIN_REQ_TYPE_SIGN_CSR) { Dn = X509_NAME_oneline(AdminRequest.get_body().get_signCsr().get_request().GetX509_REQ()->req_info->subject, NULL, 0); if(Dn) { str = Dn; free(Dn); } } PARSER_COMMAND_BEGIN(Entity_CA, response, 0, AdminRequest, ClientCert, hUser, ExecuteCmd, me_this, mLogsType) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_ENTITY_GET_MY_CONF) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_SEND_ADMIN_MAIL) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_SEND_MAIL) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_NEW_REQUEST) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_CA_CERT) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_CA_REV) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_CERT_PUBLICATION) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_REV_PUBLICATION) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_CRL_PUBLICATION) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_LOGIN, Entity_CA::UserLogin, LOG_MESSAGE_TYPE_USER_LOGIN, (ClientCert)?(char*)ClientCert.GetStringName():NULL, LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_ENUM_LOGS, Entity_CA::EnumLogs) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_LOGS_COUNT, Entity_CA::GetLogsCount) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_CREATE_ROOT_CA, Entity_CA::CreateRootCa, LOG_MESSAGE_TYPE_CREATE_ROOT_CA, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_CREATE_CHILD_CA, Entity_CA::CreateChildCa, LOG_MESSAGE_TYPE_CREATE_CHILD_CA, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_IMPORT_CHILD_CA_CERT, Entity_CA::ImportChildCaCert, LOG_MESSAGE_TYPE_IMPORT_CHILD_CA_CERT, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_CA_STATUS, Entity_CA::GetStatus) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_CA_P7B, Entity_CA::GetCaP7b) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_MY_ACL, Entity_CA::GetMyACL) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_LOGS_TYPE, Entity_CA::GetLogsType) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_SEND_ADMIN_MAIL, Entity_CA::AdminSendMail, LOG_MESSAGE_TYPE_ADD_ADMIN_MAIL_QUEUE, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_ENUM_CERTS, Entity_CA::EnumCerts) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_ENUM_CRLS, Entity_CA::EnumCrls) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_SIGN_CSR, Entity_CA::SignCSR, LOG_MESSAGE_TYPE_SIGN_CSR, (char*)str.c_str(), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_REVOKE_CERT, Entity_CA::RevokeCert, LOG_MESSAGE_TYPE_CERT_REVOCATION, "", AdminRequest.get_body().get_serial()) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_CHECK_LOGS, Entity_CA::CheckLogsIntegrity) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_GENERATE_CRL, Entity_CA::GenerateCRL, LOG_MESSAGE_TYPE_GEN_CRL, "CRL", LOG_NO_OBJECTID) PARSER_COMMAND_END(Entity_CA) } void Entity_CA::LogsTypeGet(mVector & cLogsType) { Private_ParseAdminCommand(false, NULL, cLogsType, AdminResponseBody::EmptyInstance, PKI_CERT::EmptyInstance, AdminRequest::EmptyInstance, UserHandle::EmptyInstance); } void Entity_CA::ThreadRepublish(const NewpkiThread * Thread, void *param) { Entity_CA * me_this = (Entity_CA*)param; time_t currTime; time_t lastTime; mVector certs; mVector crls; long index; bool res; size_t i; // We resend all the known certificates/revocation/crl every 12 hours while(!Thread->ShouldStop()) { index = 0; do { certs.clear(); res = me_this->m_InternalCa->get_Certs(certs, CERT_STATE_VALID, index, 50); if(res) { index += certs.size(); for(i=0; !Thread->ShouldStop() && iPublishCert(certs[i].get_uid().c_str(), certs[i].get_cert()); } } } while(!Thread->ShouldStop() && res && certs.size()); certs.clear(); index = 0; do { certs.clear(); res = me_this->m_InternalCa->get_Certs(certs, CERT_STATE_REVOKED, index, 50); if(res) { index += certs.size(); for(i=0; !Thread->ShouldStop() && iPublishRevocation(certs[i].get_uid().c_str(), certs[i].get_cert(), certs[i].get_revDate()); } } } while(!Thread->ShouldStop() && res && certs.size()); certs.clear(); index = 0; do { crls.clear(); res = me_this->m_InternalCa->get_Crls(crls, index, 50); if(res) { index += crls.size(); for(i=0; !Thread->ShouldStop() && iPublishCrl(crls[i]); } } } while(!Thread->ShouldStop() && res && crls.size()); time(&lastTime); do { NewpkiThread::Sleep(500); time(&currTime); } while( ((currTime - lastTime) < (12 * 60 * 60)) && !Thread->ShouldStop()); } } void Entity_CA::ThreadGenerateCRL(const NewpkiThread * Thread, void *param) { Entity_CA * me_this = (Entity_CA*)param; PKI_CRL lastCRL; time_t currTime; long crldays; long crlhours; mString err; if(!me_this) return; while(!Thread->ShouldStop()) { //Get CA conf me_this->ConfAccessLock.LockRead(); //Do we have a conf ? if(!me_this->myConf) { me_this->ConfAccessLock.UnlockRead(); goto wait_next; } //We get validity length if(!me_this->m_crldays && !me_this->m_crlhours) { me_this->ConfAccessLock.UnlockRead(); goto wait_next; } crldays = me_this->m_crldays; crlhours = me_this->m_crlhours; me_this->ConfAccessLock.UnlockRead(); //We get last CRL if(!me_this->m_InternalCa->GetLastCrl(lastCRL)) goto wait_next; time_gmt(&currTime); //Is the last CRL about to expire ? //we add a tolerance of 10% before the actual //crl expiration, this should be enough to //do the generation and the publication if( ((lastCRL.GetEndTime() - ((lastCRL.GetEndTime() - lastCRL.GetStartTime()) * 0.1)) > currTime) || (lastCRL.GetEndTime() - currTime) > (crlhours*3600 + crldays * 86400) ) goto wait_next; me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_REQUEST, LOG_MESSAGE_TYPE_GEN_CRL, 0, me_this->m_EntityCert.GetStringName(), LOG_NO_OBJECTID, "CRL"); if(!me_this->Private_GenerateCRL(lastCRL)) { ERR_to_mstring(err); me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_FAILURE, LOG_MESSAGE_TYPE_GEN_CRL, 0, me_this->m_EntityCert.GetStringName(), LOG_NO_OBJECTID, "CRL", err.c_str()); } else { me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_SUCCESS, LOG_MESSAGE_TYPE_GEN_CRL, 0, me_this->m_EntityCert.GetStringName(), LOG_NO_OBJECTID, "CRL"); } wait_next: NewpkiThread::Sleep(800); } } bool Entity_CA::Private_GenerateCRL(PKI_CRL & Crl) { m_CrlExts.Lock(); if(!m_InternalCa->Generate_CRL(Crl, m_crldays, m_crlhours, &m_CrlExts, "sha1")) { m_CrlExts.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_CrlExts.Unlock(); if(!OnNewCRL(Crl)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::Private_SignCSR(const char * ldap_uid, const PKI_CSR & csr, unsigned long validity, PKI_CERT & Cert) { CERT_STATE CertStatus; if(!csr.CheckSignature()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_CSR_SIGNATURE); return false; } if(!OnNewRequest(csr)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_Exts.Lock(); if(!m_InternalCa->Sign(csr, Cert, ldap_uid?ldap_uid:"", validity, &m_Exts, true)) { m_Exts.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_Exts.Unlock(); if(!OnNewCertificate(ldap_uid, Cert)) { Private_RevokeCERT(ldap_uid, Cert.GetSerial(), CertStatus); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::Private_RevokeCERT(const char * ldap_uid, unsigned long serial, CERT_STATE & CertStatus) { PKI_CERT Cert; time_t rev_date; mString uid; if(!m_InternalCa->Revoke(serial, Cert, CertStatus, rev_date, uid)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!ldap_uid && uid.size()) { ldap_uid = uid.c_str(); } if(!OnNewRevocation(ldap_uid, Cert, rev_date)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::ProceedWithCertification(const NewpkiCertRequest & cert_request, NewpkiResponse & currResponse) { PKI_CERT cert; PKI_CERT * myCert; CERT_STATE CertStatus; currResponse.get_certResponse().set_id(cert_request.get_id()); // The CRL is optional, so we don't trigger an error if(!m_InternalCa->GetLastCrl(currResponse.get_certResponse().get_lastCrl())) { ERR_clear_error(); } //Proceed with signature if(!Private_SignCSR(cert_request.get_ldapUid().c_str(), cert_request.get_request(), cert_request.get_validity(), cert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); goto certification_err; } // Build the answer if(!currResponse.get_certResponse().set_certificate(cert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_MALLOC); goto certification_err; } if(!currResponse.get_certResponse().set_parentcerts(myConf.get_parentcerts())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_MALLOC); goto certification_err; } // Add my own cert as well myCert = m_InternalCa->get_CaCert(); if(!myCert) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); goto certification_err; } currResponse.get_certResponse().get_parentcerts().push_back(*myCert); currResponse.get_certResponse().set_status(CERT_RESPONSE_OK); return true; certification_err: currResponse.get_certResponse().set_status(CERT_RESPONSE_ERROR); ERR_to_ERROR_ENTRIES(currResponse.get_certResponse().get_errors()); if(cert) Private_RevokeCERT(cert_request.get_ldapUid().c_str(), cert.GetSerial(), CertStatus); return false; } bool Entity_CA::ProceedWithRequest(const NewpkiRequest & req, NewpkiResponse & resp, LOG_MESSAGE_TYPE & LogType, mString & ObjectName, mString & Err) { const char * ldap_uid; char * dn_line; CERT_STATE CertStatus; PKI_CRL Crl; //Proceed with the request switch(req.get_type()) { case NEWPKI_REQUEST_TYPE_REV: LogType = LOG_MESSAGE_TYPE_CA_REV; ObjectName.sprintf("Serial: %ld", req.get_revRequest().get_serial()); if(!resp.set_type(NEWPKI_RESPONSE_TYPE_REV)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } // The CRL is optional, so we don't trigger an error if(!m_InternalCa->GetLastCrl(resp.get_revResponse().get_lastCrl())) { ERR_clear_error(); } ldap_uid = req.get_revRequest().get_ldapUid().c_str(); resp.get_revResponse().set_id(req.get_revRequest().get_id()); if(!Private_RevokeCERT(ldap_uid, req.get_revRequest().get_serial(), CertStatus)) { ERR_to_mstring(Err); resp.get_revResponse().set_status(REV_RESPONSE_ERROR); ERR_to_ERROR_ENTRIES(resp.get_revResponse().get_errors()); } else { resp.get_revResponse().set_status(REV_RESPONSE_OK); } resp.get_revResponse().set_certStatus(CertStatus); break; case NEWPKI_REQUEST_TYPE_CERT: LogType = LOG_MESSAGE_TYPE_CA_CERT; // Get the DN dn_line = X509_NAME_oneline(req.get_certRequest().get_request().GetX509_REQ()->req_info->subject, NULL, 0); if(dn_line) { ObjectName = dn_line; free(dn_line); } if(!resp.set_type(NEWPKI_RESPONSE_TYPE_CERT)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!ProceedWithCertification(req.get_certRequest(), resp)) { ERR_to_mstring(Err); } break; default: LogType = LOG_MESSAGE_TYPE_CA_CERT; NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); ERR_to_mstring(Err); return false; break; } return true; } bool Entity_CA::GetStatus(COMMAND_PARAMETERS) { int status; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_USER) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_STATUS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(*m_InternalCa) { status = STATUS_CA_OK; } else if(myConf.get_privkey()) { status = STATUS_CA_AWAITING_CERT; } else { status = STATUS_CA_NO_CERT; } response.set_status(status); return true; } bool Entity_CA::GetCaP7b(COMMAND_PARAMETERS) { size_t i; PKI_CERT * cert; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_USER) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_P7B)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } cert = m_InternalCa->get_CaCert(); if(!cert) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } for(i=0; iGetX509())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!response.get_p7b().Generate()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::CreateRootCa(COMMAND_PARAMETERS) { if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CREATE_ROOT_CA)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } //The internal CA has already been generated if(*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_NONE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } InternalCaKey privKey; PKI_RSA tmpPrivKey; PKI_CERT tmpCert; PKI_CSR tmpCsr; HashTable_String Exts; HashTable_Dn Dn; //Load the extensions if(!Exts.From_EXTENSION_VALUE(body.get_createRootCa().get_extensions())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } //Load the DN if(!Dn.From_X509_NAME(body.get_createRootCa().get_dn())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } //Load/generate private key if(!CA_Handler::GEN_PRIVATE_KEY_load(body.get_createRootCa().get_privkey(), tmpPrivKey, privKey, m_Engine)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //We generate the CSR if(!tmpCsr.GenerateCSR(Dn, tmpPrivKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //We now generate the certificate if(!tmpCert.CreateSelfSigned(tmpCsr, &Exts, body.get_createRootCa().get_validity(), 0)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!tmpCert.SetPrivateKey(tmpPrivKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Initialize the CA if(!m_InternalCa->Create(tmpCert, privKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!m_InternalCa->Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::CreateChildCa(COMMAND_PARAMETERS) { if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CREATE_CHILD_CA)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } //The internal CA has already been generated if(*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } PKI_RSA tmpPrivKey; HashTable_Dn Dn; //Load the DN if(!Dn.From_X509_NAME(body.get_createChildCa().get_dn())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } if(!response.set_type(ADMIN_RESP_TYPE_CSR)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Load/generate private key ConfAccessLock.LockWrite(); //Did we already generate the key ? if(myConf.get_privkey()) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!CA_Handler::GEN_PRIVATE_KEY_load(body.get_createChildCa().get_privkey(), tmpPrivKey, myConf.get_privkey(), m_Engine)) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //We generate the CSR if(!response.get_csr().GenerateCSR(Dn, tmpPrivKey)) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Wa save the new key in database if(!WritePersonnalConf()) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } ConfAccessLock.UnlockWrite(); return true; } bool Entity_CA::ImportChildCaCert(COMMAND_PARAMETERS) { int i; X509 * currCert; PKI_RSA tmpPrivKey; PKI_CERT tmpCert; X509 * myCert; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CREATE_ROOT_CA)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } //The internal CA has already been generated if(*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_NONE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } ConfAccessLock.LockWrite(); //Do we have the key ? if(!myConf.get_privkey()) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } //Load saved private key if(!CA_Handler::INTERNAL_CA_KEY_load(myConf.get_privkey(), tmpPrivKey, m_Engine)) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } // Empty the parent cert list from a previous attempt myConf.get_parentcerts().clear(); // Find our certificate and our parent's myCert = NULL; for(i=0; iCreate(tmpCert, myConf.get_privkey())) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Update conf without the key myConf.get_privkey().Clear(); if(!WritePersonnalConf()) { ConfAccessLock.UnlockWrite(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } ConfAccessLock.UnlockWrite(); return true; } bool Entity_CA::EnumCerts(COMMAND_PARAMETERS) { if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_CERTS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } switch(body.get_enumObjects().get_state()) { case CERT_STATE_VALID: if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_REVOKE_CERT) && !AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_SIGN_CERT)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } break; case CERT_STATE_REVOKED: if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_REVOKE_CERT)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } break; default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } if(!*m_InternalCa) { return true; } else { if(!m_InternalCa->get_Certs(response.get_certs(), (CERT_STATE)body.get_enumObjects().get_state(), body.get_enumObjects().get_index(), body.get_enumObjects().get_num())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } return true; } bool Entity_CA::EnumCrls(COMMAND_PARAMETERS) { if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_CRLS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!*m_InternalCa) { return true; } else { if(!m_InternalCa->get_Crls(response.get_crls(), body.get_enumObjects().get_index(), body.get_enumObjects().get_num())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } return true; } bool Entity_CA::SignCSR(COMMAND_PARAMETERS) { PKI_CERT cert; size_t i; PKI_CERT * parentCert; CERT_STATE CertStatus; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_SIGN_CERT)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_P7B)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!Private_SignCSR(body.get_signCsr().get_uid().c_str(), body.get_signCsr().get_request(), body.get_signCsr().get_days(), cert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } // Let's generate the P7B ConfAccessLock.LockRead(); // We first add all the parents for(i=0; iget_CaCert(); if(!parentCert) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); Private_RevokeCERT(NULL, cert.GetSerial(), CertStatus); return false; } if(!response.get_p7b().AddCert(parentCert->GetX509())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Private_RevokeCERT(NULL, cert.GetSerial(), CertStatus); return false; } // We now add the final user cert ! if(!response.get_p7b().AddCert(cert.GetX509())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Private_RevokeCERT(NULL, cert.GetSerial(), CertStatus); return false; } // We now really generate the P7B if(!response.get_p7b().Generate()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); Private_RevokeCERT(NULL, cert.GetSerial(), CertStatus); return false; } return true; } bool Entity_CA::RevokeCert(COMMAND_PARAMETERS) { CERT_STATE CertStatus; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_REVOKE_CERT)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_NONE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!Private_RevokeCERT(NULL, body.get_serial(), CertStatus)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::OnNewRequest(const PKI_CSR & Csr) { if(m_PlugCA) { m_PlugOptions.Lock(); if(!m_PlugCA->OnNewRequest(m_PlugOptions, m_EntityCert, m_Jobs.GetMailer(), Csr)) { m_PlugOptions.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_PlugOptions.Unlock(); } return true; } bool Entity_CA::OnNewCertificate(const char * ldap_uid, const PKI_CERT & Cert) { if(m_PlugCA) { m_PlugOptions.Lock(); if(!m_PlugCA->OnNewCertificate(m_PlugOptions, m_EntityCert, m_Jobs.GetMailer(), Cert)) { m_PlugOptions.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_PlugOptions.Unlock(); } if(!PublishCert(ldap_uid, Cert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::OnNewRevocation(const char * ldap_uid, const PKI_CERT & Cert, time_t rev_date) { if(m_PlugCA) { m_PlugOptions.Lock(); if(!m_PlugCA->OnNewRevocation(m_PlugOptions, m_EntityCert, m_Jobs.GetMailer(), Cert.GetSerial())) { m_PlugOptions.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_PlugOptions.Unlock(); } if(!PublishRevocation(ldap_uid, Cert, rev_date)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::OnNewCRL(const PKI_CRL & crl) { if(m_PlugCA) { m_PlugOptions.Lock(); if(!m_PlugCA->OnNewCRL(m_PlugOptions, m_EntityCert, m_Jobs.GetMailer(), crl)) { m_PlugOptions.Unlock(); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } m_PlugOptions.Unlock(); } if(!PublishCrl(crl)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::Responder_TreatRequest(const NewpkiRequest & Request, const mString & SenderName, NewpkiResponse & Response) { mString StrErr; mString ObjectName; LOG_MESSAGE_TYPE LogType; StrErr = ""; ObjectName = _sv("Unknown"); if(ProceedWithRequest(Request, Response, LogType, ObjectName, StrErr)) { if(StrErr.size()) m_Logging->LogMessage(LOG_STATUS_TYPE_FAILURE, LogType, 0, SenderName.c_str(), LOG_NO_OBJECTID, ObjectName.c_str(), StrErr.c_str()); else m_Logging->LogMessage(LOG_STATUS_TYPE_SUCCESS, LogType, 0, SenderName.c_str(), LOG_NO_OBJECTID, ObjectName.c_str()); return true; } else { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } bool Entity_CA::Responder_ValidateRequest(const NewpkiRequest & Request, const X509_PUBKEY * Requester, mString & SenderName) { size_t i; ConfAccessLock.LockRead(); // We need to search that the RA has the right // to request a certificate from us for(i=0; i < myConf.get_conf().get_ras().get_list().size(); i++) { //Is it the same public key if(myConf.get_conf().get_ras().get_list()[i].get_rassl() == Requester) { SenderName = myConf.get_conf().get_ras().get_list()[i].get_name(); break; } } if(i == myConf.get_conf().get_ras().get_list().size()) { //This RA is not allowed to request a certificate //from us NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); ConfAccessLock.UnlockRead(); return false; } ConfAccessLock.UnlockRead(); // Is it the right type of request ? if(Request.get_type() != NEWPKI_REQUEST_TYPE_REV && Request.get_type() != NEWPKI_REQUEST_TYPE_CERT) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } return true; } bool Entity_CA::Requester_OnNewResponse(const Asn1OctetString & transactionID, const X509_PUBKEY * sender, const NewpkiResponse & Response) { size_t i; LOG_MESSAGE_TYPE LogType; LOG_MESSAGE_STATUS Status; mString Error; mString ObjectName; mString SenderName; switch(Response.get_type()) { case NEWPKI_RESPONSE_TYPE_PUB: ConfAccessLock.LockRead(); // Now search the Pub SenderName = _sv("Unknown"); for(i=0; i < myConf.get_conf().get_publications().get_list().size(); i++) { // Are the public keys the same ? if(myConf.get_conf().get_publications().get_list()[i].get_pubssl() == sender) { SenderName = myConf.get_conf().get_publications().get_list()[i].get_name(); break; } } ConfAccessLock.UnlockRead(); switch(Response.get_pubResponse().get_type()) { case NEWPKI_PUB_REQUEST_CERT: LogType = LOG_MESSAGE_TYPE_CERT_PUBLICATION; break; case NEWPKI_PUB_REQUEST_REV: LogType = LOG_MESSAGE_TYPE_REV_PUBLICATION; break; case NEWPKI_PUB_REQUEST_CRL: LogType = LOG_MESSAGE_TYPE_CRL_PUBLICATION; break; default: return true; break; } //switch(ASN1_INTEGER_GET(Response->d.pub_response->type)) if(Response.get_pubResponse().get_status() == PUB_RESPONSE_PUBLISHED) { Status = LOG_STATUS_TYPE_SUCCESS; } else { Status = LOG_STATUS_TYPE_FAILURE; ERROR_ENTRIES_to_string(Response.get_pubResponse().get_errors(), Error); } // if(ASN1_INTEGER_GET(Response->d.pub_response->type) == PUB_RESPONSE_PUBLISHED) ObjectName = Response.get_pubResponse().get_object(); break; default: return true; break; } // switch(Response->type) if(Status == LOG_STATUS_TYPE_SUCCESS) { m_Logging->LogMessage(LOG_STATUS_TYPE_SUCCESS, LogType, 0, SenderName.c_str(), LOG_NO_OBJECTID, ObjectName.c_str()); } else if(Status == LOG_STATUS_TYPE_FAILURE) { m_Logging->LogMessage(LOG_STATUS_TYPE_FAILURE, LogType, 0, SenderName.c_str(), LOG_NO_OBJECTID, ObjectName.c_str(), Error.c_str()); } return true; } bool Entity_CA::GenerateCRL(COMMAND_PARAMETERS) { PKI_CRL crl; if(!hUser) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_CA_GENERATE_CRL)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!*m_InternalCa) { NEWPKIerr(PKI_ERROR_TXT, ERROR_CA_NOT_CONFIGURED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_NONE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!Private_GenerateCRL(crl)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_CA::PublishDatas(const char * ldap_uid, const char * object, const NewpkiPubRequestBody & PubBody) { NewpkiRequest Request; size_t i; PKI_CERT * currCert; if(!Request.set_type(NEWPKI_REQUEST_TYPE_PUB)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(ldap_uid) Request.get_pubRequest().set_ldapUid(ldap_uid); Request.get_pubRequest().set_object(object); if(!Request.get_pubRequest().set_body(PubBody)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } Request.get_pubRequest().get_parentcerts() = myConf.get_parentcerts(); currCert = m_InternalCa->get_CaCert(); if(currCert) { Request.get_pubRequest().get_parentcerts().push_back(*currCert); } ConfAccessLock.LockRead(); for(i=0; i