/* 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 */ // RepStore.cpp: implementation of the RepStore class. // ////////////////////////////////////////////////////////////////////// #include "RepStore.h" #include "svintl.h" ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// RepStore::RepStore(const mString & EntityName, ENGINE * e):NewPKIStore(EntityName, e, NEWPKISTORE_TYPE_NONE) { } RepStore::~RepStore() { } bool RepStore::CreateTables(const SQL_Connection * DbConn) { SQL sql(DbConn); long i; char * CommonCreates[] = {REPSTORE_CREATE_1, REPSTORE_CREATE_2, 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; } bool RepStore::Create() { return true; } bool RepStore::Load() { return true; } bool RepStore::InsertRequest(const CryptedNewpkiRequest &request, int & Status) { ConfAccessLock.LockWrite(); SQL sql(m_DbConn); mString req; mString strTransactionID; mString strPubKey; mString pem_datas; mString pem_signature; RepStoreSig signature; long NumRows; CRYPTED_NEWPKI_REQUEST * lRequest; int SetStatus; bool Exists; int RespStatus; time_t startTime; // If we import a request but we already have its response // we must directly mark the request as deleted. if(!ResponseExists(request.get_transactionid(), Exists, RespStatus)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } SetStatus = Exists?REP_OBJECT_STATE_DELETED:REP_OBJECT_STATE_ACTIVE; //Convert transaction ID to string if(!transactionIDtoString(request.get_transactionid(), strTransactionID)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } //Search if we already have this transaction req.sprintf(REPSTORE_SEARCH_REQ, strTransactionID.c_str()); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(NumRows) { if(!sql.Value(0, "state", req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(req.c_int() == REP_OBJECT_STATE_DELETED) Status = REP_OBJECT_STATE_DELETED; else Status = NEWPKI_OBJECT_STATUS_KNOWN; ConfAccessLock.UnlockWrite(); return true; } Status = NEWPKI_OBJECT_STATUS_IMPORTED; // Convert public key to string if(!X509_PUBKEYtoHash(request.get_recipient(), strPubKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } lRequest = NULL; if(!request.give_Datas(&lRequest)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); if(lRequest) ASN1_item_free((ASN1_VALUE*)lRequest, CryptedNewpkiRequest::get_ASN1_ITEM()); ConfAccessLock.UnlockWrite(); return false; } //Get the signature into pem if(!CRYPTED_NEWPKI_REQUEST_sign(lRequest, signature, (EVP_PKEY*)m_EntityCert.GetPrivateKey().GetRsaKey())) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ASN1_item_free((ASN1_VALUE*)lRequest, CryptedNewpkiRequest::get_ASN1_ITEM()); ConfAccessLock.UnlockWrite(); return false; } ASN1_item_free((ASN1_VALUE*)lRequest, CryptedNewpkiRequest::get_ASN1_ITEM()); if(!signature.to_PEM(pem_signature)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!request.to_PEM(pem_datas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(req.sprintf(REPSTORE_INSERT_REQ, strTransactionID.c_str(), strPubKey.c_str(), pem_datas.c_str(), pem_signature.c_str(), SetStatus) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } // Every 100000 requests we request the table to be optimized if( (sql.GetLastID() % 100000) == 0 ) { time(&startTime); NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimizing requests table...")); if(!sql.OptimizeTable(REPSTORE_REQUESTS_TABLE)) { req = ""; ERR_to_mstring(req); NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to optimize requests table - Reason: %s"), req.c_str()); ERR_clear_error(); } else { NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimized requests table in %ld secondes"), time(NULL) - startTime); } } ConfAccessLock.UnlockWrite(); return true; } bool RepStore::InsertResponse(const CryptedNewpkiResponse &response, int & Status) { ConfAccessLock.LockWrite(); SQL sql(m_DbConn); mString req; mString strTransactionID; mString strPubKey; mString pem_datas; mString pem_signature; RepStoreSig signature; bool Exists; CRYPTED_NEWPKI_RESPONSE * lResponse; time_t startTime; //Convert transaction ID to string if(!transactionIDtoString(response.get_transactionid(), strTransactionID)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } //Search if we already have this transaction if(!ResponseExists(response.get_transactionid(), Exists, Status)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(Exists) { ConfAccessLock.UnlockWrite(); return true; } Status = NEWPKI_OBJECT_STATUS_IMPORTED; // Since we have the response, there is no need // to synchronize over the request anymore req.sprintf(REPSTORE_SET_REQ_STATE, REP_OBJECT_STATE_DELETED, strTransactionID.c_str()); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } // Convert public key to string if(!X509_PUBKEYtoHash(response.get_recipient(), strPubKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } //Get the signature into pem lResponse = NULL; if(!response.give_Datas(&lResponse)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); if(lResponse) ASN1_item_free((ASN1_VALUE*)lResponse, CryptedNewpkiResponse::get_ASN1_ITEM()); ConfAccessLock.UnlockWrite(); return false; } if(!CRYPTED_NEWPKI_RESPONSE_sign(lResponse, signature, (EVP_PKEY*)m_EntityCert.GetPrivateKey().GetRsaKey())) { ASN1_item_free((ASN1_VALUE*)lResponse, CryptedNewpkiResponse::get_ASN1_ITEM()); NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } ASN1_item_free((ASN1_VALUE*)lResponse, CryptedNewpkiResponse::get_ASN1_ITEM()); if(!signature.to_PEM(pem_signature)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!response.to_PEM(pem_datas)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(req.sprintf(REPSTORE_INSERT_RESP, strTransactionID.c_str(), strPubKey.c_str(), pem_datas.c_str(), pem_signature.c_str(), REP_OBJECT_STATE_ACTIVE) <= 0) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockWrite(); return false; } // Every 100000 responses we request the table to be optimized if( (sql.GetLastID() % 100000) == 0 ) { time(&startTime); NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimizing responses table...")); if(!sql.OptimizeTable(REPSTORE_RESPONSES_TABLE)) { req = ""; ERR_to_mstring(req); NewpkiDebug(LOG_LEVEL_WARNING, m_EntityName.c_str(), _sv("Failed to optimize responses table - Reason: %s"), req.c_str()); ERR_clear_error(); } else { NewpkiDebug(LOG_LEVEL_INFO, m_EntityName.c_str(), _sv("Optimized responses table in %ld secondes"), time(NULL) - startTime); } } ConfAccessLock.UnlockWrite(); return true; } bool RepStore::RequestExists(const Asn1OctetString & transactionID, bool & exists) { ConfAccessLock.LockRead(); mString strTransactionID; mString req; SQL sql(m_DbConn); long NumRows; if(!transactionIDtoString(transactionID, strTransactionID)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } req.sprintf(REPSTORE_REQ_EXISTS, strTransactionID.c_str()); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } if(NumRows) exists = true; else exists = false; ConfAccessLock.UnlockRead(); return true; } bool RepStore::ResponseExists(const Asn1OctetString & transactionID, bool & exists, int & Status) { ConfAccessLock.LockRead(); mString strTransactionID; mString req; SQL sql(m_DbConn); long NumRows; if(!transactionIDtoString(transactionID, strTransactionID)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } req.sprintf(REPSTORE_SEARCH_RESP, strTransactionID.c_str()); if(!sql.Execute(req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } if(!sql.NumRows(&NumRows)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } if(NumRows) { if(!sql.Value(0, "state", req)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); ConfAccessLock.UnlockRead(); return false; } if(req.c_int() == REP_OBJECT_STATE_DELETED) Status = NEWPKI_OBJECT_STATUS_DELETED; else Status = NEWPKI_OBJECT_STATUS_KNOWN; exists = true; } else exists = false; ConfAccessLock.UnlockRead(); return true; } bool RepStore::GetRequests(CryptedNewpkiRequests & Requests, const EVP_PKEY *pubkey, const TransactionIds & ListIds, bool inList) { size_t i, v_index; mString strPubKey; mString strTransactionID; mString req; mString where; long NumRows; // We build the WHERE part of the request if(pubkey) { //Convert the recipient key to string if(!EVP_PKEYtoHash(pubkey, strPubKey)) { NEWPKIerr(PKI_ERROR_TXT, ERROR_ABORT); return false; } where.sprintf("WHERE recipient='%s'", strPubKey.c_str()); strPubKey=""; } // Add the state clause, we only consider the // active responses, all the deleted responses are // still present for integrity purposes if(pubkey) where += " AND state="; else where = "WHERE state="; req.sprintf("%d", REP_OBJECT_STATE_ACTIVE); where += req; if(ListIds && ListIds.get_transactionids().size()) { where += " AND ("; for(i=0; i