LCOV - code coverage report
Current view: top level - security/manager/ssl - LocalCertService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 233 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 30 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
       5             : #include "LocalCertService.h"
       6             : 
       7             : #include "CryptoTask.h"
       8             : #include "ScopedNSSTypes.h"
       9             : #include "cert.h"
      10             : #include "mozilla/Casting.h"
      11             : #include "mozilla/ModuleUtils.h"
      12             : #include "mozilla/RefPtr.h"
      13             : #include "nsIPK11Token.h"
      14             : #include "nsIPK11TokenDB.h"
      15             : #include "nsIX509Cert.h"
      16             : #include "nsIX509CertValidity.h"
      17             : #include "nsLiteralString.h"
      18             : #include "nsProxyRelease.h"
      19             : #include "nsServiceManagerUtils.h"
      20             : #include "nsString.h"
      21             : #include "pk11pub.h"
      22             : 
      23             : namespace mozilla {
      24             : 
      25             : // Given a name, searches the internal certificate/key database for a
      26             : // self-signed certificate with subject and issuer distinguished name equal to
      27             : // "CN={name}". This assumes that the user has already authenticated to the
      28             : // internal DB if necessary.
      29             : static nsresult
      30           0 : FindLocalCertByName(const nsACString& aName,
      31             :             /*out*/ UniqueCERTCertificate& aResult)
      32             : {
      33           0 :   aResult.reset(nullptr);
      34           0 :   NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
      35           0 :   nsAutoCString expectedDistinguishedName(commonNamePrefix + aName);
      36           0 :   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
      37           0 :   if (!slot) {
      38           0 :     return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
      39             :   }
      40           0 :   UniqueCERTCertList certList(PK11_ListCertsInSlot(slot.get()));
      41           0 :   if (!certList) {
      42           0 :     return NS_ERROR_UNEXPECTED;
      43             :   }
      44           0 :   for (const CERTCertListNode* node = CERT_LIST_HEAD(certList);
      45           0 :        !CERT_LIST_END(node, certList); node = CERT_LIST_NEXT(node)) {
      46             :     // If this isn't a self-signed cert, it's not what we're interested in.
      47           0 :     if (!node->cert->isRoot) {
      48           0 :       continue;
      49             :     }
      50           0 :     if (!expectedDistinguishedName.Equals(node->cert->subjectName)) {
      51           0 :       continue; // Subject should match nickname
      52             :     }
      53           0 :     if (!expectedDistinguishedName.Equals(node->cert->issuerName)) {
      54           0 :       continue; // Issuer should match nickname
      55             :     }
      56             :     // We found a match.
      57           0 :     aResult.reset(CERT_DupCertificate(node->cert));
      58           0 :     return NS_OK;
      59             :   }
      60           0 :   return NS_OK;
      61             : }
      62             : 
      63           0 : class LocalCertTask : public CryptoTask
      64             : {
      65             : protected:
      66           0 :   explicit LocalCertTask(const nsACString& aNickname)
      67           0 :     : mNickname(aNickname)
      68             :   {
      69           0 :   }
      70             : 
      71           0 :   nsresult RemoveExisting()
      72             :   {
      73             :     // Search for any existing self-signed certs with this name and remove them
      74             :     for (;;) {
      75           0 :       UniqueCERTCertificate cert;
      76           0 :       nsresult rv = FindLocalCertByName(mNickname, cert);
      77           0 :       if (NS_FAILED(rv)) {
      78           0 :         return rv;
      79             :       }
      80             :       // If we didn't find a match, we're done.
      81           0 :       if (!cert) {
      82           0 :         return NS_OK;
      83             :       }
      84           0 :       rv = MapSECStatus(PK11_DeleteTokenCertAndKey(cert.get(), nullptr));
      85           0 :       if (NS_FAILED(rv)) {
      86           0 :         return rv;
      87             :       }
      88           0 :     }
      89             :   }
      90             : 
      91             :   nsCString mNickname;
      92             : };
      93             : 
      94           0 : class LocalCertGetTask final : public LocalCertTask
      95             : {
      96             : public:
      97           0 :   LocalCertGetTask(const nsACString& aNickname,
      98             :                    nsILocalCertGetCallback* aCallback)
      99           0 :     : LocalCertTask(aNickname)
     100             :     , mCallback(new nsMainThreadPtrHolder<nsILocalCertGetCallback>(
     101           0 :         "LocalCertGetTask::mCallback", aCallback))
     102           0 :     , mCert(nullptr)
     103             :   {
     104           0 :   }
     105             : 
     106             : private:
     107           0 :   virtual nsresult CalculateResult() override
     108             :   {
     109             :     // Try to lookup an existing cert in the DB
     110           0 :     nsresult rv = GetFromDB();
     111             :     // Make a new one if getting fails
     112           0 :     if (NS_FAILED(rv)) {
     113           0 :       rv = Generate();
     114             :     }
     115             :     // If generation fails, we're out of luck
     116           0 :     if (NS_FAILED(rv)) {
     117           0 :       return rv;
     118             :     }
     119             : 
     120             :     // Validate cert, make a new one if it fails
     121           0 :     rv = Validate();
     122           0 :     if (NS_FAILED(rv)) {
     123           0 :       rv = Generate();
     124             :     }
     125             :     // If generation fails, we're out of luck
     126           0 :     if (NS_FAILED(rv)) {
     127           0 :       return rv;
     128             :     }
     129             : 
     130           0 :     return NS_OK;
     131             :   }
     132             : 
     133           0 :   nsresult Generate()
     134             :   {
     135             :     nsresult rv;
     136             : 
     137             :     // Get the key slot for generation later
     138           0 :     UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
     139           0 :     if (!slot) {
     140           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     141             :     }
     142             : 
     143             :     // Remove existing certs with this name (if any)
     144           0 :     rv = RemoveExisting();
     145           0 :     if (NS_FAILED(rv)) {
     146           0 :       return rv;
     147             :     }
     148             : 
     149             :     // Generate a new cert
     150           0 :     NS_NAMED_LITERAL_CSTRING(commonNamePrefix, "CN=");
     151           0 :     nsAutoCString subjectNameStr(commonNamePrefix + mNickname);
     152           0 :     UniqueCERTName subjectName(CERT_AsciiToName(subjectNameStr.get()));
     153           0 :     if (!subjectName) {
     154           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     155             :     }
     156             : 
     157             :     // Use the well-known NIST P-256 curve
     158           0 :     SECOidData* curveOidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1);
     159           0 :     if (!curveOidData) {
     160           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     161             :     }
     162             : 
     163             :     // Get key params from the curve
     164           0 :     ScopedAutoSECItem keyParams(2 + curveOidData->oid.len);
     165           0 :     keyParams.data[0] = SEC_ASN1_OBJECT_ID;
     166           0 :     keyParams.data[1] = curveOidData->oid.len;
     167           0 :     memcpy(keyParams.data + 2, curveOidData->oid.data, curveOidData->oid.len);
     168             : 
     169             :     // Generate cert key pair
     170             :     SECKEYPublicKey* tempPublicKey;
     171             :     UniqueSECKEYPrivateKey privateKey(
     172             :       PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &keyParams,
     173             :                            &tempPublicKey, true /* token */,
     174           0 :                            true /* sensitive */, nullptr));
     175           0 :     UniqueSECKEYPublicKey publicKey(tempPublicKey);
     176           0 :     tempPublicKey = nullptr;
     177           0 :     if (!privateKey || !publicKey) {
     178           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     179             :     }
     180             : 
     181             :     // Create subject public key info and cert request
     182             :     UniqueCERTSubjectPublicKeyInfo spki(
     183           0 :       SECKEY_CreateSubjectPublicKeyInfo(publicKey.get()));
     184           0 :     if (!spki) {
     185           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     186             :     }
     187             :     UniqueCERTCertificateRequest certRequest(
     188           0 :       CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr));
     189           0 :     if (!certRequest) {
     190           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     191             :     }
     192             : 
     193             :     // Valid from one day before to 1 year after
     194             :     static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
     195             :                                * PRTime(60)  // sec
     196             :                                * PRTime(60)  // min
     197             :                                * PRTime(24); // hours
     198             : 
     199           0 :     PRTime now = PR_Now();
     200           0 :     PRTime notBefore = now - oneDay;
     201           0 :     PRTime notAfter = now + (PRTime(365) * oneDay);
     202           0 :     UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
     203           0 :     if (!validity) {
     204           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     205             :     }
     206             : 
     207             :     // Generate random serial
     208             :     unsigned long serial;
     209             :     // This serial in principle could collide, but it's unlikely
     210           0 :     rv = MapSECStatus(PK11_GenerateRandomOnSlot(
     211             :            slot.get(), BitwiseCast<unsigned char*, unsigned long*>(&serial),
     212           0 :            sizeof(serial)));
     213           0 :     if (NS_FAILED(rv)) {
     214           0 :       return rv;
     215             :     }
     216             : 
     217             :     // Create the cert from these pieces
     218             :     UniqueCERTCertificate cert(
     219             :       CERT_CreateCertificate(serial, subjectName.get(), validity.get(),
     220           0 :                              certRequest.get()));
     221           0 :     if (!cert) {
     222           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     223             :     }
     224             : 
     225             :     // Update the cert version to X509v3
     226           0 :     if (!cert->version.data) {
     227           0 :       return NS_ERROR_INVALID_POINTER;
     228             :     }
     229           0 :     *(cert->version.data) = SEC_CERTIFICATE_VERSION_3;
     230           0 :     cert->version.len = 1;
     231             : 
     232             :     // Set cert signature algorithm
     233           0 :     PLArenaPool* arena = cert->arena;
     234           0 :     if (!arena) {
     235           0 :       return NS_ERROR_INVALID_POINTER;
     236             :     }
     237           0 :     rv = MapSECStatus(
     238           0 :            SECOID_SetAlgorithmID(arena, &cert->signature,
     239           0 :                                  SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, 0));
     240           0 :     if (NS_FAILED(rv)) {
     241           0 :       return rv;
     242             :     }
     243             : 
     244             :     // Encode and self-sign the cert
     245             :     UniqueSECItem certDER(
     246           0 :       SEC_ASN1EncodeItem(nullptr, nullptr, cert.get(),
     247           0 :                          SEC_ASN1_GET(CERT_CertificateTemplate)));
     248           0 :     if (!certDER) {
     249           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     250             :     }
     251           0 :     rv = MapSECStatus(
     252           0 :            SEC_DerSignData(arena, &cert->derCert, certDER->data, certDER->len,
     253             :                            privateKey.get(),
     254           0 :                            SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE));
     255           0 :     if (NS_FAILED(rv)) {
     256           0 :       return rv;
     257             :     }
     258             : 
     259             :     // Create a CERTCertificate from the signed data
     260             :     UniqueCERTCertificate certFromDER(
     261           0 :       CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &cert->derCert, nullptr,
     262           0 :                               true /* perm */, true /* copyDER */));
     263           0 :     if (!certFromDER) {
     264           0 :       return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     265             :     }
     266             : 
     267             :     // Save the cert in the DB
     268           0 :     rv = MapSECStatus(PK11_ImportCert(slot.get(), certFromDER.get(),
     269             :                                       CK_INVALID_HANDLE, mNickname.get(),
     270           0 :                                       false /* unused */));
     271           0 :     if (NS_FAILED(rv)) {
     272           0 :       return rv;
     273             :     }
     274             : 
     275             :     // We should now have cert in the DB, read it back in nsIX509Cert form
     276           0 :     return GetFromDB();
     277             :   }
     278             : 
     279           0 :   nsresult GetFromDB()
     280             :   {
     281           0 :     UniqueCERTCertificate cert;
     282           0 :     nsresult rv = FindLocalCertByName(mNickname, cert);
     283           0 :     if (NS_FAILED(rv)) {
     284           0 :       return rv;
     285             :     }
     286           0 :     if (!cert) {
     287           0 :       return NS_ERROR_FAILURE;
     288             :     }
     289           0 :     mCert = nsNSSCertificate::Create(cert.get());
     290           0 :     return NS_OK;
     291             :   }
     292             : 
     293           0 :   nsresult Validate()
     294             :   {
     295             :     // Verify cert is self-signed
     296             :     bool selfSigned;
     297           0 :     nsresult rv = mCert->GetIsSelfSigned(&selfSigned);
     298           0 :     if (NS_FAILED(rv)) {
     299           0 :       return rv;
     300             :     }
     301           0 :     if (!selfSigned) {
     302           0 :       return NS_ERROR_FAILURE;
     303             :     }
     304             : 
     305             :     // Check that subject and issuer match nickname
     306           0 :     nsXPIDLString subjectName;
     307           0 :     nsXPIDLString issuerName;
     308           0 :     mCert->GetSubjectName(subjectName);
     309           0 :     mCert->GetIssuerName(issuerName);
     310           0 :     if (!subjectName.Equals(issuerName)) {
     311           0 :       return NS_ERROR_FAILURE;
     312             :     }
     313           0 :     NS_NAMED_LITERAL_STRING(commonNamePrefix, "CN=");
     314             :     nsAutoString subjectNameFromNickname(
     315           0 :       commonNamePrefix + NS_ConvertASCIItoUTF16(mNickname));
     316           0 :     if (!subjectName.Equals(subjectNameFromNickname)) {
     317           0 :       return NS_ERROR_FAILURE;
     318             :     }
     319             : 
     320           0 :     nsCOMPtr<nsIX509CertValidity> validity;
     321           0 :     mCert->GetValidity(getter_AddRefs(validity));
     322             : 
     323             :     PRTime notBefore, notAfter;
     324           0 :     validity->GetNotBefore(&notBefore);
     325           0 :     validity->GetNotAfter(&notAfter);
     326             : 
     327             :     // Ensure cert will last at least one more day
     328             :     static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
     329             :                                * PRTime(60)  // sec
     330             :                                * PRTime(60)  // min
     331             :                                * PRTime(24); // hours
     332           0 :     PRTime now = PR_Now();
     333           0 :     if (notBefore > now ||
     334           0 :         notAfter < (now - oneDay)) {
     335           0 :       return NS_ERROR_FAILURE;
     336             :     }
     337             : 
     338           0 :     return NS_OK;
     339             :   }
     340             : 
     341           0 :   virtual void ReleaseNSSResources() override {}
     342             : 
     343           0 :   virtual void CallCallback(nsresult rv) override
     344             :   {
     345           0 :     (void) mCallback->HandleCert(mCert, rv);
     346           0 :   }
     347             : 
     348             :   nsMainThreadPtrHandle<nsILocalCertGetCallback> mCallback;
     349             :   nsCOMPtr<nsIX509Cert> mCert; // out
     350             : };
     351             : 
     352           0 : class LocalCertRemoveTask final : public LocalCertTask
     353             : {
     354             : public:
     355           0 :   LocalCertRemoveTask(const nsACString& aNickname,
     356             :                       nsILocalCertCallback* aCallback)
     357           0 :     : LocalCertTask(aNickname)
     358             :     , mCallback(new nsMainThreadPtrHolder<nsILocalCertCallback>(
     359           0 :         "LocalCertRemoveTask::mCallback", aCallback))
     360             :   {
     361           0 :   }
     362             : 
     363             : private:
     364           0 :   virtual nsresult CalculateResult() override
     365             :   {
     366           0 :     return RemoveExisting();
     367             :   }
     368             : 
     369           0 :   virtual void ReleaseNSSResources() override {}
     370             : 
     371           0 :   virtual void CallCallback(nsresult rv) override
     372             :   {
     373           0 :     (void) mCallback->HandleResult(rv);
     374           0 :   }
     375             : 
     376             :   nsMainThreadPtrHandle<nsILocalCertCallback> mCallback;
     377             : };
     378             : 
     379           0 : NS_IMPL_ISUPPORTS(LocalCertService, nsILocalCertService)
     380             : 
     381           0 : LocalCertService::LocalCertService()
     382             : {
     383           0 : }
     384             : 
     385           0 : LocalCertService::~LocalCertService()
     386             : {
     387           0 : }
     388             : 
     389             : nsresult
     390           0 : LocalCertService::LoginToKeySlot()
     391             : {
     392             :   nsresult rv;
     393             : 
     394             :   // Get access to key slot
     395           0 :   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
     396           0 :   if (!slot) {
     397           0 :     return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     398             :   }
     399             : 
     400             :   // If no user password yet, set it an empty one
     401           0 :   if (PK11_NeedUserInit(slot.get())) {
     402           0 :     rv = MapSECStatus(PK11_InitPin(slot.get(), "", ""));
     403           0 :     if (NS_FAILED(rv)) {
     404           0 :       return rv;
     405             :     }
     406             :   }
     407             : 
     408             :   // If user has a password set, prompt to login
     409           0 :   if (PK11_NeedLogin(slot.get()) && !PK11_IsLoggedIn(slot.get(), nullptr)) {
     410             :     // Switching to XPCOM to get the UI prompt that PSM owns
     411             :     nsCOMPtr<nsIPK11TokenDB> tokenDB =
     412           0 :       do_GetService(NS_PK11TOKENDB_CONTRACTID);
     413           0 :     if (!tokenDB) {
     414           0 :       return NS_ERROR_FAILURE;
     415             :     }
     416           0 :     nsCOMPtr<nsIPK11Token> keyToken;
     417           0 :     tokenDB->GetInternalKeyToken(getter_AddRefs(keyToken));
     418           0 :     if (!keyToken) {
     419           0 :       return NS_ERROR_FAILURE;
     420             :     }
     421             :     // Prompt the user to login
     422           0 :     return keyToken->Login(false /* force */);
     423             :   }
     424             : 
     425           0 :   return NS_OK;
     426             : }
     427             : 
     428             : NS_IMETHODIMP
     429           0 : LocalCertService::GetOrCreateCert(const nsACString& aNickname,
     430             :                                   nsILocalCertGetCallback* aCallback)
     431             : {
     432           0 :   if (NS_WARN_IF(aNickname.IsEmpty())) {
     433           0 :     return NS_ERROR_INVALID_ARG;
     434             :   }
     435           0 :   if (NS_WARN_IF(!aCallback)) {
     436           0 :     return NS_ERROR_INVALID_POINTER;
     437             :   }
     438             : 
     439             :   // Before sending off the task, login to key slot if needed
     440           0 :   nsresult rv = LoginToKeySlot();
     441           0 :   if (NS_FAILED(rv)) {
     442           0 :     aCallback->HandleCert(nullptr, rv);
     443           0 :     return NS_OK;
     444             :   }
     445             : 
     446           0 :   RefPtr<LocalCertGetTask> task(new LocalCertGetTask(aNickname, aCallback));
     447           0 :   return task->Dispatch("LocalCertGet");
     448             : }
     449             : 
     450             : NS_IMETHODIMP
     451           0 : LocalCertService::RemoveCert(const nsACString& aNickname,
     452             :                              nsILocalCertCallback* aCallback)
     453             : {
     454           0 :   if (NS_WARN_IF(aNickname.IsEmpty())) {
     455           0 :     return NS_ERROR_INVALID_ARG;
     456             :   }
     457           0 :   if (NS_WARN_IF(!aCallback)) {
     458           0 :     return NS_ERROR_INVALID_POINTER;
     459             :   }
     460             : 
     461             :   // Before sending off the task, login to key slot if needed
     462           0 :   nsresult rv = LoginToKeySlot();
     463           0 :   if (NS_FAILED(rv)) {
     464           0 :     aCallback->HandleResult(rv);
     465           0 :     return NS_OK;
     466             :   }
     467             : 
     468             :   RefPtr<LocalCertRemoveTask> task(
     469           0 :     new LocalCertRemoveTask(aNickname, aCallback));
     470           0 :   return task->Dispatch("LocalCertRm");
     471             : }
     472             : 
     473             : NS_IMETHODIMP
     474           0 : LocalCertService::GetLoginPromptRequired(bool* aRequired)
     475             : {
     476             :   nsresult rv;
     477             : 
     478             :   // Get access to key slot
     479           0 :   UniquePK11SlotInfo slot(PK11_GetInternalKeySlot());
     480           0 :   if (!slot) {
     481           0 :     return mozilla::psm::GetXPCOMFromNSSError(PR_GetError());
     482             :   }
     483             : 
     484             :   // If no user password yet, set it an empty one
     485           0 :   if (PK11_NeedUserInit(slot.get())) {
     486           0 :     rv = MapSECStatus(PK11_InitPin(slot.get(), "", ""));
     487           0 :     if (NS_FAILED(rv)) {
     488           0 :       return rv;
     489             :     }
     490             :   }
     491             : 
     492           0 :   *aRequired = PK11_NeedLogin(slot.get()) &&
     493           0 :                !PK11_IsLoggedIn(slot.get(), nullptr);
     494           0 :   return NS_OK;
     495             : }
     496             : 
     497             : #define LOCALCERTSERVICE_CID \
     498             : { 0x47402be2, 0xe653, 0x45d0, \
     499             :   { 0x8d, 0xaa, 0x9f, 0x0d, 0xce, 0x0a, 0xc1, 0x48 } }
     500             : 
     501           0 : NS_GENERIC_FACTORY_CONSTRUCTOR(LocalCertService)
     502             : 
     503             : NS_DEFINE_NAMED_CID(LOCALCERTSERVICE_CID);
     504             : 
     505             : static const Module::CIDEntry kLocalCertServiceCIDs[] = {
     506             :   { &kLOCALCERTSERVICE_CID, false, nullptr, LocalCertServiceConstructor },
     507             :   { nullptr }
     508             : };
     509             : 
     510             : static const Module::ContractIDEntry kLocalCertServiceContracts[] = {
     511             :   { LOCALCERTSERVICE_CONTRACTID, &kLOCALCERTSERVICE_CID },
     512             :   { nullptr }
     513             : };
     514             : 
     515             : static const Module kLocalCertServiceModule = {
     516             :   Module::kVersion,
     517             :   kLocalCertServiceCIDs,
     518             :   kLocalCertServiceContracts
     519             : };
     520             : 
     521             : NSMODULE_DEFN(LocalCertServiceModule) = &kLocalCertServiceModule;
     522             : 
     523             : } // namespace mozilla

Generated by: LCOV version 1.13