Bug 1347613 - nss-tool: allow removing certs and keys from a DB, changing a DB password r=ttaubert
authorStefan Gschiel <stefan.gschiel.sg@gmail.com>
Wed, 15 Mar 2017 17:08:34 +0100
changeset 13211 cf81ccc154dd5fe5523444e425467af9b97d1dfa
parent 13210 dc5174017668b85009fd92a40d2654aee5040265
child 13212 fa7e3e2a524c4e03e1ea5dc4e5b18db19c778e8c
push id2082
push userttaubert@mozilla.com
push dateWed, 15 Mar 2017 16:21:07 +0000
reviewersttaubert
bugs1347613
Bug 1347613 - nss-tool: allow removing certs and keys from a DB, changing a DB password r=ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D247
nss-tool/common/util.cc
nss-tool/common/util.h
nss-tool/db/dbtool.cc
nss-tool/db/dbtool.h
--- a/nss-tool/common/util.cc
+++ b/nss-tool/common/util.cc
@@ -80,46 +80,81 @@ static std::vector<char> ReadFromIstream
     char buf[1024];
     is.read(buf, sizeof(buf));
     certData.insert(certData.end(), buf, buf + is.gcount());
   }
 
   return certData;
 }
 
-bool InitSlotPassword(void) {
-  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
-  if (slot.get() == nullptr) {
-    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
-    return false;
-  }
-
-  std::cout << "Enter a password which will be used to encrypt your keys."
-            << std::endl
-            << std::endl;
+static std::string GetNewPasswordFromUser(void) {
   std::string pw;
 
   while (true) {
     pw = GetPassword("Enter new password: ");
     if (pw == GetPassword("Re-enter password: ")) {
       break;
     }
 
     std::cerr << "Passwords do not match. Try again." << std::endl;
   }
 
+  return pw;
+}
+
+bool InitSlotPassword(void) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+    return false;
+  }
+
+  std::cout << "Enter a password which will be used to encrypt your keys."
+            << std::endl
+            << std::endl;
+  std::string pw = GetNewPasswordFromUser();
+
   SECStatus rv = PK11_InitPin(slot.get(), nullptr, pw.c_str());
   if (rv != SECSuccess) {
     std::cerr << "Init db password failed." << std::endl;
     return false;
   }
 
   return true;
 }
 
+bool ChangeSlotPassword(void) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+    return false;
+  }
+
+  // get old password and authenticate to db
+  PK11_SetPasswordFunc(&GetModulePassword);
+  std::string oldPw = GetPassword("Enter your current password: ");
+  PwData pwData = {PW_PLAINTEXT, const_cast<char *>(oldPw.c_str())};
+  SECStatus rv = PK11_Authenticate(slot.get(), false /*loadCerts*/, &pwData);
+  if (rv != SECSuccess) {
+    std::cerr << "Password incorrect." << std::endl;
+    return false;
+  }
+
+  // get new password
+  std::string newPw = GetNewPasswordFromUser();
+
+  if (PK11_ChangePW(slot.get(), oldPw.c_str(), newPw.c_str()) != SECSuccess) {
+    std::cerr << "Failed to change password." << std::endl;
+    return false;
+  }
+
+  std::cout << "Password changed successfully." << std::endl;
+  return true;
+}
+
 bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot) {
   if (!PK11_NeedLogin(slot.get())) {
     return true;
   }
 
   PK11_SetPasswordFunc(&GetModulePassword);
   std::string pw = GetPassword("Enter your password: ");
   PwData pwData = {PW_PLAINTEXT, const_cast<char *>(pw.c_str())};
--- a/nss-tool/common/util.h
+++ b/nss-tool/common/util.h
@@ -13,13 +13,14 @@
 
 enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 };
 typedef struct {
   PwDataType source;
   char *data;
 } PwData;
 
 bool InitSlotPassword(void);
+bool ChangeSlotPassword(void);
 bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot);
 std::string StringToHex(const ScopedSECItem &input);
 std::vector<char> ReadInputData(std::string &dataPath);
 
 #endif  // util_h__
--- a/nss-tool/db/dbtool.cc
+++ b/nss-tool/db/dbtool.cc
@@ -15,28 +15,29 @@
 
 #include <cert.h>
 #include <certdb.h>
 #include <nss.h>
 #include <pk11pub.h>
 #include <prerror.h>
 #include <prio.h>
 
-const std::vector<std::string> kCommandArgs({"--create", "--list-certs",
-                                             "--import-cert", "--list-keys",
-                                             "--import-key"});
+const std::vector<std::string> kCommandArgs(
+    {"--create", "--list-certs", "--import-cert", "--list-keys", "--import-key",
+     "--delete-cert", "--delete-key", "--change-password"});
 
 static bool HasSingleCommandArgument(const ArgParser &parser) {
   auto pred = [&](const std::string &cmd) { return parser.Has(cmd); };
   return std::count_if(kCommandArgs.begin(), kCommandArgs.end(), pred) == 1;
 }
 
 static bool HasArgumentRequiringWriteAccess(const ArgParser &parser) {
   return parser.Has("--create") || parser.Has("--import-cert") ||
-         parser.Has("--import-key");
+         parser.Has("--import-key") || parser.Has("--delete-cert") ||
+         parser.Has("--delete-key") || parser.Has("--change-password");
 }
 
 static std::string PrintFlags(unsigned int flags) {
   std::stringstream ss;
   if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) &&
       !(flags & CERTDB_TRUSTED_CLIENT_CA)) {
     ss << "c";
   }
@@ -68,21 +69,24 @@ static std::string PrintFlags(unsigned i
 }
 
 static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza",
                                           "dh",   "kea", "ec"};
 
 void DBTool::Usage() {
   std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
   std::cerr << "  --create" << std::endl;
+  std::cerr << "  --change-password" << std::endl;
   std::cerr << "  --list-certs" << std::endl;
   std::cerr << "  --import-cert [<path>] --name <name> [--trusts <trusts>]"
             << std::endl;
   std::cerr << "  --list-keys" << std::endl;
   std::cerr << "  --import-key [<path> [-- name <name>]]" << std::endl;
+  std::cerr << "  --delete-cert <name>" << std::endl;
+  std::cerr << "  --delete-key <name>" << std::endl;
 }
 
 bool DBTool::Run(const std::vector<std::string> &arguments) {
   ArgParser parser(arguments);
 
   if (!HasSingleCommandArgument(parser)) {
     Usage();
     return false;
@@ -141,16 +145,22 @@ bool DBTool::Run(const std::vector<std::
     ret = InitSlotPassword();
     if (ret) {
       std::cout << "DB files created successfully." << std::endl;
     }
   } else if (parser.Has("--list-keys")) {
     ret = ListKeys();
   } else if (parser.Has("--import-key")) {
     ret = ImportKey(parser);
+  } else if (parser.Has("--delete-cert")) {
+    ret = DeleteCert(parser);
+  } else if (parser.Has("--delete-key")) {
+    ret = DeleteKey(parser);
+  } else if (parser.Has("--change-password")) {
+    ret = ChangeSlotPassword();
   }
 
   // shutdown nss
   if (NSS_Shutdown() != SECSuccess) {
     std::cerr << "NSS Shutdown failed!" << std::endl;
     return false;
   }
 
@@ -392,8 +402,97 @@ bool DBTool::ImportKey(const ArgParser &
     std::cerr << "Importing a private key in DER format failed with error "
               << PR_ErrorToName(PR_GetError()) << std::endl;
     return false;
   }
 
   std::cout << "Key import succeeded." << std::endl;
   return true;
 }
+
+bool DBTool::DeleteCert(const ArgParser &parser) {
+  std::string certName = parser.Get("--delete-cert");
+  if (certName.empty()) {
+    std::cerr << "A name is required to delete a certificate." << std::endl;
+    Usage();
+    return false;
+  }
+
+  ScopedCERTCertificate cert(CERT_FindCertByNicknameOrEmailAddr(
+      CERT_GetDefaultCertDB(), certName.c_str()));
+  if (!cert) {
+    std::cerr << "Could not find certificate with name " << certName << "."
+              << std::endl;
+    return false;
+  }
+
+  SECStatus rv = SEC_DeletePermCertificate(cert.get());
+  if (rv != SECSuccess) {
+    std::cerr << "Unable to delete certificate with name " << certName << "."
+              << std::endl;
+    return false;
+  }
+
+  std::cout << "Certificate with name " << certName << " deleted successfully."
+            << std::endl;
+  return true;
+}
+
+bool DBTool::DeleteKey(const ArgParser &parser) {
+  std::string keyName = parser.Get("--delete-key");
+  if (keyName.empty()) {
+    std::cerr << "A name is required to delete a key." << std::endl;
+    Usage();
+    return false;
+  }
+
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+    return false;
+  }
+
+  if (!DBLoginIfNeeded(slot)) {
+    return false;
+  }
+
+  ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
+      slot.get(), const_cast<char *>(keyName.c_str()), nullptr));
+  if (list.get() == nullptr) {
+    std::cerr << "Fetching private keys with nickname " << keyName
+              << " failed with error " << PR_ErrorToName(PR_GetError())
+              << std::endl;
+    return false;
+  }
+
+  unsigned int foundKeys = 0, deletedKeys = 0;
+  SECKEYPrivateKeyListNode *node;
+  for (node = PRIVKEY_LIST_HEAD(list.get());
+       !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+    SECKEYPrivateKey *privKey = node->key;
+    foundKeys++;
+    // see PK11_DeleteTokenPrivateKey for example usage
+    // calling PK11_DeleteTokenPrivateKey directly does not work because it also
+    // destroys the SECKEYPrivateKey (by calling SECKEY_DestroyPrivateKey) -
+    // then SECKEY_DestroyPrivateKeyList does not
+    // work because it also calls SECKEY_DestroyPrivateKey
+    SECStatus rv =
+        PK11_DestroyTokenObject(privKey->pkcs11Slot, privKey->pkcs11ID);
+    if (rv == SECSuccess) {
+      deletedKeys++;
+    }
+  }
+
+  if (foundKeys > deletedKeys) {
+    std::cerr << "Some keys could not be deleted." << std::endl;
+  }
+
+  if (deletedKeys > 0) {
+    std::cout << "Found " << foundKeys << " keys." << std::endl;
+    std::cout << "Successfully deleted " << deletedKeys
+              << " key(s) with nickname " << keyName << "." << std::endl;
+  } else {
+    std::cout << "No key with nickname " << keyName << " found to delete."
+              << std::endl;
+  }
+
+  return true;
+}
--- a/nss-tool/db/dbtool.h
+++ b/nss-tool/db/dbtool.h
@@ -15,11 +15,13 @@ class DBTool {
 
  private:
   void Usage();
   bool PathHasDBFiles(std::string path);
   void ListCertificates();
   bool ImportCertificate(const ArgParser& parser);
   bool ListKeys();
   bool ImportKey(const ArgParser& parser);
+  bool DeleteCert(const ArgParser& parser);
+  bool DeleteKey(const ArgParser& parser);
 };
 
 #endif  // dbtool_h__