зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1712837 - introduce ipcclientcerts to allow client certificates to work with the socket process r=rmf,kershaw,necko-reviewers,ipc-reviewers,nika,jschanck
This patch introduces ipcclientcerts, a PKCS#11 module that the socket process can load to get access to client certificates and keys managed by the parent process. This enables client certificate authentication to work with the socket process (particularly for keys stored outside of NSS, as with osclientcerts or third-party PKCS#11 modules). Differential Revision: https://phabricator.services.mozilla.com/D122392
This commit is contained in:
Родитель
6d37a3da3d
Коммит
c02a76841b
|
@ -2437,6 +2437,19 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipcclientcerts-static"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"env_logger",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"pkcs11",
|
||||
"rsclientcerts",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.8.2"
|
||||
|
|
|
@ -9,6 +9,7 @@ members = [
|
|||
"js/src/rust",
|
||||
"js/src/wasm/cranelift",
|
||||
"netwerk/test/http3server",
|
||||
"security/manager/ssl/ipcclientcerts",
|
||||
"security/manager/ssl/osclientcerts",
|
||||
"testing/geckodriver",
|
||||
"toolkit/crashreporter/rust_minidump_writer_linux",
|
||||
|
|
|
@ -49,7 +49,8 @@ this.pkcs11 = class extends ExtensionAPI {
|
|||
}
|
||||
if (
|
||||
manifestLib !== ctypes.libraryName("nssckbi") &&
|
||||
manifestLib !== ctypes.libraryName("osclientcerts")
|
||||
manifestLib !== ctypes.libraryName("osclientcerts") &&
|
||||
manifestLib !== ctypes.libraryName("ipcclientcerts")
|
||||
) {
|
||||
return hostInfo.manifest;
|
||||
}
|
||||
|
|
|
@ -204,6 +204,26 @@ add_task(async function test_pkcs11() {
|
|||
/No such PKCS#11 module osclientcerts/,
|
||||
"getModuleSlots should not work on the built-in osclientcerts module"
|
||||
);
|
||||
await browser.test.assertRejects(
|
||||
browser.pkcs11.installModule("ipcclientcerts", 0),
|
||||
/No such PKCS#11 module ipcclientcerts/,
|
||||
"installModule should not work on the built-in ipcclientcerts module"
|
||||
);
|
||||
await browser.test.assertRejects(
|
||||
browser.pkcs11.uninstallModule("ipcclientcerts"),
|
||||
/No such PKCS#11 module ipcclientcerts/,
|
||||
"uninstallModule should not work on the built-in ipcclientcerts module"
|
||||
);
|
||||
await browser.test.assertRejects(
|
||||
browser.pkcs11.isModuleInstalled("ipcclientcerts"),
|
||||
/No such PKCS#11 module ipcclientcerts/,
|
||||
"isModuleLoaded should not work on the built-in ipcclientcerts module"
|
||||
);
|
||||
await browser.test.assertRejects(
|
||||
browser.pkcs11.getModuleSlots("ipcclientcerts"),
|
||||
/No such PKCS#11 module ipcclientcerts/,
|
||||
"getModuleSlots should not work on the built-in ipcclientcerts module"
|
||||
);
|
||||
browser.test.notifyPass("pkcs11");
|
||||
} catch (e) {
|
||||
browser.test.fail(`Error: ${String(e)} :: ${e.stack}`);
|
||||
|
|
|
@ -372,6 +372,8 @@ bin/libfreebl_64int_3.so
|
|||
#endif
|
||||
#endif
|
||||
|
||||
@BINPATH@/@DLL_PREFIX@ipcclientcerts@DLL_SUFFIX@
|
||||
|
||||
; For process sandboxing
|
||||
#if defined(MOZ_SANDBOX)
|
||||
#if defined(XP_LINUX)
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "ssl.h"
|
||||
#include "mozpkix/nss_scoped_ptrs.h"
|
||||
#include "secerr.h"
|
||||
#include "sslerr.h"
|
||||
|
||||
#include "mozilla/Sprintf.h"
|
||||
#include "mozilla/dom/CryptoBuffer.h"
|
||||
|
@ -24,10 +25,103 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
SECItem* WrapPrivateKeyInfoWithEmptyPassword(
|
||||
SECKEYPrivateKey* pk) /* encrypt this private key */
|
||||
{
|
||||
if (!pk) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// For private keys, NSS cannot export anything other than RSA, but we need EC
|
||||
// also. So, we use the private key encryption function to serialize instead,
|
||||
// using a hard-coded dummy password; this is not intended to provide any
|
||||
// additional security, it just works around a limitation in NSS.
|
||||
SECItem dummyPassword = {siBuffer, nullptr, 0};
|
||||
UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
|
||||
slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr));
|
||||
|
||||
if (!epki) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SEC_ASN1EncodeItem(
|
||||
nullptr, nullptr, epki.get(),
|
||||
NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false));
|
||||
}
|
||||
|
||||
SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
|
||||
SECItem* derPKI, const UniqueCERTCertificate& aCert,
|
||||
SECKEYPrivateKey** privk) {
|
||||
if (!derPKI || !aCert || !privk) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get()));
|
||||
// This is a pointer to data inside publicKey
|
||||
SECItem* publicValue = nullptr;
|
||||
switch (publicKey->keyType) {
|
||||
case dsaKey:
|
||||
publicValue = &publicKey->u.dsa.publicValue;
|
||||
break;
|
||||
case dhKey:
|
||||
publicValue = &publicKey->u.dh.publicValue;
|
||||
break;
|
||||
case rsaKey:
|
||||
publicValue = &publicKey->u.rsa.modulus;
|
||||
break;
|
||||
case ecKey:
|
||||
publicValue = &publicKey->u.ec.publicValue;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!temparena) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
SECKEYEncryptedPrivateKeyInfo* epki =
|
||||
PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo);
|
||||
if (!epki) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
SECStatus rv = SEC_ASN1DecodeItem(
|
||||
temparena.get(), epki,
|
||||
NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI);
|
||||
if (rv != SECSuccess) {
|
||||
// If SEC_ASN1DecodeItem fails, we cannot assume anything about the
|
||||
// validity of the data in epki. The best we can do is free the arena
|
||||
// and return.
|
||||
return rv;
|
||||
}
|
||||
|
||||
// See comment in WrapPrivateKeyInfoWithEmptyPassword about this
|
||||
// dummy password stuff.
|
||||
SECItem dummyPassword = {siBuffer, nullptr, 0};
|
||||
return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
|
||||
slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false,
|
||||
publicKey->keyType, KU_ALL, privk, nullptr);
|
||||
}
|
||||
|
||||
nsresult DtlsIdentity::Serialize(nsTArray<uint8_t>* aKeyDer,
|
||||
nsTArray<uint8_t>* aCertDer) {
|
||||
ScopedSECItem derPki(
|
||||
psm::WrapPrivateKeyInfoWithEmptyPassword(private_key_.get()));
|
||||
ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get()));
|
||||
if (!derPki) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
@ -50,7 +144,7 @@ RefPtr<DtlsIdentity> DtlsIdentity::Deserialize(
|
|||
static_cast<unsigned int>(aKeyDer.Length())};
|
||||
|
||||
SECKEYPrivateKey* privateKey;
|
||||
if (psm::UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
|
||||
if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
|
||||
SECSuccess) {
|
||||
MOZ_ASSERT(false);
|
||||
return nullptr;
|
||||
|
|
|
@ -57,6 +57,7 @@
|
|||
#include "mozilla/dom/WebAuthnTransactionChild.h"
|
||||
#include "mozilla/dom/MIDIPortChild.h"
|
||||
#include "mozilla/dom/MIDIManagerChild.h"
|
||||
#include "mozilla/psm/IPCClientCertsChild.h"
|
||||
#include "mozilla/RemoteLazyInputStreamChild.h"
|
||||
#include "nsID.h"
|
||||
#include "nsTraceRefcnt.h"
|
||||
|
|
|
@ -71,6 +71,8 @@
|
|||
#include "mozilla/net/HttpBackgroundChannelParent.h"
|
||||
#include "mozilla/net/HttpConnectionMgrParent.h"
|
||||
#include "mozilla/net/WebSocketConnectionParent.h"
|
||||
#include "mozilla/psm/IPCClientCertsChild.h"
|
||||
#include "mozilla/psm/IPCClientCertsParent.h"
|
||||
#include "mozilla/psm/VerifySSLServerCertParent.h"
|
||||
#include "nsIHttpChannelInternal.h"
|
||||
#include "nsIPrincipal.h"
|
||||
|
@ -1047,6 +1049,21 @@ mozilla::ipc::IPCResult BackgroundParentImpl::RecvPMessagePortConstructor(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
already_AddRefed<psm::PIPCClientCertsParent>
|
||||
BackgroundParentImpl::AllocPIPCClientCertsParent() {
|
||||
// This should only be called in the parent process with the socket process
|
||||
// as the child process, not any content processes, hence the check that the
|
||||
// child ID be 0.
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(mozilla::ipc::BackgroundParent::GetChildID(this) == 0);
|
||||
if (!XRE_IsParentProcess() ||
|
||||
mozilla::ipc::BackgroundParent::GetChildID(this) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<psm::IPCClientCertsParent> result = new psm::IPCClientCertsParent();
|
||||
return result.forget();
|
||||
}
|
||||
|
||||
bool BackgroundParentImpl::DeallocPMessagePortParent(
|
||||
PMessagePortParent* aActor) {
|
||||
AssertIsInMainOrSocketProcess();
|
||||
|
|
|
@ -295,6 +295,8 @@ class BackgroundParentImpl : public PBackgroundParent,
|
|||
PMessagePortParent* aActor, const nsID& aUUID,
|
||||
const nsID& aDestinationUUID, const uint32_t& aSequenceID) override;
|
||||
|
||||
already_AddRefed<PIPCClientCertsParent> AllocPIPCClientCertsParent() override;
|
||||
|
||||
bool DeallocPMessagePortParent(PMessagePortParent* aActor) override;
|
||||
|
||||
mozilla::ipc::IPCResult RecvMessagePortForceClose(
|
||||
|
|
|
@ -25,6 +25,7 @@ include protocol PFileSystemRequest;
|
|||
include protocol PGamepadEventChannel;
|
||||
include protocol PGamepadTestChannel;
|
||||
include protocol PHttpBackgroundChannel;
|
||||
include protocol PIPCClientCerts;
|
||||
include protocol PIdleScheduler;
|
||||
include protocol PRemoteLazyInputStream;
|
||||
include protocol PMediaTransport;
|
||||
|
@ -105,6 +106,7 @@ sync protocol PBackground
|
|||
manages PGamepadEventChannel;
|
||||
manages PGamepadTestChannel;
|
||||
manages PHttpBackgroundChannel;
|
||||
manages PIPCClientCerts;
|
||||
manages PIdleScheduler;
|
||||
manages PRemoteLazyInputStream;
|
||||
manages PLockManager;
|
||||
|
@ -287,6 +289,8 @@ parent:
|
|||
|
||||
async PLockManager(ContentPrincipalInfo aPrincipalInfo, nsID aClientId);
|
||||
|
||||
async PIPCClientCerts();
|
||||
|
||||
child:
|
||||
async PCache();
|
||||
async PCacheStreamControl();
|
||||
|
|
|
@ -1072,6 +1072,10 @@ description = Reflection is cold code, but synchronous by spec.
|
|||
# -
|
||||
[PSocketProcess::GetTLSClientCert]
|
||||
description = Synchronously get client certificate and key from parent process. Once bug 696976 has been fixed, this can be removed.
|
||||
[PIPCClientCerts::FindObjects]
|
||||
description = Finds certificates and private keys in the parent process. As this is called from PKCS#11, there is no way to make this asynchronous.
|
||||
[PIPCClientCerts::Sign]
|
||||
description = Performs a signature on given data with a key corresponding to the given identifier. This is called from PKCS#11, so there is no way to make this asynchronous.
|
||||
|
||||
#############################################################
|
||||
# AVOID ADDING NEW MESSAGES TO THIS FILE #
|
||||
|
|
|
@ -128,7 +128,7 @@ parent:
|
|||
ByteArray aServerCert,
|
||||
ByteArray? aClientCert,
|
||||
ByteArray[] aCollectedCANames)
|
||||
returns (bool aSucceeded, ByteArray aOutCert, ByteArray aOutKey, ByteArray[] aBuiltChain);
|
||||
returns (bool aSucceeded, ByteArray aOutCert, ByteArray[] aBuiltChain);
|
||||
async PProxyConfigLookup(nsIURI aUri, uint32_t aFlags);
|
||||
async CachePushCheck(nsIURI aPushedURL,
|
||||
OriginAttributes aOriginAttributes,
|
||||
|
|
|
@ -165,7 +165,15 @@ bool SocketProcessChild::Init(base::ProcessId aParentPid,
|
|||
// Initialize DNS Service here, since it needs to be done in main thread.
|
||||
nsCOMPtr<nsIDNSService> dns =
|
||||
do_GetService("@mozilla.org/network/dns-service;1", &rv);
|
||||
return NS_SUCCEEDED(rv);
|
||||
if (NS_FAILED(rv)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!EnsureNSSInitializedChromeOrContent()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SocketProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
|
@ -214,6 +222,7 @@ mozilla::ipc::IPCResult SocketProcessChild::RecvInit(
|
|||
if (aAttributes.mInitSandbox()) {
|
||||
Unused << RecvInitLinuxSandbox(aAttributes.mSandboxBroker());
|
||||
}
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -472,9 +481,7 @@ SocketProcessChild::GetAndRemoveDataBridge(uint64_t aChannelId) {
|
|||
}
|
||||
|
||||
mozilla::ipc::IPCResult SocketProcessChild::RecvClearSessionCache() {
|
||||
if (EnsureNSSInitializedChromeOrContent()) {
|
||||
nsNSSComponent::DoClearSSLExternalAndInternalSessionCache();
|
||||
}
|
||||
nsNSSComponent::DoClearSSLExternalAndInternalSessionCache();
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -49,11 +49,13 @@ bool SocketProcessImpl::Init(int aArgc, char* aArgv[]) {
|
|||
LoadLibraryW(L"nss3.dll");
|
||||
LoadLibraryW(L"softokn3.dll");
|
||||
LoadLibraryW(L"freebl3.dll");
|
||||
LoadLibraryW(L"ipcclientcerts.dll");
|
||||
mozilla::SandboxTarget::Instance()->StartSandbox();
|
||||
#elif defined(__OpenBSD__) && defined(MOZ_SANDBOX)
|
||||
PR_LoadLibrary("libnss3.so");
|
||||
PR_LoadLibrary("libsoftokn3.so");
|
||||
PR_LoadLibrary("libfreebl3.so");
|
||||
PR_LoadLibrary("libipcclientcerts.so");
|
||||
StartOpenBSDSandbox(GeckoProcessType_Socket);
|
||||
#endif
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "nsIConsoleService.h"
|
||||
#include "nsIHttpActivityObserver.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsNSSComponent.h"
|
||||
#include "nsNSSIOLayer.h"
|
||||
#include "nsIOService.h"
|
||||
#include "nsHttpHandler.h"
|
||||
|
@ -311,8 +312,7 @@ mozilla::ipc::IPCResult SocketProcessParent::RecvGetTLSClientCert(
|
|||
const int32_t& aPort, const uint32_t& aProviderFlags,
|
||||
const uint32_t& aProviderTlsFlags, const ByteArray& aServerCert,
|
||||
Maybe<ByteArray>&& aClientCert, nsTArray<ByteArray>&& aCollectedCANames,
|
||||
bool* aSucceeded, ByteArray* aOutCert, ByteArray* aOutKey,
|
||||
nsTArray<ByteArray>* aBuiltChain) {
|
||||
bool* aSucceeded, ByteArray* aOutCert, nsTArray<ByteArray>* aBuiltChain) {
|
||||
*aSucceeded = false;
|
||||
|
||||
SECItem serverCertItem = {
|
||||
|
@ -342,16 +342,15 @@ mozilla::ipc::IPCResult SocketProcessParent::RecvGetTLSClientCert(
|
|||
}
|
||||
|
||||
UniqueCERTCertificate cert;
|
||||
UniqueSECKEYPrivateKey key;
|
||||
UniqueCERTCertList builtChain;
|
||||
SECStatus status =
|
||||
DoGetClientAuthData(std::move(info), serverCert,
|
||||
std::move(collectedCANames), cert, key, builtChain);
|
||||
std::move(collectedCANames), cert, builtChain);
|
||||
if (status != SECSuccess) {
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
SerializeClientCertAndKey(cert, key, *aOutCert, *aOutKey);
|
||||
aOutCert->data().AppendElements(cert->derCert.data, cert->derCert.len);
|
||||
|
||||
if (builtChain) {
|
||||
for (CERTCertListNode* n = CERT_LIST_HEAD(builtChain);
|
||||
|
|
|
@ -99,8 +99,14 @@ class SocketProcessParent final
|
|||
const int32_t& aPort, const uint32_t& aProviderFlags,
|
||||
const uint32_t& aProviderTlsFlags, const ByteArray& aServerCert,
|
||||
Maybe<ByteArray>&& aClientCert, nsTArray<ByteArray>&& aCollectedCANames,
|
||||
bool* aSucceeded, ByteArray* aOutCert, ByteArray* aOutKey,
|
||||
nsTArray<ByteArray>* aBuiltChain);
|
||||
bool* aSucceeded, ByteArray* aOutCert, nsTArray<ByteArray>* aBuiltChain);
|
||||
|
||||
mozilla::ipc::IPCResult RecvFindIPCClientCertObjects(
|
||||
nsTArray<IPCClientCertObject>* aObjects);
|
||||
mozilla::ipc::IPCResult RecvIPCClientCertSign(ByteArray aCert,
|
||||
ByteArray aData,
|
||||
ByteArray aParams,
|
||||
ByteArray* aSignature);
|
||||
|
||||
already_AddRefed<PProxyConfigLookupParent> AllocPProxyConfigLookupParent(
|
||||
nsIURI* aURI, const uint32_t& aProxyResolveFlags);
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "nsNSSCertHelper.h"
|
||||
#include "nsNSSCertificate.h"
|
||||
#include "nsNSSCertificateDB.h"
|
||||
#include "nsNSSIOLayer.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
@ -1604,8 +1605,12 @@ void DisableMD5() {
|
|||
NSS_USE_ALG_IN_CERT_SIGNATURE | NSS_USE_ALG_IN_CMS_SIGNATURE);
|
||||
}
|
||||
|
||||
// Load a given PKCS#11 module located in the given directory. It will be named
|
||||
// the given module name. Optionally pass some string parameters to it via
|
||||
// 'params'. This argument will be provided to C_Initialize when called on the
|
||||
// module.
|
||||
bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
|
||||
const nsCString& dir) {
|
||||
const nsCString& dir, /* optional */ const char* params) {
|
||||
// If a module exists with the same name, make a best effort attempt to delete
|
||||
// it. Note that it isn't possible to delete the internal module, so checking
|
||||
// the return value would be detrimental in that case.
|
||||
|
@ -1629,6 +1634,11 @@ bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
|
|||
pkcs11ModuleSpec.AppendLiteral("\" library=\"");
|
||||
pkcs11ModuleSpec.Append(fullLibraryPath);
|
||||
pkcs11ModuleSpec.AppendLiteral("\"");
|
||||
if (params) {
|
||||
pkcs11ModuleSpec.AppendLiteral("\" parameters=\"");
|
||||
pkcs11ModuleSpec.Append(params);
|
||||
pkcs11ModuleSpec.AppendLiteral("\"");
|
||||
}
|
||||
|
||||
UniqueSECMODModule userModule(SECMOD_LoadUserModule(
|
||||
const_cast<char*>(pkcs11ModuleSpec.get()), nullptr, false));
|
||||
|
@ -1643,6 +1653,19 @@ bool LoadUserModuleAt(const char* moduleName, const char* libraryName,
|
|||
return true;
|
||||
}
|
||||
|
||||
const char* kIPCClientCertsModuleName = "IPC Client Cert Module";
|
||||
|
||||
bool LoadIPCClientCertsModule(const nsCString& dir) {
|
||||
// The IPC client certs module needs to be able to call back into gecko to be
|
||||
// able to communicate with the parent process over IPC. This is achieved by
|
||||
// serializing the addresses of the relevant functions and passing them as an
|
||||
// extra string parameter that will be available when C_Initialize is called
|
||||
// on IPC client certs.
|
||||
nsPrintfCString addrs("%p,%p", DoFindObjects, DoSign);
|
||||
return LoadUserModuleAt(kIPCClientCertsModuleName, "ipcclientcerts", dir,
|
||||
addrs.get());
|
||||
}
|
||||
|
||||
const char* kOSClientCertsModuleName = "OS Client Cert Module";
|
||||
|
||||
bool LoadOSClientCertsModule(const nsCString& dir) {
|
||||
|
@ -1652,7 +1675,8 @@ bool LoadOSClientCertsModule(const nsCString& dir) {
|
|||
return false;
|
||||
}
|
||||
#endif
|
||||
return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir);
|
||||
return LoadUserModuleAt(kOSClientCertsModuleName, "osclientcerts", dir,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
bool LoadLoadableRoots(const nsCString& dir) {
|
||||
|
@ -1663,7 +1687,7 @@ bool LoadLoadableRoots(const nsCString& dir) {
|
|||
// "Root Certs" module allows us to load the correct one. See bug 1406396.
|
||||
int unusedModType;
|
||||
Unused << SECMOD_DeleteModule("Root Certs", &unusedModType);
|
||||
return LoadUserModuleAt(kRootModuleName, "nssckbi", dir);
|
||||
return LoadUserModuleAt(kRootModuleName, "nssckbi", dir, nullptr);
|
||||
}
|
||||
|
||||
nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
|
|
|
@ -76,6 +76,23 @@ bool LoadOSClientCertsModule(const nsCString& dir);
|
|||
|
||||
extern const char* kOSClientCertsModuleName;
|
||||
|
||||
/**
|
||||
* Loads the IPC client certs module.
|
||||
*
|
||||
* @param dir
|
||||
* The path to the directory containing the module. This should be the
|
||||
* same as where all of the other gecko libraries live.
|
||||
* @return true if the module was successfully loaded, false otherwise.
|
||||
*/
|
||||
bool LoadIPCClientCertsModule(const nsCString& dir);
|
||||
|
||||
extern const char* kIPCClientCertsModuleName;
|
||||
|
||||
/**
|
||||
* Unloads the loadable roots module and os client certs module, if loaded.
|
||||
*/
|
||||
void UnloadUserModules();
|
||||
|
||||
nsresult DefaultServerNicknameForCert(const CERTCertificate* cert,
|
||||
/*out*/ nsCString& nickname);
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 "IPCClientCertsChild.h"
|
||||
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
|
||||
namespace mozilla::psm {
|
||||
|
||||
IPCClientCertsChild::IPCClientCertsChild() = default;
|
||||
|
||||
} // namespace mozilla::psm
|
|
@ -0,0 +1,36 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 mozilla_psm_IPCClientCertsChild_h__
|
||||
#define mozilla_psm_IPCClientCertsChild_h__
|
||||
|
||||
#include "mozilla/psm/PIPCClientCertsChild.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class BackgroundChildImpl;
|
||||
} // namespace ipc
|
||||
|
||||
namespace psm {
|
||||
|
||||
class IPCClientCertsChild final : public PIPCClientCertsChild {
|
||||
friend class mozilla::ipc::BackgroundChildImpl;
|
||||
|
||||
public:
|
||||
IPCClientCertsChild();
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IPCClientCertsChild);
|
||||
|
||||
private:
|
||||
~IPCClientCertsChild() = default;
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 "IPCClientCertsParent.h"
|
||||
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
|
||||
namespace mozilla::psm {
|
||||
|
||||
IPCClientCertsParent::IPCClientCertsParent() = default;
|
||||
|
||||
// When the IPC client certs module needs to find certificate and key objects
|
||||
// in the socket process, it will cause this function to be called in the
|
||||
// parent process. The parent process needs to find all certificates with
|
||||
// private keys (because these are potential client certificates).
|
||||
mozilla::ipc::IPCResult IPCClientCertsParent::RecvFindObjects(
|
||||
nsTArray<IPCClientCertObject>* aObjects) {
|
||||
UniqueCERTCertList certList(psm::FindClientCertificatesWithPrivateKeys());
|
||||
if (!certList) {
|
||||
return IPC_OK();
|
||||
}
|
||||
CERTCertListNode* n = CERT_LIST_HEAD(certList);
|
||||
while (!CERT_LIST_END(n, certList)) {
|
||||
nsTArray<uint8_t> certDER(n->cert->derCert.data, n->cert->derCert.len);
|
||||
uint32_t slotType;
|
||||
UniqueSECKEYPublicKey pubkey(CERT_ExtractPublicKey(n->cert));
|
||||
if (!pubkey) {
|
||||
return IPC_OK();
|
||||
}
|
||||
switch (SECKEY_GetPublicKeyType(pubkey.get())) {
|
||||
case rsaKey:
|
||||
case rsaPssKey: {
|
||||
slotType = PK11_DoesMechanism(n->cert->slot, CKM_RSA_PKCS_PSS)
|
||||
? kIPCClientCertsSlotTypeModern
|
||||
: kIPCClientCertsSlotTypeLegacy;
|
||||
nsTArray<uint8_t> modulus(pubkey->u.rsa.modulus.data,
|
||||
pubkey->u.rsa.modulus.len);
|
||||
RSAKey rsakey(modulus, certDER, slotType);
|
||||
aObjects->AppendElement(std::move(rsakey));
|
||||
break;
|
||||
}
|
||||
case ecKey: {
|
||||
slotType = kIPCClientCertsSlotTypeModern;
|
||||
nsTArray<uint8_t> params(pubkey->u.ec.DEREncodedParams.data,
|
||||
pubkey->u.ec.DEREncodedParams.len);
|
||||
ECKey eckey(params, certDER, slotType);
|
||||
aObjects->AppendElement(std::move(eckey));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
n = CERT_LIST_NEXT(n);
|
||||
continue;
|
||||
}
|
||||
Certificate cert(certDER, slotType);
|
||||
aObjects->AppendElement(std::move(cert));
|
||||
|
||||
n = CERT_LIST_NEXT(n);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
// When the IPC client certs module needs to sign data using a key managed by
|
||||
// the parent process, it will cause this function to be called in the parent
|
||||
// process. The parent process needs to find the key corresponding to the given
|
||||
// certificate and sign the given data with the given parameters.
|
||||
mozilla::ipc::IPCResult IPCClientCertsParent::RecvSign(ByteArray aCert,
|
||||
ByteArray aData,
|
||||
ByteArray aParams,
|
||||
ByteArray* aSignature) {
|
||||
SECItem certItem = {siBuffer, const_cast<uint8_t*>(aCert.data().Elements()),
|
||||
static_cast<unsigned int>(aCert.data().Length())};
|
||||
aSignature->data().Clear();
|
||||
|
||||
UniqueCERTCertificate cert(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
|
||||
if (!cert) {
|
||||
return IPC_OK();
|
||||
}
|
||||
UniqueSECKEYPrivateKey key(PK11_FindKeyByAnyCert(cert.get(), nullptr));
|
||||
if (!key) {
|
||||
return IPC_OK();
|
||||
}
|
||||
SECItem params = {siBuffer, aParams.data().Elements(),
|
||||
static_cast<unsigned int>(aParams.data().Length())};
|
||||
SECItem* paramsPtr = aParams.data().Length() > 0 ? ¶ms : nullptr;
|
||||
CK_MECHANISM_TYPE mechanism;
|
||||
switch (key->keyType) {
|
||||
case ecKey:
|
||||
mechanism = CKM_ECDSA;
|
||||
break;
|
||||
case rsaKey:
|
||||
mechanism = aParams.data().Length() > 0 ? CKM_RSA_PKCS_PSS : CKM_RSA_PKCS;
|
||||
break;
|
||||
default:
|
||||
return IPC_OK();
|
||||
}
|
||||
uint32_t len = PK11_SignatureLen(key.get());
|
||||
UniqueSECItem sig(::SECITEM_AllocItem(nullptr, nullptr, len));
|
||||
SECItem hash = {siBuffer, aData.data().Elements(),
|
||||
static_cast<unsigned int>(aData.data().Length())};
|
||||
SECStatus srv =
|
||||
PK11_SignWithMechanism(key.get(), mechanism, paramsPtr, sig.get(), &hash);
|
||||
if (srv != SECSuccess) {
|
||||
return IPC_OK();
|
||||
}
|
||||
aSignature->data().AppendElements(sig->data, sig->len);
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace mozilla::psm
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 mozilla_psm_IPCClientCertsParent_h__
|
||||
#define mozilla_psm_IPCClientCertsParent_h__
|
||||
|
||||
#include "mozilla/psm/PIPCClientCertsParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class BackgroundParentImpl;
|
||||
} // namespace ipc
|
||||
|
||||
namespace psm {
|
||||
|
||||
class IPCClientCertsParent final : public PIPCClientCertsParent {
|
||||
friend class mozilla::ipc::BackgroundParentImpl;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IPCClientCertsParent)
|
||||
|
||||
mozilla::ipc::IPCResult RecvFindObjects(
|
||||
nsTArray<IPCClientCertObject>* aObjects);
|
||||
mozilla::ipc::IPCResult RecvSign(ByteArray aCert, ByteArray aData,
|
||||
ByteArray aParams, ByteArray* aSignature);
|
||||
|
||||
private:
|
||||
IPCClientCertsParent();
|
||||
~IPCClientCertsParent() = default;
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -0,0 +1,35 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 protocol PBackground;
|
||||
|
||||
include PSMIPCTypes;
|
||||
|
||||
using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
[RefCounted] sync protocol PIPCClientCerts
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
parent:
|
||||
// Called from the socket process to the parent process to find client
|
||||
// certificates and associated keys.
|
||||
sync FindObjects() returns (IPCClientCertObject[] aObjects);
|
||||
|
||||
// Called from the socket process to the parent process to sign the given
|
||||
// data with the given parameters using the key associated with the given
|
||||
// certificate. Used when a TLS server requests a client authentication
|
||||
// certificate.
|
||||
sync Sign(ByteArray aCert, ByteArray aData, ByteArray aParams)
|
||||
returns (ByteArray aSignature);
|
||||
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
|
@ -1,161 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 "CTVerifyResult.h"
|
||||
#include "PSMIPCCommon.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
SECItem* WrapPrivateKeyInfoWithEmptyPassword(
|
||||
SECKEYPrivateKey* pk) /* encrypt this private key */
|
||||
{
|
||||
if (!pk) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// For private keys, NSS cannot export anything other than RSA, but we need EC
|
||||
// also. So, we use the private key encryption function to serialize instead,
|
||||
// using a hard-coded dummy password; this is not intended to provide any
|
||||
// additional security, it just works around a limitation in NSS.
|
||||
SECItem dummyPassword = {siBuffer, nullptr, 0};
|
||||
UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
|
||||
slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr));
|
||||
|
||||
if (!epki) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SEC_ASN1EncodeItem(
|
||||
nullptr, nullptr, epki.get(),
|
||||
NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false));
|
||||
}
|
||||
|
||||
SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
|
||||
SECItem* derPKI, const UniqueCERTCertificate& aCert,
|
||||
SECKEYPrivateKey** privk) {
|
||||
if (!derPKI || !aCert || !privk) {
|
||||
PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get()));
|
||||
// This is a pointer to data inside publicKey
|
||||
SECItem* publicValue = nullptr;
|
||||
switch (publicKey->keyType) {
|
||||
case dsaKey:
|
||||
publicValue = &publicKey->u.dsa.publicValue;
|
||||
break;
|
||||
case dhKey:
|
||||
publicValue = &publicKey->u.dh.publicValue;
|
||||
break;
|
||||
case rsaKey:
|
||||
publicValue = &publicKey->u.rsa.modulus;
|
||||
break;
|
||||
case ecKey:
|
||||
publicValue = &publicKey->u.ec.publicValue;
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT(false);
|
||||
PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0);
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||
if (!slot) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
|
||||
if (!temparena) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
SECKEYEncryptedPrivateKeyInfo* epki =
|
||||
PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo);
|
||||
if (!epki) {
|
||||
return SECFailure;
|
||||
}
|
||||
|
||||
SECStatus rv = SEC_ASN1DecodeItem(
|
||||
temparena.get(), epki,
|
||||
NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI);
|
||||
if (rv != SECSuccess) {
|
||||
// If SEC_ASN1DecodeItem fails, we cannot assume anything about the
|
||||
// validity of the data in epki. The best we can do is free the arena
|
||||
// and return.
|
||||
return rv;
|
||||
}
|
||||
|
||||
// See comment in WrapPrivateKeyInfoWithEmptyPassword about this
|
||||
// dummy password stuff.
|
||||
SECItem dummyPassword = {siBuffer, nullptr, 0};
|
||||
return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
|
||||
slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false,
|
||||
publicKey->keyType, KU_ALL, privk, nullptr);
|
||||
}
|
||||
|
||||
void SerializeClientCertAndKey(const UniqueCERTCertificate& aCert,
|
||||
const UniqueSECKEYPrivateKey& aKey,
|
||||
ByteArray& aOutSerializedCert,
|
||||
ByteArray& aOutSerializedKey) {
|
||||
if (!aCert || !aKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniqueSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(aKey.get()));
|
||||
if (!derPki) {
|
||||
return;
|
||||
}
|
||||
|
||||
aOutSerializedCert.data().AppendElements(aCert->derCert.data,
|
||||
aCert->derCert.len);
|
||||
aOutSerializedKey.data().AppendElements(derPki->data, derPki->len);
|
||||
}
|
||||
|
||||
void DeserializeClientCertAndKey(const ByteArray& aSerializedCert,
|
||||
const ByteArray& aSerializedKey,
|
||||
UniqueCERTCertificate& aOutCert,
|
||||
UniqueSECKEYPrivateKey& aOutKey) {
|
||||
if (aSerializedCert.data().IsEmpty() || aSerializedKey.data().IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SECItem item = {siBuffer,
|
||||
const_cast<uint8_t*>(aSerializedCert.data().Elements()),
|
||||
static_cast<unsigned int>(aSerializedCert.data().Length())};
|
||||
|
||||
UniqueCERTCertificate cert(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &item, nullptr, false, true));
|
||||
|
||||
if (!cert) {
|
||||
return;
|
||||
}
|
||||
|
||||
SECItem derPKI = {siBuffer,
|
||||
const_cast<uint8_t*>(aSerializedKey.data().Elements()),
|
||||
static_cast<unsigned int>(aSerializedKey.data().Length())};
|
||||
|
||||
SECKEYPrivateKey* privateKey;
|
||||
if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) !=
|
||||
SECSuccess) {
|
||||
MOZ_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
aOutCert = std::move(cert);
|
||||
aOutKey.reset(privateKey);
|
||||
}
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
|
@ -1,34 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||
|
||||
/* 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 PSMIPCCommon_h__
|
||||
#define PSMIPCCommon_h__
|
||||
|
||||
#include "mozilla/psm/PSMIPCTypes.h"
|
||||
#include "seccomon.h"
|
||||
#include "ScopedNSSTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace psm {
|
||||
|
||||
SECItem* WrapPrivateKeyInfoWithEmptyPassword(SECKEYPrivateKey* pk);
|
||||
SECStatus UnwrapPrivateKeyInfoWithEmptyPassword(
|
||||
SECItem* derPKI, const UniqueCERTCertificate& aCert,
|
||||
SECKEYPrivateKey** privk);
|
||||
void SerializeClientCertAndKey(const UniqueCERTCertificate& aCert,
|
||||
const UniqueSECKEYPrivateKey& aKey,
|
||||
ByteArray& aOutSerializedCert,
|
||||
ByteArray& aOutSerializedKey);
|
||||
void DeserializeClientCertAndKey(const ByteArray& aSerializedCert,
|
||||
const ByteArray& aSerializedKey,
|
||||
UniqueCERTCertificate& aOutCert,
|
||||
UniqueSECKEYPrivateKey& aOutKey);
|
||||
|
||||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // PSMIPCCommon_h__
|
|
@ -12,6 +12,36 @@ struct ByteArray{
|
|||
uint8_t[] data;
|
||||
};
|
||||
|
||||
// For ECKey, RSAKey, and Certificate, slotType indicates which slot this object
|
||||
// should exist on:
|
||||
// 1: modern (supports EC, RSA-PSS)
|
||||
// 2: legacy (only supports RSA PKCS#1v1.5)
|
||||
|
||||
struct ECKey{
|
||||
uint8_t[] params; // the EC point representing this key
|
||||
uint8_t[] cert; // the encoded certificate containing this key
|
||||
uint32_t slotType;
|
||||
};
|
||||
|
||||
struct RSAKey{
|
||||
uint8_t[] modulus; // the modulus of this RSA key
|
||||
uint8_t[] cert; // the encoded certificate containing this key
|
||||
uint32_t slotType;
|
||||
};
|
||||
|
||||
struct Certificate{
|
||||
uint8_t[] der; // the encoding of this certificate
|
||||
uint32_t slotType;
|
||||
};
|
||||
|
||||
// Helper type for sending keys and certificates over IPC for use by IPC client
|
||||
// certs.
|
||||
union IPCClientCertObject{
|
||||
ECKey;
|
||||
RSAKey;
|
||||
Certificate;
|
||||
};
|
||||
|
||||
struct DelegatedCredentialInfoArg {
|
||||
uint32_t scheme;
|
||||
uint32_t authKeyBits;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
[package]
|
||||
name = "ipcclientcerts-static"
|
||||
version = "0.1.0"
|
||||
authors = ["Dana Keeler <dkeeler@mozilla.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
byteorder = "1.3"
|
||||
env_logger = {version = "0.8", default-features = false } # disable `regex` to reduce code size
|
||||
lazy_static = "1"
|
||||
log = "0.4"
|
||||
pkcs11 = "0.4"
|
||||
rsclientcerts = { path = "../rsclientcerts" }
|
||||
sha2 = "0.8"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
|
@ -0,0 +1 @@
|
|||
C_GetFunctionList
|
|
@ -0,0 +1,22 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
USE_LIBS += ["ipcclientcerts-static"]
|
||||
|
||||
SOURCES += [
|
||||
"stub.cpp",
|
||||
]
|
||||
|
||||
if CONFIG["OS_ARCH"] == "WINNT":
|
||||
OS_LIBS += [
|
||||
"userenv",
|
||||
"ws2_32",
|
||||
]
|
||||
|
||||
SharedLibrary("ipcclientcerts")
|
||||
|
||||
NoVisibilityFlags()
|
||||
SYMBOLS_FILE = "ipcclientcerts.symbols"
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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 "pkcs11.h"
|
||||
|
||||
// The build system builds the rust library ipcclientcerts as a static library
|
||||
// called ipcclientcerts_static. On macOS and Windows, that static library can
|
||||
// be linked with an empty file and turned into a shared library with the
|
||||
// function C_GetFunctionList exposed. This allows that shared library to be
|
||||
// used as a PKCS#11 module (see osclientcerts).
|
||||
// Unfortunately, on Linux, exposing the C_GetFunctionList in the static
|
||||
// library doesn't work for some unknown reason. As a workaround, this file
|
||||
// declares its own C_GetFunctionList that can be exposed in the shared
|
||||
// library. It then calls the function IPCCC_GetFunctionList exposed
|
||||
// (internally to the linkage in question) by ipcclientcerts. This enables
|
||||
// the build system to ultimately turn ipcclientcerts into a shared library
|
||||
// that exposes a C_GetFunctionList function, meaning it can be used as a
|
||||
// PKCS#11 module.
|
||||
|
||||
extern "C" {
|
||||
|
||||
CK_RV IPCCC_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList);
|
||||
|
||||
CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) {
|
||||
return IPCCC_GetFunctionList(ppFunctionList);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
DIRS += ["dynamic-library"]
|
||||
|
||||
RustLibrary("ipcclientcerts-static")
|
|
@ -0,0 +1,380 @@
|
|||
/* -*- Mode: rust; rust-indent-offset: 4 -*- */
|
||||
/* 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/. */
|
||||
|
||||
use pkcs11::types::*;
|
||||
use rsclientcerts::error::{Error, ErrorType};
|
||||
use rsclientcerts::manager::{ClientCertsBackend, CryptokiObject, Sign, SlotType};
|
||||
use rsclientcerts::util::*;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::ffi::c_void;
|
||||
use std::thread;
|
||||
|
||||
use crate::FindObjectsFunction;
|
||||
use crate::SignFunction;
|
||||
|
||||
pub struct Cert {
|
||||
class: Vec<u8>,
|
||||
token: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
label: Vec<u8>,
|
||||
value: Vec<u8>,
|
||||
issuer: Vec<u8>,
|
||||
serial_number: Vec<u8>,
|
||||
subject: Vec<u8>,
|
||||
slot_type: SlotType,
|
||||
}
|
||||
|
||||
impl Cert {
|
||||
fn new(der: &[u8], slot_type: SlotType) -> Result<Cert, Error> {
|
||||
let (serial_number, issuer, subject) = read_encoded_certificate_identifiers(der)?;
|
||||
let id = Sha256::digest(der).to_vec();
|
||||
Ok(Cert {
|
||||
class: serialize_uint(CKO_CERTIFICATE)?,
|
||||
token: serialize_uint(CK_TRUE)?,
|
||||
id,
|
||||
label: b"IPC certificate".to_vec(),
|
||||
value: der.to_vec(),
|
||||
issuer,
|
||||
serial_number,
|
||||
subject,
|
||||
slot_type,
|
||||
})
|
||||
}
|
||||
|
||||
fn class(&self) -> &[u8] {
|
||||
&self.class
|
||||
}
|
||||
|
||||
fn token(&self) -> &[u8] {
|
||||
&self.token
|
||||
}
|
||||
|
||||
fn id(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn label(&self) -> &[u8] {
|
||||
&self.label
|
||||
}
|
||||
|
||||
fn value(&self) -> &[u8] {
|
||||
&self.value
|
||||
}
|
||||
|
||||
fn issuer(&self) -> &[u8] {
|
||||
&self.issuer
|
||||
}
|
||||
|
||||
fn serial_number(&self) -> &[u8] {
|
||||
&self.serial_number
|
||||
}
|
||||
|
||||
fn subject(&self) -> &[u8] {
|
||||
&self.subject
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptokiObject for Cert {
|
||||
fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
|
||||
if self.slot_type != slot_type {
|
||||
return false;
|
||||
}
|
||||
for (attr_type, attr_value) in attrs {
|
||||
let comparison = match *attr_type {
|
||||
CKA_CLASS => self.class(),
|
||||
CKA_TOKEN => self.token(),
|
||||
CKA_LABEL => self.label(),
|
||||
CKA_ID => self.id(),
|
||||
CKA_VALUE => self.value(),
|
||||
CKA_ISSUER => self.issuer(),
|
||||
CKA_SERIAL_NUMBER => self.serial_number(),
|
||||
CKA_SUBJECT => self.subject(),
|
||||
_ => return false,
|
||||
};
|
||||
if attr_value.as_slice() != comparison {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
|
||||
let result = match attribute {
|
||||
CKA_CLASS => self.class(),
|
||||
CKA_TOKEN => self.token(),
|
||||
CKA_LABEL => self.label(),
|
||||
CKA_ID => self.id(),
|
||||
CKA_VALUE => self.value(),
|
||||
CKA_ISSUER => self.issuer(),
|
||||
CKA_SERIAL_NUMBER => self.serial_number(),
|
||||
CKA_SUBJECT => self.subject(),
|
||||
_ => return None,
|
||||
};
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Key {
|
||||
cert: Vec<u8>,
|
||||
class: Vec<u8>,
|
||||
token: Vec<u8>,
|
||||
id: Vec<u8>,
|
||||
private: Vec<u8>,
|
||||
key_type: Vec<u8>,
|
||||
modulus: Option<Vec<u8>>,
|
||||
ec_params: Option<Vec<u8>>,
|
||||
slot_type: SlotType,
|
||||
sign: SignFunction,
|
||||
}
|
||||
|
||||
impl Key {
|
||||
fn new(
|
||||
modulus: Option<&[u8]>,
|
||||
ec_params: Option<&[u8]>,
|
||||
cert: &[u8],
|
||||
slot_type: SlotType,
|
||||
sign: SignFunction,
|
||||
) -> Result<Key, Error> {
|
||||
let id = Sha256::digest(cert).to_vec();
|
||||
let key_type = if modulus.is_some() { CKK_RSA } else { CKK_EC };
|
||||
Ok(Key {
|
||||
cert: cert.to_vec(),
|
||||
class: serialize_uint(CKO_PRIVATE_KEY)?,
|
||||
token: serialize_uint(CK_TRUE)?,
|
||||
id,
|
||||
private: serialize_uint(CK_TRUE)?,
|
||||
key_type: serialize_uint(key_type)?,
|
||||
modulus: modulus.map(|b| b.to_vec()),
|
||||
ec_params: ec_params.map(|b| b.to_vec()),
|
||||
slot_type,
|
||||
sign,
|
||||
})
|
||||
}
|
||||
|
||||
fn class(&self) -> &[u8] {
|
||||
&self.class
|
||||
}
|
||||
|
||||
fn token(&self) -> &[u8] {
|
||||
&self.token
|
||||
}
|
||||
|
||||
pub fn id(&self) -> &[u8] {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn private(&self) -> &[u8] {
|
||||
&self.private
|
||||
}
|
||||
|
||||
fn key_type(&self) -> &[u8] {
|
||||
&self.key_type
|
||||
}
|
||||
|
||||
fn modulus(&self) -> Option<&[u8]> {
|
||||
match &self.modulus {
|
||||
Some(modulus) => Some(modulus.as_slice()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn ec_params(&self) -> Option<&[u8]> {
|
||||
match &self.ec_params {
|
||||
Some(ec_params) => Some(ec_params.as_slice()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CryptokiObject for Key {
|
||||
fn matches(&self, slot_type: SlotType, attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)]) -> bool {
|
||||
if self.slot_type != slot_type {
|
||||
return false;
|
||||
}
|
||||
for (attr_type, attr_value) in attrs {
|
||||
let comparison = match *attr_type {
|
||||
CKA_CLASS => self.class(),
|
||||
CKA_TOKEN => self.token(),
|
||||
CKA_ID => self.id(),
|
||||
CKA_PRIVATE => self.private(),
|
||||
CKA_KEY_TYPE => self.key_type(),
|
||||
CKA_MODULUS => {
|
||||
if let Some(modulus) = self.modulus() {
|
||||
modulus
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
CKA_EC_PARAMS => {
|
||||
if let Some(ec_params) = self.ec_params() {
|
||||
ec_params
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
_ => return false,
|
||||
};
|
||||
if attr_value.as_slice() != comparison {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn get_attribute(&self, attribute: CK_ATTRIBUTE_TYPE) -> Option<&[u8]> {
|
||||
match attribute {
|
||||
CKA_CLASS => Some(self.class()),
|
||||
CKA_TOKEN => Some(self.token()),
|
||||
CKA_ID => Some(self.id()),
|
||||
CKA_PRIVATE => Some(self.private()),
|
||||
CKA_KEY_TYPE => Some(self.key_type()),
|
||||
CKA_MODULUS => self.modulus(),
|
||||
CKA_EC_PARAMS => self.ec_params(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sign for Key {
|
||||
fn get_signature_length(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<usize, Error> {
|
||||
// Unfortunately we don't have a way of getting the length of a signature without creating
|
||||
// one.
|
||||
let dummy_signature_bytes = self.sign(data, params)?;
|
||||
Ok(dummy_signature_bytes.len())
|
||||
}
|
||||
|
||||
fn sign(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
let mut signature = Vec::new();
|
||||
let (params_len, params) = match params {
|
||||
Some(params) => (
|
||||
std::mem::size_of::<CK_RSA_PKCS_PSS_PARAMS>(),
|
||||
params as *const _ as *const u8,
|
||||
),
|
||||
None => (0, std::ptr::null()),
|
||||
};
|
||||
(self.sign)(
|
||||
self.cert.len(),
|
||||
self.cert.as_ptr(),
|
||||
data.len(),
|
||||
data.as_ptr(),
|
||||
params_len,
|
||||
params,
|
||||
Some(sign_callback),
|
||||
&mut signature as *mut _ as *mut c_void,
|
||||
);
|
||||
if signature.len() > 0 {
|
||||
Ok(signature)
|
||||
} else {
|
||||
Err(error_here!(ErrorType::LibraryFailure))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn sign_callback(data_len: usize, data: *const u8, ctx: *mut c_void) {
|
||||
let signature: &mut Vec<u8> = std::mem::transmute(ctx);
|
||||
signature.clear();
|
||||
signature.extend_from_slice(std::slice::from_raw_parts(data, data_len));
|
||||
}
|
||||
|
||||
unsafe extern "C" fn find_objects_callback(
|
||||
typ: u8,
|
||||
data_len: usize,
|
||||
data: *const u8,
|
||||
extra_len: usize,
|
||||
extra: *const u8,
|
||||
slot_type: u32,
|
||||
ctx: *mut c_void,
|
||||
) {
|
||||
let data = std::slice::from_raw_parts(data, data_len);
|
||||
let extra = std::slice::from_raw_parts(extra, extra_len);
|
||||
let slot_type = match slot_type {
|
||||
1 => SlotType::Modern,
|
||||
2 => SlotType::Legacy,
|
||||
_ => return,
|
||||
};
|
||||
let find_objects_context: &mut FindObjectsContext = std::mem::transmute(ctx);
|
||||
match typ {
|
||||
1 => match Cert::new(data, slot_type) {
|
||||
Ok(cert) => find_objects_context.certs.push(cert),
|
||||
Err(e) => {
|
||||
log_with_thread_id!(error, "find_objects_callback: couldn't create Cert: {}", e)
|
||||
}
|
||||
},
|
||||
2 => match Key::new(
|
||||
Some(data),
|
||||
None,
|
||||
extra,
|
||||
slot_type,
|
||||
find_objects_context.sign,
|
||||
) {
|
||||
Ok(key) => find_objects_context.keys.push(key),
|
||||
Err(e) => {
|
||||
log_with_thread_id!(error, "find_objects_callback: couldn't create Key: {}", e)
|
||||
}
|
||||
},
|
||||
3 => match Key::new(
|
||||
None,
|
||||
Some(data),
|
||||
extra,
|
||||
slot_type,
|
||||
find_objects_context.sign,
|
||||
) {
|
||||
Ok(key) => find_objects_context.keys.push(key),
|
||||
Err(e) => {
|
||||
log_with_thread_id!(error, "find_objects_callback: couldn't create Key: {}", e)
|
||||
}
|
||||
},
|
||||
_ => log_with_thread_id!(error, "find_objects_callback: unknown type {}", typ),
|
||||
}
|
||||
}
|
||||
|
||||
struct FindObjectsContext {
|
||||
certs: Vec<Cert>,
|
||||
keys: Vec<Key>,
|
||||
sign: SignFunction,
|
||||
}
|
||||
|
||||
impl FindObjectsContext {
|
||||
fn new(sign: SignFunction) -> FindObjectsContext {
|
||||
FindObjectsContext {
|
||||
certs: Vec::new(),
|
||||
keys: Vec::new(),
|
||||
sign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Backend {
|
||||
find_objects: FindObjectsFunction,
|
||||
sign: SignFunction,
|
||||
}
|
||||
|
||||
impl Backend {
|
||||
pub fn new(find_objects: FindObjectsFunction, sign: SignFunction) -> Backend {
|
||||
Backend { find_objects, sign }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientCertsBackend for Backend {
|
||||
type Cert = Cert;
|
||||
type Key = Key;
|
||||
|
||||
fn find_objects(&self) -> Result<(Vec<Cert>, Vec<Key>), Error> {
|
||||
let mut find_objects_context = FindObjectsContext::new(self.sign);
|
||||
(self.find_objects)(
|
||||
Some(find_objects_callback),
|
||||
&mut find_objects_context as *mut _ as *mut c_void,
|
||||
);
|
||||
Ok((find_objects_context.certs, find_objects_context.keys))
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -12,6 +12,8 @@ if (CONFIG["OS_ARCH"] == "WINNT" and CONFIG["CPU_ARCH"] != "aarch64") or CONFIG[
|
|||
] == "Darwin":
|
||||
DIRS += ["osclientcerts"]
|
||||
|
||||
DIRS += ["ipcclientcerts"]
|
||||
|
||||
TEST_DIRS += ["tests"]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
|
@ -93,6 +95,8 @@ EXPORTS.mozilla += [
|
|||
]
|
||||
|
||||
EXPORTS.mozilla.psm += [
|
||||
"IPCClientCertsChild.h",
|
||||
"IPCClientCertsParent.h",
|
||||
"PSMIPCCommon.h",
|
||||
"TransportSecurityInfo.h",
|
||||
"VerifySSLServerCertChild.h",
|
||||
|
@ -111,6 +115,8 @@ UNIFIED_SOURCES += [
|
|||
"CSTrustDomain.cpp",
|
||||
"DataStorage.cpp",
|
||||
"EnterpriseRoots.cpp",
|
||||
"IPCClientCertsChild.cpp",
|
||||
"IPCClientCertsParent.cpp",
|
||||
"LocalCertService.cpp",
|
||||
"nsCertOverrideService.cpp",
|
||||
"nsClientAuthRemember.cpp",
|
||||
|
@ -189,6 +195,7 @@ if CONFIG["OS_ARCH"] == "WINNT":
|
|||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
"PIPCClientCerts.ipdl",
|
||||
"PSMIPCTypes.ipdlh",
|
||||
"PVerifySSLServerCert.ipdl",
|
||||
]
|
||||
|
@ -209,6 +216,7 @@ LOCAL_INCLUDES += [
|
|||
"/dom/crypto",
|
||||
"/netwerk/base",
|
||||
"/security/certverifier",
|
||||
"/xpcom/build",
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "mozilla/ArrayUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include "nsNSSComponent.h"
|
||||
|
||||
#include "BinaryPath.h"
|
||||
#include "CryptoTask.h"
|
||||
#include "EnterpriseRoots.h"
|
||||
#include "ExtendedValidation.h"
|
||||
|
@ -21,6 +22,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/EndianUtils.h"
|
||||
#include "mozilla/FilePreferences.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/ProfilerLabels.h"
|
||||
|
@ -100,6 +102,38 @@ int nsNSSComponent::mInstanceCount = 0;
|
|||
// Forward declaration.
|
||||
nsresult CommonInit();
|
||||
|
||||
// Take an nsIFile and get a c-string representation of the location of that
|
||||
// file (encapsulated in an nsACString). This function handles a
|
||||
// platform-specific issue on Windows where Unicode characters that cannot be
|
||||
// mapped to the system's codepage will be dropped, resulting in a c-string
|
||||
// that is useless to describe the location of the file in question.
|
||||
// This operation is generally to be avoided, except when interacting with
|
||||
// third-party or legacy libraries that cannot handle `nsIFile`s (such as NSS).
|
||||
nsresult FileToCString(const nsCOMPtr<nsIFile>& file, nsACString& result) {
|
||||
#ifdef XP_WIN
|
||||
// Native path will drop Unicode characters that cannot be mapped to system's
|
||||
// codepage, using short (canonical) path as workaround.
|
||||
nsCOMPtr<nsILocalFileWin> fileWin = do_QueryInterface(file);
|
||||
if (!fileWin) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get nsILocalFileWin"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return fileWin->GetNativeCanonicalPath(result);
|
||||
#else
|
||||
return file->GetNativePath(result);
|
||||
#endif
|
||||
}
|
||||
|
||||
void TruncateFromLastDirectorySeparator(nsCString& path) {
|
||||
static const nsAutoCString kSeparatorString(
|
||||
mozilla::FilePreferences::kPathSeparator);
|
||||
int32_t index = path.RFind(kSeparatorString);
|
||||
if (index == kNotFound) {
|
||||
return;
|
||||
}
|
||||
path.Truncate(index);
|
||||
}
|
||||
|
||||
// This function can be called from chrome or content or socket processes
|
||||
// to ensure that NSS is initialized.
|
||||
bool EnsureNSSInitializedChromeOrContent() {
|
||||
|
@ -149,6 +183,36 @@ bool EnsureNSSInitializedChromeOrContent() {
|
|||
if (NS_FAILED(CommonInit())) {
|
||||
return false;
|
||||
}
|
||||
// This returns the path to the binary currently running, which in most
|
||||
// cases is "plugin-container".
|
||||
UniqueFreePtr<char> pluginContainerPath(BinaryPath::Get());
|
||||
if (!pluginContainerPath) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("failed to get get plugin-container path"));
|
||||
return false;
|
||||
}
|
||||
nsAutoCString ipcClientCertsDirString(pluginContainerPath.get());
|
||||
// On most platforms, ipcclientcerts is in the same directory as
|
||||
// plugin-container. To obtain the path to that directory, truncate from
|
||||
// the last directory separator.
|
||||
// On macOS, plugin-container is in
|
||||
// Firefox.app/Contents/MacOS/plugin-container.app/Contents/MacOS/,
|
||||
// whereas ipcclientcerts is in Firefox.app/Contents/MacOS/. Consequently,
|
||||
// this truncation from the last directory separator has to happen 4 times
|
||||
// total. Normally this would be done using nsIFile APIs, but due to when
|
||||
// this is initialized in the socket process, those aren't available.
|
||||
TruncateFromLastDirectorySeparator(ipcClientCertsDirString);
|
||||
#ifdef XP_MACOSX
|
||||
TruncateFromLastDirectorySeparator(ipcClientCertsDirString);
|
||||
TruncateFromLastDirectorySeparator(ipcClientCertsDirString);
|
||||
TruncateFromLastDirectorySeparator(ipcClientCertsDirString);
|
||||
#endif
|
||||
if (!LoadIPCClientCertsModule(ipcClientCertsDirString)) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("failed to load ipcclientcerts from '%s'",
|
||||
ipcClientCertsDirString.get()));
|
||||
return false;
|
||||
}
|
||||
initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
@ -750,18 +814,7 @@ static nsresult GetDirectoryPath(const char* directoryKey, nsCString& result) {
|
|||
("could not get '%s' from directory service", directoryKey));
|
||||
return rv;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
// Native path will drop Unicode characters that cannot be mapped to system's
|
||||
// codepage, using short (canonical) path as workaround.
|
||||
nsCOMPtr<nsILocalFileWin> directoryWin = do_QueryInterface(directory);
|
||||
if (!directoryWin) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get nsILocalFileWin"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return directoryWin->GetNativeCanonicalPath(result);
|
||||
#else
|
||||
return directory->GetNativePath(result);
|
||||
#endif
|
||||
return FileToCString(directory, result);
|
||||
}
|
||||
|
||||
class BackgroundLoadOSClientCertsModuleTask final : public CryptoTask {
|
||||
|
@ -841,8 +894,6 @@ NS_IMETHODIMP
|
|||
nsNSSComponent::HasUserCertsInstalled(bool* result) {
|
||||
NS_ENSURE_ARG_POINTER(result);
|
||||
|
||||
BlockUntilLoadableCertsLoaded();
|
||||
|
||||
// FindClientCertificatesWithPrivateKeys won't ever return an empty list, so
|
||||
// all we need to do is check if this is null or not.
|
||||
UniqueCERTCertList certList(FindClientCertificatesWithPrivateKeys());
|
||||
|
@ -941,18 +992,7 @@ static nsresult GetNSS3Directory(nsCString& result) {
|
|||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get parent directory?"));
|
||||
return rv;
|
||||
}
|
||||
#ifdef XP_WIN
|
||||
// Native path will drop Unicode characters that cannot be mapped to system's
|
||||
// codepage, using short (canonical) path as workaround.
|
||||
nsCOMPtr<nsILocalFileWin> nss3DirectoryWin = do_QueryInterface(nss3Directory);
|
||||
if (!nss3DirectoryWin) {
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("couldn't get nsILocalFileWin"));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return nss3DirectoryWin->GetNativeCanonicalPath(result);
|
||||
#else
|
||||
return nss3Directory->GetNativePath(result);
|
||||
#endif
|
||||
return FileToCString(nss3Directory, result);
|
||||
}
|
||||
|
||||
// The loadable roots library is probably in the same directory we loaded the
|
||||
|
@ -2620,6 +2660,9 @@ UniqueCERTCertList FindClientCertificatesWithPrivateKeys() {
|
|||
});
|
||||
MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
|
||||
("FindClientCertificatesWithPrivateKeys"));
|
||||
|
||||
BlockUntilLoadableCertsLoaded();
|
||||
|
||||
UniqueCERTCertList certsWithPrivateKeys(CERT_NewCertList());
|
||||
if (!certsWithPrivateKeys) {
|
||||
return nullptr;
|
||||
|
|
|
@ -25,8 +25,12 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/net/SSLTokensCache.h"
|
||||
#include "mozilla/net/SocketProcessChild.h"
|
||||
#include "mozilla/psm/IPCClientCertsChild.h"
|
||||
#include "mozilla/psm/PIPCClientCertsChild.h"
|
||||
#include "mozpkix/pkixnss.h"
|
||||
#include "mozpkix/pkixtypes.h"
|
||||
#include "mozpkix/pkixutil.h"
|
||||
|
@ -54,6 +58,7 @@
|
|||
#include "sslproto.h"
|
||||
|
||||
using namespace mozilla::psm;
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
//#define DEBUG_SSL_VERBOSE //Enable this define to get minimal
|
||||
// reports when doing SSL read/write
|
||||
|
@ -1826,8 +1831,7 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
|
|||
: mInfo(std::move(info)),
|
||||
mServerCert(serverCert.get()),
|
||||
mCollectedCANames(std::move(collectedCANames)),
|
||||
mSelectedCertificate(nullptr),
|
||||
mSelectedKey(nullptr) {}
|
||||
mSelectedCertificate(nullptr) {}
|
||||
|
||||
virtual mozilla::pkix::Result BuildChainForCertificate(
|
||||
CERTCertificate* cert, UniqueCERTCertList& builtChain);
|
||||
|
@ -1837,10 +1841,6 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
|
|||
UniqueCERTCertificate TakeSelectedCertificate() {
|
||||
return std::move(mSelectedCertificate);
|
||||
}
|
||||
// Take the private key for the selected certificate. Will be null if no
|
||||
// certificate was selected or an error prevented selecting one or getting
|
||||
// the corresponding key.
|
||||
UniqueSECKEYPrivateKey TakeSelectedKey() { return std::move(mSelectedKey); }
|
||||
|
||||
protected:
|
||||
virtual void RunOnTargetThread() override;
|
||||
|
@ -1850,7 +1850,6 @@ class ClientAuthDataRunnable : public SyncRunnableBase {
|
|||
nsTArray<nsTArray<uint8_t>> mCollectedCANames;
|
||||
nsTArray<nsTArray<uint8_t>> mEnterpriseCertificates;
|
||||
UniqueCERTCertificate mSelectedCertificate;
|
||||
UniqueSECKEYPrivateKey mSelectedKey;
|
||||
};
|
||||
|
||||
class RemoteClientAuthDataRunnable : public ClientAuthDataRunnable {
|
||||
|
@ -1946,30 +1945,42 @@ SECStatus nsNSS_SSLGetClientAuthData(void* arg, PRFileDesc* socket,
|
|||
nsTArray<nsTArray<uint8_t>> collectedCANames(CollectCANames(caNames));
|
||||
|
||||
UniqueCERTCertificate selectedCertificate;
|
||||
UniqueSECKEYPrivateKey selectedKey;
|
||||
UniqueCERTCertList builtChain;
|
||||
SECStatus status = DoGetClientAuthData(
|
||||
std::move(authInfo), serverCert, std::move(collectedCANames),
|
||||
selectedCertificate, selectedKey, builtChain);
|
||||
SECStatus status = DoGetClientAuthData(std::move(authInfo), serverCert,
|
||||
std::move(collectedCANames),
|
||||
selectedCertificate, builtChain);
|
||||
if (status != SECSuccess) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (selectedCertificate && selectedKey) {
|
||||
if (builtChain) {
|
||||
info->SetClientCertChain(std::move(builtChain));
|
||||
} else {
|
||||
MOZ_LOG(
|
||||
gPIPNSSLog, LogLevel::Debug,
|
||||
("[%p] couldn't determine chain for selected client cert", socket));
|
||||
}
|
||||
*pRetCert = selectedCertificate.release();
|
||||
*pRetKey = selectedKey.release();
|
||||
// Make joinConnection prohibit joining after we've sent a client cert
|
||||
info->SetSentClientCert();
|
||||
if (info->GetSSLVersionUsed() == nsISSLSocketControl::TLS_VERSION_1_3) {
|
||||
Telemetry::Accumulate(Telemetry::TLS_1_3_CLIENT_AUTH_USES_PHA,
|
||||
info->IsHandshakeCompleted());
|
||||
// Currently, the IPC client certs module only refreshes its view of
|
||||
// available certificates and keys if the platform issues a search for all
|
||||
// certificates or keys. In the socket process, such a search may not have
|
||||
// happened, so this ensures it has.
|
||||
if (XRE_IsSocketProcess()) {
|
||||
UniqueCERTCertList certList(FindClientCertificatesWithPrivateKeys());
|
||||
Unused << certList;
|
||||
}
|
||||
|
||||
if (selectedCertificate) {
|
||||
UniqueSECKEYPrivateKey selectedKey(
|
||||
PK11_FindKeyByAnyCert(selectedCertificate.get(), nullptr));
|
||||
if (selectedKey) {
|
||||
if (builtChain) {
|
||||
info->SetClientCertChain(std::move(builtChain));
|
||||
} else {
|
||||
MOZ_LOG(
|
||||
gPIPNSSLog, LogLevel::Debug,
|
||||
("[%p] couldn't determine chain for selected client cert", socket));
|
||||
}
|
||||
*pRetCert = selectedCertificate.release();
|
||||
*pRetKey = selectedKey.release();
|
||||
// Make joinConnection prohibit joining after we've sent a client cert
|
||||
info->SetSentClientCert();
|
||||
if (info->GetSSLVersionUsed() == nsISSLSocketControl::TLS_VERSION_1_3) {
|
||||
Telemetry::Accumulate(Telemetry::TLS_1_3_CLIENT_AUTH_USES_PHA,
|
||||
info->IsHandshakeCompleted());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1980,7 +1991,6 @@ SECStatus DoGetClientAuthData(ClientAuthInfo&& info,
|
|||
const UniqueCERTCertificate& serverCert,
|
||||
nsTArray<nsTArray<uint8_t>>&& collectedCANames,
|
||||
UniqueCERTCertificate& outCert,
|
||||
UniqueSECKEYPrivateKey& outKey,
|
||||
UniqueCERTCertList& outBuiltChain) {
|
||||
// XXX: This should be done asynchronously; see bug 696976
|
||||
RefPtr<ClientAuthDataRunnable> runnable =
|
||||
|
@ -1997,8 +2007,7 @@ SECStatus DoGetClientAuthData(ClientAuthInfo&& info,
|
|||
}
|
||||
|
||||
outCert = runnable->TakeSelectedCertificate();
|
||||
outKey = runnable->TakeSelectedKey();
|
||||
if (outCert && outKey) {
|
||||
if (outCert) {
|
||||
mozilla::pkix::Result result =
|
||||
runnable->BuildChainForCertificate(outCert.get(), outBuiltChain);
|
||||
if (result != Success) {
|
||||
|
@ -2033,7 +2042,7 @@ class ClientAuthCertNonverifyingTrustDomain final : public TrustDomain {
|
|||
|
||||
virtual mozilla::pkix::Result CheckRevocation(
|
||||
EndEntityOrCA endEntityOrCA, const CertID& certID, Time time,
|
||||
Duration validityDuration,
|
||||
mozilla::pkix::Duration validityDuration,
|
||||
/*optional*/ const Input* stapledOCSPresponse,
|
||||
/*optional*/ const Input* aiaExtension,
|
||||
/*optional*/ const Input* sctExtension) override {
|
||||
|
@ -2309,8 +2318,6 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
|||
if (NS_WARN_IF(!mSelectedCertificate)) {
|
||||
return;
|
||||
}
|
||||
mSelectedKey.reset(
|
||||
PK11_FindKeyByAnyCert(mSelectedCertificate.get(), nullptr));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2360,7 +2367,6 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
|||
} else {
|
||||
// this is a good cert to present
|
||||
mSelectedCertificate.reset(CERT_DupCertificate(node->cert));
|
||||
mSelectedKey = std::move(tmpKey);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2372,8 +2378,6 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
|||
|
||||
if (lowPrioNonrepCert) {
|
||||
mSelectedCertificate = std::move(lowPrioNonrepCert);
|
||||
mSelectedKey.reset(
|
||||
PK11_FindKeyByAnyCert(mSelectedCertificate.get(), nullptr));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -2422,8 +2426,6 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
|||
if (NS_WARN_IF(!mSelectedCertificate)) {
|
||||
return;
|
||||
}
|
||||
mSelectedKey.reset(
|
||||
PK11_FindKeyByAnyCert(mSelectedCertificate.get(), nullptr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2485,8 +2487,6 @@ void ClientAuthDataRunnable::RunOnTargetThread() {
|
|||
if (NS_WARN_IF(!mSelectedCertificate)) {
|
||||
return;
|
||||
}
|
||||
mSelectedKey.reset(
|
||||
PK11_FindKeyByAnyCert(mSelectedCertificate.get(), nullptr));
|
||||
}
|
||||
|
||||
if (cars && wantRemember) {
|
||||
|
@ -2550,18 +2550,19 @@ void RemoteClientAuthDataRunnable::RunOnTargetThread() {
|
|||
|
||||
bool succeeded = false;
|
||||
ByteArray cert;
|
||||
ByteArray key;
|
||||
mozilla::net::SocketProcessChild::GetSingleton()->SendGetTLSClientCert(
|
||||
nsCString(mInfo.HostName()), mInfo.OriginAttributesRef(), mInfo.Port(),
|
||||
mInfo.ProviderFlags(), mInfo.ProviderTlsFlags(), serverCertSerialized,
|
||||
clientCertSerialized, collectedCANames, &succeeded, &cert, &key,
|
||||
&mBuiltChain);
|
||||
clientCertSerialized, collectedCANames, &succeeded, &cert, &mBuiltChain);
|
||||
|
||||
if (!succeeded) {
|
||||
return;
|
||||
}
|
||||
|
||||
DeserializeClientCertAndKey(cert, key, mSelectedCertificate, mSelectedKey);
|
||||
SECItem certItem = {siBuffer, const_cast<uint8_t*>(cert.data().Elements()),
|
||||
static_cast<unsigned int>(cert.data().Length())};
|
||||
mSelectedCertificate.reset(CERT_NewTempCertificate(
|
||||
CERT_GetDefaultCertDB(), &certItem, nullptr, false, true));
|
||||
}
|
||||
|
||||
static PRFileDesc* nsSSLIOLayerImportFD(PRFileDesc* fd,
|
||||
|
@ -2948,3 +2949,91 @@ loser:
|
|||
}
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
already_AddRefed<IPCClientCertsChild> GetIPCClientCertsActor() {
|
||||
PBackgroundChild* backgroundActor =
|
||||
BackgroundChild::GetOrCreateForSocketParentBridgeForCurrentThread();
|
||||
if (!backgroundActor) {
|
||||
return nullptr;
|
||||
}
|
||||
RefPtr<PIPCClientCertsChild> actor =
|
||||
SingleManagedOrNull(backgroundActor->ManagedPIPCClientCertsChild());
|
||||
if (!actor) {
|
||||
actor = backgroundActor->SendPIPCClientCertsConstructor(
|
||||
new IPCClientCertsChild());
|
||||
if (!actor) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return actor.forget().downcast<IPCClientCertsChild>();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
const uint8_t kIPCClientCertsObjectTypeCert = 1;
|
||||
const uint8_t kIPCClientCertsObjectTypeRSAKey = 2;
|
||||
const uint8_t kIPCClientCertsObjectTypeECKey = 3;
|
||||
|
||||
// This function is provided to the IPC client certs module so it can cause the
|
||||
// parent process to find certificates and keys and send identifying
|
||||
// information about them over IPC.
|
||||
void DoFindObjects(FindObjectsCallback cb, void* ctx) {
|
||||
RefPtr<IPCClientCertsChild> ipcClientCertsActor(GetIPCClientCertsActor());
|
||||
if (!ipcClientCertsActor) {
|
||||
return;
|
||||
}
|
||||
nsTArray<IPCClientCertObject> objects;
|
||||
if (!ipcClientCertsActor->SendFindObjects(&objects)) {
|
||||
return;
|
||||
}
|
||||
for (const auto& object : objects) {
|
||||
switch (object.type()) {
|
||||
case IPCClientCertObject::TECKey:
|
||||
cb(kIPCClientCertsObjectTypeECKey, object.get_ECKey().params().Length(),
|
||||
object.get_ECKey().params().Elements(),
|
||||
object.get_ECKey().cert().Length(),
|
||||
object.get_ECKey().cert().Elements(), object.get_ECKey().slotType(),
|
||||
ctx);
|
||||
break;
|
||||
case IPCClientCertObject::TRSAKey:
|
||||
cb(kIPCClientCertsObjectTypeRSAKey,
|
||||
object.get_RSAKey().modulus().Length(),
|
||||
object.get_RSAKey().modulus().Elements(),
|
||||
object.get_RSAKey().cert().Length(),
|
||||
object.get_RSAKey().cert().Elements(),
|
||||
object.get_RSAKey().slotType(), ctx);
|
||||
break;
|
||||
case IPCClientCertObject::TCertificate:
|
||||
cb(kIPCClientCertsObjectTypeCert,
|
||||
object.get_Certificate().der().Length(),
|
||||
object.get_Certificate().der().Elements(), 0, nullptr,
|
||||
object.get_Certificate().slotType(), ctx);
|
||||
break;
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("unhandled IPCClientCertObject type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This function is provided to the IPC client certs module so it can cause the
|
||||
// parent process to sign the given data using the key corresponding to the
|
||||
// given certificate, using the given parameters.
|
||||
void DoSign(size_t cert_len, const uint8_t* cert, size_t data_len,
|
||||
const uint8_t* data, size_t params_len, const uint8_t* params,
|
||||
SignCallback cb, void* ctx) {
|
||||
RefPtr<IPCClientCertsChild> ipcClientCertsActor(GetIPCClientCertsActor());
|
||||
if (!ipcClientCertsActor) {
|
||||
return;
|
||||
}
|
||||
ByteArray certBytes(nsTArray<uint8_t>(cert, cert_len));
|
||||
ByteArray dataBytes(nsTArray<uint8_t>(data, data_len));
|
||||
ByteArray paramsBytes(nsTArray<uint8_t>(params, params_len));
|
||||
ByteArray signature;
|
||||
if (!ipcClientCertsActor->SendSign(certBytes, dataBytes, paramsBytes,
|
||||
&signature)) {
|
||||
return;
|
||||
}
|
||||
cb(signature.data().Length(), signature.data().Elements(), ctx);
|
||||
}
|
||||
} // extern "C"
|
||||
|
|
|
@ -27,6 +27,9 @@ class SharedSSLState;
|
|||
} // namespace psm
|
||||
} // namespace mozilla
|
||||
|
||||
const uint32_t kIPCClientCertsSlotTypeModern = 1;
|
||||
const uint32_t kIPCClientCertsSlotTypeLegacy = 2;
|
||||
|
||||
using mozilla::OriginAttributes;
|
||||
|
||||
class nsIObserver;
|
||||
|
@ -354,11 +357,22 @@ nsresult nsSSLIOLayerAddToSocket(int32_t family, const char* host, int32_t port,
|
|||
bool forSTARTTLS, uint32_t flags,
|
||||
uint32_t tlsFlags);
|
||||
|
||||
extern "C" {
|
||||
using FindObjectsCallback = void (*)(uint8_t type, size_t id_len,
|
||||
const uint8_t* id, size_t data_len,
|
||||
const uint8_t* data, uint32_t slotType,
|
||||
void* ctx);
|
||||
void DoFindObjects(FindObjectsCallback cb, void* ctx);
|
||||
using SignCallback = void (*)(size_t data_len, const uint8_t* data, void* ctx);
|
||||
void DoSign(size_t cert_len, const uint8_t* cert, size_t data_len,
|
||||
const uint8_t* data, size_t params_len, const uint8_t* params,
|
||||
SignCallback cb, void* ctx);
|
||||
}
|
||||
|
||||
SECStatus DoGetClientAuthData(ClientAuthInfo&& info,
|
||||
const mozilla::UniqueCERTCertificate& serverCert,
|
||||
nsTArray<nsTArray<uint8_t>>&& collectedCANames,
|
||||
mozilla::UniqueCERTCertificate& outCert,
|
||||
mozilla::UniqueSECKEYPrivateKey& outKey,
|
||||
mozilla::UniqueCERTCertList& outBuiltChain);
|
||||
|
||||
#endif // nsNSSIOLayer_h
|
||||
|
|
|
@ -88,8 +88,7 @@ macro_rules! manager_guard_to_manager {
|
|||
// Helper macro to prefix log messages with the current thread ID.
|
||||
macro_rules! log_with_thread_id {
|
||||
($log_level:ident, $($message:expr),*) => {
|
||||
let message = format!($($message),*);
|
||||
$log_level!("{:?} {}", thread::current().id(), message);
|
||||
$log_level!("{:?} {}", thread::current().id(), format_args!($($message),*));
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -146,7 +146,7 @@ impl ManagerProxy {
|
|||
}
|
||||
ManagerArguments::StartSearch(session, attrs) => {
|
||||
ManagerReturnValue::StartSearch(
|
||||
real_manager.start_search(session, &attrs),
|
||||
real_manager.start_search(session, attrs),
|
||||
)
|
||||
}
|
||||
ManagerArguments::Search(session, max_objects) => {
|
||||
|
@ -167,11 +167,11 @@ impl ManagerProxy {
|
|||
}
|
||||
ManagerArguments::GetSignatureLength(session, data) => {
|
||||
ManagerReturnValue::GetSignatureLength(
|
||||
real_manager.get_signature_length(session, &data),
|
||||
real_manager.get_signature_length(session, data),
|
||||
)
|
||||
}
|
||||
ManagerArguments::Sign(session, data) => {
|
||||
ManagerReturnValue::Sign(real_manager.sign(session, &data))
|
||||
ManagerReturnValue::Sign(real_manager.sign(session, data))
|
||||
}
|
||||
ManagerArguments::Stop => ManagerReturnValue::Stop(Ok(())),
|
||||
};
|
||||
|
@ -402,23 +402,23 @@ impl<B: ClientCertsBackend> Object<B> {
|
|||
|
||||
fn get_signature_length(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
data: Vec<u8>,
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<usize, Error> {
|
||||
match self {
|
||||
Object::Cert(_) => Err(error_here!(ErrorType::InvalidArgument)),
|
||||
Object::Key(key) => key.get_signature_length(data, params),
|
||||
Object::Key(key) => key.get_signature_length(&data, params),
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(
|
||||
&mut self,
|
||||
data: &[u8],
|
||||
data: Vec<u8>,
|
||||
params: &Option<CK_RSA_PKCS_PSS_PARAMS>,
|
||||
) -> Result<Vec<u8>, Error> {
|
||||
match self {
|
||||
Object::Cert(_) => Err(error_here!(ErrorType::InvalidArgument)),
|
||||
Object::Key(key) => key.sign(data, params),
|
||||
Object::Key(key) => key.sign(&data, params),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -426,7 +426,7 @@ impl<B: ClientCertsBackend> Object<B> {
|
|||
/// The `Manager` keeps track of the state of this module with respect to the PKCS #11
|
||||
/// specification. This includes what sessions are open, which search and sign operations are
|
||||
/// ongoing, and what objects are known and by what handle.
|
||||
struct Manager<B: ClientCertsBackend> {
|
||||
pub struct Manager<B: ClientCertsBackend> {
|
||||
/// A map of session to session type (modern or legacy). Sessions can be created (opened) and
|
||||
/// later closed.
|
||||
sessions: BTreeMap<CK_SESSION_HANDLE, SlotType>,
|
||||
|
@ -546,7 +546,7 @@ impl<B: ClientCertsBackend> Manager<B> {
|
|||
pub fn start_search(
|
||||
&mut self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
attrs: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)],
|
||||
attrs: Vec<(CK_ATTRIBUTE_TYPE, Vec<u8>)>,
|
||||
) -> Result<(), Error> {
|
||||
let slot_type = match self.sessions.get(&session) {
|
||||
Some(slot_type) => *slot_type,
|
||||
|
@ -554,7 +554,7 @@ impl<B: ClientCertsBackend> Manager<B> {
|
|||
};
|
||||
// If the search is for an attribute we don't support, no objects will match. This check
|
||||
// saves us having to look through all of our objects.
|
||||
for (attr, _) in attrs {
|
||||
for (attr, _) in &attrs {
|
||||
if !SUPPORTED_ATTRIBUTES.contains(attr) {
|
||||
self.searches.insert(session, Vec::new());
|
||||
return Ok(());
|
||||
|
@ -565,12 +565,12 @@ impl<B: ClientCertsBackend> Manager<B> {
|
|||
// indication for the backend to re-scan for new objects from tokens that may have been
|
||||
// inserted or certificates that may have been imported into the OS. Since these searches
|
||||
// are relatively rare, this minimizes the impact of doing these re-scans.
|
||||
if search_is_for_all_certificates_or_keys(attrs)? {
|
||||
if search_is_for_all_certificates_or_keys(&attrs)? {
|
||||
self.maybe_find_new_objects()?;
|
||||
}
|
||||
let mut handles = Vec::new();
|
||||
for (handle, object) in &self.objects {
|
||||
if object.matches(slot_type, attrs) {
|
||||
if object.matches(slot_type, &attrs) {
|
||||
handles.push(*handle);
|
||||
}
|
||||
}
|
||||
|
@ -651,7 +651,7 @@ impl<B: ClientCertsBackend> Manager<B> {
|
|||
pub fn get_signature_length(
|
||||
&mut self,
|
||||
session: CK_SESSION_HANDLE,
|
||||
data: &[u8],
|
||||
data: Vec<u8>,
|
||||
) -> Result<usize, Error> {
|
||||
let (key_handle, params) = match self.signs.get(&session) {
|
||||
Some((key_handle, params)) => (key_handle, params),
|
||||
|
@ -664,7 +664,7 @@ impl<B: ClientCertsBackend> Manager<B> {
|
|||
key.get_signature_length(data, params)
|
||||
}
|
||||
|
||||
pub fn sign(&mut self, session: CK_SESSION_HANDLE, data: &[u8]) -> Result<Vec<u8>, Error> {
|
||||
pub fn sign(&mut self, session: CK_SESSION_HANDLE, data: Vec<u8>) -> Result<Vec<u8>, Error> {
|
||||
// Performing the signature (via C_Sign, which is the only way we support) finishes the sign
|
||||
// operation, so it needs to be removed here.
|
||||
let (key_handle, params) = match self.signs.remove(&session) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче