Delete created identities/keys if the client doesn't complete successfully; update client command line tool
Client command line tool:
* remove infinite loop asking for user input when input is of wrong format
* add signal handling
Refs: #4962
Change-Id: Ib4f8143f6efd45dee7232f99c5b791f09926bb46
diff --git a/src/client-module.cpp b/src/client-module.cpp
index 9ae71c0..0daa72c 100644
--- a/src/client-module.cpp
+++ b/src/client-module.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -40,7 +40,10 @@
{
}
-ClientModule::~ClientModule() = default;
+ClientModule::~ClientModule()
+{
+ endSession();
+}
shared_ptr<Interest>
ClientModule::generateProbeInfoInterest(const Name& caName)
@@ -158,14 +161,22 @@
// generate a newly key pair or use an existing key
const auto& pib = m_keyChain.getPib();
+ security::pib::Identity identity;
try {
- auto identity = pib.getIdentity(m_identityName);
- m_key = m_keyChain.createKey(identity);
+ identity = pib.getIdentity(m_identityName);
}
catch (const security::Pib::Error& e) {
- auto identity = m_keyChain.createIdentity(m_identityName);
+ identity = m_keyChain.createIdentity(m_identityName);
+ m_isNewlyCreatedIdentity = true;
+ m_isNewlyCreatedKey = true;
+ }
+ try {
m_key = identity.getDefaultKey();
}
+ catch (const security::Pib::Error& e) {
+ m_key = m_keyChain.createKey(identity);
+ m_isNewlyCreatedKey = true;
+ }
// generate certificate request
security::v2::Certificate certRequest;
@@ -286,19 +297,20 @@
return interest;
}
-void
+shared_ptr<security::v2::Certificate>
ClientModule::onDownloadResponse(const Data& reply)
{
try {
security::v2::Certificate cert(reply.getContent().blockFromValue());
m_keyChain.addCertificate(m_key, cert);
_LOG_TRACE("Got DOWNLOAD response and installed the cert " << cert.getName());
+ m_isCertInstalled = true;
+ return make_shared<security::v2::Certificate>(cert);
}
catch (const std::exception& e) {
_LOG_ERROR("Cannot add replied certificate into the keychain " << e.what());
- return;
+ return nullptr;
}
- m_isCertInstalled = true;
}
void
@@ -307,6 +319,25 @@
onDownloadResponse(reply);
}
+void
+ClientModule::endSession()
+{
+ if (getApplicationStatus() == STATUS_SUCCESS || getApplicationStatus() == STATUS_ENDED) {
+ return;
+ }
+ if (m_isNewlyCreatedIdentity) {
+ // put the identity into the if scope is because it may cause an error
+ // outside since when endSession is called, identity may not have been created yet.
+ auto identity = m_keyChain.getPib().getIdentity(m_identityName);
+ m_keyChain.deleteIdentity(identity);
+ }
+ else if (m_isNewlyCreatedKey) {
+ auto identity = m_keyChain.getPib().getIdentity(m_identityName);
+ m_keyChain.deleteKey(identity, m_key);
+ }
+ m_status = STATUS_ENDED;
+}
+
JsonSection
ClientModule::getJsonFromData(const Data& data)
{
diff --git a/src/client-module.hpp b/src/client-module.hpp
index b3655ef..fb20d4b 100644
--- a/src/client-module.hpp
+++ b/src/client-module.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -22,8 +22,8 @@
#define NDNCERT_CLIENT_MODULE_HPP
#include "client-config.hpp"
-#include "crypto-support/crypto-helper.hpp"
#include "certificate-request.hpp"
+#include "crypto-support/crypto-helper.hpp"
namespace ndn {
namespace ndncert {
@@ -47,7 +47,6 @@
public:
ClientModule(security::v2::KeyChain& keyChain);
- virtual
~ClientModule();
ClientConfig&
@@ -109,7 +108,7 @@
shared_ptr<Interest>
generateCertFetchInterest();
- void
+ shared_ptr<security::v2::Certificate>
onDownloadResponse(const Data& reply);
void
@@ -125,6 +124,9 @@
static std::vector<std::string>
parseProbeComponents(const std::string& probe);
+ void
+ endSession();
+
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
const JsonSection
genProbeRequestJson(const ClientCaItem& ca, const std::string& probeInfo);
@@ -148,6 +150,8 @@
std::string m_certId = "";
std::list<std::string> m_challengeList;
bool m_isCertInstalled = false;
+ bool m_isNewlyCreatedIdentity = false;
+ bool m_isNewlyCreatedKey = false;
int m_remainingTries = 0;
time::system_clock::TimePoint m_freshBefore;
diff --git a/src/ndncert-common.hpp b/src/ndncert-common.hpp
index 9835578..860fc8c 100644
--- a/src/ndncert-common.hpp
+++ b/src/ndncert-common.hpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -116,7 +116,8 @@
STATUS_PENDING = 2,
STATUS_SUCCESS = 3,
STATUS_FAILURE = 4,
- STATUS_NOT_STARTED = 5
+ STATUS_NOT_STARTED = 5,
+ STATUS_ENDED = 6
};
// Pre-defined challenge status
diff --git a/tools/ndncert-client.cpp b/tools/ndncert-client.cpp
index ea6f3be..bc5bb5a 100644
--- a/tools/ndncert-client.cpp
+++ b/tools/ndncert-client.cpp
@@ -1,6 +1,6 @@
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
/**
- * Copyright (c) 2017-2019, Regents of the University of California.
+ * Copyright (c) 2017-2020, Regents of the University of California.
*
* This file is part of ndncert, a certificate management system based on NDN.
*
@@ -22,9 +22,11 @@
#include "challenge-module.hpp"
#include <iostream>
#include <string>
+#include <csignal>
#include <boost/program_options/options_description.hpp>
#include <boost/program_options/variables_map.hpp>
#include <boost/program_options/parsers.hpp>
+#include <boost/asio.hpp>
#include <ndn-cxx/security/verification-helpers.hpp>
namespace ndn {
@@ -81,25 +83,20 @@
static void
captureValidityPeriod()
{
- if (validityPeriod <= 0) {
- std::cerr << "Step " << nStep++
- << ": Please type in your expected validity period of your certificate."
- << " Type the number of hours (168 for week, 730 for month, 8760 for year). The CA may change the validity"
- << " period if your expected period is too long." << std::endl;
- std::string periodStr = "";
- do {
- getline(std::cin, periodStr);
- try {
- validityPeriod = std::stoi(periodStr);
- }
- catch (const std::exception& e) {
- validityPeriod = -1;
- }
- if (validityPeriod > 0) {
- break;
- }
- std::cerr << "Invalid period time. Please input the period again." << std::endl;
- } while (true);
+ if (validityPeriod > 0) {
+ return;
+ }
+ std::cerr << "Step " << nStep++
+ << ": Please type in your expected validity period of your certificate."
+ << " Type the number of hours (168 for week, 730 for month, 8760 for year)."
+ << " The CA may reject your application if your expected period is too long." << std::endl;
+ std::string periodStr = "";
+ getline(std::cin, periodStr);
+ try {
+ validityPeriod = std::stoi(periodStr);
+ }
+ catch (const std::exception& e) {
+ validityPeriod = -1;
}
}
@@ -118,10 +115,14 @@
static void
downloadCb(const Data& reply)
{
- client.onDownloadResponse(reply);
+ auto cert = client.onDownloadResponse(reply);
+ if (cert == nullptr) {
+ std::cerr << "Certificate cannot be installed to your local keychain" << std::endl;
+ return;
+ }
std::cerr << "Step " << nStep++
- << ": DONE! Certificate has already been installed to local keychain\n";
- return;
+ << ": DONE! Certificate has already been installed to local keychain\n"
+ << "Certificate Name: " << cert->getName().toUri() << std::endl;
}
static void
@@ -142,12 +143,7 @@
std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
std::string redo = "";
std::list<std::string> capturedParams;
- do {
- capturedParams = captureParams(requirement);
- std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
- getline(std::cin, redo);
- boost::algorithm::to_lower(redo);
- } while (redo != "ok");
+ capturedParams = captureParams(requirement);
auto it1 = capturedParams.begin();
auto it2 = requirement.begin();
for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
@@ -173,23 +169,21 @@
int count = 0;
std::string choice = "";
std::cerr << "Step " << nStep++ << ": Please type in the challenge index that you want to perform\n";
- do {
- count = 0;
- for (auto item : challengeList) {
- std::cerr << "\t" << count++ << " : "<< item << std::endl;
- }
- getline(std::cin, choice);
- try {
- challengeIndex = std::stoi(choice);
- }
- catch (const std::exception& e) {
- challengeIndex = -1;
- }
- if (challengeIndex >= 0 && challengeIndex < count) {
- break;
- }
- std::cerr << "Your input index is out of range. Please type in again." << std::endl;
- } while (true);
+ count = 0;
+ for (auto item : challengeList) {
+ std::cerr << "\t" << count++ << " : "<< item << std::endl;
+ }
+ getline(std::cin, choice);
+ try {
+ challengeIndex = std::stoi(choice);
+ }
+ catch (const std::exception& e) {
+ challengeIndex = -1;
+ }
+ if (challengeIndex < 0 || challengeIndex >= count) {
+ std::cerr << "Your input index is out of range. Exit." << std::endl;
+ return;
+ }
}
auto it = challengeList.begin();
std::advance(it, challengeIndex);
@@ -208,12 +202,7 @@
std::cerr << "Step " << nStep++ << ": Please satisfy following instruction(s)\n";
std::string redo = "";
std::list<std::string> capturedParams;
- do {
- capturedParams = captureParams(requirement);
- std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
- getline(std::cin, redo);
- boost::algorithm::to_lower(redo);
- } while (redo != "ok");
+ capturedParams = captureParams(requirement);
auto it1 = capturedParams.begin();
auto it2 = requirement.begin();
for (; it1 != capturedParams.end() && it2 != requirement.end(); it1++, it2++) {
@@ -261,12 +250,20 @@
{
client.onProbeResponse(reply);
captureValidityPeriod();
+ if (validityPeriod <= 0) {
+ std::cerr << "Invalid period time. Exit." << std::endl;
+ return;
+ }
auto probeToken = make_shared<Data>(reply);
auto now = time::system_clock::now();
std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
- face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
- Name(), probeToken),
- bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
+ auto interest = client.generateNewInterest(now, now + time::hours(validityPeriod), Name(), probeToken);
+ if (interest != nullptr) {
+ face.expressInterest(*interest, bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
+ }
+ else {
+ std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
+ }
}
static void
@@ -318,12 +315,7 @@
std::vector<std::string> probeFields = ClientModule::parseProbeComponents(targetCaItem.m_probe);
std::string redo = "";
std::list<std::string> capturedParams;
- do {
- capturedParams = captureParams(probeFields);
- std::cerr << "If everything is correct, please type in OK; otherwise, type in REDO" << std::endl;
- getline(std::cin, redo);
- boost::algorithm::to_lower(redo);
- } while (redo != "ok");
+ capturedParams = captureParams(probeFields);
std::string probeInfo;
for (const auto& item : capturedParams) {
probeInfo += item;
@@ -334,23 +326,55 @@
bind(&probeCb, _2), bind(&onNackCb), bind(&timeoutCb));
}
else {
- std::cerr << "Step " << nStep++ << ": Please type in the identity name you want to get (with CA prefix)\n";
+ std::cerr << "Step " << nStep++ << ": Please type in the full identity name you want to get (with CA prefix)\n";
std::string identityNameStr;
getline(std::cin, identityNameStr);
captureValidityPeriod();
+ if (validityPeriod <= 0) {
+ std::cerr << "Invalid period time. Exit." << std::endl;
+ return;
+ }
+ Name idName(identityNameStr);
std::cerr << "The validity period of your certificate will be: " << validityPeriod << " hours" << std::endl;
auto now = time::system_clock::now();
- face.expressInterest(*client.generateNewInterest(now, now + time::hours(validityPeriod),
- Name(identityNameStr)),
- bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
+ auto interest = client.generateNewInterest(now, now + time::hours(validityPeriod), idName);
+ if (interest != nullptr) {
+ face.expressInterest(*interest, bind(&newCb, _2), bind(&onNackCb), bind(&timeoutCb));
+ }
+ else {
+ std::cerr << "Cannot generate the Interest for NEW step. Exit" << std::endl;
+ }
}
}
}
+static void
+handleSignal(const boost::system::error_code& error, int signalNum)
+{
+ if (error) {
+ return;
+ }
+ const char* signalName = ::strsignal(signalNum);
+ std::cerr << "Exiting on signal ";
+ if (signalName == nullptr) {
+ std::cerr << signalNum;
+ }
+ else {
+ std::cerr << signalName;
+ }
+ std::cerr << std::endl;
+ client.endSession();
+ face.getIoService().stop();
+}
int
main(int argc, char* argv[])
{
+ boost::asio::signal_set terminateSignals(face.getIoService());
+ terminateSignals.add(SIGINT);
+ terminateSignals.add(SIGTERM);
+ terminateSignals.async_wait(handleSignal);
+
namespace po = boost::program_options;
std::string configFilePath = std::string(SYSCONFDIR) + "/ndncert/client.conf";
po::options_description description("General Usage\n ndncert-client [-h] [-c] [-v]\n");