security/manager/ssl/src/nsSmartCardMonitor.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial