Bug 1330980 - Add first version of new "nss" tool r=ttaubert
authorStefan Gschiel <stefan.gschiel.sg@gmail.com>
Fri, 13 Jan 2017 15:57:39 +0100
changeset 13027 3e75c6ad7f8ddd997a025033a9ea378696dd87b7
parent 13026 2343d7ff7b1e534756f33cd98f19f751205b1aa7
child 13028 11d24016351006d2fc4704bb954f8f3c728c00f6
push id1930
push userttaubert@mozilla.com
push dateFri, 13 Jan 2017 15:42:21 +0000
reviewersttaubert
bugs1330980
Bug 1330980 - Add first version of new "nss" tool r=ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D84
automation/taskcluster/scripts/run_clang_format.sh
nss-tool/.clang-format
nss-tool/common/argparse.cc
nss-tool/common/argparse.h
nss-tool/common/scoped_ptrs.h
nss-tool/db/dbtool.cc
nss-tool/db/dbtool.h
nss-tool/nss_tool.cc
nss-tool/nss_tool.gyp
nss.gyp
--- a/automation/taskcluster/scripts/run_clang_format.sh
+++ b/automation/taskcluster/scripts/run_clang_format.sh
@@ -36,16 +36,17 @@ else
          "$top/lib/sysinit" \
          "$top/lib/util" \
          "$top/gtests/common" \
          "$top/gtests/der_gtest" \
          "$top/gtests/freebl_gtest" \
          "$top/gtests/pk11_gtest" \
          "$top/gtests/ssl_gtest" \
          "$top/gtests/util_gtest" \
+         "$top/nss-tool" \
     )
 fi
 
 for dir in "${dirs[@]}"; do
     find "$dir" -type f \( -name '*.[ch]' -o -name '*.cc' \) -exec clang-format -i {} \+
 done
 
 TMPFILE=$(mktemp /tmp/$(basename $0).XXXXXX)
new file mode 100644
--- /dev/null
+++ b/nss-tool/.clang-format
@@ -0,0 +1,4 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+...
new file mode 100644
--- /dev/null
+++ b/nss-tool/common/argparse.cc
@@ -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/. */
+
+#include "argparse.h"
+
+ArgParser::ArgParser(const std::vector<std::string>& arguments) {
+  for (size_t i = 0; i < arguments.size(); i++) {
+    std::string arg = arguments.at(i);
+    if (arg.find("--") == 0) {
+      // look for an option argument
+      if (i + 1 < arguments.size() && arguments.at(i + 1).find("--") != 0) {
+        programArgs_[arg] = arguments.at(i + 1);
+        i++;
+      } else {
+        programArgs_[arg] = "";
+      }
+    } else {
+      // positional argument (e.g. required argument)
+      positionalArgs_.push_back(arg);
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/nss-tool/common/argparse.h
@@ -0,0 +1,30 @@
+/* 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 argparse_h__
+#define argparse_h__
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class ArgParser {
+ public:
+  ArgParser(const std::vector<std::string>& arguments);
+
+  bool Has(std::string arg) { return programArgs_.count(arg) > 0; }
+
+  std::string Get(std::string arg) { return programArgs_[arg]; }
+
+  size_t GetPositionalArgumentCount() { return positionalArgs_.size(); }
+  std::string GetPositionalArgument(size_t pos) {
+    return positionalArgs_.at(pos);
+  }
+
+ private:
+  std::unordered_map<std::string, std::string> programArgs_;
+  std::vector<std::string> positionalArgs_;
+};
+
+#endif  // argparse_h__
new file mode 100644
--- /dev/null
+++ b/nss-tool/common/scoped_ptrs.h
@@ -0,0 +1,57 @@
+/* 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 scoped_ptrs_h__
+#define scoped_ptrs_h__
+
+#include <memory>
+#include "cert.h"
+#include "keyhi.h"
+#include "pk11pub.h"
+
+struct ScopedDelete {
+  void operator()(CERTCertificate* cert) { CERT_DestroyCertificate(cert); }
+  void operator()(CERTCertificateList* list) {
+    CERT_DestroyCertificateList(list);
+  }
+  void operator()(CERTSubjectPublicKeyInfo* spki) {
+    SECKEY_DestroySubjectPublicKeyInfo(spki);
+  }
+  void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
+  void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
+  void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
+  void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
+  void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
+  void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
+
+  void operator()(CERTCertList* list) { CERT_DestroyCertList(list); }
+};
+
+template <class T>
+struct ScopedMaybeDelete {
+  void operator()(T* ptr) {
+    if (ptr) {
+      ScopedDelete del;
+      del(ptr);
+    }
+  }
+};
+
+#define SCOPED(x) typedef std::unique_ptr<x, ScopedMaybeDelete<x> > Scoped##x
+
+SCOPED(CERTCertificate);
+SCOPED(CERTCertificateList);
+SCOPED(CERTSubjectPublicKeyInfo);
+SCOPED(PK11SlotInfo);
+SCOPED(PK11SymKey);
+SCOPED(SECAlgorithmID);
+SCOPED(SECItem);
+SCOPED(SECKEYPublicKey);
+SCOPED(SECKEYPrivateKey);
+
+SCOPED(CERTCertList);
+
+#undef SCOPED
+
+#endif
new file mode 100644
--- /dev/null
+++ b/nss-tool/db/dbtool.cc
@@ -0,0 +1,131 @@
+/* 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 "dbtool.h"
+#include "argparse.h"
+#include "scoped_ptrs.h"
+
+#include <iomanip>
+#include <iostream>
+#include <memory>
+#include <sstream>
+
+#include <cert.h>
+#include <certdb.h>
+#include <nss.h>
+
+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";
+  }
+  if ((flags & CERTDB_TERMINAL_RECORD) && !(flags & CERTDB_TRUSTED)) {
+    ss << "p";
+  }
+  if (flags & CERTDB_TRUSTED_CA) {
+    ss << "C";
+  }
+  if (flags & CERTDB_TRUSTED_CLIENT_CA) {
+    ss << "T";
+  }
+  if (flags & CERTDB_TRUSTED) {
+    ss << "P";
+  }
+  if (flags & CERTDB_USER) {
+    ss << "u";
+  }
+  if (flags & CERTDB_SEND_WARN) {
+    ss << "w";
+  }
+  if (flags & CERTDB_INVISIBLE_CA) {
+    ss << "I";
+  }
+  if (flags & CERTDB_GOVT_APPROVED_CA) {
+    ss << "G";
+  }
+  return ss.str();
+}
+
+void DBTool::Usage() {
+  std::cerr << "Usage: nss db [--path <directory>] --list-certs" << std::endl;
+}
+
+bool DBTool::Run(const std::vector<std::string> &arguments) {
+  ArgParser parser(arguments);
+
+  std::string initDir(".");
+  if (parser.Has("--path")) {
+    initDir = parser.Get("--path");
+  }
+
+  if (!parser.Has("--list-certs")) {
+    return false;
+  }
+  std::cout << "Using database directory: " << initDir << std::endl
+            << std::endl;
+
+  // init NSS
+  const char *certPrefix = "";  // certutil -P option  --- can leave this empty
+  SECStatus rv =
+      NSS_Initialize(initDir.c_str(), certPrefix, certPrefix, "secmod.db", 0);
+  if (rv != SECSuccess) {
+    std::cerr << "NSS init failed!" << std::endl;
+    return false;
+  }
+
+  ListCertificates();
+
+  // shutdown nss
+  if (NSS_Shutdown() != SECSuccess) {
+    std::cerr << "NSS Shutdown failed!" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+void DBTool::ListCertificates() {
+  ScopedCERTCertList list(PK11_ListCerts(PK11CertListAll, nullptr));
+  CERTCertListNode *node;
+
+  std::cout << std::setw(60) << std::left << "Certificate Nickname"
+            << " "
+            << "Trust Attributes" << std::endl;
+  std::cout << std::setw(60) << std::left << ""
+            << " "
+            << "SSL,S/MIME,JAR/XPI" << std::endl
+            << std::endl;
+
+  for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
+       node = CERT_LIST_NEXT(node)) {
+    CERTCertificate *cert = node->cert;
+
+    std::string name("(unknown)");
+    char *appData = static_cast<char *>(node->appData);
+    if (appData && strlen(appData) > 0) {
+      name = appData;
+    } else if (cert->nickname && strlen(cert->nickname) > 0) {
+      name = cert->nickname;
+    } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+      name = cert->emailAddr;
+    }
+
+    CERTCertTrust trust;
+    std::string trusts;
+    if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
+      std::stringstream ss;
+      ss << PrintFlags(trust.sslFlags);
+      ss << ",";
+      ss << PrintFlags(trust.emailFlags);
+      ss << ",";
+      ss << PrintFlags(trust.objectSigningFlags);
+      trusts = ss.str();
+    } else {
+      trusts = ",,";
+    }
+    std::cout << std::setw(60) << std::left << name << " " << trusts
+              << std::endl;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/nss-tool/db/dbtool.h
@@ -0,0 +1,20 @@
+/* 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 dbtool_h__
+#define dbtool_h__
+
+#include <string>
+
+class DBTool {
+ public:
+  bool Run(const std::vector<std::string>& arguments);
+
+  void Usage();
+
+ private:
+  void ListCertificates();
+};
+
+#endif  // dbtool_h__
new file mode 100644
--- /dev/null
+++ b/nss-tool/nss_tool.cc
@@ -0,0 +1,43 @@
+/* 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 <iostream>
+#include <string>
+#include <vector>
+
+#include <prinit.h>
+
+#include "argparse.h"
+#include "db/dbtool.h"
+
+static void Usage() {
+  std::cerr << "Usage: nss <command> <subcommand> [options]" << std::endl;
+  std::cerr << "       nss db [--path <directory>] --list-certs" << std::endl;
+}
+
+int main(int argc, char **argv) {
+  if (argc < 2) {
+    Usage();
+    return 1;
+  }
+
+  if (std::string(argv[1]) != "db") {
+    Usage();
+    return 1;
+  }
+
+  int exit_code = 0;
+  PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+  std::vector<std::string> arguments(argv + 2, argv + argc);
+  DBTool tool;
+  if (!tool.Run(arguments)) {
+    tool.Usage();
+    exit_code = 1;
+  }
+
+  PR_Cleanup();
+
+  return exit_code;
+}
new file mode 100644
--- /dev/null
+++ b/nss-tool/nss_tool.gyp
@@ -0,0 +1,27 @@
+# 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/.
+{
+  'includes' : [
+    '../coreconf/config.gypi',
+    '../cmd/platlibs.gypi',
+  ],
+  'targets' : [
+    {
+      'target_name' : 'nss',
+      'type' : 'executable',
+      'sources' : [
+        'nss_tool.cc',
+        'common/argparse.cc',
+        'db/dbtool.cc',
+      ],
+      'include_dirs': [
+        'common',
+      ],
+      'dependencies' : [
+        '<(DEPTH)/exports.gyp:dbm_exports',
+        '<(DEPTH)/exports.gyp:nss_exports'
+      ],
+    }
+  ],
+}
--- a/nss.gyp
+++ b/nss.gyp
@@ -115,16 +115,17 @@
           'dependencies': [
             'cmd/crlutil/crlutil.gyp:crlutil',
             'cmd/pwdecrypt/pwdecrypt.gyp:pwdecrypt',
             'cmd/signtool/signtool.gyp:signtool',
             'cmd/signver/signver.gyp:signver',
             'cmd/smimetools/smimetools.gyp:cmsutil',
             'cmd/ssltap/ssltap.gyp:ssltap',
             'cmd/symkeyutil/symkeyutil.gyp:symkeyutil',
+            'nss-tool/nss_tool.gyp:nss',
           ],
         }],
       ],
     },
   ],
   'conditions': [
     [ 'disable_tests==0', {
       'targets': [