Change command line option processing (#82)
* Changes command line option processing: - Use getopt_long in libc and support long options - GNU style option processing (don't try to enforce non-conflicting options) - GNU style usage message - Separate encrypt and decrypt settings - --unprotect -> --allow-raw-read, --protect -> --no-allow-raw-read for clarification - Change reading key file from stdin using --key-file=- - Always print detailed status, remove summary "Drive Encryption" line * Other formatting and style cleanup * Test output changes
This commit is contained in:
446
src/main.cpp
446
src/main.cpp
@@ -26,6 +26,7 @@ GNU General Public License for more details.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <getopt.h>
|
||||
#include <sys/mtio.h>
|
||||
#include <sys/stat.h>
|
||||
#include <syslog.h>
|
||||
@@ -43,6 +44,8 @@ GNU General Public License for more details.
|
||||
|
||||
#include "scsiencrypt.h"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
static std::optional<std::vector<std::uint8_t>>
|
||||
key_from_hex_chars(const std::string& s)
|
||||
{
|
||||
@@ -72,21 +75,43 @@ key_from_hex_chars(const std::string& s)
|
||||
}
|
||||
|
||||
// shows the command usage
|
||||
static void showUsage()
|
||||
static void print_usage(std::ostream& os)
|
||||
{
|
||||
std::cerr << "Usage: stenc --version | "
|
||||
"-f <device> [--detail] [-e <on/mixed/rawread/off> [-k <file>] "
|
||||
"[-kd <description>] [-a <index>] [--protect | --unprotect] "
|
||||
"[--ckod] ]\n\n"
|
||||
"Type 'man stenc' for more information.\n";
|
||||
}
|
||||
|
||||
// exits to shell with an error message
|
||||
static void errorOut(const std::string& message)
|
||||
{
|
||||
std::cerr << "Error: " << message << "\n";
|
||||
showUsage();
|
||||
exit(EXIT_FAILURE);
|
||||
os << "\
|
||||
Usage: stenc [OPTION...]\n\
|
||||
\n\
|
||||
Mandatory arguments to long options are mandatory for short options too.\n\
|
||||
-f, --file=DEVICE use DEVICE as the tape drive to operate on\n\
|
||||
-e, --encrypt=ENC-MODE set encryption mode to ENC-MODE\n\
|
||||
-d, --decrypt=DEC-MODE set decryption mode to DEC-MODE\n\
|
||||
-k, --key-file=FILE read encryption key and key descriptor from FILE,\n\
|
||||
or standard input when FILE is -\n\
|
||||
-a, --algorithm=INDEX use encryption algorithm INDEX\n\
|
||||
--allow-raw-read mark written blocks to allow raw reads of\n\
|
||||
encrypted data\n\
|
||||
--no-allow-raw-read mark written blocks to disallow raw reads of\n\
|
||||
encrypted data\n\
|
||||
--ckod clear key on demount of tape media\n\
|
||||
-h, --help print this usage statement and exit\n\
|
||||
--version print version information and exit\n\
|
||||
\n\
|
||||
ENC-MODE is one of the following:\n\
|
||||
off Data written to DEVICE will not be encrypted\n\
|
||||
on Data written to DEVICE will be encrypted\n\
|
||||
\n\
|
||||
DEC-MODE is one of the following:\n\
|
||||
off Data read from DEVICE will not be decrypted and only unencrypted\n\
|
||||
records can be read.\n\
|
||||
on Data read from DEVICE will be decrypted and only encrypted records\n\
|
||||
can be read.\n\
|
||||
mixed Data read from DEVICE will be decrypted, if needed. Both encrypted\n\
|
||||
and unencrypted records can be read.\n\
|
||||
\n\
|
||||
INDEX is a number that selects the encryption algorithm and mode to use.\n\
|
||||
\n\
|
||||
When neither options to set encryption or decryption mode are given, print\n\
|
||||
encryption status and capabilities of DEVICE, including a list of supported\n\
|
||||
algorithm indexes.\n";
|
||||
}
|
||||
|
||||
static void print_algorithm_name(std::ostream& os, const uint32_t code)
|
||||
@@ -123,7 +148,8 @@ static void print_algorithms(std::ostream& os, const scsi::page_dec& page)
|
||||
|
||||
for (auto ad_ptr: algorithms) {
|
||||
auto& ad {*ad_ptr};
|
||||
os << std::left << std::setw(5) << (unsigned int) {ad.algorithm_index};
|
||||
os << std::left << std::setw(5)
|
||||
<< static_cast<unsigned int>(ad.algorithm_index);
|
||||
print_algorithm_name(os, ntohl(ad.security_algorithm_code));
|
||||
os.put('\n');
|
||||
|
||||
@@ -194,39 +220,8 @@ static void inquiryDrive(const std::string& tapeDevice)
|
||||
print_device_inquiry(std::cout, iresult);
|
||||
}
|
||||
|
||||
static void print_device_status(std::ostream& os, const scsi::page_des& opt,
|
||||
bool detail)
|
||||
static void print_device_status(std::ostream& os, const scsi::page_des& opt)
|
||||
{
|
||||
std::string emode = "unknown";
|
||||
os << std::left << std::setw(25) << "Drive Encryption:";
|
||||
if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt
|
||||
opt.decryption_mode == scsi::decrypt_mode::on // read only encrypted data
|
||||
) {
|
||||
emode = "on";
|
||||
}
|
||||
if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt
|
||||
opt.decryption_mode ==
|
||||
scsi::decrypt_mode::mixed // read encrypted and unencrypted
|
||||
) {
|
||||
emode = "mixed";
|
||||
}
|
||||
|
||||
if (opt.encryption_mode == scsi::encrypt_mode::on && // encrypt
|
||||
opt.decryption_mode ==
|
||||
scsi::decrypt_mode::raw // read encrypted and unencrypted
|
||||
) {
|
||||
emode = "rawread";
|
||||
}
|
||||
|
||||
if (opt.encryption_mode == scsi::encrypt_mode::off && // encrypt
|
||||
opt.decryption_mode ==
|
||||
scsi::decrypt_mode::off // read encrypted and unencrypted
|
||||
) {
|
||||
emode = "off";
|
||||
}
|
||||
|
||||
os << emode << "\n";
|
||||
if (detail) {
|
||||
os << std::left << std::setw(25) << "Drive Output:";
|
||||
switch (opt.decryption_mode) {
|
||||
case scsi::decrypt_mode::off:
|
||||
@@ -274,11 +269,10 @@ static void print_device_status(std::ostream& os, const scsi::page_des& opt,
|
||||
}
|
||||
|
||||
os << std::setw(25) << "Key Instance Counter:" << std::dec
|
||||
<< ntohl(opt.key_instance_counter) << "\n";
|
||||
<< ntohl(opt.key_instance_counter) << '\n';
|
||||
if (opt.algorithm_index != 0) {
|
||||
os << std::setw(25) << "Encryption Algorithm:" << std::dec
|
||||
<< static_cast<unsigned int>(opt.algorithm_index) << "\n";
|
||||
}
|
||||
<< static_cast<unsigned int>(opt.algorithm_index) << '\n';
|
||||
}
|
||||
auto kads {scsi::read_page_kads(opt)};
|
||||
for (auto kd: kads) {
|
||||
@@ -299,25 +293,24 @@ static void print_device_status(std::ostream& os, const scsi::page_des& opt,
|
||||
}
|
||||
}
|
||||
|
||||
static void showDriveStatus(const std::string& tapeDrive, bool detail)
|
||||
static void showDriveStatus(const std::string& tapeDrive)
|
||||
{
|
||||
alignas(4) scsi::page_buffer buffer;
|
||||
scsi::get_des(tapeDrive, buffer, sizeof(buffer));
|
||||
auto& opt {reinterpret_cast<const scsi::page_des&>(buffer)};
|
||||
|
||||
print_device_status(std::cout, opt, detail);
|
||||
print_device_status(std::cout, opt);
|
||||
}
|
||||
|
||||
static void print_volume_status(std::ostream& os, const scsi::page_nbes& opt)
|
||||
{
|
||||
auto compression_status {static_cast<std::uint8_t>(
|
||||
(opt.status & scsi::page_nbes::status_compression_mask) >>
|
||||
scsi::page_nbes::status_compression_pos)};
|
||||
opt.status & scsi::page_nbes::status_compression_mask)};
|
||||
// From vendor docs, no known drives actually report anything other than 0
|
||||
if (compression_status != 0u) {
|
||||
os << std::left << std::setw(25) << "Volume Compressed:";
|
||||
switch (compression_status) {
|
||||
case 0u:
|
||||
case 0u << scsi::page_nbes::status_compression_pos:
|
||||
os << "Drive cannot determine\n";
|
||||
break;
|
||||
default:
|
||||
@@ -328,28 +321,27 @@ static void print_volume_status(std::ostream& os, const scsi::page_nbes& opt)
|
||||
}
|
||||
os << std::left << std::setw(25) << "Volume Encryption:";
|
||||
auto encryption_status {static_cast<std::uint8_t>(
|
||||
(opt.status & scsi::page_nbes::status_encryption_mask) >>
|
||||
scsi::page_nbes::status_encryption_pos)};
|
||||
opt.status & scsi::page_nbes::status_encryption_mask)};
|
||||
auto kads {read_page_kads(opt)};
|
||||
switch (encryption_status) {
|
||||
case 0u:
|
||||
case 1u:
|
||||
case 0u << scsi::page_nbes::status_encryption_pos:
|
||||
case 1u << scsi::page_nbes::status_encryption_pos:
|
||||
os << "Unable to determine\n";
|
||||
break;
|
||||
case 2u:
|
||||
case 2u << scsi::page_nbes::status_encryption_pos:
|
||||
os << "Tape position not at a logical block\n";
|
||||
break;
|
||||
case 3u:
|
||||
case 3u << scsi::page_nbes::status_encryption_pos:
|
||||
os << "Not encrypted\n";
|
||||
break;
|
||||
case 5u:
|
||||
case 5u << scsi::page_nbes::status_encryption_pos:
|
||||
os << "Encrypted and able to decrypt\n";
|
||||
if ((opt.flags & scsi::page_nbes::flags_rdmds_mask) ==
|
||||
scsi::page_nbes::flags_rdmds_mask) {
|
||||
os << std::left << std::setw(25) << " Protected from raw read\n";
|
||||
}
|
||||
break;
|
||||
case 6u:
|
||||
case 6u << scsi::page_nbes::status_encryption_pos:
|
||||
os << "Encrypted, but unable to decrypt due to invalid key.\n";
|
||||
for (auto kd: kads) {
|
||||
switch (kd->type) {
|
||||
@@ -380,7 +372,7 @@ static void print_volume_status(std::ostream& os, const scsi::page_nbes& opt)
|
||||
if (opt.algorithm_index != 0) {
|
||||
os << std::left << std::setw(25)
|
||||
<< "Volume Algorithm:" << static_cast<unsigned int>(opt.algorithm_index)
|
||||
<< "\n";
|
||||
<< '\n';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,104 +395,104 @@ static void echo(bool on)
|
||||
}
|
||||
|
||||
#if !defined(CATCH_CONFIG_MAIN)
|
||||
int main(int argc, const char **argv)
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
std::string tapeDrive;
|
||||
int action = 0; // 0 = status, 1 =setting param, 2 = generating key
|
||||
std::string keyFile, keyDesc;
|
||||
bool detail = false;
|
||||
std::string keyFile;
|
||||
|
||||
scsi::encrypt_mode enc_mode;
|
||||
scsi::decrypt_mode dec_mode;
|
||||
std::optional<scsi::encrypt_mode> enc_mode;
|
||||
std::optional<scsi::decrypt_mode> dec_mode;
|
||||
std::uint8_t algorithm_index;
|
||||
std::vector<uint8_t> key;
|
||||
std::string key_name;
|
||||
scsi::sde_rdmc rdmc {};
|
||||
bool ckod {};
|
||||
|
||||
// First load all of the options
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string thisCmd = argv[i];
|
||||
std::string nextCmd = "";
|
||||
if (i + 1 < argc) {
|
||||
if (strncmp(argv[i + 1], "-", 1) != 0)
|
||||
nextCmd = argv[i + 1];
|
||||
}
|
||||
if (thisCmd == "--version") {
|
||||
std::cout << "stenc v" << VERSION << " - SCSI Tape Encryption Manager\n";
|
||||
std::cout << "https://github.com/scsitape/stenc \n";
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (thisCmd == "-e") {
|
||||
if (nextCmd == "") {
|
||||
errorOut("Key file not specified after -k option");
|
||||
}
|
||||
if (nextCmd == "on") {
|
||||
// encrypt, read only encrypted data
|
||||
enc_mode = scsi::encrypt_mode::on;
|
||||
enum opt_key : int {
|
||||
opt_version = 256,
|
||||
opt_ckod,
|
||||
opt_rdmc_enable,
|
||||
opt_rdmc_disable,
|
||||
};
|
||||
|
||||
const struct option long_options[] = {
|
||||
{"algorithm", required_argument, nullptr, 'a'},
|
||||
{"decrypt", required_argument, nullptr, 'd'},
|
||||
{"encrypt", required_argument, nullptr, 'e'},
|
||||
{"file", required_argument, nullptr, 'f'},
|
||||
{"key-file", required_argument, nullptr, 'k'},
|
||||
{"help", no_argument, nullptr, 'h'},
|
||||
{"ckod", no_argument, nullptr, opt_ckod},
|
||||
{"allow-raw-read", no_argument, nullptr, opt_rdmc_enable},
|
||||
{"no-allow-raw-read", no_argument, nullptr, opt_rdmc_disable},
|
||||
{"version", no_argument, nullptr, opt_version},
|
||||
{nullptr, 0, nullptr, 0},
|
||||
};
|
||||
|
||||
int opt_char;
|
||||
while ((opt_char = getopt_long(argc, argv, "+a:d:e:f:k:h", long_options,
|
||||
nullptr)) != -1) {
|
||||
switch (opt_char) {
|
||||
case 'a':
|
||||
algorithm_index = std::atoi(optarg);
|
||||
break;
|
||||
case 'd': {
|
||||
std::string arg {optarg};
|
||||
if (arg == "on"s) {
|
||||
dec_mode = scsi::decrypt_mode::on;
|
||||
} else if (nextCmd == "mixed") {
|
||||
// encrypt, read encrypted and unencrypted data
|
||||
enc_mode = scsi::encrypt_mode::on;
|
||||
dec_mode = scsi::decrypt_mode::mixed;
|
||||
} else if (nextCmd == "rawread") {
|
||||
// encrypt, read encrypted and unencrypted data
|
||||
enc_mode = scsi::encrypt_mode::on;
|
||||
dec_mode = scsi::decrypt_mode::raw;
|
||||
} else if (nextCmd == "off") {
|
||||
// encrypt, read encrypted and unencrypted data
|
||||
enc_mode = scsi::encrypt_mode::off;
|
||||
} else if (arg == "off"s) {
|
||||
dec_mode = scsi::decrypt_mode::off;
|
||||
} else if (arg == "mixed"s) {
|
||||
dec_mode = scsi::decrypt_mode::mixed;
|
||||
} else {
|
||||
errorOut("Unknown encryption mode '" + nextCmd +
|
||||
"'"); // encrypt, read encrypted and unencrypted data
|
||||
print_usage(std::cerr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
i++; // skip the next argument
|
||||
action = 1;
|
||||
} else if (thisCmd == "-f") {
|
||||
if (nextCmd == "")
|
||||
errorOut("Device not specified after -f option.");
|
||||
tapeDrive = nextCmd; // set the tape drive
|
||||
i++; // skip the next argument
|
||||
} else if (thisCmd == "-k") {
|
||||
if (nextCmd == "")
|
||||
errorOut("Key file not specified after -k option");
|
||||
keyFile = nextCmd; // set the key file
|
||||
i++; // skip the next argument
|
||||
} else if (thisCmd == "-kd") {
|
||||
if (nextCmd == "")
|
||||
errorOut("Key description not specified after the -kd option");
|
||||
keyDesc = nextCmd; // set the key file
|
||||
if (keyDesc.size() > SSP_UKAD_LENGTH) {
|
||||
errorOut("Key description too long!");
|
||||
break;
|
||||
}
|
||||
i++; // skip the next argument
|
||||
} else if (thisCmd == "--protect") {
|
||||
if (rdmc == scsi::sde_rdmc::enabled) {
|
||||
errorOut("'--protect' cannot be specified at the same time as "
|
||||
"'--unprotect'");
|
||||
case 'e': {
|
||||
std::string arg {optarg};
|
||||
if (arg == "on"s) {
|
||||
enc_mode = scsi::encrypt_mode::on;
|
||||
} else if (arg == "off"s) {
|
||||
enc_mode = scsi::encrypt_mode::off;
|
||||
} else {
|
||||
print_usage(std::cerr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rdmc = scsi::sde_rdmc::disabled;
|
||||
} else if (thisCmd == "--unprotect") {
|
||||
if (rdmc == scsi::sde_rdmc::disabled) {
|
||||
errorOut("'--unprotect' cannot be specified at the same time as "
|
||||
"'--protect'");
|
||||
break;
|
||||
}
|
||||
rdmc = scsi::sde_rdmc::enabled;
|
||||
} else if (thisCmd == "--ckod") {
|
||||
case 'f':
|
||||
tapeDrive = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
keyFile = optarg;
|
||||
break;
|
||||
case opt_ckod:
|
||||
ckod = true;
|
||||
} else if (thisCmd == "--detail") {
|
||||
detail = true;
|
||||
} else if (thisCmd == "-a") {
|
||||
if (nextCmd == "")
|
||||
errorOut("You must specify a numeric algorithm index when using the -a "
|
||||
"flag");
|
||||
algorithm_index = std::atoi(nextCmd.c_str());
|
||||
i++; // skip the next argument
|
||||
} else {
|
||||
errorOut("Unknown command '" + thisCmd + "'");
|
||||
break;
|
||||
case opt_rdmc_enable:
|
||||
rdmc = scsi::sde_rdmc::enabled;
|
||||
break;
|
||||
case opt_rdmc_disable:
|
||||
rdmc = scsi::sde_rdmc::disabled;
|
||||
break;
|
||||
case static_cast<int>('h'):
|
||||
print_usage(std::cout);
|
||||
exit(EXIT_SUCCESS);
|
||||
case opt_version:
|
||||
std::cout << "stenc " VERSION " - SCSI Tape Encryption Manager\n"
|
||||
<< "https://github.com/scsitape/stenc\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
default:
|
||||
print_usage(std::cerr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
if (optind != argc) { // left-over unparsed arguments or options
|
||||
print_usage(std::cerr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// select device from env variable or system default if not given with -f
|
||||
if (tapeDrive.empty()) {
|
||||
@@ -511,23 +503,17 @@ int main(int argc, const char **argv)
|
||||
tapeDrive = DEFTAPE;
|
||||
}
|
||||
}
|
||||
if (dec_mode == scsi::decrypt_mode::raw && rdmc == scsi::sde_rdmc::disabled) {
|
||||
errorOut(
|
||||
"'--protect' is not valid when setting encryption mode to 'rawread'");
|
||||
}
|
||||
|
||||
openlog("stenc", LOG_CONS, LOG_USER);
|
||||
|
||||
if (action == 0) {
|
||||
std::cout << "Status for " << tapeDrive << "\n"
|
||||
if (!enc_mode && !dec_mode) {
|
||||
std::cout << "Status for " << tapeDrive << '\n'
|
||||
<< "--------------------------------------------------\n";
|
||||
|
||||
try {
|
||||
if (detail) {
|
||||
inquiryDrive(tapeDrive);
|
||||
}
|
||||
showDriveStatus(tapeDrive, detail);
|
||||
if (detail && scsi::is_device_ready(tapeDrive)) {
|
||||
showDriveStatus(tapeDrive);
|
||||
if (scsi::is_device_ready(tapeDrive)) {
|
||||
try {
|
||||
showVolumeStatus(tapeDrive);
|
||||
} catch (const scsi::scsi_error& err) {
|
||||
@@ -540,98 +526,106 @@ int main(int argc, const char **argv)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (detail) {
|
||||
alignas(4) scsi::page_buffer buffer {};
|
||||
scsi::get_dec(tapeDrive, buffer, sizeof(buffer));
|
||||
auto& page {reinterpret_cast<const scsi::page_dec&>(buffer)};
|
||||
|
||||
print_algorithms(std::cout, page);
|
||||
}
|
||||
exit(EXIT_SUCCESS);
|
||||
} catch (const scsi::scsi_error& err) {
|
||||
std::cerr << "stenc: " << err.what() << '\n';
|
||||
scsi::print_sense_data(std::cerr, err.get_sense());
|
||||
exit(EXIT_FAILURE);
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << '\n';
|
||||
std::cerr << "stenc: " << err.what() << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (enc_mode == scsi::encrypt_mode::on) {
|
||||
if (keyFile.empty()) {
|
||||
std::string p1;
|
||||
std::string p2;
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
std::cout << "Enter key in hex format: ";
|
||||
echo(false);
|
||||
getline(std::cin, p1);
|
||||
echo(true);
|
||||
std::cout << "\nRe-enter key in hex format: ";
|
||||
echo(false);
|
||||
getline(std::cin, p2);
|
||||
echo(true);
|
||||
std::cout << "\n";
|
||||
if (p1 != p2) {
|
||||
std::cout << "Keys do not match!\n";
|
||||
} else if (p1.empty()) {
|
||||
std::cout << "Key cannot be empty!\n";
|
||||
// Infer encrypt/decrypt mode when only one is specified
|
||||
if (enc_mode && !dec_mode) {
|
||||
if (enc_mode == scsi::encrypt_mode::off) {
|
||||
dec_mode = scsi::decrypt_mode::off;
|
||||
std::cerr << "Decrypt mode not specified, using decrypt = off\n";
|
||||
} else if (enc_mode == scsi::encrypt_mode::on) {
|
||||
dec_mode = scsi::decrypt_mode::on;
|
||||
std::cerr << "Decrypt mode not specified, using decrypt = on\n";
|
||||
} else {
|
||||
if (auto key_bytes = key_from_hex_chars(p1)) {
|
||||
std::cout << "Set encryption using this key? [y/n]: ";
|
||||
std::string ans = "";
|
||||
getline(std::cin, ans);
|
||||
if (ans == "y") {
|
||||
key = *key_bytes;
|
||||
done = true;
|
||||
std::cerr << "stenc: Unexpected encrypt mode "
|
||||
<< static_cast<unsigned int>(*enc_mode) << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
} else if (!enc_mode && dec_mode) {
|
||||
if (dec_mode == scsi::decrypt_mode::off) {
|
||||
enc_mode = scsi::encrypt_mode::off;
|
||||
std::cerr << "Encrypt mode not specified, using encrypt = off\n";
|
||||
} else if (dec_mode == scsi::decrypt_mode::on ||
|
||||
dec_mode == scsi::decrypt_mode::mixed) {
|
||||
enc_mode = scsi::encrypt_mode::on;
|
||||
std::cerr << "Encrypt mode not specified, using encrypt = on\n";
|
||||
} else {
|
||||
std::cout << "Invalid key!\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
key_name = keyDesc;
|
||||
} else {
|
||||
// set keyInput here
|
||||
std::string keyInput;
|
||||
std::ifstream myfile(keyFile.c_str());
|
||||
if (myfile.is_open()) {
|
||||
getline(myfile, keyInput);
|
||||
getline(myfile, keyDesc);
|
||||
myfile.close();
|
||||
if (auto key_bytes = key_from_hex_chars(keyInput)) {
|
||||
key = *key_bytes;
|
||||
} else {
|
||||
errorOut("Invalid key found in '" + keyFile + "'");
|
||||
}
|
||||
key_name = keyDesc;
|
||||
} else
|
||||
errorOut("Could not open '" + keyFile + "' for reading");
|
||||
std::cerr << "stenc: Unexpected decrypt mode "
|
||||
<< static_cast<unsigned int>(*dec_mode) << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (enc_mode != scsi::encrypt_mode::off ||
|
||||
dec_mode != scsi::decrypt_mode::off) {
|
||||
if (keyFile.empty()) {
|
||||
std::cerr << "stenc: Encryption key required but no key file specified\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// set keyInput here
|
||||
std::string keyInput;
|
||||
|
||||
if (keyFile == "-"s) { // Read key file from standard input
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
std::cout << "Enter key (input will be hidden): ";
|
||||
echo(false);
|
||||
}
|
||||
std::getline(std::cin, keyInput);
|
||||
if (isatty(STDIN_FILENO)) {
|
||||
std::cout << "\nEnter key descriptor (optional): ";
|
||||
echo(true);
|
||||
}
|
||||
std::getline(std::cin, key_name);
|
||||
} else {
|
||||
std::ifstream myfile {keyFile};
|
||||
if (!myfile.is_open()) {
|
||||
std::cerr << "stenc: Cannot open " << keyFile << ": " << strerror(errno)
|
||||
<< '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
std::getline(myfile, keyInput);
|
||||
std::getline(myfile, key_name);
|
||||
}
|
||||
|
||||
if (auto key_bytes = key_from_hex_chars(keyInput)) {
|
||||
key = *key_bytes;
|
||||
} else {
|
||||
std::cerr << "stenc: Invalid key in key file\n";
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
if (enc_mode != scsi::encrypt_mode::on) {
|
||||
key_name.erase(); // key descriptor only valid when key is used for writing
|
||||
}
|
||||
|
||||
// Write the options to the tape device
|
||||
std::cout << "Turning "
|
||||
<< (enc_mode != scsi::encrypt_mode::off ? "on" : "off")
|
||||
<< " encryption on device '" << tapeDrive << "'..." << std::endl;
|
||||
std::cerr << "Changing encryption settings on device " << tapeDrive << '\n';
|
||||
try {
|
||||
auto sde_buffer {scsi::make_sde(enc_mode, dec_mode, algorithm_index, key,
|
||||
key_name, rdmc, ckod)};
|
||||
auto sde_buffer {scsi::make_sde(enc_mode.value(), dec_mode.value(),
|
||||
algorithm_index, key, key_name, rdmc,
|
||||
ckod)};
|
||||
scsi::write_sde(tapeDrive, sde_buffer.get());
|
||||
|
||||
alignas(4) scsi::page_buffer buffer {};
|
||||
scsi::get_des(tapeDrive, buffer, sizeof(buffer));
|
||||
auto& opt {reinterpret_cast<const scsi::page_des&>(buffer)};
|
||||
|
||||
if (enc_mode != scsi::encrypt_mode::off &&
|
||||
opt.encryption_mode == scsi::encrypt_mode::off) {
|
||||
errorOut("Turning encryption on for '" + tapeDrive + "' failed!");
|
||||
}
|
||||
if (enc_mode == scsi::encrypt_mode::off &&
|
||||
opt.encryption_mode != scsi::encrypt_mode::off) {
|
||||
errorOut("Turning encryption off for '" + tapeDrive + "' failed!");
|
||||
}
|
||||
|
||||
if (enc_mode != scsi::encrypt_mode::off) {
|
||||
std::stringstream msg;
|
||||
msg << "Encryption turned on for device '" << tapeDrive << "'. ";
|
||||
@@ -639,7 +633,7 @@ int main(int argc, const char **argv)
|
||||
msg << "Key Descriptor: '" << key_name << "'";
|
||||
}
|
||||
msg << " Key Instance: " << std::dec << ntohl(opt.key_instance_counter)
|
||||
<< std::endl;
|
||||
<< '\n';
|
||||
|
||||
syslog(LOG_NOTICE, "%s", msg.str().c_str());
|
||||
} else {
|
||||
@@ -647,22 +641,18 @@ int main(int argc, const char **argv)
|
||||
|
||||
msg << "Encryption turned off for device '" << tapeDrive << "'.";
|
||||
msg << " Key Instance: " << std::dec << ntohl(opt.key_instance_counter)
|
||||
<< std::endl;
|
||||
<< '\n';
|
||||
|
||||
syslog(LOG_NOTICE, "%s", msg.str().c_str());
|
||||
}
|
||||
std::cout << "Success! See system logs for a key change audit log.\n";
|
||||
exit(EXIT_SUCCESS);
|
||||
std::cerr << "Success! See system logs for a key change audit log.\n";
|
||||
} catch (const scsi::scsi_error& err) {
|
||||
std::cerr << "stenc: " << err.what() << '\n';
|
||||
scsi::print_sense_data(std::cerr, err.get_sense());
|
||||
exit(EXIT_FAILURE);
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << '\n';
|
||||
}
|
||||
|
||||
if (enc_mode != scsi::encrypt_mode::off) {
|
||||
errorOut("Turning encryption on for '" + tapeDrive + "' failed!");
|
||||
} else {
|
||||
errorOut("Turning encryption off for '" + tapeDrive + "' failed!");
|
||||
std::cerr << "stenc: " << err.what() << '\n';
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
#endif // defined(CATCH_CONFIG_MAIN)
|
||||
|
||||
@@ -303,6 +303,9 @@ void print_sense_data(std::ostream& os, const sense_data& sd)
|
||||
case sense_data::no_sense:
|
||||
os << "No specific error";
|
||||
break;
|
||||
case sense_data::recovered_error:
|
||||
os << "Recovered error";
|
||||
break;
|
||||
case sense_data::not_ready:
|
||||
os << "Device not ready";
|
||||
break;
|
||||
@@ -329,10 +332,10 @@ void print_sense_data(std::ostream& os, const sense_data& sd)
|
||||
os << " (0x" << HEX(sense_key) << ")\n";
|
||||
|
||||
os << std::left << std::setw(25) << " ASC:"
|
||||
<< "0x" << HEX(sd.additional_sense_code) << "\n";
|
||||
<< "0x" << HEX(sd.additional_sense_code) << '\n';
|
||||
|
||||
os << std::left << std::setw(25) << " ASCQ:"
|
||||
<< "0x" << HEX(sd.additional_sense_qualifier) << "\n";
|
||||
<< "0x" << HEX(sd.additional_sense_qualifier) << '\n';
|
||||
|
||||
if (sd.additional_sense_length > 0) {
|
||||
os << std::left << std::setw(25) << " Additional data: "
|
||||
@@ -341,7 +344,7 @@ void print_sense_data(std::ostream& os, const sense_data& sd)
|
||||
for (int i = 0; i < sd.additional_sense_length; i++) {
|
||||
os << HEX(sd.additional_sense_bytes[i]);
|
||||
}
|
||||
os << "\n";
|
||||
os << '\n';
|
||||
}
|
||||
#ifdef DEBUGSCSI
|
||||
os << std::left << std::setw(25) << " Raw Sense:"
|
||||
@@ -351,7 +354,7 @@ void print_sense_data(std::ostream& os, const sense_data& sd)
|
||||
for (int i = 0; i < sense_data::maximum_size; i++) {
|
||||
os << HEX(rawsense[i]);
|
||||
}
|
||||
os << "\n";
|
||||
os << '\n';
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -344,7 +344,7 @@ using sense_buffer = std::array<std::uint8_t, sense_data::maximum_size>;
|
||||
class scsi_error : public std::runtime_error {
|
||||
public:
|
||||
explicit scsi_error(std::unique_ptr<sense_buffer>&& buf)
|
||||
: sense_buf {std::move(buf)}, std::runtime_error {""}
|
||||
: sense_buf {std::move(buf)}, std::runtime_error {"SCSI I/O error"}
|
||||
{}
|
||||
const sense_data& get_sense() const
|
||||
{
|
||||
|
||||
@@ -62,13 +62,12 @@ TEST_CASE("SCSI get device encryption status output 1", "[output]")
|
||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Drive Encryption: off\n\
|
||||
Drive Output: Not decrypting\n\
|
||||
Raw encrypted data not outputted\n\
|
||||
Drive Input: Not encrypting\n\
|
||||
Key Instance Counter: 0\n"s};
|
||||
std::ostringstream oss;
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(page), true);
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(page));
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
@@ -81,7 +80,6 @@ TEST_CASE("SCSI get device encryption status output 2", "[output]")
|
||||
0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21,
|
||||
};
|
||||
const std::string expected_output {"\
|
||||
Drive Encryption: on\n\
|
||||
Drive Output: Decrypting\n\
|
||||
Unencrypted data not outputted\n\
|
||||
Drive Input: Encrypting\n\
|
||||
@@ -89,7 +87,7 @@ Key Instance Counter: 1\n\
|
||||
Encryption Algorithm: 1\n\
|
||||
Drive Key Desc.(uKAD): Hello world!\n"s};
|
||||
std::ostringstream oss;
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(page), true);
|
||||
print_device_status(oss, reinterpret_cast<const scsi::page_des&>(page));
|
||||
REQUIRE(oss.str() == expected_output);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user