/src/mozilla-central/security/manager/ssl/nsKeygenThread.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
2 | | * |
3 | | * This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include "nsKeygenThread.h" |
8 | | |
9 | | #include "mozilla/Assertions.h" |
10 | | #include "mozilla/DebugOnly.h" |
11 | | |
12 | | #include "GeckoProfiler.h" |
13 | | #include "PSMRunnable.h" |
14 | | #include "nsCOMPtr.h" |
15 | | #include "nsIObserver.h" |
16 | | #include "nsThreadUtils.h" |
17 | | #include "pk11func.h" |
18 | | |
19 | | using namespace mozilla; |
20 | | using namespace mozilla::psm; |
21 | | |
22 | | NS_IMPL_ISUPPORTS(nsKeygenThread, nsIKeygenThread) |
23 | | |
24 | | |
25 | | nsKeygenThread::nsKeygenThread() |
26 | | :mutex("nsKeygenThread.mutex"), |
27 | | iAmRunning(false), |
28 | | keygenReady(false), |
29 | | statusDialogClosed(false), |
30 | | alreadyReceivedParams(false), |
31 | | privateKey(nullptr), |
32 | | publicKey(nullptr), |
33 | | slot(nullptr), |
34 | | flags(0), |
35 | | altSlot(nullptr), |
36 | | altFlags(0), |
37 | | usedSlot(nullptr), |
38 | | keyGenMechanism(0), |
39 | | params(nullptr), |
40 | | wincx(nullptr), |
41 | | threadHandle(nullptr) |
42 | 0 | { |
43 | 0 | } |
44 | | |
45 | | nsKeygenThread::~nsKeygenThread() |
46 | 0 | { |
47 | 0 | // clean up in the unlikely case that nobody consumed our results |
48 | 0 |
|
49 | 0 | if (privateKey) |
50 | 0 | SECKEY_DestroyPrivateKey(privateKey); |
51 | 0 |
|
52 | 0 | if (publicKey) |
53 | 0 | SECKEY_DestroyPublicKey(publicKey); |
54 | 0 |
|
55 | 0 | if (usedSlot) |
56 | 0 | PK11_FreeSlot(usedSlot); |
57 | 0 | } |
58 | | |
59 | | void nsKeygenThread::SetParams( |
60 | | PK11SlotInfo *a_slot, |
61 | | PK11AttrFlags a_flags, |
62 | | PK11SlotInfo *a_alternative_slot, |
63 | | PK11AttrFlags a_alternative_flags, |
64 | | uint32_t a_keyGenMechanism, |
65 | | void *a_params, |
66 | | void *a_wincx ) |
67 | 0 | { |
68 | 0 | MutexAutoLock lock(mutex); |
69 | 0 |
|
70 | 0 | if (!alreadyReceivedParams) { |
71 | 0 | alreadyReceivedParams = true; |
72 | 0 | slot = (a_slot) ? PK11_ReferenceSlot(a_slot) : nullptr; |
73 | 0 | flags = a_flags; |
74 | 0 | altSlot = (a_alternative_slot) ? PK11_ReferenceSlot(a_alternative_slot) : nullptr; |
75 | 0 | altFlags = a_alternative_flags; |
76 | 0 | keyGenMechanism = a_keyGenMechanism; |
77 | 0 | params = a_params; |
78 | 0 | wincx = a_wincx; |
79 | 0 | } |
80 | 0 | } |
81 | | |
82 | | nsresult nsKeygenThread::ConsumeResult( |
83 | | PK11SlotInfo **a_used_slot, |
84 | | SECKEYPrivateKey **a_privateKey, |
85 | | SECKEYPublicKey **a_publicKey) |
86 | 0 | { |
87 | 0 | if (!a_used_slot || !a_privateKey || !a_publicKey) { |
88 | 0 | return NS_ERROR_FAILURE; |
89 | 0 | } |
90 | 0 | |
91 | 0 | nsresult rv; |
92 | 0 |
|
93 | 0 | MutexAutoLock lock(mutex); |
94 | 0 |
|
95 | 0 | // GetParams must not be called until thread creator called |
96 | 0 | // Join on this thread. |
97 | 0 | MOZ_ASSERT(keygenReady, "Logic error in nsKeygenThread::GetParams"); |
98 | 0 |
|
99 | 0 | if (keygenReady) { |
100 | 0 | *a_privateKey = privateKey; |
101 | 0 | *a_publicKey = publicKey; |
102 | 0 | *a_used_slot = usedSlot; |
103 | 0 |
|
104 | 0 | privateKey = 0; |
105 | 0 | publicKey = 0; |
106 | 0 | usedSlot = 0; |
107 | 0 |
|
108 | 0 | rv = NS_OK; |
109 | 0 | } |
110 | 0 | else { |
111 | 0 | rv = NS_ERROR_FAILURE; |
112 | 0 | } |
113 | 0 |
|
114 | 0 | return rv; |
115 | 0 | } |
116 | | |
117 | | static void nsKeygenThreadRunner(void *arg) |
118 | 0 | { |
119 | 0 | AUTO_PROFILER_REGISTER_THREAD("Keygen"); |
120 | 0 | NS_SetCurrentThreadName("Keygen"); |
121 | 0 | nsKeygenThread *self = static_cast<nsKeygenThread *>(arg); |
122 | 0 | self->Run(); |
123 | 0 | } |
124 | | |
125 | | nsresult nsKeygenThread::StartKeyGeneration(nsIObserver* aObserver) |
126 | 0 | { |
127 | 0 | if (!NS_IsMainThread()) { |
128 | 0 | NS_ERROR("nsKeygenThread::StartKeyGeneration called off the main thread"); |
129 | 0 | return NS_ERROR_NOT_SAME_THREAD; |
130 | 0 | } |
131 | 0 |
|
132 | 0 | if (!aObserver) |
133 | 0 | return NS_OK; |
134 | 0 | |
135 | 0 | MutexAutoLock lock(mutex); |
136 | 0 |
|
137 | 0 | if (iAmRunning || keygenReady) { |
138 | 0 | return NS_OK; |
139 | 0 | } |
140 | 0 | |
141 | 0 | // We must AddRef aObserver only here on the main thread, because it |
142 | 0 | // probably does not implement a thread-safe AddRef. |
143 | 0 | mNotifyObserver = new NotifyObserverRunnable(aObserver, "keygen-finished"); |
144 | 0 |
|
145 | 0 | iAmRunning = true; |
146 | 0 |
|
147 | 0 | threadHandle = PR_CreateThread(PR_USER_THREAD, nsKeygenThreadRunner, static_cast<void*>(this), |
148 | 0 | PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD, PR_JOINABLE_THREAD, 0); |
149 | 0 |
|
150 | 0 | // bool thread_started_ok = (threadHandle != nullptr); |
151 | 0 | // we might want to return "thread started ok" to caller in the future |
152 | 0 | MOZ_ASSERT(threadHandle, "Could not create nsKeygenThreadRunner thread"); |
153 | 0 | return NS_OK; |
154 | 0 | } |
155 | | |
156 | | nsresult nsKeygenThread::UserCanceled(bool *threadAlreadyClosedDialog) |
157 | 0 | { |
158 | 0 | if (!threadAlreadyClosedDialog) |
159 | 0 | return NS_OK; |
160 | 0 | |
161 | 0 | *threadAlreadyClosedDialog = false; |
162 | 0 |
|
163 | 0 | MutexAutoLock lock(mutex); |
164 | 0 |
|
165 | 0 | if (keygenReady) |
166 | 0 | *threadAlreadyClosedDialog = statusDialogClosed; |
167 | 0 |
|
168 | 0 | // User somehow closed the dialog, but we will not cancel. |
169 | 0 | // Bad luck, we told him not do, and user still has to wait. |
170 | 0 | // However, we remember that it's closed and will not close |
171 | 0 | // it again to avoid problems. |
172 | 0 | statusDialogClosed = true; |
173 | 0 |
|
174 | 0 | return NS_OK; |
175 | 0 | } |
176 | | |
177 | | void nsKeygenThread::Run(void) |
178 | 0 | { |
179 | 0 | bool canGenerate = false; |
180 | 0 |
|
181 | 0 | { |
182 | 0 | MutexAutoLock lock(mutex); |
183 | 0 | if (alreadyReceivedParams) { |
184 | 0 | canGenerate = true; |
185 | 0 | keygenReady = false; |
186 | 0 | } |
187 | 0 | } |
188 | 0 |
|
189 | 0 | if (canGenerate) { |
190 | 0 | privateKey = PK11_GenerateKeyPairWithFlags(slot, keyGenMechanism, |
191 | 0 | params, &publicKey, |
192 | 0 | flags, wincx); |
193 | 0 |
|
194 | 0 | if (privateKey) { |
195 | 0 | usedSlot = PK11_ReferenceSlot(slot); |
196 | 0 | } |
197 | 0 | else if (altSlot) { |
198 | 0 | privateKey = PK11_GenerateKeyPairWithFlags(altSlot, keyGenMechanism, |
199 | 0 | params, &publicKey, |
200 | 0 | altFlags, wincx); |
201 | 0 | if (privateKey) { |
202 | 0 | usedSlot = PK11_ReferenceSlot(altSlot); |
203 | 0 | } |
204 | 0 | } |
205 | 0 | } |
206 | 0 |
|
207 | 0 | // This call gave us ownership over privateKey and publicKey. |
208 | 0 | // But as the params structure is owner by our caller, |
209 | 0 | // we effectively transferred ownership to the caller. |
210 | 0 | // As long as key generation can't be canceled, we don't need |
211 | 0 | // to care for cleaning this up. |
212 | 0 |
|
213 | 0 | nsCOMPtr<nsIRunnable> notifyObserver; |
214 | 0 | { |
215 | 0 | MutexAutoLock lock(mutex); |
216 | 0 |
|
217 | 0 | keygenReady = true; |
218 | 0 | iAmRunning = false; |
219 | 0 |
|
220 | 0 | // forget our parameters |
221 | 0 | if (slot) { |
222 | 0 | PK11_FreeSlot(slot); |
223 | 0 | slot = 0; |
224 | 0 | } |
225 | 0 | if (altSlot) { |
226 | 0 | PK11_FreeSlot(altSlot); |
227 | 0 | altSlot = 0; |
228 | 0 | } |
229 | 0 | keyGenMechanism = 0; |
230 | 0 | params = 0; |
231 | 0 | wincx = 0; |
232 | 0 |
|
233 | 0 | if (!statusDialogClosed && mNotifyObserver) |
234 | 0 | notifyObserver = mNotifyObserver; |
235 | 0 |
|
236 | 0 | mNotifyObserver = nullptr; |
237 | 0 | } |
238 | 0 |
|
239 | 0 | if (notifyObserver) { |
240 | 0 | DebugOnly<nsresult> rv = NS_DispatchToMainThread(notifyObserver); |
241 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), |
242 | 0 | "Failed to dispatch keygen thread observer to main thread"); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | void nsKeygenThread::Join() |
247 | 0 | { |
248 | 0 | if (!threadHandle) |
249 | 0 | return; |
250 | 0 | |
251 | 0 | PR_JoinThread(threadHandle); |
252 | 0 | threadHandle = nullptr; |
253 | 0 | } |