security/manager/ssl/src/nsSmartCardMonitor.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:e5674de03244
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 #include "nspr.h"
5
6 #include "pk11func.h"
7 #include "nsNSSComponent.h"
8 #include "nsSmartCardMonitor.h"
9 #include "nsIDOMSmartCardEvent.h"
10 #include "nsServiceManagerUtils.h"
11 #include "mozilla/unused.h"
12
13 using namespace mozilla;
14
15 //
16 // The SmartCard monitoring thread should start up for each module we load
17 // that has removable tokens. This code calls an NSS function which waits
18 // until there is a change in the token state. NSS uses the
19 // C_WaitForSlotEvent() call in PKCS #11 if the module implements the call,
20 // otherwise NSS will poll the token in a loop with a delay of 'latency'
21 // between polls. Note that the C_WaitForSlotEvent() may wake up on any type
22 // of token event, so it's necessary to filter these events down to just the
23 // insertion and removal events we are looking for.
24 //
25 // Once the event is found, It is passed to nsNSSComponent for dispatching
26 // on the UI thread, and forwarding to any interested listeners (including
27 // javascript).
28 //
29
30
31 // self linking and removing double linked entry
32 // adopts the thread it is passed.
33 class SmartCardThreadEntry {
34 public:
35 SmartCardThreadEntry *next;
36 SmartCardThreadEntry *prev;
37 SmartCardThreadEntry **head;
38 SmartCardMonitoringThread *thread;
39 SmartCardThreadEntry(SmartCardMonitoringThread *thread_,
40 SmartCardThreadEntry *next_, SmartCardThreadEntry *prev_,
41 SmartCardThreadEntry **head_) :
42 next(next_), prev(prev_), head(head_), thread(thread_) {
43 if (prev) { prev->next = this; } else { *head = this; }
44 if (next) { next->prev = this; }
45 }
46 ~SmartCardThreadEntry() {
47 if (prev) { prev->next = next; } else { *head = next; }
48 if (next) { next->prev = prev; }
49 // NOTE: automatically stops the thread
50 delete thread;
51 }
52 };
53
54 //
55 // SmartCardThreadList is a class to help manage the running threads.
56 // That way new threads could be started and old ones terminated as we
57 // load and unload modules.
58 //
59 SmartCardThreadList::SmartCardThreadList() : head(0)
60 {
61 }
62
63 SmartCardThreadList::~SmartCardThreadList()
64 {
65 // the head is self linking and unlinking, the following
66 // loop removes all entries on the list.
67 // it will also stop the thread if it happens to be running
68 while (head) {
69 delete head;
70 }
71 }
72
73 void
74 SmartCardThreadList::Remove(SECMODModule *aModule)
75 {
76 for (SmartCardThreadEntry *current = head; current; current = current->next) {
77 if (current->thread->GetModule() == aModule) {
78 // NOTE: automatically stops the thread and dequeues it from the list
79 delete current;
80 return;
81 }
82 }
83 }
84
85 // adopts the thread passed to it. Starts the thread as well
86 nsresult
87 SmartCardThreadList::Add(SmartCardMonitoringThread *thread)
88 {
89 SmartCardThreadEntry *current = new SmartCardThreadEntry(thread, head, nullptr,
90 &head);
91 // OK to forget current here, it's on the list.
92 unused << current;
93
94 return thread->Start();
95 }
96
97
98 // We really should have a Unity PL Hash function...
99 static PLHashNumber
100 unity(const void *key) { return PLHashNumber(NS_PTR_TO_INT32(key)); }
101
102 SmartCardMonitoringThread::SmartCardMonitoringThread(SECMODModule *module_)
103 : mThread(nullptr)
104 {
105 mModule = SECMOD_ReferenceModule(module_);
106 // simple hash functions, most modules have less than 3 slots, so 10 buckets
107 // should be plenty
108 mHash = PL_NewHashTable(10, unity, PL_CompareValues,
109 PL_CompareStrings, nullptr, 0);
110 }
111
112 //
113 // when we shutdown the thread, be sure to stop it first. If not, it just might
114 // crash when the mModule it is looking at disappears.
115 //
116 SmartCardMonitoringThread::~SmartCardMonitoringThread()
117 {
118 Stop();
119 SECMOD_DestroyModule(mModule);
120 if (mHash) {
121 PL_HashTableDestroy(mHash);
122 }
123 }
124
125 nsresult
126 SmartCardMonitoringThread::Start()
127 {
128 if (!mThread) {
129 mThread = PR_CreateThread(PR_SYSTEM_THREAD, LaunchExecute, this,
130 PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
131 PR_JOINABLE_THREAD, 0);
132 }
133 return mThread ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
134 }
135
136 //
137 // Should only stop if we are through with the module.
138 // CancelWait has the side effect of losing all the keys and
139 // current operations on the module!. (See the comment in
140 // SECMOD_CancelWait for why this is so..).
141 //
142 void SmartCardMonitoringThread::Stop()
143 {
144 SECStatus rv;
145
146 rv = SECMOD_CancelWait(mModule);
147 if (rv != SECSuccess) {
148 // we didn't wake up the Wait, so don't try to join the thread
149 // otherwise we will hang forever...
150 return;
151 }
152
153 // confused about the memory model here? NSPR owns the memory for
154 // threads. non-joinable threads are freed when the thread dies.
155 // joinable threads are freed after the call to PR_JoinThread.
156 // That means if SECMOD_CancelWait fails, we'll leak the mThread
157 // structure. this is considered preferable to hanging (which is
158 // what will happen if we try to join a thread that blocked).
159 if (mThread) {
160 PR_JoinThread(mThread);
161 mThread = 0;
162 }
163 }
164
165 //
166 // remember the name and series of a token in a particular slot.
167 // This is important because the name is no longer available when
168 // the token is removed. If listeners depended on this information,
169 // They would be out of luck. It also is a handy way of making sure
170 // we don't generate spurious insertion and removal events as the slot
171 // cycles through various states.
172 //
173 void
174 SmartCardMonitoringThread::SetTokenName(CK_SLOT_ID slotid,
175 const char *tokenName, uint32_t series)
176 {
177 if (mHash) {
178 if (tokenName) {
179 int len = strlen(tokenName) + 1;
180 /* this must match the allocator used in
181 * PLHashAllocOps.freeEntry DefaultFreeEntry */
182 char *entry = (char *)PR_Malloc(len+sizeof(uint32_t));
183
184 if (entry) {
185 memcpy(entry,&series,sizeof(uint32_t));
186 memcpy(&entry[sizeof(uint32_t)],tokenName,len);
187
188 PL_HashTableAdd(mHash,(void *)(uintptr_t)slotid, entry); /* adopt */
189 return;
190 }
191 }
192 else {
193 // if tokenName was not provided, remove the old one (implicit delete)
194 PL_HashTableRemove(mHash,(void *)(uintptr_t)slotid);
195 }
196 }
197 }
198
199 // retrieve the name saved above
200 const char *
201 SmartCardMonitoringThread::GetTokenName(CK_SLOT_ID slotid)
202 {
203 const char *tokenName = nullptr;
204 const char *entry;
205
206 if (mHash) {
207 entry = (const char *)PL_HashTableLookupConst(mHash,(void *)(uintptr_t)slotid);
208 if (entry) {
209 tokenName = &entry[sizeof(uint32_t)];
210 }
211 }
212 return tokenName;
213 }
214
215 // retrieve the series saved in SetTokenName above
216 uint32_t
217 SmartCardMonitoringThread::GetTokenSeries(CK_SLOT_ID slotid)
218 {
219 uint32_t series = 0;
220 const char *entry;
221
222 if (mHash) {
223 entry = (const char *)PL_HashTableLookupConst(mHash,(void *)(uintptr_t)slotid);
224 if (entry) {
225 memcpy(&series,entry,sizeof(uint32_t));
226 }
227 }
228 return series;
229 }
230
231 //
232 // helper function to pass the event off to nsNSSComponent.
233 //
234 nsresult
235 SmartCardMonitoringThread::SendEvent(const nsAString &eventType,
236 const char *tokenName)
237 {
238 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
239
240 nsresult rv;
241 nsCOMPtr<nsINSSComponent>
242 nssComponent(do_GetService(kNSSComponentCID, &rv));
243 if (NS_FAILED(rv))
244 return rv;
245
246 // NSS returns actual UTF8, not ASCII
247 nssComponent->PostEvent(eventType, NS_ConvertUTF8toUTF16(tokenName));
248 return NS_OK;
249 }
250
251 //
252 // This is the main loop.
253 //
254 void SmartCardMonitoringThread::Execute()
255 {
256 PK11SlotInfo *slot;
257 const char *tokenName = nullptr;
258
259 //
260 // populate token names for already inserted tokens.
261 //
262 PK11SlotList *sl =
263 PK11_FindSlotsByNames(mModule->dllName, nullptr, nullptr, true);
264 PK11SlotListElement *sle;
265
266 if (sl) {
267 for (sle=PK11_GetFirstSafe(sl); sle;
268 sle=PK11_GetNextSafe(sl,sle,false)) {
269 SetTokenName(PK11_GetSlotID(sle->slot),
270 PK11_GetTokenName(sle->slot), PK11_GetSlotSeries(sle->slot));
271 }
272 PK11_FreeSlotList(sl);
273 }
274
275 // loop starts..
276 do {
277 slot = SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1) );
278 if (!slot) {
279 break;
280 }
281
282 // now we have a potential insertion or removal event, see if the slot
283 // is present to determine which it is...
284 if (PK11_IsPresent(slot)) {
285 // insertion
286 CK_SLOT_ID slotID = PK11_GetSlotID(slot);
287 uint32_t series = PK11_GetSlotSeries(slot);
288
289 // skip spurious insertion events...
290 if (series != GetTokenSeries(slotID)) {
291 // if there's a token name, then we have not yet issued a remove
292 // event for the previous token, do so now...
293 tokenName = GetTokenName(slotID);
294 if (tokenName) {
295 SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
296 }
297 tokenName = PK11_GetTokenName(slot);
298 // save the token name and series
299 SetTokenName(slotID, tokenName, series);
300 SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_INSERT), tokenName);
301 }
302 } else {
303 // retrieve token name
304 CK_SLOT_ID slotID = PK11_GetSlotID(slot);
305 tokenName = GetTokenName(slotID);
306 // if there's not a token name, then the software isn't expecting
307 // a (or another) remove event.
308 if (tokenName) {
309 SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
310 // clear the token name (after we send it)
311 SetTokenName(slotID, nullptr, 0);
312 }
313 }
314 PK11_FreeSlot(slot);
315
316 } while (1);
317 }
318
319 // accessor to help searching active Monitoring threads
320 const SECMODModule * SmartCardMonitoringThread::GetModule()
321 {
322 return mModule;
323 }
324
325 // C-like calling sequence to glue into PR_CreateThread.
326 void SmartCardMonitoringThread::LaunchExecute(void *arg)
327 {
328 PR_SetCurrentThreadName("SmartCard");
329
330 ((SmartCardMonitoringThread*)arg)->Execute();
331 }
332

mercurial