From aeff143e65b856db4438854127df1822a433cfca Mon Sep 17 00:00:00 2001 From: Stefan Gschiel Date: Fri, 24 Feb 2017 13:04:52 +0100 Subject: Bug 1342351 - Implement --list-keys for nss-tool r=ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D191 --- nss-tool/common/util.cc | 135 ++++++++++++++++++++++++++++++++++++++++++++++++ nss-tool/common/util.h | 23 +++++++++ nss-tool/db/dbtool.cc | 96 ++++++++++++++++++++++++++++++++-- nss-tool/db/dbtool.h | 1 + nss-tool/nss_tool.cc | 1 - nss-tool/nss_tool.gyp | 1 + 6 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 nss-tool/common/util.cc create mode 100644 nss-tool/common/util.h (limited to 'nss-tool') diff --git a/nss-tool/common/util.cc b/nss-tool/common/util.cc new file mode 100644 index 000000000..f8abf82b3 --- /dev/null +++ b/nss-tool/common/util.cc @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "util.h" + +#include +#include +#include +#include + +#include + +#if defined(__unix__) || defined(__APPLE__) +#include +#include +#elif defined(WIN32) || defined(_WIN64) +#include +#endif + +static std::string GetPassword(const std::string &prompt) { + std::cout << prompt << std::endl; + +#if defined(__unix__) || defined(__APPLE__) + termios oldt; + tcgetattr(STDIN_FILENO, &oldt); + termios newt = oldt; + newt.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &newt); +#elif defined(WIN32) || defined(_WIN64) + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + DWORD mode = 0; + GetConsoleMode(hStdin, &mode); + SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); +#endif + + std::string pw; + std::getline(std::cin, pw); + +#if defined(__unix__) || defined(__APPLE__) + tcsetattr(STDIN_FILENO, TCSANOW, &oldt); +#elif defined(WIN32) || defined(_WIN64) + SetConsoleMode(hStdin, mode); +#endif + + return pw; +} + +static char *GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) { + if (arg == nullptr) { + return nullptr; + } + + PwData *pwData = reinterpret_cast(arg); + + if (retry > 0) { + std::cerr << "Incorrect password/PIN entered." << std::endl; + return nullptr; + } + + switch (pwData->source) { + case PW_NONE: + case PW_FROMFILE: + std::cerr << "Password input method not supported." << std::endl; + return nullptr; + case PW_PLAINTEXT: + return PL_strdup(pwData->data); + default: + break; + } + + std::cerr << "Password check failed: No password found." << std::endl; + return nullptr; +} + +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; + + while (true) { + pw = GetPassword("Enter new password: "); + if (pw == GetPassword("Re-enter password: ")) { + break; + } + + std::cerr << "Passwords do not match. Try again." << std::endl; + } + + 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 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(pw.c_str())}; + SECStatus rv = PK11_Authenticate(slot.get(), true /*loadCerts*/, &pwData); + if (rv != SECSuccess) { + std::cerr << "Could not authenticate to token " + << PK11_GetTokenName(slot.get()) << ". Failed with error " + << PR_ErrorToName(PR_GetError()) << std::endl; + return false; + } + std::cout << std::endl; + + return true; +} + +std::string StringToHex(const ScopedSECItem &input) { + std::stringstream ss; + ss << "0x"; + for (size_t i = 0; i < input->len; i++) { + ss << std::hex << std::setfill('0') << std::setw(2) + << static_cast(input->data[i]); + } + + return ss.str(); +} diff --git a/nss-tool/common/util.h b/nss-tool/common/util.h new file mode 100644 index 000000000..d4fc257ff --- /dev/null +++ b/nss-tool/common/util.h @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef util_h__ +#define util_h__ + +#include "scoped_ptrs.h" + +#include +#include + +enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 }; +typedef struct { + PwDataType source; + char *data; +} PwData; + +bool InitSlotPassword(void); +bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot); +std::string StringToHex(const ScopedSECItem &input); + +#endif // util_h__ diff --git a/nss-tool/db/dbtool.cc b/nss-tool/db/dbtool.cc index bc82b308f..f88ba4187 100644 --- a/nss-tool/db/dbtool.cc +++ b/nss-tool/db/dbtool.cc @@ -5,6 +5,7 @@ #include "dbtool.h" #include "argparse.h" #include "scoped_ptrs.h" +#include "util.h" #include #include @@ -17,8 +18,17 @@ #include #include #include +#include #include +const std::vector kCommandArgs({"--create", "--list-certs", + "--import-cert", "--list-keys"}); + +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 std::string PrintFlags(unsigned int flags) { std::stringstream ss; if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) && @@ -63,19 +73,23 @@ static std::vector ReadFromIstream(std::istream &is) { return certData; } +static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza", + "dh", "kea", "ec"}; + void DBTool::Usage() { std::cerr << "Usage: nss db [--path ]" << std::endl; std::cerr << " --create" << std::endl; std::cerr << " --list-certs" << std::endl; std::cerr << " --import-cert [] --name [--trusts ]" << std::endl; + std::cerr << " --list-keys" << std::endl; } bool DBTool::Run(const std::vector &arguments) { ArgParser parser(arguments); - if (!parser.Has("--create") && !parser.Has("--list-certs") && - !parser.Has("--import-cert")) { + if (!HasSingleCommandArgument(parser)) { + Usage(); return false; } @@ -129,7 +143,12 @@ bool DBTool::Run(const std::vector &arguments) { } else if (parser.Has("--import-cert")) { ret = ImportCertificate(parser); } else if (parser.Has("--create")) { - std::cout << "DB files created successfully." << std::endl; + ret = InitSlotPassword(); + if (ret) { + std::cout << "DB files created successfully." << std::endl; + } + } else if (parser.Has("--list-keys")) { + ret = ListKeys(); } // shutdown nss @@ -233,7 +252,7 @@ bool DBTool::ImportCertificate(const ArgParser &parser) { ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot()); if (slot.get() == nullptr) { - std::cerr << "Error: Init PK11SlotInfo failed!\n"; + std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl; return false; } @@ -279,3 +298,72 @@ bool DBTool::ImportCertificate(const ArgParser &parser) { // TODO show information about imported certificate return true; } + +bool DBTool::ListKeys() { + ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot()); + if (slot.get() == nullptr) { + std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl; + return false; + } + + if (!DBLoginIfNeeded(slot)) { + return false; + } + + ScopedSECKEYPrivateKeyList list(PK11_ListPrivateKeysInSlot(slot.get())); + if (list.get() == nullptr) { + std::cerr << "Listing private keys failed with error " + << PR_ErrorToName(PR_GetError()) << std::endl; + return false; + } + + SECKEYPrivateKeyListNode *node; + int count = 0; + for (node = PRIVKEY_LIST_HEAD(list.get()); + !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) { + char *keyNameRaw = PK11_GetPrivateKeyNickname(node->key); + std::string keyName(keyNameRaw ? "" : keyNameRaw); + + if (keyName.empty()) { + ScopedCERTCertificate cert(PK11_GetCertFromPrivateKey(node->key)); + if (cert.get()) { + if (cert->nickname && strlen(cert->nickname) > 0) { + keyName = cert->nickname; + } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) { + keyName = cert->emailAddr; + } + } + if (keyName.empty()) { + keyName = "(none)"; // default value + } + } + + SECKEYPrivateKey *key = node->key; + ScopedSECItem keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key)); + if (keyIDItem.get() == nullptr) { + std::cerr << "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!" + << std::endl; + continue; + } + + std::string keyID = StringToHex(keyIDItem); + + if (count++ == 0) { + // print header + std::cout << std::left << std::setw(20) << "" + << std::setw(20) << "key type" + << "key id" << std::endl; + } + + std::stringstream leftElem; + leftElem << "<" << count << ", " << keyName << ">"; + std::cout << std::left << std::setw(20) << leftElem.str() << std::setw(20) + << keyTypeName[key->keyType] << keyID << std::endl; + } + + if (count == 0) { + std::cout << "No keys found." << std::endl; + } + + return true; +} diff --git a/nss-tool/db/dbtool.h b/nss-tool/db/dbtool.h index 430ce244d..ed44e74ae 100644 --- a/nss-tool/db/dbtool.h +++ b/nss-tool/db/dbtool.h @@ -19,6 +19,7 @@ class DBTool { bool PathHasDBFiles(std::string path); void ListCertificates(); bool ImportCertificate(const ArgParser& parser); + bool ListKeys(); }; #endif // dbtool_h__ diff --git a/nss-tool/nss_tool.cc b/nss-tool/nss_tool.cc index 5d476f692..0b6b734bb 100644 --- a/nss-tool/nss_tool.cc +++ b/nss-tool/nss_tool.cc @@ -33,7 +33,6 @@ int main(int argc, char **argv) { std::vector arguments(argv + 2, argv + argc); DBTool tool; if (!tool.Run(arguments)) { - tool.Usage(); exit_code = 1; } diff --git a/nss-tool/nss_tool.gyp b/nss-tool/nss_tool.gyp index 26eecfe0c..95b627070 100644 --- a/nss-tool/nss_tool.gyp +++ b/nss-tool/nss_tool.gyp @@ -13,6 +13,7 @@ 'sources' : [ 'nss_tool.cc', 'common/argparse.cc', + 'common/util.cc', 'db/dbtool.cc', ], 'include_dirs': [ -- cgit v1.2.1