/* 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_REPOSITORY.cpp: implementation of the Entity_REPOSITORY class. // ////////////////////////////////////////////////////////////////////// #include "Entity_REPOSITORY.h" #include "svintl.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// Entity_REPOSITORY::Entity_REPOSITORY(ENTITY_CONSTRUCTOR_PARAMETERS): Entity(ENTITY_CONSTRUCTOR_PARAM_PASSTHRU, &myConf, &RepositoryStore), RepositoryStore(EntityName, e) { hThreadCaObjectsPush.Create(ThreadCaObjectsPush, this); } Entity_REPOSITORY::~Entity_REPOSITORY() { m_Jobs.StopAll(); hThreadCaObjectsPush.Stop(); } bool Entity_REPOSITORY::Create(const EntityCreationDatas & Params, AdminResponseBody & response) { if(!Params) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(Params.get_type() != ENTITY_TYPE_REPOSITORY) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } if(!Params.get_entityKey()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_PARAM); return false; } const char * InitialCreates[]={REP_ENTITY_CREATE_1, REP_ENTITY_CREATE_2, NULL}; //We create the database if(!Common_Create(Params.get_entityKey(), InitialCreates)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!response.get_creEntity().set_type(ENTITY_TYPE_REPOSITORY)) { 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_REPOSITORY::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_REPOSITORY) { 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_REPOSITORY::Load() { SQL sql(m_DbConn); mString req; mString pem_conf; long NumRows; if(!Common_Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!IsFullyInit()) { return true; } // We get the PKI conf if(!sql.Execute(REP_ENTITY_GET_G_CONF)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(NumRows) { if(!sql.Value(0, "conf", pem_conf)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!pki_conf.from_PEM(pem_conf.c_str())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } if(!RepositoryStore.Load()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!hThreadCaObjectsPush.Start()) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); return false; } //Start the conf pusher if(!m_Jobs.StartConfPush(true)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(pki_conf) { // Synch the conf ConfAccessLock.LockRead(); if(!m_Jobs.SetPkiConf(pki_conf)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } ConfAccessLock.UnlockRead(); } return true; } bool Entity_REPOSITORY::ParseNewConf() { return true; } bool Entity_REPOSITORY::InsertObject(const WaitingNewpkiObject & object) { SQL sql(m_DbConn); mString req; mString object_PEM; time_t startTime; // No need to insert the object, if I don't have // any other repository to send it to ConfAccessLock.LockRead(); if(!myConf.get_conf().get_repositories().size()) { ConfAccessLock.UnlockRead(); return true; } ConfAccessLock.UnlockRead(); //Add it to the list if(!object.to_PEM(object_PEM)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } req.sprintf(REP_ENTITY_INSERT_OBJECT, object_PEM.c_str()); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } // Every 100000 objects we request the table to be optimized if( (sql.GetLastID() % 100000) == 0 ) { time(&startTime); NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimizing objects table...")); if(!sql.OptimizeTable(REP_ENTITY_OBJECTS_TABLE)) { req = ""; ERR_to_mstring(req); NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to optimize objects table - Reason: %s"), req.c_str()); ERR_clear_error(); } else { NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimized objects table in %ld secondes"), time(NULL) - startTime); } } return true; } void Entity_REPOSITORY::GetObjects(mVector & localObjects, mVector & ids) { SQL sql(m_DbConn); long NumRows; long i; mString object_PEM; WaitingNewpkiObject object; localObjects.clear(); if(!sql.Execute(REP_ENTITY_GET_OBJECTS)) return; if(!sql.NumRows(&NumRows)) return; for(i=0; i & ids) { SQL sql(m_DbConn); size_t i; mString req; for(i=0; i localObjects; mVector ids; mVector Repositories; map RepositoriesSynchroTime; int Status; bool ShouldFullSynchro; time(&lastTime); while(!Thread->ShouldStop()) { me_this->ConfAccessLock.LockRead(); //Do we have the repositories list ? if(!me_this->myConf) { me_this->ConfAccessLock.UnlockRead(); goto wait_next; } //We copy the repositories not to block the rest of the program //while accessing it, since we don't know if the //remote host won't block us Repositories = me_this->myConf.get_conf().get_repositories(); me_this->ConfAccessLock.UnlockRead(); if(Repositories.size()) { // Get the waiting objects me_this->GetObjects(localObjects, ids); //For each repository we do a full synchro for(i=0; iShouldStop()) break; //If the current rep is me or is firewalled we ignore it if(me_this->m_EntityCert == Repositories[i].get_repositoryssl() || ASN1_BIT_STRING_get_bit(Repositories[i].get_flags(), REP_ENTRY_INFO_FLAG_FIREWALLED)) continue; time(&currTime); // If something went wrong during a previous negotiation // the other repository might not have sent us everything // it knows, therefore every 2 hours, we request a full // synchro // To determine who is the salve and who is the master, we use // the serial number. // We don't have the concept of master/slave when we're supposed // to do a full synchro with the other repository if(ASN1_BIT_STRING_get_bit(Repositories[i].get_flags(), REP_ENTRY_INFO_FLAG_SHOULD_FULL_SYNCHRO)) { ShouldFullSynchro = true; } else if( Repositories[i].get_repositoryssl().GetSerial() < me_this->m_EntityCert.GetSerial() && ( RepositoriesSynchroTime.find(Repositories[i].get_name()) == RepositoriesSynchroTime.end() || (currTime - RepositoriesSynchroTime[Repositories[i].get_name()]) >= 7200 ) ) { ShouldFullSynchro = true; } else { ShouldFullSynchro = false; } if(ShouldFullSynchro) { if(me_this->SynchronizeWithRepository(&ClientPki, Repositories[i])) RepositoriesSynchroTime[Repositories[i].get_name()] = time(NULL); } else { // Connect to repository if(!me_this->m_Jobs.ConnectToRepository(Repositories[i], &ClientPki, 30, err)) { NewpkiDebug(LOG_LEVEL_WARNING, me_this->m_EntityName.c_str(), _sv("Failed to push NewPKI object on repository %s (%s:%ld)\nReason:%s"), Repositories[i].get_name().c_str(), Repositories[i].get_address().c_str(), Repositories[i].get_port(), err.c_str()); me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_FAILURE, LOG_MESSAGE_TYPE_OBJECTS_PUSH, 0, NULL, LOG_NO_OBJECTID, Repositories[i].get_name().c_str(), err.c_str()); continue; } // We just push the new objects for(j=0; jShouldStop()) break; // We ignore the object if the repository already has it if(AsynchJobs::RepositoryKnowsObject(localObjects[j].get_repPath(), Repositories[i].get_repositoryssl())) continue; if(!ClientPki.PushWaitingObject(localObjects[j], Status)) { NewpkiDebug(LOG_LEVEL_WARNING, me_this->m_EntityName.c_str(), _sv("Failed to push NewPKI object on repository %s (%s:%ld)\nReason:%s"), Repositories[i].get_name().c_str(), Repositories[i].get_address().c_str(), Repositories[i].get_port(), ClientPki.GetError()); me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_FAILURE, LOG_MESSAGE_TYPE_OBJECTS_PUSH, 0, NULL, LOG_NO_OBJECTID, Repositories[i].get_name().c_str(), ClientPki.GetError()); continue; } // I tried to push a response to a repository // that has the response marked as deleted, I need // to delete is as well if(Status == NEWPKI_OBJECT_STATUS_DELETED && localObjects[j].get_object().get_type() == NEWPKI_OBJECT_TYPE_RESP) { me_this->RepositoryStore.DeleteResponse(PKI_CERT::EmptyInstance, localObjects[j].get_object().get_response().get_transactionid(), false, Status); // We let the other repositories know that they should // mark the transaction as deleted me_this->CreateNewObject(CryptedNewpkiRequest::EmptyInstance, CryptedNewpkiResponse::EmptyInstance, localObjects[j].get_object().get_response().get_transactionid(), Repositories[i].get_repositoryssl()); ERR_clear_error(); } NewpkiDebug(LOG_LEVEL_INFO, me_this->m_EntityName.c_str(), _sv("Successfully pushed NewPKI object on repository %s (%s:%ld)"), Repositories[i].get_name().c_str(), Repositories[i].get_address().c_str(), Repositories[i].get_port()); me_this->m_Logging->LogMessage(LOG_STATUS_TYPE_SUCCESS, LOG_MESSAGE_TYPE_OBJECTS_PUSH, 0, NULL, LOG_NO_OBJECTID, Repositories[i].get_name().c_str()); //Add this repository to the path AsynchJobs::AddRepositoryToObjectsPath(localObjects[j].get_repPath(), Repositories[i].get_repositoryssl()); } } } me_this->DeleteObjects(ids); localObjects.clear(); ids.clear(); } wait_next: do { NewpkiThread::Sleep(500); time(&currTime); } while( ((currTime - lastTime) < 60) && !Thread->ShouldStop()); lastTime = currTime; } } bool Entity_REPOSITORY::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_REPOSITORY::Private_ParseAdminCommand(bool ExecuteCmd, Entity * me_this, mVector & mLogsType, AdminResponseBody & response, const PKI_CERT & ClientCert, const AdminRequest & AdminRequest, UserHandle & hUser) { int pos; const char * cn = NULL; mString transactionId; if(ClientCert && AdminRequest) { switch(AdminRequest.get_body().get_type()) { case ADMIN_REQ_TYPE_GET_MY_REQS: case ADMIN_REQ_TYPE_GET_MY_RESPS: case ADMIN_REQ_TYPE_PUSH_REQ: case ADMIN_REQ_TYPE_PUSH_RESP: if(((pos = ClientCert.GetCertDN().SeekEntryName("commonName", -1)) != HASHTABLE_NOT_FOUND) ) cn = ClientCert.GetCertDN().Get(pos); else cn = "Unknown"; break; case ADMIN_REQ_TYPE_DELETE_RESPONSE: if(!NewPKIStore::transactionIDtoString(AdminRequest.get_body().get_transactionId(), transactionId)) { transactionId = _sv("none"); } break; default: break; } } PARSER_COMMAND_BEGIN(Entity_REPOSITORY, response, 0, AdminRequest, ClientCert, hUser, ExecuteCmd, me_this, mLogsType) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_CONFIG_PUSH) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_SEND_ADMIN_MAIL) PARSER_ADD_LOG_ENTRY(LOG_MESSAGE_TYPE_SEND_MAIL) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_LOGIN, Entity_REPOSITORY::UserLogin, LOG_MESSAGE_TYPE_USER_LOGIN, (ClientCert)?ClientCert.GetStringName():"", LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_PUSH_CONFIG, Entity_REPOSITORY::PushConfiguration, LOG_MESSAGE_TYPE_CONFIG_IMPORT, "", AdminRequest.get_body().get_conf().get_confs().get_version()) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_GET_MY_CONFIG, Entity_REPOSITORY::GetMyConf, LOG_MESSAGE_TYPE_ENTITY_CONFIG_GET, (ClientCert)?ClientCert.GetStringName():"", LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_ENUM_LOGS, Entity_REPOSITORY::EnumLogs) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_LOGS_COUNT, Entity_REPOSITORY::GetLogsCount) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_GET_LOGS_TYPE, Entity_REPOSITORY::GetLogsType) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_PUSH_NEWPKI_OBJECTS, Entity_REPOSITORY::PushWaitingObject, LOG_MESSAGE_TYPE_OBJECTS_IMPORT, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_GET_MY_REQS, Entity_REPOSITORY::GetMyRequests, LOG_MESSAGE_TYPE_GET_MY_REQS, cn, LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_GET_MY_RESPS, Entity_REPOSITORY::GetMyResponses, LOG_MESSAGE_TYPE_GET_MY_RESPS, cn, LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_CHECK_LOGS, Entity_REPOSITORY::CheckLogsIntegrity) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_PUSH_REQ, Entity_REPOSITORY::ImportRequest, LOG_MESSAGE_TYPE_INSERT_REQ, cn, LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_PUSH_RESP, Entity_REPOSITORY::ImportResponse, LOG_MESSAGE_TYPE_INSERT_RESP, cn, LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY( ADMIN_REQ_TYPE_SYNCH_OBJECTS, Entity_REPOSITORY::SynchronizeObjects) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_GET_CONFIG, Entity_REPOSITORY::GetConfiguration, LOG_MESSAGE_TYPE_GET_CONFIGURATION, _sv("none"), LOG_NO_OBJECTID) PARSER_COMMAND_ENTRY_LOG( ADMIN_REQ_TYPE_DELETE_RESPONSE, Entity_REPOSITORY::DeleteResponse, LOG_MESSAGE_TYPE_DELETE_RESPONSE, transactionId.c_str(), LOG_NO_OBJECTID) PARSER_COMMAND_END(Entity_REPOSITORY) } void Entity_REPOSITORY::LogsTypeGet(mVector & cLogsType) { Private_ParseAdminCommand(false, NULL, cLogsType, AdminResponseBody::EmptyInstance, PKI_CERT::EmptyInstance, AdminRequest::EmptyInstance, UserHandle::EmptyInstance); } bool Entity_REPOSITORY::PushConfiguration(COMMAND_PARAMETERS) { // Figure out what kind of certificate it is: // At this point we can get: // - the PKI entity certificate // - A user certificate // - An entity certificate // If its a user certificate we have to check // the acls to see if he can do this action switch(AclValidator.ValidateCert(UserCert)) { //The PKI entity or another entity case INTERNAL_CA_TYPE_ENTITY: case 0: break; //A user case INTERNAL_CA_TYPE_USER: if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_IMPORT_CONF, false)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } break; //Not allowed default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } if(!response.set_type(ADMIN_RESP_TYPE_NONE)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!OnNewPkiConf(body.get_conf())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::GetMyConf(COMMAND_PARAMETERS) { size_t i; //Is this a valid entity cert ? if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_ENTITY) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } ConfAccessLock.LockRead(); if(pki_conf) { //Find entity conf for(i=0; i & acl_list) { int i; static ACL_TYPE list_acls[] = { ACL_TYPE_VIEW_LOGS, ACL_TYPE_IMPORT_CONF, ACL_TYPE_IMPORT_NEWPKI_OBJECTS, (ACL_TYPE)0 }; for(i=0; list_acls[i]; i++) { acl_list.push_back(list_acls[i]); } } bool Entity_REPOSITORY::Upgrade(const char * Version) { return true; } bool Entity_REPOSITORY::PushWaitingObject(COMMAND_PARAMETERS) { // Figure out what kind of certificate it is: // At this point we can get: // - A user certificate // - An entity certificate // If its a user certificate we have to check // the acls to see if he can do this action switch(AclValidator.ValidateCert(UserCert)) { //The PKI entity or another entity case INTERNAL_CA_TYPE_ENTITY: if(!IsKnownRepository(UserCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } break; case INTERNAL_CA_TYPE_USER: if(!AclValidator.CanUserPerform(UserCert, ACL_TYPE_IMPORT_NEWPKI_OBJECTS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } break; //Not allowed case 0: default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } int intStatus; if(!response.set_type(ADMIN_RESP_TYPE_OBJECT_STATUS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!OnNewObject(body.get_waitingObj(), intStatus)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } response.set_status(intStatus); return true; } bool Entity_REPOSITORY::GetMyRequests(COMMAND_PARAMETERS) { // Figure out what kind of certificate it is: // At this point we can only get an entity certificate switch(AclValidator.ValidateCert(UserCert)) { //The PKI entity or another entity case INTERNAL_CA_TYPE_ENTITY: break; default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } if(!response.set_type(ADMIN_RESP_TYPE_REQS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Get the requests from the RepStore, minus the one in body->d.transactions_ids if(!RepositoryStore.GetRequests(response.get_objectReqs(), UserCert.GetPublicKey(), body.get_transactionsIds(), false)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::GetMyResponses(COMMAND_PARAMETERS) { // Figure out what kind of certificate it is: // At this point we can only get an entity certificate switch(AclValidator.ValidateCert(UserCert)) { //The PKI entity or another entity case INTERNAL_CA_TYPE_ENTITY: break; //Not allowed case 0: default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } if(!response.set_type(ADMIN_RESP_TYPE_RESPS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Get the responses from the RepStore if(!RepositoryStore.GetResponses(response.get_objectResps(), UserCert.GetPublicKey(), body.get_transactionsIds(), true)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::ImportRequest(COMMAND_PARAMETERS) { int Status; // Figure out what kind of certificate it is: // At this point we can only get an entity certificates if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_ENTITY) { 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; } //Insert the request in the RepStore if(!RepositoryStore.InsertRequest(body.get_newReq(), Status)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(Status == NEWPKI_OBJECT_STATUS_IMPORTED) { if(!CreateNewObject(body.get_newReq(), CryptedNewpkiResponse::EmptyInstance, Asn1OctetString::EmptyInstance)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } return true; } bool Entity_REPOSITORY::ImportResponse(COMMAND_PARAMETERS) { int Status; // Figure out what kind of certificate it is: // At this point we can only get an entity certificates if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_ENTITY) { 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; } //Insert the response in the RepStore if(!RepositoryStore.InsertResponse(body.get_newResp(), Status)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(Status == NEWPKI_OBJECT_STATUS_IMPORTED) { if(!CreateNewObject(CryptedNewpkiRequest::EmptyInstance, body.get_newResp(), Asn1OctetString::EmptyInstance)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } return true; } bool Entity_REPOSITORY::Responder_TreatRequest(const NewpkiRequest & Request, const mString & SenderName, NewpkiResponse & Response) { return true; } bool Entity_REPOSITORY::Responder_ValidateRequest(const NewpkiRequest & Request, const X509_PUBKEY * Requester, mString & SenderName) { return true; } bool Entity_REPOSITORY::Requester_OnNewResponse(const Asn1OctetString & transactionID, const X509_PUBKEY * sender, const NewpkiResponse & Response) { return true; } bool Entity_REPOSITORY::ParseSynchroObject(const ObjectsListSynchro & inObjects, ObjectsListSynchro & outObjects, bool & get_req, bool & get_resps, unsigned long & index_req, unsigned long & index_resp) { bool Exists; bool ret; int Status; size_t i, v_index; CryptedNewpkiRequests reqs; CryptedNewpkiResponses resps; mString strTid; Asn1OctetString transactionID; // Create the list of requests we want the peer to send us for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(transactionID, strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Requesting request object synchronization for %s"), strTid.c_str()); } } else if(GetDebugLevel() >= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(transactionID, strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Request object %s is known"), strTid.c_str()); } } // Create the list of responses we want the peer to send us // or that it should delete for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(transactionID, strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Requesting response object deletion for %s"), strTid.c_str()); } } else if(Status == NEWPKI_OBJECT_STATUS_KNOWN && GetDebugLevel() >= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(transactionID, strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Response object %s is known"), strTid.c_str()); } } else { // We don't know it, we want it ! outObjects.get_wantResponses().get_transactionids().push_back(transactionID); if(GetDebugLevel() >= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(transactionID, strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Requesting response object synchronization for %s"), strTid.c_str()); } } } // The other peer sent my a list of objects I requested // I need to integrate them for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(inObjects.get_objects()[i].get_object().get_response().get_transactionid(), strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Requesting response object deletion for %s"), strTid.c_str()); } } else if(Status == NEWPKI_OBJECT_STATUS_IMPORTED) { if(GetDebugLevel() >= LOG_LEVEL_DEBUG) { switch(inObjects.get_objects()[i].get_object().get_type()) { case NEWPKI_OBJECT_TYPE_RESP: NewPKIStore::transactionIDtoString(inObjects.get_objects()[i].get_object().get_response().get_transactionid(), strTid); NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Importing request object %s"), strTid.c_str()); break; case NEWPKI_OBJECT_TYPE_REQ: NewPKIStore::transactionIDtoString(inObjects.get_objects()[i].get_object().get_request().get_transactionid(), strTid); NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Importing response object %s"), strTid.c_str()); break; case NEWPKI_OBJECT_TYPE_DEL: NewPKIStore::transactionIDtoString(inObjects.get_objects()[i].get_object().get_transactionid(), strTid); NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Importing deletion for object %s"), strTid.c_str()); break; } } } } // Does the peer want some requests ? v_index = 0; if(inObjects.get_wantRequests().get_transactionids().size()) { //Get the requests from my store, the repository asked me if(!RepositoryStore.GetRequests(reqs, NULL, inObjects.get_wantRequests(), true)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } outObjects.get_objects().clear(); for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(reqs.get_requests()[i].get_transactionid(), strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Sending request object %s"), strTid.c_str()); } } reqs.Clear(); } // Does the peer want some responses ? if(inObjects.get_wantResponses().get_transactionids().size()) { //Get the responses from my store, the repository asked me if(!RepositoryStore.GetResponses(resps, NULL, inObjects.get_wantResponses(), true)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(resps.get_responses()[i].get_transactionid(), strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Sending response object %s"), strTid.c_str()); } v_index++; } resps.Clear(); } // Are we asked to mark as delete some responses ? if(inObjects.get_deleteResponses().get_transactionids().size()) { for(i=0; i= LOG_LEVEL_DEBUG && NewPKIStore::transactionIDtoString(inObjects.get_deleteResponses().get_transactionids()[i], strTid)) { NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Deleting response object %s"), strTid.c_str()); } } } NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Reading from request: %ld"), index_req); // Get the list of active requests we know if(get_req && !RepositoryStore.GetKnownRequests(outObjects.get_knownRequests(), index_req, 10)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Read %ld requests"), outObjects.get_knownRequests().get_transactionids().size()); NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Reading from response: %ld"), index_resp); // Get the list of active responses we know if(get_resps && !RepositoryStore.GetKnownResponses(outObjects.get_knownResponses(), index_resp, 10)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Read %ld responses"), outObjects.get_knownResponses().get_transactionids().size()); if(outObjects.get_knownRequests().get_transactionids().size() != 10) get_req = false; if(outObjects.get_knownResponses().get_transactionids().size() != 10) get_resps = false; index_req += outObjects.get_knownRequests().get_transactionids().size(); index_resp += outObjects.get_knownResponses().get_transactionids().size(); return true; } bool Entity_REPOSITORY::SynchronizeWithRepository(PkiClient * ClientPki, const RepEntryInfo & Repository) { ObjectsListSynchro local; ObjectsListSynchro remote; bool StopSynch; bool get_req; bool get_resps; unsigned long index_req; unsigned long index_resp; mString err; index_req = 0; index_resp = 0; StopSynch = false; get_req = true; get_resps = true; NewpkiDebug(LOG_LEVEL_DEBUG, m_EntityName.c_str(), _sv("Requesting full synchro with respository %s (%s:%ld)"), Repository.get_name().c_str(), Repository.get_address().c_str(), Repository.get_port()); //We connect to the repository if(!m_Jobs.ConnectToRepository(Repository, ClientPki, 0, err)) { NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to synchronize objects on respository %s (%s:%ld)\nReason:%s"), Repository.get_name().c_str(), Repository.get_address().c_str(), Repository.get_port(), err.c_str()); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } do { local.Clear(); if(!ParseSynchroObject(remote, local, get_req, get_resps, index_req, index_resp)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!ClientPki->SynchObjects(local, remote)) { NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to synchronize objects on respository %s (%s:%ld)\nReason:%s"), Repository.get_name().c_str(), Repository.get_address().c_str(), Repository.get_port(), ClientPki->GetError()); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!remote.get_deleteResponses().get_transactionids().size() && !remote.get_knownRequests().get_transactionids().size() && !remote.get_knownResponses().get_transactionids().size() && !remote.get_objects().size() && !remote.get_wantRequests().get_transactionids().size() && !remote.get_wantResponses().get_transactionids().size() && !local.get_deleteResponses().get_transactionids().size() && !local.get_knownRequests().get_transactionids().size() && !local.get_knownResponses().get_transactionids().size() && !local.get_objects().size() && !local.get_wantRequests().get_transactionids().size() && !local.get_wantResponses().get_transactionids().size() ) { StopSynch = true; } } while(!StopSynch); NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Successfully synchronized with respository %s (%s:%ld)"), Repository.get_name().c_str(), Repository.get_address().c_str(), Repository.get_port()); ClientPki->CloseConnection(); return true; } bool Entity_REPOSITORY::CreateNewObject(const CryptedNewpkiRequest & request, const CryptedNewpkiResponse & response, const Asn1OctetString & DeleteTid, const PKI_CERT & AddRep) { WaitingNewpkiObject object; // Set the synchro struct // request, response, DeleteTid are freed by CreateWaitingObject if(!CreateWaitingObject(object, request, response, DeleteTid, AddRep)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Add it to the list if(!InsertObject(object)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::CreateWaitingObject(WaitingNewpkiObject & object, const CryptedNewpkiRequest & request, const CryptedNewpkiResponse & response, const Asn1OctetString & DeleteTid, const PKI_CERT & AddRep) { if(request) { if(!object.get_object().set_type(NEWPKI_OBJECT_TYPE_REQ)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!object.get_object().set_request(request)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } else if(response) { if(!object.get_object().set_type(NEWPKI_OBJECT_TYPE_RESP)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!object.get_object().set_response(response)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } else if(DeleteTid) { if(!object.get_object().set_type(NEWPKI_OBJECT_TYPE_DEL)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(!object.get_object().set_transactionid(DeleteTid)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } else { NEWPKIerr(PKI_ERROR_TXT, ERROR_BAD_DATAS); return false; } if(AddRep) { if(!AsynchJobs::AddRepositoryToObjectsPath(object.get_repPath(), AddRep)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } } if(!AsynchJobs::AddRepositoryToObjectsPath(object.get_repPath(), m_EntityCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::SynchronizeObjects(COMMAND_PARAMETERS) { bool get_req; bool get_resps; unsigned long index_req; unsigned long index_resp; // Figure out what kind of certificate it is if(AclValidator.ValidateCert(UserCert) != INTERNAL_CA_TYPE_ENTITY || !IsKnownRepository(UserCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; } if(!response.set_type(ADMIN_RESP_TYPE_KNOWN_OBJECTS)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(hUser.get_context("index_req").size()) index_req = hUser.get_context("index_req").c_ulng(); else index_req = 0; if(hUser.get_context("index_resp").size()) index_resp = hUser.get_context("index_resp").c_ulng(); else index_resp = 0; get_req = true; get_resps = true; if(!ParseSynchroObject(body.get_knownObjects(), response.get_knownObjects(), get_req, get_resps, index_req, index_resp)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } hUser.get_context("index_req").sprintf("%ld", index_req); hUser.get_context("index_resp").sprintf("%ld", index_resp); return true; } bool Entity_REPOSITORY::OnNewObject(const WaitingNewpkiObject & object, int & Status) { bool ret; WaitingNewpkiObject n_object; // Verify that the NewPKI object hasn't already // been pushed on me if(AsynchJobs::RepositoryKnowsObject(object.get_repPath(), m_EntityCert)) { Status = NEWPKI_OBJECT_STATUS_KNOWN; return true; } //Insert new obects to repository switch(object.get_object().get_type()) { case NEWPKI_OBJECT_TYPE_REQ: ret = RepositoryStore.InsertRequest(object.get_object().get_request(), Status); break; case NEWPKI_OBJECT_TYPE_RESP: ret = RepositoryStore.InsertResponse(object.get_object().get_response(), Status); break; case NEWPKI_OBJECT_TYPE_DEL: ret = RepositoryStore.DeleteResponse(PKI_CERT::EmptyInstance, object.get_object().get_transactionid(), false, Status); break; default: NEWPKIerr(PKI_ERROR_TXT, ERROR_NOT_ALLOWED); return false; break; } if(!ret) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } if(Status != NEWPKI_OBJECT_STATUS_IMPORTED) // We ignore it, if it already exists { return true; } if(! (n_object = object) ) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Add my-self to the rep_path if(!AsynchJobs::AddRepositoryToObjectsPath(n_object.get_repPath(), m_EntityCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } //Add it to the list if(!InsertObject(n_object)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } return true; } bool Entity_REPOSITORY::OnNewPkiConf(const ExportedPkiConf & conf) { size_t i; mString req; mString conf_pem; EXPORTED_PKI_CONF_BODY * lConf = NULL; ConfAccessLock.LockWrite(); //Check configuration validity, has it been signed by the PKI? if(!conf.get_confs().give_Datas(&lConf)) { if(lConf) ASN1_item_free((ASN1_VALUE*)lConf, ExportedPkiConfBody::get_ASN1_ITEM()); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(ASN1_item_verify(ExportedPkiConfBody::get_ASN1_ITEM(), conf.get_sig()->algor, conf.get_sig()->digest, (char*)lConf, (EVP_PKEY*)myConf.get_cas().get_pkicert().GetPublicKey()) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); ASN1_item_free((ASN1_VALUE*)lConf, ExportedPkiConfBody::get_ASN1_ITEM()); return false; } ASN1_item_free((ASN1_VALUE*)lConf, ExportedPkiConfBody::get_ASN1_ITEM()); //Check configuration version, we ignore it if we have a higher version if(pki_conf && pki_conf.get_confs().get_version() >= conf.get_confs().get_version()) { ConfAccessLock.UnlockWrite(); return true; } //Copy conf if(! (pki_conf = conf) ) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!AsynchJobs::AddRepositoryToObjectsPath(pki_conf.get_repPath(), m_EntityCert)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } // Synch the conf if(!m_Jobs.SetPkiConf(pki_conf)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } //Insert global conf into database if(!pki_conf.to_PEM(conf_pem)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } SQL sql(m_DbConn); if(req.sprintf(REP_ENTITY_INSERT_G_CONF, conf_pem.c_str()) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_UNKNOWN); ConfAccessLock.UnlockWrite(); return false; } if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("New PKI conf version %ld"), pki_conf.get_confs().get_version()); //Find my own personal conf for(i=0; i