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