dbtool.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. #include "dbtool.h"
  5. #include "argparse.h"
  6. #include "nss_scoped_ptrs.h"
  7. #include "util.h"
  8. #include <iomanip>
  9. #include <iostream>
  10. #include <regex>
  11. #include <sstream>
  12. #include <cert.h>
  13. #include <certdb.h>
  14. #include <nss.h>
  15. #include <pk11pub.h>
  16. #include <prerror.h>
  17. #include <prio.h>
  18. const std::vector<std::string> kCommandArgs(
  19. {"--create", "--list-certs", "--import-cert", "--list-keys", "--import-key",
  20. "--delete-cert", "--delete-key", "--change-password"});
  21. static bool HasSingleCommandArgument(const ArgParser &parser) {
  22. auto pred = [&](const std::string &cmd) { return parser.Has(cmd); };
  23. return std::count_if(kCommandArgs.begin(), kCommandArgs.end(), pred) == 1;
  24. }
  25. static bool HasArgumentRequiringWriteAccess(const ArgParser &parser) {
  26. return parser.Has("--create") || parser.Has("--import-cert") ||
  27. parser.Has("--import-key") || parser.Has("--delete-cert") ||
  28. parser.Has("--delete-key") || parser.Has("--change-password");
  29. }
  30. static std::string PrintFlags(unsigned int flags) {
  31. std::stringstream ss;
  32. if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) &&
  33. !(flags & CERTDB_TRUSTED_CLIENT_CA)) {
  34. ss << "c";
  35. }
  36. if ((flags & CERTDB_TERMINAL_RECORD) && !(flags & CERTDB_TRUSTED)) {
  37. ss << "p";
  38. }
  39. if (flags & CERTDB_TRUSTED_CA) {
  40. ss << "C";
  41. }
  42. if (flags & CERTDB_TRUSTED_CLIENT_CA) {
  43. ss << "T";
  44. }
  45. if (flags & CERTDB_TRUSTED) {
  46. ss << "P";
  47. }
  48. if (flags & CERTDB_USER) {
  49. ss << "u";
  50. }
  51. if (flags & CERTDB_SEND_WARN) {
  52. ss << "w";
  53. }
  54. if (flags & CERTDB_INVISIBLE_CA) {
  55. ss << "I";
  56. }
  57. if (flags & CERTDB_GOVT_APPROVED_CA) {
  58. ss << "G";
  59. }
  60. return ss.str();
  61. }
  62. static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza",
  63. "dh", "kea", "ec"};
  64. void DBTool::Usage() {
  65. std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
  66. std::cerr << " --create" << std::endl;
  67. std::cerr << " --change-password" << std::endl;
  68. std::cerr << " --list-certs" << std::endl;
  69. std::cerr << " --import-cert [<path>] --name <name> [--trusts <trusts>]"
  70. << std::endl;
  71. std::cerr << " --list-keys" << std::endl;
  72. std::cerr << " --import-key [<path> [-- name <name>]]" << std::endl;
  73. std::cerr << " --delete-cert <name>" << std::endl;
  74. std::cerr << " --delete-key <name>" << std::endl;
  75. }
  76. bool DBTool::Run(const std::vector<std::string> &arguments) {
  77. ArgParser parser(arguments);
  78. if (!HasSingleCommandArgument(parser)) {
  79. Usage();
  80. return false;
  81. }
  82. PRAccessHow how = PR_ACCESS_READ_OK;
  83. bool readOnly = true;
  84. if (HasArgumentRequiringWriteAccess(parser)) {
  85. how = PR_ACCESS_WRITE_OK;
  86. readOnly = false;
  87. }
  88. std::string initDir(".");
  89. if (parser.Has("--path")) {
  90. initDir = parser.Get("--path");
  91. }
  92. if (PR_Access(initDir.c_str(), how) != PR_SUCCESS) {
  93. std::cerr << "Directory '" << initDir
  94. << "' does not exist or you don't have permissions!" << std::endl;
  95. return false;
  96. }
  97. std::cout << "Using database directory: " << initDir << std::endl
  98. << std::endl;
  99. bool dbFilesExist = PathHasDBFiles(initDir);
  100. if (parser.Has("--create") && dbFilesExist) {
  101. std::cerr << "Trying to create database files in a directory where they "
  102. "already exists. Delete the db files before creating new ones."
  103. << std::endl;
  104. return false;
  105. }
  106. if (!parser.Has("--create") && !dbFilesExist) {
  107. std::cerr << "No db files found." << std::endl;
  108. std::cerr << "Create them using 'nss db --create [--path /foo/bar]' before "
  109. "continuing."
  110. << std::endl;
  111. return false;
  112. }
  113. // init NSS
  114. const char *certPrefix = ""; // certutil -P option --- can leave this empty
  115. SECStatus rv = NSS_Initialize(initDir.c_str(), certPrefix, certPrefix,
  116. "secmod.db", readOnly ? NSS_INIT_READONLY : 0);
  117. if (rv != SECSuccess) {
  118. std::cerr << "NSS init failed!" << std::endl;
  119. return false;
  120. }
  121. bool ret = true;
  122. if (parser.Has("--list-certs")) {
  123. ListCertificates();
  124. } else if (parser.Has("--import-cert")) {
  125. ret = ImportCertificate(parser);
  126. } else if (parser.Has("--create")) {
  127. ret = InitSlotPassword();
  128. if (ret) {
  129. std::cout << "DB files created successfully." << std::endl;
  130. }
  131. } else if (parser.Has("--list-keys")) {
  132. ret = ListKeys();
  133. } else if (parser.Has("--import-key")) {
  134. ret = ImportKey(parser);
  135. } else if (parser.Has("--delete-cert")) {
  136. ret = DeleteCert(parser);
  137. } else if (parser.Has("--delete-key")) {
  138. ret = DeleteKey(parser);
  139. } else if (parser.Has("--change-password")) {
  140. ret = ChangeSlotPassword();
  141. }
  142. // shutdown nss
  143. if (NSS_Shutdown() != SECSuccess) {
  144. std::cerr << "NSS Shutdown failed!" << std::endl;
  145. return false;
  146. }
  147. return ret;
  148. }
  149. bool DBTool::PathHasDBFiles(std::string path) {
  150. std::regex certDBPattern("cert.*\\.db");
  151. std::regex keyDBPattern("key.*\\.db");
  152. PRDir *dir = PR_OpenDir(path.c_str());
  153. if (!dir) {
  154. std::cerr << "Directory " << path << " could not be accessed!" << std::endl;
  155. return false;
  156. }
  157. PRDirEntry *ent;
  158. bool dbFileExists = false;
  159. while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH))) {
  160. if (std::regex_match(ent->name, certDBPattern) ||
  161. std::regex_match(ent->name, keyDBPattern) ||
  162. "secmod.db" == std::string(ent->name)) {
  163. dbFileExists = true;
  164. break;
  165. }
  166. }
  167. (void)PR_CloseDir(dir);
  168. return dbFileExists;
  169. }
  170. void DBTool::ListCertificates() {
  171. ScopedCERTCertList list(PK11_ListCerts(PK11CertListAll, nullptr));
  172. CERTCertListNode *node;
  173. std::cout << std::setw(60) << std::left << "Certificate Nickname"
  174. << " "
  175. << "Trust Attributes" << std::endl;
  176. std::cout << std::setw(60) << std::left << ""
  177. << " "
  178. << "SSL,S/MIME,JAR/XPI" << std::endl
  179. << std::endl;
  180. for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
  181. node = CERT_LIST_NEXT(node)) {
  182. CERTCertificate *cert = node->cert;
  183. std::string name("(unknown)");
  184. char *appData = static_cast<char *>(node->appData);
  185. if (appData && strlen(appData) > 0) {
  186. name = appData;
  187. } else if (cert->nickname && strlen(cert->nickname) > 0) {
  188. name = cert->nickname;
  189. } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
  190. name = cert->emailAddr;
  191. }
  192. CERTCertTrust trust;
  193. std::string trusts;
  194. if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
  195. std::stringstream ss;
  196. ss << PrintFlags(trust.sslFlags);
  197. ss << ",";
  198. ss << PrintFlags(trust.emailFlags);
  199. ss << ",";
  200. ss << PrintFlags(trust.objectSigningFlags);
  201. trusts = ss.str();
  202. } else {
  203. trusts = ",,";
  204. }
  205. std::cout << std::setw(60) << std::left << name << " " << trusts
  206. << std::endl;
  207. }
  208. }
  209. bool DBTool::ImportCertificate(const ArgParser &parser) {
  210. if (!parser.Has("--name")) {
  211. std::cerr << "A name (--name) is required to import a certificate."
  212. << std::endl;
  213. Usage();
  214. return false;
  215. }
  216. std::string derFilePath = parser.Get("--import-cert");
  217. std::string certName = parser.Get("--name");
  218. std::string trustString("TCu,Cu,Tu");
  219. if (parser.Has("--trusts")) {
  220. trustString = parser.Get("--trusts");
  221. }
  222. CERTCertTrust trust;
  223. SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
  224. if (rv != SECSuccess) {
  225. std::cerr << "Cannot decode trust string!" << std::endl;
  226. return false;
  227. }
  228. ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
  229. if (slot.get() == nullptr) {
  230. std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
  231. return false;
  232. }
  233. std::vector<uint8_t> certData = ReadInputData(derFilePath);
  234. ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(
  235. reinterpret_cast<char *>(certData.data()), certData.size()));
  236. if (cert.get() == nullptr) {
  237. std::cerr << "Error: Could not decode certificate!" << std::endl;
  238. return false;
  239. }
  240. rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
  241. certName.c_str(), PR_FALSE);
  242. if (rv != SECSuccess) {
  243. // TODO handle authentication -> PK11_Authenticate (see certutil.c line
  244. // 134)
  245. std::cerr << "Error: Could not add certificate to database!" << std::endl;
  246. return false;
  247. }
  248. rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), &trust);
  249. if (rv != SECSuccess) {
  250. std::cerr << "Cannot change cert's trust" << std::endl;
  251. return false;
  252. }
  253. std::cout << "Certificate import was successful!" << std::endl;
  254. // TODO show information about imported certificate
  255. return true;
  256. }
  257. bool DBTool::ListKeys() {
  258. ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
  259. if (slot.get() == nullptr) {
  260. std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
  261. return false;
  262. }
  263. if (!DBLoginIfNeeded(slot)) {
  264. return false;
  265. }
  266. ScopedSECKEYPrivateKeyList list(PK11_ListPrivateKeysInSlot(slot.get()));
  267. if (list.get() == nullptr) {
  268. std::cerr << "Listing private keys failed with error "
  269. << PR_ErrorToName(PR_GetError()) << std::endl;
  270. return false;
  271. }
  272. SECKEYPrivateKeyListNode *node;
  273. int count = 0;
  274. for (node = PRIVKEY_LIST_HEAD(list.get());
  275. !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
  276. char *keyNameRaw = PK11_GetPrivateKeyNickname(node->key);
  277. std::string keyName(keyNameRaw ? keyNameRaw : "");
  278. if (keyName.empty()) {
  279. ScopedCERTCertificate cert(PK11_GetCertFromPrivateKey(node->key));
  280. if (cert.get()) {
  281. if (cert->nickname && strlen(cert->nickname) > 0) {
  282. keyName = cert->nickname;
  283. } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
  284. keyName = cert->emailAddr;
  285. }
  286. }
  287. if (keyName.empty()) {
  288. keyName = "(none)"; // default value
  289. }
  290. }
  291. SECKEYPrivateKey *key = node->key;
  292. ScopedSECItem keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key));
  293. if (keyIDItem.get() == nullptr) {
  294. std::cerr << "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!"
  295. << std::endl;
  296. continue;
  297. }
  298. std::string keyID = StringToHex(keyIDItem);
  299. if (count++ == 0) {
  300. // print header
  301. std::cout << std::left << std::setw(20) << "<key#, key name>"
  302. << std::setw(20) << "key type"
  303. << "key id" << std::endl;
  304. }
  305. std::stringstream leftElem;
  306. leftElem << "<" << count << ", " << keyName << ">";
  307. std::cout << std::left << std::setw(20) << leftElem.str() << std::setw(20)
  308. << keyTypeName[key->keyType] << keyID << std::endl;
  309. }
  310. if (count == 0) {
  311. std::cout << "No keys found." << std::endl;
  312. }
  313. return true;
  314. }
  315. bool DBTool::ImportKey(const ArgParser &parser) {
  316. std::string privKeyFilePath = parser.Get("--import-key");
  317. std::string name;
  318. if (parser.Has("--name")) {
  319. name = parser.Get("--name");
  320. }
  321. ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
  322. if (slot.get() == nullptr) {
  323. std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
  324. return false;
  325. }
  326. if (!DBLoginIfNeeded(slot)) {
  327. return false;
  328. }
  329. std::vector<uint8_t> privKeyData = ReadInputData(privKeyFilePath);
  330. if (privKeyData.empty()) {
  331. return false;
  332. }
  333. SECItem pkcs8PrivKeyItem = {
  334. siBuffer, reinterpret_cast<unsigned char *>(privKeyData.data()),
  335. static_cast<unsigned int>(privKeyData.size())};
  336. SECItem nickname = {siBuffer, nullptr, 0};
  337. if (!name.empty()) {
  338. nickname.data = const_cast<unsigned char *>(
  339. reinterpret_cast<const unsigned char *>(name.c_str()));
  340. nickname.len = static_cast<unsigned int>(name.size());
  341. }
  342. SECStatus rv = PK11_ImportDERPrivateKeyInfo(
  343. slot.get(), &pkcs8PrivKeyItem,
  344. nickname.data == nullptr ? nullptr : &nickname, nullptr /*publicValue*/,
  345. true /*isPerm*/, false /*isPrivate*/, KU_ALL, nullptr);
  346. if (rv != SECSuccess) {
  347. std::cerr << "Importing a private key in DER format failed with error "
  348. << PR_ErrorToName(PR_GetError()) << std::endl;
  349. return false;
  350. }
  351. std::cout << "Key import succeeded." << std::endl;
  352. return true;
  353. }
  354. bool DBTool::DeleteCert(const ArgParser &parser) {
  355. std::string certName = parser.Get("--delete-cert");
  356. if (certName.empty()) {
  357. std::cerr << "A name is required to delete a certificate." << std::endl;
  358. Usage();
  359. return false;
  360. }
  361. ScopedCERTCertificate cert(CERT_FindCertByNicknameOrEmailAddr(
  362. CERT_GetDefaultCertDB(), certName.c_str()));
  363. if (!cert) {
  364. std::cerr << "Could not find certificate with name " << certName << "."
  365. << std::endl;
  366. return false;
  367. }
  368. SECStatus rv = SEC_DeletePermCertificate(cert.get());
  369. if (rv != SECSuccess) {
  370. std::cerr << "Unable to delete certificate with name " << certName << "."
  371. << std::endl;
  372. return false;
  373. }
  374. std::cout << "Certificate with name " << certName << " deleted successfully."
  375. << std::endl;
  376. return true;
  377. }
  378. bool DBTool::DeleteKey(const ArgParser &parser) {
  379. std::string keyName = parser.Get("--delete-key");
  380. if (keyName.empty()) {
  381. std::cerr << "A name is required to delete a key." << std::endl;
  382. Usage();
  383. return false;
  384. }
  385. ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
  386. if (slot.get() == nullptr) {
  387. std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
  388. return false;
  389. }
  390. if (!DBLoginIfNeeded(slot)) {
  391. return false;
  392. }
  393. ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
  394. slot.get(), const_cast<char *>(keyName.c_str()), nullptr));
  395. if (list.get() == nullptr) {
  396. std::cerr << "Fetching private keys with nickname " << keyName
  397. << " failed with error " << PR_ErrorToName(PR_GetError())
  398. << std::endl;
  399. return false;
  400. }
  401. unsigned int foundKeys = 0, deletedKeys = 0;
  402. SECKEYPrivateKeyListNode *node;
  403. for (node = PRIVKEY_LIST_HEAD(list.get());
  404. !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
  405. SECKEYPrivateKey *privKey = node->key;
  406. foundKeys++;
  407. // see PK11_DeleteTokenPrivateKey for example usage
  408. // calling PK11_DeleteTokenPrivateKey directly does not work because it also
  409. // destroys the SECKEYPrivateKey (by calling SECKEY_DestroyPrivateKey) -
  410. // then SECKEY_DestroyPrivateKeyList does not
  411. // work because it also calls SECKEY_DestroyPrivateKey
  412. SECStatus rv =
  413. PK11_DestroyTokenObject(privKey->pkcs11Slot, privKey->pkcs11ID);
  414. if (rv == SECSuccess) {
  415. deletedKeys++;
  416. }
  417. }
  418. if (foundKeys > deletedKeys) {
  419. std::cerr << "Some keys could not be deleted." << std::endl;
  420. }
  421. if (deletedKeys > 0) {
  422. std::cout << "Found " << foundKeys << " keys." << std::endl;
  423. std::cout << "Successfully deleted " << deletedKeys
  424. << " key(s) with nickname " << keyName << "." << std::endl;
  425. } else {
  426. std::cout << "No key with nickname " << keyName << " found to delete."
  427. << std::endl;
  428. }
  429. return true;
  430. }