356 lines
13 KiB
C++
356 lines
13 KiB
C++
/* 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/. */
|
|
|
|
#if !defined(NIGHTLY_BUILD) || defined(MOZ_NO_SMART_CARDS)
|
|
# error This file should only be used under NIGHTLY_BUILD and when MOZ_NO_SMART_CARDS is not defined.
|
|
#endif // !NIGHTLY_BUILD || MOZ_NO_SMART_CARDS
|
|
|
|
#include "mozilla/psm/PKCS11ModuleChild.h"
|
|
|
|
#include "NSSCertDBTrustDomain.h"
|
|
#include "PKCS11ModuleDB.h"
|
|
#include "PKCS11Token.h"
|
|
#include "ScopedNSSTypes.h"
|
|
#include "mozilla/ipc/Endpoint.h"
|
|
#include "nsDebugImpl.h"
|
|
|
|
namespace mozilla::psm {
|
|
|
|
char* RemotePKCS11PasswordPrompt(PK11SlotInfo* slot, PRBool _retry, void* ctx) {
|
|
MOZ_ASSERT(ctx);
|
|
if (!ctx) {
|
|
return nullptr;
|
|
}
|
|
PKCS11ModuleChild* pkcs11ModuleChild(static_cast<PKCS11ModuleChild*>(ctx));
|
|
if (PK11_ProtectedAuthenticationPath(slot)) {
|
|
return pkcs11ModuleChild->InitiateProtectedAuth(slot);
|
|
}
|
|
return pkcs11ModuleChild->PromptForPassword(slot);
|
|
}
|
|
|
|
nsresult ConfigureNSSInPKCS11UtilityProcess(const nsACString& profilePath) {
|
|
if (InitializeNSS(profilePath, NSSDBConfig::ReadWrite,
|
|
PKCS11DBConfig::LoadModules) != SECSuccess) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
PK11_SetPasswordFunc(RemotePKCS11PasswordPrompt);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult PKCS11ModuleChild::Start(Endpoint<PPKCS11ModuleChild>&& aEndpoint,
|
|
nsCString&& aProfilePath) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(!mTaskQueue);
|
|
MOZ_ASSERT(!mAuthTaskQueue);
|
|
|
|
nsDebugImpl::SetMultiprocessMode("PKCS11ModuleChild");
|
|
|
|
nsresult rv = NS_CreateBackgroundTaskQueue("PKCS11ModuleChild::IPC",
|
|
getter_AddRefs(mTaskQueue));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
rv = NS_CreateBackgroundTaskQueue("PKCS11ModuleChild::Auth",
|
|
getter_AddRefs(mAuthTaskQueue));
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
rv = mTaskQueue->Dispatch(NS_NewRunnableFunction(
|
|
"PKCS11ModuleChild::StartBind",
|
|
[self = RefPtr{this}, endpoint = std::move(aEndpoint),
|
|
profilePath = std::move(aProfilePath)]() mutable {
|
|
if (profilePath.IsEmpty()) {
|
|
NS_WARNING(
|
|
"no profile path for utility process: loading PKCS#11 modules "
|
|
"will fail");
|
|
} else {
|
|
if (NS_FAILED(ConfigureNSSInPKCS11UtilityProcess(profilePath))) {
|
|
NS_WARNING(
|
|
"could not load NSS in utility process: loading PKCS#11 "
|
|
"modules will fail");
|
|
}
|
|
}
|
|
MOZ_ALWAYS_TRUE(endpoint.Bind(self));
|
|
}));
|
|
return rv;
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvAddModule(nsCString&& aModuleName,
|
|
nsCString&& aLibraryPath,
|
|
uint32_t aMechanismFlags,
|
|
uint32_t aCipherFlags,
|
|
AddModuleResolver&& aResolver) {
|
|
aResolver(PKCS11ModuleDB::DoAddModule(aModuleName, aLibraryPath,
|
|
aMechanismFlags, aCipherFlags));
|
|
return IPC_OK();
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvDeleteModule(
|
|
nsCString&& aModuleName, DeleteModuleResolver&& aResolver) {
|
|
aResolver(PKCS11ModuleDB::DoDeleteModule(aModuleName));
|
|
return IPC_OK();
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvListModules(
|
|
ListModulesResolver&& aResolver) {
|
|
nsTArray<ModuleInfo> modules;
|
|
nsresult rv = PKCS11ModuleDB::DoListModules(modules);
|
|
using Type = std::tuple<const nsresult&, nsTArray<ModuleInfo>&&>;
|
|
aResolver(Type(rv, std::move(modules)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
template <typename Operation>
|
|
nsresult DoWithToken(SECMODModuleID aModuleID, CK_SLOT_ID aSlotID,
|
|
TokenInfo& aTokenInfo, Operation&& operation) {
|
|
UniquePK11SlotInfo slot(SECMOD_LookupSlot(aModuleID, aSlotID));
|
|
if (!slot) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsresult rv = operation(slot.get());
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
RefPtr<PKCS11Token> token(MakeAndAddRef<PKCS11Token>(slot.get()));
|
|
return token->GetTokenInfo(aTokenInfo);
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvResetToken(
|
|
SECMODModuleID aModuleID, CK_SLOT_ID aSlotID,
|
|
ResetTokenResolver&& aResolver) {
|
|
TokenInfo tokenInfo;
|
|
nsresult rv =
|
|
DoWithToken(aModuleID, aSlotID, tokenInfo, [](PK11SlotInfo* slot) {
|
|
SECStatus rv = PK11_ResetToken(slot, nullptr);
|
|
if (rv != SECSuccess) {
|
|
return MapSECStatus(rv);
|
|
}
|
|
return NS_OK;
|
|
});
|
|
using Type = std::tuple<const nsresult&, TokenInfo&&>;
|
|
aResolver(Type(rv, std::move(tokenInfo)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvLoginToken(
|
|
SECMODModuleID aModuleID, CK_SLOT_ID aSlotID,
|
|
LoginTokenResolver&& aResolver) {
|
|
mAuthTaskQueue->Dispatch(NS_NewRunnableFunction(
|
|
__func__, [self = RefPtr{this}, moduleID(aModuleID), slotID(aSlotID),
|
|
resolver(std::move(aResolver))] {
|
|
TokenInfo tokenInfo;
|
|
nsresult rv = DoWithToken(
|
|
moduleID, slotID, tokenInfo, [self](PK11SlotInfo* slot) {
|
|
SECStatus rv = PK11_Authenticate(slot, true, self.get());
|
|
if (rv != SECSuccess) {
|
|
return MapSECStatus(rv);
|
|
}
|
|
return NS_OK;
|
|
});
|
|
self->mTaskQueue->Dispatch(NS_NewRunnableFunction(
|
|
__func__, [rv, tokenInfo(std::move(tokenInfo)),
|
|
resolver(std::move(resolver))] {
|
|
resolver(std::make_pair(rv, std::move(tokenInfo)));
|
|
}));
|
|
}));
|
|
return IPC_OK();
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvLogoutToken(
|
|
SECMODModuleID aModuleID, CK_SLOT_ID aSlotID,
|
|
LogoutTokenResolver&& aResolver) {
|
|
TokenInfo tokenInfo;
|
|
nsresult rv =
|
|
DoWithToken(aModuleID, aSlotID, tokenInfo, [](PK11SlotInfo* slot) {
|
|
// PK11_Logout() can fail if the user wasn't logged in beforehand. We
|
|
// want this method to succeed even in this case, so we ignore the
|
|
// return value.
|
|
(void)PK11_Logout(slot);
|
|
return NS_OK;
|
|
});
|
|
using Type = std::tuple<const nsresult&, TokenInfo&&>;
|
|
aResolver(Type(rv, std::move(tokenInfo)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsresult DoChangeTokenPassword(SECMODModuleID aModuleID, CK_SLOT_ID aSlotID,
|
|
const nsCString& aOldPassword,
|
|
const nsCString& aNewPassword,
|
|
TokenInfo& aTokenInfo) {
|
|
UniquePK11SlotInfo slot(SECMOD_LookupSlot(aModuleID, aSlotID));
|
|
if (!slot) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
nsresult rv = DoChangePassword(slot, aOldPassword, aNewPassword);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
RefPtr<PKCS11Token> token(MakeAndAddRef<PKCS11Token>(slot.get()));
|
|
return token->GetTokenInfo(aTokenInfo);
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvChangeTokenPassword(
|
|
SECMODModuleID aModuleID, CK_SLOT_ID aSlotID, const nsCString& aOldPassword,
|
|
const nsCString& aNewPassword, ChangeTokenPasswordResolver&& aResolver) {
|
|
TokenInfo tokenInfo;
|
|
nsresult rv = DoChangeTokenPassword(aModuleID, aSlotID, aOldPassword,
|
|
aNewPassword, tokenInfo);
|
|
using Type = std::tuple<const nsresult&, TokenInfo&&>;
|
|
aResolver(Type(rv, std::move(tokenInfo)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
char* PKCS11ModuleChild::PromptForPassword(PK11SlotInfo* slot) {
|
|
MonitorAutoLock authPromptMonitorLock(mAuthPromptMonitor);
|
|
mMaybePasswordForPrompt.reset();
|
|
mMaybeProtectedAuthPrompt.reset();
|
|
|
|
MOZ_ASSERT(mAuthTaskQueue->IsOnCurrentThread());
|
|
if (!mAuthTaskQueue->IsOnCurrentThread()) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCString tokenName(PK11_GetTokenName(slot));
|
|
mTaskQueue->Dispatch(NS_NewRunnableFunction(
|
|
__func__, [self = RefPtr{this}, tokenName(std::move(tokenName))] {
|
|
self->SendPromptPassword(tokenName)->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[self](const PPKCS11ModuleChild::PromptPasswordPromise::
|
|
ResolveOrRejectValue& value) {
|
|
MonitorAutoLock authPromptMonitorLock(self->mAuthPromptMonitor);
|
|
if (value.IsResolve()) {
|
|
self->mMaybePasswordForPrompt.emplace(
|
|
std::move(value.ResolveValue()));
|
|
} else {
|
|
self->mMaybePasswordForPrompt.emplace(
|
|
std::make_tuple(NS_ERROR_FAILURE, ""_ns));
|
|
}
|
|
authPromptMonitorLock.Notify();
|
|
});
|
|
}));
|
|
|
|
while (mMaybePasswordForPrompt.isNothing()) {
|
|
authPromptMonitorLock.Wait();
|
|
}
|
|
auto passwordPromptResult(mMaybePasswordForPrompt.take());
|
|
MOZ_ASSERT(passwordPromptResult.isSome());
|
|
if (passwordPromptResult.isNothing()) {
|
|
return nullptr;
|
|
}
|
|
if (NS_FAILED(std::get<0>(*passwordPromptResult))) {
|
|
return nullptr;
|
|
}
|
|
return ToNewCString(std::get<1>(*passwordPromptResult));
|
|
}
|
|
|
|
char* PKCS11ModuleChild::InitiateProtectedAuth(PK11SlotInfo* slot) {
|
|
static uint64_t id = 0;
|
|
|
|
MonitorAutoLock authPromptMonitorLock(mAuthPromptMonitor);
|
|
mMaybePasswordForPrompt.reset();
|
|
id++;
|
|
mMaybeProtectedAuthPrompt.emplace(
|
|
std::make_pair(ProtectedAuthState::InProgress, id));
|
|
|
|
MOZ_ASSERT(mAuthTaskQueue->IsOnCurrentThread());
|
|
if (!mAuthTaskQueue->IsOnCurrentThread()) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Dispatch a background task to call C_Login. The call will block until the
|
|
// protected authentication (e.g. card reader PIN entry) succeeds or fails.
|
|
nsresult rv = NS_DispatchBackgroundTask(
|
|
NS_NewRunnableFunction(
|
|
__func__,
|
|
[self = RefPtr{this},
|
|
slot = UniquePK11SlotInfo(PK11_ReferenceSlot(slot)), id = id]() {
|
|
SECStatus rv = PK11_CheckUserPassword(slot.get(), nullptr);
|
|
ProtectedAuthState newState = ProtectedAuthState::Cancelled;
|
|
switch (rv) {
|
|
case SECSuccess:
|
|
newState = ProtectedAuthState::Succeeded;
|
|
break;
|
|
case SECWouldBlock:
|
|
newState = ProtectedAuthState::DoRetry;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// Notify the thread waiting on this authentication that it has
|
|
// completed.
|
|
MonitorAutoLock authPromptMonitorLock(self->mAuthPromptMonitor);
|
|
if (self->mMaybeProtectedAuthPrompt.isSome() &&
|
|
std::get<1>(*self->mMaybeProtectedAuthPrompt) == id) {
|
|
self->mMaybeProtectedAuthPrompt =
|
|
Some(std::make_pair(newState, id));
|
|
authPromptMonitorLock.Notify();
|
|
}
|
|
}),
|
|
NS_DISPATCH_EVENT_MAY_BLOCK);
|
|
if (NS_FAILED(rv)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Meanwhile, tell the parent process to show an indication that a protected
|
|
// auth attempt is in progress.
|
|
nsCString tokenName(PK11_GetTokenName(slot));
|
|
mTaskQueue->Dispatch(NS_NewRunnableFunction(
|
|
__func__,
|
|
[self = RefPtr{this}, tokenName(std::move(tokenName)), id = id] {
|
|
self->SendShowProtectedAuthPrompt(tokenName, id);
|
|
}));
|
|
|
|
// Wait for either the background call to C_Login to complete or the parent
|
|
// to tell this thread to stop waiting (indicating a cancel). In the latter
|
|
// case, the background call to C_Login will continue to block until
|
|
// presumably the token times out the operation.
|
|
while (mMaybeProtectedAuthPrompt.isSome() &&
|
|
std::get<0>(*mMaybeProtectedAuthPrompt) ==
|
|
ProtectedAuthState::InProgress) {
|
|
authPromptMonitorLock.Wait();
|
|
}
|
|
|
|
// Tell the parent process to discard any remaining protected auth indicators
|
|
// now that this attempt has finished.
|
|
mTaskQueue->Dispatch(
|
|
NS_NewRunnableFunction(__func__, [self = RefPtr{this}, id = id] {
|
|
self->SendDismissProtectedAuthPrompt(id);
|
|
}));
|
|
|
|
auto protectedAuthPrompt(mMaybeProtectedAuthPrompt.take());
|
|
MOZ_ASSERT(protectedAuthPrompt.isSome() &&
|
|
std::get<1>(*protectedAuthPrompt) == id);
|
|
if (protectedAuthPrompt.isNothing() ||
|
|
std::get<1>(*protectedAuthPrompt) != id) {
|
|
return nullptr;
|
|
}
|
|
switch (std::get<0>(*protectedAuthPrompt)) {
|
|
case ProtectedAuthState::Succeeded:
|
|
return strdup(PK11_PW_AUTHENTICATED);
|
|
case ProtectedAuthState::DoRetry:
|
|
return strdup(PK11_PW_RETRY);
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
ipc::IPCResult PKCS11ModuleChild::RecvCancelProtectedAuth(uint64_t id) {
|
|
MonitorAutoLock authPromptMonitorLock(mAuthPromptMonitor);
|
|
// If the current in-progress protected auth prompt matches the ID, indicate
|
|
// that it should be cancelled.
|
|
if (mMaybeProtectedAuthPrompt.isSome() &&
|
|
std::get<1>(*mMaybeProtectedAuthPrompt) == id) {
|
|
mMaybeProtectedAuthPrompt =
|
|
Some(std::make_pair(ProtectedAuthState::Cancelled, id));
|
|
authPromptMonitorLock.Notify();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
} // namespace mozilla::psm
|