michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "pk11func.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsKeygenThread.h" michael@0: #include "nsIObserver.h" michael@0: #include "nsNSSShutDown.h" michael@0: #include "PSMRunnable.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::psm; michael@0: michael@0: NS_IMPL_ISUPPORTS(nsKeygenThread, nsIKeygenThread) michael@0: michael@0: michael@0: nsKeygenThread::nsKeygenThread() michael@0: :mutex("nsKeygenThread.mutex"), michael@0: iAmRunning(false), michael@0: keygenReady(false), michael@0: statusDialogClosed(false), michael@0: alreadyReceivedParams(false), michael@0: privateKey(nullptr), michael@0: publicKey(nullptr), michael@0: slot(nullptr), michael@0: flags(0), michael@0: altSlot(nullptr), michael@0: altFlags(0), michael@0: usedSlot(nullptr), michael@0: keyGenMechanism(0), michael@0: params(nullptr), michael@0: wincx(nullptr), michael@0: threadHandle(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsKeygenThread::~nsKeygenThread() michael@0: { michael@0: // clean up in the unlikely case that nobody consumed our results michael@0: michael@0: if (privateKey) michael@0: SECKEY_DestroyPrivateKey(privateKey); michael@0: michael@0: if (publicKey) michael@0: SECKEY_DestroyPublicKey(publicKey); michael@0: michael@0: if (usedSlot) michael@0: PK11_FreeSlot(usedSlot); michael@0: } michael@0: michael@0: void nsKeygenThread::SetParams( michael@0: PK11SlotInfo *a_slot, michael@0: PK11AttrFlags a_flags, michael@0: PK11SlotInfo *a_alternative_slot, michael@0: PK11AttrFlags a_alternative_flags, michael@0: uint32_t a_keyGenMechanism, michael@0: void *a_params, michael@0: void *a_wincx ) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (!alreadyReceivedParams) { michael@0: alreadyReceivedParams = true; michael@0: slot = (a_slot) ? PK11_ReferenceSlot(a_slot) : nullptr; michael@0: flags = a_flags; michael@0: altSlot = (a_alternative_slot) ? PK11_ReferenceSlot(a_alternative_slot) : nullptr; michael@0: altFlags = a_alternative_flags; michael@0: keyGenMechanism = a_keyGenMechanism; michael@0: params = a_params; michael@0: wincx = a_wincx; michael@0: } michael@0: } michael@0: michael@0: nsresult nsKeygenThread::ConsumeResult( michael@0: PK11SlotInfo **a_used_slot, michael@0: SECKEYPrivateKey **a_privateKey, michael@0: SECKEYPublicKey **a_publicKey) michael@0: { michael@0: if (!a_used_slot || !a_privateKey || !a_publicKey) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv; michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: // GetParams must not be called until thread creator called michael@0: // Join on this thread. michael@0: NS_ASSERTION(keygenReady, "logic error in nsKeygenThread::GetParams"); michael@0: michael@0: if (keygenReady) { michael@0: *a_privateKey = privateKey; michael@0: *a_publicKey = publicKey; michael@0: *a_used_slot = usedSlot; michael@0: michael@0: privateKey = 0; michael@0: publicKey = 0; michael@0: usedSlot = 0; michael@0: michael@0: rv = NS_OK; michael@0: } michael@0: else { michael@0: rv = NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: static void nsKeygenThreadRunner(void *arg) michael@0: { michael@0: PR_SetCurrentThreadName("Keygen"); michael@0: nsKeygenThread *self = static_cast(arg); michael@0: self->Run(); michael@0: } michael@0: michael@0: nsresult nsKeygenThread::StartKeyGeneration(nsIObserver* aObserver) michael@0: { michael@0: if (!NS_IsMainThread()) { michael@0: NS_ERROR("nsKeygenThread::StartKeyGeneration called off the main thread"); michael@0: return NS_ERROR_NOT_SAME_THREAD; michael@0: } michael@0: michael@0: if (!aObserver) michael@0: return NS_OK; michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (iAmRunning || keygenReady) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We must AddRef aObserver only here on the main thread, because it michael@0: // probably does not implement a thread-safe AddRef. michael@0: mNotifyObserver = new NotifyObserverRunnable(aObserver, "keygen-finished"); michael@0: michael@0: iAmRunning = true; michael@0: michael@0: threadHandle = PR_CreateThread(PR_USER_THREAD, nsKeygenThreadRunner, static_cast(this), michael@0: PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); michael@0: michael@0: // bool thread_started_ok = (threadHandle != nullptr); michael@0: // we might want to return "thread started ok" to caller in the future michael@0: NS_ASSERTION(threadHandle, "Could not create nsKeygenThreadRunner thread\n"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult nsKeygenThread::UserCanceled(bool *threadAlreadyClosedDialog) michael@0: { michael@0: if (!threadAlreadyClosedDialog) michael@0: return NS_OK; michael@0: michael@0: *threadAlreadyClosedDialog = false; michael@0: michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: if (keygenReady) michael@0: *threadAlreadyClosedDialog = statusDialogClosed; michael@0: michael@0: // User somehow closed the dialog, but we will not cancel. michael@0: // Bad luck, we told him not do, and user still has to wait. michael@0: // However, we remember that it's closed and will not close michael@0: // it again to avoid problems. michael@0: statusDialogClosed = true; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void nsKeygenThread::Run(void) michael@0: { michael@0: nsNSSShutDownPreventionLock locker; michael@0: bool canGenerate = false; michael@0: michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: if (alreadyReceivedParams) { michael@0: canGenerate = true; michael@0: keygenReady = false; michael@0: } michael@0: } michael@0: michael@0: if (canGenerate) { michael@0: privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, michael@0: params, &publicKey, michael@0: flags, wincx); michael@0: michael@0: if (privateKey) { michael@0: usedSlot = PK11_ReferenceSlot(slot); michael@0: } michael@0: else if (altSlot) { michael@0: privateKey = PK11_GenerateKeyPairWithFlags(altSlot, keyGenMechanism, michael@0: params, &publicKey, michael@0: altFlags, wincx); michael@0: if (privateKey) { michael@0: usedSlot = PK11_ReferenceSlot(altSlot); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // This call gave us ownership over privateKey and publicKey. michael@0: // But as the params structure is owner by our caller, michael@0: // we effectively transferred ownership to the caller. michael@0: // As long as key generation can't be canceled, we don't need michael@0: // to care for cleaning this up. michael@0: michael@0: nsCOMPtr notifyObserver; michael@0: { michael@0: MutexAutoLock lock(mutex); michael@0: michael@0: keygenReady = true; michael@0: iAmRunning = false; michael@0: michael@0: // forget our parameters michael@0: if (slot) { michael@0: PK11_FreeSlot(slot); michael@0: slot = 0; michael@0: } michael@0: if (altSlot) { michael@0: PK11_FreeSlot(altSlot); michael@0: altSlot = 0; michael@0: } michael@0: keyGenMechanism = 0; michael@0: params = 0; michael@0: wincx = 0; michael@0: michael@0: if (!statusDialogClosed && mNotifyObserver) michael@0: notifyObserver = mNotifyObserver; michael@0: michael@0: mNotifyObserver = nullptr; michael@0: } michael@0: michael@0: if (notifyObserver) { michael@0: DebugOnly rv = NS_DispatchToMainThread(notifyObserver); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), michael@0: "failed to dispatch keygen thread observer to main thread"); michael@0: } michael@0: } michael@0: michael@0: void nsKeygenThread::Join() michael@0: { michael@0: if (!threadHandle) michael@0: return; michael@0: michael@0: PR_JoinThread(threadHandle); michael@0: threadHandle = nullptr; michael@0: michael@0: return; michael@0: }