From 992e41b90f74c4e35c1b200b8c905af1ac5283b3 Mon Sep 17 00:00:00 2001
From: Lasse Tjernaes Wardenaer <lasse.tjernaes.wardenaer@cern.ch>
Date: Fri, 18 Nov 2022 13:19:09 +0100
Subject: [PATCH] Resolve "Enhance cta-readtp to handle encryption"

---
 ReleaseNotes.md                     |  5 +++
 tapeserver/readtp/ReadtpCmd.cpp     | 53 +++++++++++++++++++++++++++--
 tapeserver/readtp/ReadtpCmd.hpp     | 20 +++++++++--
 tapeserver/readtp/ReadtpCmdMain.cpp | 23 ++++++++++++-
 4 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index 53f28c0b8f..83c964ccaf 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,3 +1,8 @@
+# v.NEXT
+
+### Features
+- cta/CTA#211 - Add functionality for reading encrypted tapes with cta-readtp
+
 # v4.7.14-1
 
 ## Summary
diff --git a/tapeserver/readtp/ReadtpCmd.cpp b/tapeserver/readtp/ReadtpCmd.cpp
index 778f5bd3f9..e6a7f78050 100644
--- a/tapeserver/readtp/ReadtpCmd.cpp
+++ b/tapeserver/readtp/ReadtpCmd.cpp
@@ -29,6 +29,7 @@
 #include "rdbms/Login.hpp"
 #include "scheduler/RetrieveJob.hpp"
 #include "tapeserver/castor/tape/Constants.hpp"
+#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp"
 #include "tapeserver/castor/tape/tapeserver/daemon/Payload.hpp"
 #include "tapeserver/castor/tape/tapeserver/file/ReadSession.hpp"
 #include "tapeserver/castor/tape/tapeserver/file/ReadSessionFactory.hpp"
@@ -39,7 +40,6 @@
 #include "tapeserver/readtp/TapeFseqRange.hpp"
 #include "tapeserver/readtp/TapeFseqRangeListSequence.hpp"
 
-
 namespace cta {
 namespace tapeserver {
 namespace readtp {
@@ -49,14 +49,16 @@ namespace readtp {
 //------------------------------------------------------------------------------
 ReadtpCmd::ReadtpCmd(std::istream &inStream, std::ostream &outStream,
   std::ostream &errStream, cta::log::StdoutLogger &log, cta::log::DummyLogger &dummyLog,
-  cta::mediachanger::MediaChangerFacade &mc):
+  cta::mediachanger::MediaChangerFacade &mc, const bool useEncryption, 
+  const std::string& externalEncryptionKeyScript):
   CmdLineTool(inStream, outStream, errStream),
   m_log(log),
   m_dummyLog(dummyLog),
   m_mc(mc),
   m_useLbp(true),
   m_nbSuccessReads(0),
-  m_nbFailedReads(0) {
+  m_nbFailedReads(0),
+  m_encryptionControl(useEncryption, externalEncryptionKeyScript) {
 }
 
 //------------------------------------------------------------------------------
@@ -107,6 +109,16 @@ int ReadtpCmd::exceptionThrowingMain(const int argc, char *const *const argv) {
     returnCode = 1; 
   }
   unloadTape(m_vid, drive);
+
+  // Disable encryption (or at least try)
+  try {
+    if (m_encryptionControl.disable(drive)) {
+      m_log(cta::log::INFO, "Turned encryption off before unmounting");
+    }
+  } catch (cta::exception::Exception& ex) {
+    m_log(cta::log::ERR, "Failed to turn off encryption before unmounting");
+  }
+
   dismountTape(m_vid);
 
   return returnCode;
@@ -425,6 +437,9 @@ void ReadtpCmd::readTapeFile(
   volInfo.nbFiles = 0;
   volInfo.mountType = cta::common::dataStructures::MountType::Retrieve;
   volInfo.labelFormat = labelFormat;
+
+  configureEncryption(m_vid, drive);
+
   const auto readSession = castor::tape::tapeFile::ReadSessionFactory::create(drive, volInfo, m_useLbp);
 
   catalogue::TapeFileSearchCriteria searchCriteria;
@@ -561,6 +576,38 @@ void ReadtpCmd::rewindDrive(
   m_log(cta::log::INFO, "Successfully rewound tape", params);
 }
 
+//------------------------------------------------------------------------------
+// enableEncryption
+//------------------------------------------------------------------------------
+void ReadtpCmd::configureEncryption(
+  const std::string &vid,
+  castor::tape::tapeserver::drive::DriveInterface &drive) {
+  try {
+    // We want those scoped params to last for the whole mount.
+    // This will allow each session to be logged with its encryption
+    // status:
+    std::list<cta::log::Param> params;
+    {
+      auto encryptionStatus = m_encryptionControl.enable(drive, vid, castor::tape::tapeserver::daemon::EncryptionControl::SetTag::NO_SET_TAG);
+      if (encryptionStatus.on) {
+        params.push_back(cta::log::Param("encryption", "on"));
+        params.push_back(cta::log::Param("encryptionKey", encryptionStatus.keyName));
+        params.push_back(cta::log::Param("stdout", encryptionStatus.stdout));
+        m_log(cta::log::INFO, "Drive encryption enabled for this mount", params);
+      } else {
+        params.push_back(cta::log::Param("encryption", "off"));
+        m_log(cta::log::INFO, "Drive encryption not enabled for this mount", params);
+      }
+    }
+  }
+  catch (cta::exception::Exception& ex) {
+    std::list<cta::log::Param> params;
+    params.push_back(cta::log::Param("ErrorMessage", ex.getMessage().str()));
+    m_log(cta::log::ERR, "Drive encryption could not be enabled for this mount.", params);
+    throw;
+  }
+}
+
 //------------------------------------------------------------------------------
 // printUsage
 //------------------------------------------------------------------------------
diff --git a/tapeserver/readtp/ReadtpCmd.hpp b/tapeserver/readtp/ReadtpCmd.hpp
index 0f32465f59..9a95a0a617 100644
--- a/tapeserver/readtp/ReadtpCmd.hpp
+++ b/tapeserver/readtp/ReadtpCmd.hpp
@@ -41,6 +41,7 @@ namespace catalogue {
 class Catalogue;
 }
 
+
 namespace tapeserver {
 namespace readtp {
 
@@ -62,7 +63,8 @@ public:
   ReadtpCmd(std::istream &inStream, std::ostream &outStream,
     std::ostream &errStream, cta::log::StdoutLogger &log,
     cta::log::DummyLogger &dummyLog,
-    cta::mediachanger::MediaChangerFacade &mc);
+    cta::mediachanger::MediaChangerFacade &mc, const bool useEncryption,
+    const std::string& externalEncryptionKeyScript);
 
   /**
    * Destructor.
@@ -97,6 +99,14 @@ private:
    */
   void readAndSetConfiguration(const std::string &userName, const ReadtpCmdLineArgs &cmdLineArgs);
 
+  /**
+  * Configures encryption to be able to read from an encrypted tape
+  *
+  * @param vid The volume identifier of the tape to be mounted.
+  * @param drive The tape drive.
+  */
+  void configureEncryption(const std::string &vid, castor::tape::tapeserver::drive::DriveInterface &drive);
+
   /**
    * Reads a file line by line, strips comments and returns a list of the file lines.
    *
@@ -286,7 +296,7 @@ private:
    * The object representing the media changer.
    */
   cta::mediachanger::MediaChangerFacade &m_mc;
-  
+
   /**
    * The boolean variable which determinate logical block protection usage by
    * readtp commands. Hard coded when we create the class.
@@ -307,6 +317,12 @@ private:
    * Number of failed reads.
    */
   uint64_t m_nbFailedReads;
+
+  /**
+  * Encryption helper object 
+  */
+  castor::tape::tapeserver::daemon::EncryptionControl m_encryptionControl;
+
 }; // class ReadtpCmd
 
 CTA_GENERATE_EXCEPTION_CLASS(NoSuchFSeqException);
diff --git a/tapeserver/readtp/ReadtpCmdMain.cpp b/tapeserver/readtp/ReadtpCmdMain.cpp
index 9302c00e50..a6facb6732 100644
--- a/tapeserver/readtp/ReadtpCmdMain.cpp
+++ b/tapeserver/readtp/ReadtpCmdMain.cpp
@@ -17,12 +17,17 @@
 
 #include <iostream>
 
+#include "tapeserver/castor/tape/tapeserver/daemon/EncryptionControl.hpp"
+#include "tapeserver/daemon/TapedConfiguration.hpp"
 #include "tapeserver/readtp/ReadtpCmd.hpp"
 
 //------------------------------------------------------------------------------
 // main
 //------------------------------------------------------------------------------
 int main(const int argc, char *const *const argv) {
+
+  const std::string DAEMON_CONFIG = "/etc/cta/cta-taped.conf";
+
   char buf[256];
   std::string hostName;
   if (gethostname(buf, sizeof(buf))) {
@@ -35,6 +40,22 @@ int main(const int argc, char *const *const argv) {
   cta::log::DummyLogger dummyLog("dummy", "dummy");
   cta::mediachanger::MediaChangerFacade mc(log);
 
-  cta::tapeserver::readtp::ReadtpCmd cmd(std::cin, std::cout, std::cerr, log, dummyLog, mc);
+  bool useEncryption;
+  std::string externalEncryptionKeyScript;
+
+  try {
+    // Config file needed to find the cta-get-encryption-key script
+    const cta::tape::daemon::TapedConfiguration tapedConfig =
+      cta::tape::daemon::TapedConfiguration::createFromCtaConf(DAEMON_CONFIG, log);
+    externalEncryptionKeyScript = tapedConfig.externalEncryptionKeyScript.value();
+    useEncryption = tapedConfig.useEncryption.value() == "yes" ? true : false;
+  }
+  catch(...) {
+    cta::exception::Exception ex;
+    ex.getMessage() << "ReadtpCmd: Error while trying to read TapedConfiguration config file: " << DAEMON_CONFIG;
+    throw ex;
+  }
+
+  cta::tapeserver::readtp::ReadtpCmd cmd(std::cin, std::cout, std::cerr, log, dummyLog, mc, useEncryption, externalEncryptionKeyScript);
   return cmd.main(argc, argv);
 }
\ No newline at end of file
-- 
GitLab