michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsCertPicker.h" michael@0: #include "pkix/pkixtypes.h" michael@0: #include "nsMemory.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsNSSComponent.h" michael@0: #include "nsNSSCertificate.h" michael@0: #include "nsReadableUtils.h" michael@0: #include "nsICertPickDialogs.h" michael@0: #include "nsNSSShutDown.h" michael@0: #include "nsNSSCertHelper.h" michael@0: #include "ScopedNSSTypes.h" michael@0: michael@0: #include "cert.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsCertPicker, nsIUserCertPicker) michael@0: michael@0: nsCertPicker::nsCertPicker() michael@0: { michael@0: } michael@0: michael@0: nsCertPicker::~nsCertPicker() michael@0: { michael@0: } michael@0: michael@0: NS_IMETHODIMP nsCertPicker::PickByUsage(nsIInterfaceRequestor *ctx, michael@0: const char16_t *selectedNickname, michael@0: int32_t certUsage, michael@0: bool allowInvalid, michael@0: bool allowDuplicateNicknames, michael@0: bool *canceled, michael@0: nsIX509Cert **_retval) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: int32_t selectedIndex = -1; michael@0: bool selectionFound = false; michael@0: char16_t **certNicknameList = nullptr; michael@0: char16_t **certDetailsList = nullptr; michael@0: CERTCertListNode* node = nullptr; michael@0: nsresult rv = NS_OK; michael@0: michael@0: { michael@0: // Iterate over all certs. This assures that user is logged in to all hardware tokens. michael@0: nsCOMPtr ctx = new PipUIContext(); michael@0: mozilla::pkix::ScopedCERTCertList allcerts( michael@0: PK11_ListCerts(PK11CertListUnique, ctx)); michael@0: } michael@0: michael@0: /* find all user certs that are valid and for SSL */ michael@0: /* note that we are allowing expired certs in this list */ michael@0: michael@0: mozilla::pkix::ScopedCERTCertList certList( michael@0: CERT_FindUserCertsByUsage(CERT_GetDefaultCertDB(), michael@0: (SECCertUsage)certUsage, michael@0: !allowDuplicateNicknames, michael@0: !allowInvalid, michael@0: ctx)); michael@0: if (!certList) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: ScopedCERTCertNicknames nicknames(getNSSCertNicknamesFromCertList(certList.get())); michael@0: if (!nicknames) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: certNicknameList = (char16_t **)nsMemory::Alloc(sizeof(char16_t *) * nicknames->numnicknames); michael@0: certDetailsList = (char16_t **)nsMemory::Alloc(sizeof(char16_t *) * nicknames->numnicknames); michael@0: michael@0: if (!certNicknameList || !certDetailsList) { michael@0: nsMemory::Free(certNicknameList); michael@0: nsMemory::Free(certDetailsList); michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: int32_t CertsToUse; michael@0: michael@0: for (CertsToUse = 0, node = CERT_LIST_HEAD(certList.get()); michael@0: !CERT_LIST_END(node, certList.get()) && michael@0: CertsToUse < nicknames->numnicknames; michael@0: node = CERT_LIST_NEXT(node) michael@0: ) michael@0: { michael@0: nsNSSCertificate *tempCert = nsNSSCertificate::Create(node->cert); michael@0: michael@0: if (tempCert) { michael@0: michael@0: // XXX we really should be using an nsCOMPtr instead of manually add-refing, michael@0: // but nsNSSCertificate does not have a default constructor. michael@0: michael@0: NS_ADDREF(tempCert); michael@0: michael@0: nsAutoString i_nickname(NS_ConvertUTF8toUTF16(nicknames->nicknames[CertsToUse])); michael@0: nsAutoString nickWithSerial; michael@0: nsAutoString details; michael@0: michael@0: if (!selectionFound) { michael@0: if (i_nickname == nsDependentString(selectedNickname)) { michael@0: selectedIndex = CertsToUse; michael@0: selectionFound = true; michael@0: } michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(tempCert->FormatUIStrings(i_nickname, nickWithSerial, details))) { michael@0: certNicknameList[CertsToUse] = ToNewUnicode(nickWithSerial); michael@0: certDetailsList[CertsToUse] = ToNewUnicode(details); michael@0: } michael@0: else { michael@0: certNicknameList[CertsToUse] = nullptr; michael@0: certDetailsList[CertsToUse] = nullptr; michael@0: } michael@0: michael@0: NS_RELEASE(tempCert); michael@0: michael@0: ++CertsToUse; michael@0: } michael@0: } michael@0: michael@0: if (CertsToUse) { michael@0: nsICertPickDialogs *dialogs = nullptr; michael@0: rv = getNSSDialogs((void**)&dialogs, michael@0: NS_GET_IID(nsICertPickDialogs), michael@0: NS_CERTPICKDIALOGS_CONTRACTID); michael@0: michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsPSMUITracker tracker; michael@0: if (tracker.isUIForbidden()) { michael@0: rv = NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: else { michael@0: /* Throw up the cert picker dialog and get back the index of the selected cert */ michael@0: rv = dialogs->PickCertificate(ctx, michael@0: (const char16_t**)certNicknameList, (const char16_t**)certDetailsList, michael@0: CertsToUse, &selectedIndex, canceled); michael@0: } michael@0: michael@0: NS_RELEASE(dialogs); michael@0: } michael@0: } michael@0: michael@0: int32_t i; michael@0: for (i = 0; i < CertsToUse; ++i) { michael@0: nsMemory::Free(certNicknameList[i]); michael@0: nsMemory::Free(certDetailsList[i]); michael@0: } michael@0: nsMemory::Free(certNicknameList); michael@0: nsMemory::Free(certDetailsList); michael@0: michael@0: if (!CertsToUse) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: if (NS_SUCCEEDED(rv) && !*canceled) { michael@0: for (i = 0, node = CERT_LIST_HEAD(certList); michael@0: !CERT_LIST_END(node, certList); michael@0: ++i, node = CERT_LIST_NEXT(node)) { michael@0: michael@0: if (i == selectedIndex) { michael@0: nsNSSCertificate *cert = nsNSSCertificate::Create(node->cert); michael@0: if (!cert) { michael@0: rv = NS_ERROR_OUT_OF_MEMORY; michael@0: break; michael@0: } michael@0: michael@0: nsIX509Cert *x509 = 0; michael@0: nsresult rv = cert->QueryInterface(NS_GET_IID(nsIX509Cert), (void**)&x509); michael@0: if (NS_FAILED(rv)) { michael@0: break; michael@0: } michael@0: michael@0: NS_ADDREF(x509); michael@0: *_retval = x509; michael@0: NS_RELEASE(cert); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return rv; michael@0: }