Commit 9d1f9f2b authored by Eric Cano's avatar Eric Cano
Browse files

CASTOR-4739: tapeserverd should support localfile, rfio, xroot and rados...

CASTOR-4739: tapeserverd should support localfile, rfio, xroot and rados striper access for disk files

Removed all OpenSSL support from tapeserverd as we opted for CryptoPP. This restores the passing of
memory leaks and race conditions detection.
parent 623e11df
......@@ -51,6 +51,10 @@ bool DiskWriteTask::execute(RecallReportPacker& reporter,log::LogContext& lc,
try{
std::auto_ptr<tape::diskFile::WriteFile> writeFile(
fileFactory.createWriteFile(m_recallingFile->path()));
log::ScopedParamContainer URLcontext(lc);
URLcontext.add("actualURL", writeFile->URL())
.add("path", m_recallingFile->path());
lc.log(LOG_INFO, "Opened disk file for write");
m_stats.openingTime+=localTime.secs(utils::Timer::resetCounter);
int blockId = 0;
......
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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, or (at your option) any later version.
* 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.
*
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#include "castor/tape/tapeserver/exception/OpenSSL.hpp"
#include <openssl/err.h>
castor::tape::server::exception::OpenSSL::OpenSSL( const std::string & what) {
std::stringstream w;
if (what.size())
w << what << " ";
// Dump the OpenSSL error stack
// (open SSL maintains a stack of errors per thread)
while (unsigned long SSLError = ::ERR_get_error()) {
// SSL errors are stored in at least 120 char buffers
const size_t len = 200;
char buff[len];
::ERR_error_string_n(SSLError, buff, len);
w << "[" << buff << "]";
}
// Flush the SSL error queue
::ERR_clear_error();
getMessage().str(w.str());
}
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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, or (at your option) any later version.
* 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.
*
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#pragma once
#include "castor/exception/Exception.hpp"
namespace castor {
namespace tape {
namespace server {
namespace exception {
/**
* A class retrieving OpenSSL errors codes and turning them into a string.
* Comes with exception launchers
*/
class OpenSSL: public castor::exception::Exception {
public:
OpenSSL(const std::string & context);
virtual ~OpenSSL() throw() {};
template<class T>
static void throwOnNULL(const T * ptr, std::string context = "") {
if (!ptr) throw OpenSSL(context);
}
template<class T>
static void throwOnZero(const T val, std::string context = "") {
if (!val) throw OpenSSL(context);
}
};
}}}}
......@@ -4,8 +4,6 @@ add_library(File
File.cpp
DiskFile.cpp
Structures.cpp
OpenSSLLocker.cpp
../exception/XrootCl.cpp
../exception/OpenSSL.cpp)
../exception/XrootCl.cpp)
target_link_libraries (File castorrfio XrdCl cryptopp)
......@@ -26,7 +26,6 @@
#include "castor/tape/tapeserver/file/DiskFileImplementations.hpp"
#include <cryptopp/base64.h>
#include <cryptopp/osrng.h>
#include <openssl/pem.h>
namespace unitTests {
......@@ -121,7 +120,7 @@ namespace unitTests {
// Run the threads
std::vector<CryptoPPThread> m_threads;
m_threads.resize(10);
m_threads.resize(3);
for (std::vector<CryptoPPThread>::iterator i=m_threads.begin();
i!=m_threads.end(); i++) {
i->setKey(privateKey);
......@@ -133,19 +132,12 @@ namespace unitTests {
}
TEST(castor_CryptoPP, agreesWithOpenSSL) {
// Import the key for Open SSL
BIO * BIObuf = BIO_new(BIO_s_mem());
BIO_write(BIObuf, somePrivateKey.c_str(), somePrivateKey.size());
EVP_PKEY * OSSLKey;
OSSLKey = PEM_read_bio_PrivateKey(BIObuf, NULL, NULL, NULL);
BIO_free(BIObuf);
// We need one reference locker per thread for proper cleanup
std::auto_ptr<castor::tape::diskFile::OpenSSLLockerRef> lockerRef(
castor::tape::diskFile::OpenSSLLockerRefFactory());
// Import the key for CryptoPP
PEMKeyString CryptoPPKey(somePrivateKey);
std::string msg("Any random message will do!");
std::string osslSign(castor::tape::diskFile::OpenSSLSigner::sign(msg,OSSLKey));
// This is the output of:
// echo -n 'Any random message will do!' | openssl dgst -sha1 -sign ~/testRSAPrivate.pem | openssl enc -base64 | tr -d '\n' ; echo
std::string osslSign("bfqLxACTFS7fMKH5ewNUOaglRlIGCEPWGhx4fRPErFGHtuCi2yWlYFsXIfjBxOT+yCyKRpTnZWGJTbcP72eT7os2qCqIOejAM3nTcsChHN5f3UyADvsi1f7C3DqhYVKVFQPaBdb3zm8IBHsFjmu2EzVE5juc1C9L+ztVmoABptw=");
std::string CryptoPPSign(castor::tape::diskFile::CryptoPPSigner::sign(msg,CryptoPPKey));
std::cout << CryptoPPSign << std::endl;
ASSERT_EQ(osslSign,CryptoPPSign);
......@@ -162,6 +154,5 @@ namespace unitTests {
castor::tape::diskFile::CryptoPPSigner::sign("MtvFsd09F8UQNpwsULF6eMyVkRDIU+uAvBXyJs/LoNM5HrjoJgZrig==",CryptoPPKey));
ASSERT_EQ("EzSR5Fd1kfmdrVhCiYgoWQ7E1MSdv8OYng3L7LepCfS9OStlEFTkJcMezt4VRqUZnarlcIZ0yPAvrmOUscjrAOAbqA0rMYKsvHnAwd19RaH54QZhtRCDwMloxpuLmUC1cmyJ/PAdRoMYCoHiMVr7yQw0CnVJ5168MUe5o0v3swY=",
castor::tape::diskFile::CryptoPPSigner::sign("v3lPb49U+Zz+DNdzoTf2R8AU+AFP+/9/7nLlJV1+HNf3Z+Nzl/HuiQ==",CryptoPPKey));
EVP_PKEY_free(OSSLKey);
}
}
......@@ -28,12 +28,10 @@
#include "castor/exception/SErrnum.hpp"
#include "castor/server/MutexLocker.hpp"
#include "castor/tape/tapegateway/FilesToMigrateList.hpp"
#include "castor/tape/tapeserver/exception/OpenSSL.hpp"
#include <rfio_api.h>
#include <xrootd/XrdCl/XrdClFile.hh>
#include <radosstriper/libradosstriper.hpp>
#include <algorithm>
#include <openssl/pem.h>
#include <cryptopp/base64.h>
#include <cryptopp/osrng.h>
......@@ -51,9 +49,7 @@ DiskFileFactory::DiskFileFactory(const std::string & remoteFileProtocol,
m_URLXrootFile("^(root://.*)$"),
m_URLCephFile("^radosStriper://(.*)$"),
m_remoteFileProtocol(remoteFileProtocol),
m_openSSLLocker(OpenSSLLockerRefFactory()),
m_xrootPrivateKeyFile(xrootPrivateKeyFile),
m_xrootOpenSSLPrivateKey(NULL),
m_xrootCryptoPPPrivateKeyLoaded(false)
{
// Lowercase the protocol string
......@@ -61,32 +57,6 @@ DiskFileFactory::DiskFileFactory(const std::string & remoteFileProtocol,
m_remoteFileProtocol.begin(), ::tolower);
}
DiskFileFactory::~DiskFileFactory() {
if (m_xrootOpenSSLPrivateKey) {
EVP_PKEY_free(m_xrootOpenSSLPrivateKey);
}
}
EVP_PKEY* DiskFileFactory::xrootOpenSSLPrivateKey() {
if (!m_xrootOpenSSLPrivateKey) {
FILE* fPrivKey = ::fopen(m_xrootPrivateKeyFile.c_str(), "r");
castor::exception::Errnum::throwOnNull(fPrivKey,
std::string("In DiskFileFactory::xrootPrivateKey() failed to fopen() on ")+
m_xrootPrivateKeyFile);
try {
m_xrootOpenSSLPrivateKey = PEM_read_PrivateKey(fPrivKey,NULL,NULL,NULL);
castor::tape::server::exception::OpenSSL::throwOnNULL(m_xrootOpenSSLPrivateKey,
std::string("In DiskFileFactory::xrootPrivateKey() failed to PEM_read_PrivateKey() on ")+
m_xrootPrivateKeyFile);
} catch (...) {
::fclose(fPrivKey);
throw;
}
::fclose(fPrivKey);
}
return m_xrootOpenSSLPrivateKey;
}
const CryptoPP::RSA::PrivateKey & DiskFileFactory::xrootCryptoPPPrivateKey() {
if(!m_xrootCryptoPPPrivateKeyLoaded) {
// The loading of a PEM-style key is described in
......@@ -371,69 +341,6 @@ RfioWriteFile::~RfioWriteFile() throw() {
}
}
//==============================================================================
// OPENSSL SIGNER
//==============================================================================
castor::server::Mutex OpenSSLSigner::s_mutex;
std::string OpenSSLSigner::sign(const std::string msg, EVP_PKEY* privateKey) {
// One signature at a time as OpenSSL has race conditions issues...
castor::server::MutexLocker ml(&s_mutex);
// Sign the block
std::string signature;
EVP_MD_CTX mdCtx;
BIO * b64 = NULL;
BIO * bmem = NULL;
EVP_MD_CTX_init(&mdCtx);
try {
EVP_SignInit(&mdCtx, EVP_sha1());
castor::tape::server::exception::OpenSSL::throwOnZero(
EVP_SignUpdate(&mdCtx, msg.c_str(), msg.size()),
"In XrootReadFile::XrootReadFile failed EVP_SignUpdate");
// Sign the hash. Note that we need to serialize this code
unsigned char sig_buf[16384];
unsigned int sig_len = sizeof(sig_buf);
castor::tape::server::exception::OpenSSL::throwOnZero(
EVP_SignFinal(&mdCtx, sig_buf, &sig_len, privateKey),
"In XrootReadFile::XrootReadFile failed EVP_SignFinal()");
// We could cleanup the context now, but it's simpler to do it after the
// try block.
// Encode the signature in base 64
b64 = BIO_new(BIO_f_base64());
bmem = BIO_new(BIO_s_mem());
castor::tape::server::exception::OpenSSL::throwOnZero(
b64 || bmem,
"In XrootReadFile::XrootReadFile failed BIO_new()");
b64 = BIO_push(b64,bmem);
BIO_write(b64, sig_buf, sig_len);
(void)BIO_flush(b64);
// The signature is expected to be less than 200 bytes so we should get it in 1 pass
//char buff[200];
//int len=BIO_read(bmem,buff, sizeof(buff));
//while (len > 0) {
// signature.append(buff, len);
// len=BIO_read(b64,buff, sizeof(buff));
//}
BUF_MEM* bptr;
BIO_get_mem_ptr(b64, &bptr);
signature.append(bptr->data,bptr->length);
} catch (...) {
EVP_MD_CTX_cleanup(&mdCtx);
BIO_free(bmem);
BIO_free(b64);
throw;
}
EVP_MD_CTX_cleanup(&mdCtx);
BIO_free(bmem);
BIO_free(b64);
// Remove the newlines in the signature (it's truncated in lines)
signature.erase(std::remove(signature.begin(), signature.end(), '\n'), signature.end());
return signature;
}
//==============================================================================
// CRYPTOPP SIGNER
//==============================================================================
......
......@@ -24,9 +24,7 @@
#pragma once
#include "castor/tape/tapeserver/utils/Regex.hpp"
#include <openssl/evp.h>
#include <cryptopp/rsa.h>
#include "castor/tape/tapeserver/file/OpenSSLLocker.hpp"
#include <memory>
/*
* This file only contains the interface declaration of the base classes
......@@ -57,7 +55,6 @@ namespace castor {
const std::string & xrootPrivateKey);
ReadFile * createReadFile(const std::string & path);
WriteFile * createWriteFile(const std::string & path);
~DiskFileFactory();
private:
Regex m_NoURLLocalFile;
Regex m_NoURLRemoteFile;
......@@ -67,14 +64,11 @@ namespace castor {
Regex m_URLXrootFile;
Regex m_URLCephFile;
std::string m_remoteFileProtocol;
std::auto_ptr<OpenSSLLockerRef> m_openSSLLocker;
std::string m_xrootPrivateKeyFile;
EVP_PKEY *m_xrootOpenSSLPrivateKey;
CryptoPP::RSA::PrivateKey m_xrootCryptoPPPrivateKey;
bool m_xrootCryptoPPPrivateKeyLoaded;
/** Return the private key. Read it from the file if necessary. */
EVP_PKEY* xrootOpenSSLPrivateKey();
const CryptoPP::RSA::PrivateKey & xrootCryptoPPPrivateKey();
};
......
......@@ -29,9 +29,9 @@
#include "castor/tape/tapegateway/FileToRecallStruct.hpp"
#include "castor/tape/tapegateway/FileToMigrateStruct.hpp"
#include "castor/tape/tapeserver/client/ClientInterface.hpp"
#include "castor/tape/tapeserver/exception/XrootCl.hpp"
#include <xrootd/XrdCl/XrdClFile.hh>
#include <cryptopp/rsa.h>
namespace castor {
......@@ -44,7 +44,7 @@ namespace castor {
public:
LocalReadFile(const std::string &path);
virtual size_t size() const;
virtual size_t read(void *data, const size_t size);
virtual size_t read(void *data, const size_t size) const;
virtual ~LocalReadFile() throw();
private:
int m_fd;
......@@ -65,7 +65,7 @@ namespace castor {
public:
RfioReadFile(const std::string &rfioUrl);
virtual size_t size() const;
virtual size_t read(void *data, const size_t size);
virtual size_t read(void *data, const size_t size) const;
virtual ~RfioReadFile() throw();
private:
int m_fd;
......@@ -82,37 +82,47 @@ namespace castor {
bool m_closeTried;
};
struct CryptoPPSigner {
static std::string sign(const std::string msg,
const CryptoPP::RSA::PrivateKey & privateKey);
static castor::server::Mutex s_mutex;
};
class XrootReadFile: public ReadFile {
public:
XrootReadFile(const std::string &xrootUrl);
XrootReadFile(const std::string &xrootUrl,
const CryptoPP::RSA::PrivateKey & privateKey);
virtual size_t size() const;
virtual size_t read(void *data, const size_t size);
virtual size_t read(void *data, const size_t size) const;
virtual ~XrootReadFile() throw();
private:
// There is no const-correctness with XrdCl...
mutable XrdCl::File m_xrootFile;
uint64_t m_readPosition;
typedef castor::exception::XrootCl XrootClEx;
mutable uint64_t m_readPosition;
typedef castor::tape::server::exception::XrootCl XrootClEx;
std::string m_signedURL;
};
class XrootWriteFile: public WriteFile {
public:
XrootWriteFile(const std::string &xrootUrl);
XrootWriteFile(const std::string &xrootUrl,
const CryptoPP::RSA::PrivateKey & privateKey);
virtual void write(const void *data, const size_t size);
virtual void close();
virtual ~XrootWriteFile() throw();
private:
XrdCl::File m_xrootFile;
uint64_t m_writePosition;
typedef castor::exception::XrootCl XrootClEx;
typedef castor::tape::server::exception::XrootCl XrootClEx;
bool m_closeTried;
std::string m_signedURL;
};
class RadosStriperReadFile: public ReadFile {
public:
RadosStriperReadFile(const std::string &xrootUrl);
virtual size_t size() const;
virtual size_t read(void *data, const size_t size);
virtual size_t read(void *data, const size_t size) const;
virtual ~RadosStriperReadFile() throw();
};
......
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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, or (at your option) any later version.
* 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.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#include <openssl/crypto.h>
#include "castor/tape/tapeserver/file/OpenSSLLocker.hpp"
#include "castor/server/Mutex.hpp"
#include "castor/server/MutexLocker.hpp"
#include <sys/types.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <vector>
#include "castor/exception/Exception.hpp"
#include "castor/log/SyslogLogger.hpp"
#include <openssl/err.h>
#include <openssl/evp.h>
extern "C" {
void castorTapeOpenSSLLockingCallback(int mode, int n, const char * file, int line);
void castorTapeOpenSSLThreadIdCallback(CRYPTO_THREADID * threadId);
}
namespace castor {
namespace tape {
namespace diskFile {
/**
* A singleton implementing and registering the minimal callbacks for thread
* safe operations of OpenSSL. It is designed to be instantiated late in
* the tape session. The file factory will do it. The class will then
* "register" itself in a static pointer, which is needed as the OpenSSL callbacks
* do not provide a void* context pointer.
*
* We do this so that the main process for not have statically allocated mutexes,
* which could give side effects to initial forking.
*/
class OpenSSLLocker {
public:
OpenSSLLocker () {
if (gOpenSSLLocker) {
throw castor::exception::Exception("Attempt to instantiate several OpenSSLLockers");
} else {
gOpenSSLLocker = this;
}
// In the current implementation of OpenSSL, CRYPTO_num_locks returns
// CRYPTO_NUM_LOCKS, which is 41. a pretty reasonable number.
// Just resize the array
m_mutexes.resize(::CRYPTO_num_locks());
::CRYPTO_set_locking_callback(castorTapeOpenSSLLockingCallback);
::CRYPTO_THREADID_set_callback(castorTapeOpenSSLThreadIdCallback);
// Also load the crypto error messages
::ERR_load_crypto_strings();
}
~OpenSSLLocker() {
// According to Network Security with OpenSSL, Chapter 4, we can unset the
// callbacks
::CRYPTO_THREADID_set_callback(NULL);
::CRYPTO_set_locking_callback(NULL);
// Cleanup OpenSSL's structures
//::CONF_modules_free();
//::ENGINE_cleanup();
//::CONF_modules_unload(1);
::ERR_free_strings();
::EVP_cleanup();
::CRYPTO_cleanup_all_ex_data();
gOpenSSLLocker = NULL;
}
void lockingCallback(int mode, int n, const char * file, int line) throw () {
try {
if (n < 0 || (size_t)n >= m_mutexes.size())
throw castor::exception::Exception("In castor::tape::diskFile::OpenSSLLocker: index overflow");
if (mode & CRYPTO_LOCK) {
m_mutexes[n].lock();
} else {
m_mutexes[n].unlock();
}
} catch (castor::exception::Exception & e) {
// Simple handling for the moment: log and die. IF not sufficient we will
// have to use SSL's error system to push errors to the caller.
// See https://www.openssl.org/docs/crypto/err.html
castor::log::SyslogLogger sl("tapeserverd");
sl(LOG_EMERG, e.what());
abort();
} catch (...) {
castor::log::SyslogLogger sl("tapeserverd");
castor::exception::Exception e("Unknown exception in castor::tape::diskFile::OpenSSLLocker");
sl(LOG_EMERG, e.what());
abort();
}
}
static OpenSSLLocker * gOpenSSLLocker;
static castor::server::Mutex gCreationMutex;
static int gRefCount;
private:
std::vector<castor::server::Mutex> m_mutexes;
};
struct OpenSSLLockerRef {
OpenSSLLockerRef() {
castor::server::MutexLocker ml(&OpenSSLLocker::gCreationMutex);
if (!OpenSSLLocker::gRefCount++) {
OpenSSLLocker::gOpenSSLLocker = new OpenSSLLocker;
}
}
~OpenSSLLockerRef() {
castor::server::MutexLocker ml(&OpenSSLLocker::gCreationMutex);
if(!--OpenSSLLocker::gRefCount) {
delete OpenSSLLocker::gOpenSSLLocker;
OpenSSLLocker::gOpenSSLLocker = NULL;
}
// Remove this thread's error stack
// This implies that OpenSSLLockerRef is instantiated once per thread,
// which is the case in our application.
::ERR_remove_state(0);
}
};
OpenSSLLockerRef * OpenSSLLockerRefFactory() {
return new OpenSSLLockerRef();
}
OpenSSLLocker * OpenSSLLocker::gOpenSSLLocker;
castor::server::Mutex OpenSSLLocker::gCreationMutex;
int OpenSSLLocker::gRefCount;
}}}
void castorTapeOpenSSLLockingCallback(int mode, int n, const char * file, int line) {
castor::tape::diskFile::OpenSSLLocker::
gOpenSSLLocker->lockingCallback(mode,n,file,line);
}
void castorTapeOpenSSLThreadIdCallback(CRYPTO_THREADID * threadId) {
CRYPTO_THREADID_set_numeric(threadId, syscall(SYS_gettid));
}
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* 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, or (at your option) any later version.
* 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.
*
*
*
* @author Castor Dev team, castor-dev@cern.ch
*****************************************************************************/
#pragma once
namespace castor {
namespace tape {
namespace diskFile{
/** Forward declaration of the class */
class OpenSSLLockerRef;
OpenSSLLockerRef * OpenSSLLockerRefFactory();
}}}
/******************************************************************************
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor