From ce50eac5c5dbc5fa7961c61aabd57bd2e9273723 Mon Sep 17 00:00:00 2001 From: Chuck Lee Date: Sat, 28 Feb 2015 21:54:16 +0800 Subject: [PATCH] Bug 1012549 - 0001. Support import PKCS12 certificate. r=dkeeler r=vchang --- config/external/nss/nss.def | 1 + dom/wifi/WifiCertService.cpp | 170 +++++++++++++++++++--- dom/wifi/WifiWorker.js | 2 +- security/manager/ssl/src/ScopedNSSTypes.h | 4 + 4 files changed, 159 insertions(+), 18 deletions(-) diff --git a/config/external/nss/nss.def b/config/external/nss/nss.def index 302bb0a382a1..f3a77417276c 100644 --- a/config/external/nss/nss.def +++ b/config/external/nss/nss.def @@ -589,6 +589,7 @@ SEC_PKCS12DecoderFinish SEC_PKCS12DecoderImportBags SEC_PKCS12DecoderIterateInit SEC_PKCS12DecoderIterateNext +SEC_PKCS12DecoderRenameCertNicknames SEC_PKCS12DecoderStart SEC_PKCS12DecoderUpdate SEC_PKCS12DecoderValidateBags diff --git a/dom/wifi/WifiCertService.cpp b/dom/wifi/WifiCertService.cpp index 7ce7d0bcbd22..d65c781ba0dc 100644 --- a/dom/wifi/WifiCertService.cpp +++ b/dom/wifi/WifiCertService.cpp @@ -10,6 +10,7 @@ #include "WifiCertService.h" #include "mozilla/ClearOnShutdown.h" +#include "mozilla/Endian.h" #include "mozilla/ModuleUtils.h" #include "mozilla/RefPtr.h" #include "mozilla/dom/ToJSValue.h" @@ -71,9 +72,14 @@ private: return NS_ERROR_OUT_OF_MEMORY; } - // Only support DER format now. - return ImportDERBlob(buf, size, mResult.mNickname, - &mResult.mUsageFlag); + // Try import as DER format first. + rv = ImportDERBlob(buf, size); + if (NS_SUCCEEDED(rv)) { + return rv; + } + + // Try import as PKCS#12 format. + return ImportPKCS12Blob(buf, size, mPassword); } virtual void CallCallback(nsresult rv) @@ -84,20 +90,148 @@ private: gWifiCertService->DispatchResult(mResult); } - nsresult ImportDERBlob(char* buf, uint32_t size, - const nsAString& aNickname, - /*out*/ uint16_t* aUsageFlag) + nsresult ImportDERBlob(char* buf, uint32_t size) { - NS_ENSURE_ARG_POINTER(aUsageFlag); - // Create certificate object. ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(buf, size)); if (!cert) { return MapSECStatus(SECFailure); } - // Import certificate with nickname. - return ImportCert(cert, aNickname, aUsageFlag); + // Import certificate. + return ImportCert(cert); + } + + static SECItem* + HandleNicknameCollision(SECItem* aOldNickname, PRBool* aCancel, void* aWincx) + { + const char* dummyName = "Imported User Cert"; + const size_t dummyNameLen = strlen(dummyName); + SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, dummyNameLen + 1); + if (!newNick) { + return nullptr; + } + + newNick->type = siAsciiString; + // Dummy name, will be renamed later. + memcpy(newNick->data, dummyName, dummyNameLen + 1); + newNick->len = dummyNameLen; + + return newNick; + } + + static SECStatus + HandleNicknameUpdate(const CERTCertificate *aCert, + const SECItem *default_nickname, + SECItem **new_nickname, + void *arg) + { + WifiCertServiceResultOptions *result = (WifiCertServiceResultOptions *)arg; + + nsCString userNickname; + CopyUTF16toUTF8(result->mNickname, userNickname); + + nsCString fullNickname; + if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) { + // Accept self-signed SSL CA as server certificate. + fullNickname.AssignLiteral("WIFI_SERVERCERT_"); + fullNickname += userNickname; + result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER; + } else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) { + // User Certificate + fullNickname.AssignLiteral("WIFI_USERCERT_"); + fullNickname += userNickname; + result->mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER; + } + char* nickname; + uint32_t length = fullNickname.GetMutableData(&nickname); + + SECItem* newNick = ::SECITEM_AllocItem(nullptr, nullptr, length + 1); + if (!newNick) { + return SECFailure; + } + + newNick->type = siAsciiString; + memcpy(newNick->data, nickname, length + 1); + newNick->len = length; + + *new_nickname = newNick; + return SECSuccess; + } + + nsresult ImportPKCS12Blob(char* buf, uint32_t size, const nsAString& aPassword) + { + nsString password(aPassword); + + // password is null-terminated wide-char string. + // passwordItem is required to be big-endian form of password, stored in char + // array, including the null-termination. + uint32_t length = password.Length() + 1; + ScopedSECItem passwordItem( + ::SECITEM_AllocItem(nullptr, nullptr, length * sizeof(nsString::char_type))); + + if (!passwordItem) { + return NS_ERROR_FAILURE; + } + + mozilla::NativeEndian::copyAndSwapToBigEndian(passwordItem->data, + password.BeginReading(), + length); + // Create a decoder. + ScopedSEC_PKCS12DecoderContext p12dcx(SEC_PKCS12DecoderStart( + passwordItem, nullptr, nullptr, + nullptr, nullptr, nullptr, nullptr, + nullptr)); + + if (!p12dcx) { + return NS_ERROR_FAILURE; + } + + // Assign data to decorder. + SECStatus srv = SEC_PKCS12DecoderUpdate(p12dcx, + reinterpret_cast(buf), + size); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + + // Verify certificates. + srv = SEC_PKCS12DecoderVerify(p12dcx); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + + // Set certificate nickname and usage flag. + srv = SEC_PKCS12DecoderRenameCertNicknames(p12dcx, HandleNicknameUpdate, + &mResult); + + // Validate certificates. + srv = SEC_PKCS12DecoderValidateBags(p12dcx, HandleNicknameCollision); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + + // Initialize slot. + ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot()); + if (!slot) { + return NS_ERROR_FAILURE; + } + if (PK11_NeedLogin(slot) && PK11_NeedUserInit(slot)) { + srv = PK11_InitPin(slot, "", ""); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + } + + // Import cert and key. + srv = SEC_PKCS12DecoderImportBags(p12dcx); + if (srv != SECSuccess) { + return MapSECStatus(srv); + } + + // User certificate must be imported from PKCS#12. + return (mResult.mUsageFlag & nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER) + ? NS_OK : NS_ERROR_FAILURE; } nsresult ReadBlob(/*out*/ nsCString& aBuf) @@ -128,20 +262,22 @@ private: return NS_OK; } - nsresult ImportCert(CERTCertificate* aCert, const nsAString& aNickname, - /*out*/ uint16_t* aUsageFlag) + nsresult ImportCert(CERTCertificate* aCert) { - NS_ENSURE_ARG_POINTER(aUsageFlag); - nsCString userNickname, fullNickname; - CopyUTF16toUTF8(aNickname, userNickname); + CopyUTF16toUTF8(mResult.mNickname, userNickname); // Determine certificate nickname by adding prefix according to its type. if (aCert->isRoot && (aCert->nsCertType & NS_CERT_TYPE_SSL_CA)) { // Accept self-signed SSL CA as server certificate. fullNickname.AssignLiteral("WIFI_SERVERCERT_"); fullNickname += userNickname; - *aUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER; + mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_SERVER; + } else if (aCert->nsCertType & NS_CERT_TYPE_SSL_CLIENT) { + // User Certificate + fullNickname.AssignLiteral("WIFI_USERCERT_"); + fullNickname += userNickname; + mResult.mUsageFlag |= nsIWifiCertService::WIFI_CERT_USAGE_FLAG_USER; } else { return NS_ERROR_ABORT; } @@ -154,7 +290,7 @@ private: } // Import certificate, duplicated nickname will cause error. - SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, NULL); + SECStatus srv = CERT_AddTempCertToPerm(aCert, nickname, nullptr); if (srv != SECSuccess) { return MapSECStatus(srv); } diff --git a/dom/wifi/WifiWorker.js b/dom/wifi/WifiWorker.js index 7308b60f9263..0eb53f333c0d 100755 --- a/dom/wifi/WifiWorker.js +++ b/dom/wifi/WifiWorker.js @@ -3469,7 +3469,7 @@ WifiWorker.prototype = { WifiManager.importCert(msg.data, function(data) { if (data.status === 0) { - let usageString = ["ServerCert"]; + let usageString = ["ServerCert", "UserCert"]; let usageArray = []; for (let i = 0; i < usageString.length; i++) { if (data.usageFlag & (0x01 << i)) { diff --git a/security/manager/ssl/src/ScopedNSSTypes.h b/security/manager/ssl/src/ScopedNSSTypes.h index ec115367c3ee..c4594863f1f1 100644 --- a/security/manager/ssl/src/ScopedNSSTypes.h +++ b/security/manager/ssl/src/ScopedNSSTypes.h @@ -23,6 +23,7 @@ #include "keyhi.h" #include "cryptohi.h" #include "pk11pub.h" +#include "pkcs12.h" #include "sechash.h" #include "secpkcs7.h" #include "secport.h" @@ -240,6 +241,9 @@ MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS7ContentInfo, SEC_PKCS7ContentInfo, SEC_PKCS7DestroyContentInfo) +MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedSEC_PKCS12DecoderContext, + SEC_PKCS12DecoderContext, + SEC_PKCS12DecoderFinish) namespace internal { inline void