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.

     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"
     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"
    13 using namespace mozilla;
    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 //
    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 };
    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 }
    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 }
    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 }
    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;
    94   return thread->Start();
    95 }
    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)); }
   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 }
   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 }
   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 }
   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;
   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   }
   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 }
   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));
   184       if (entry) {  
   185         memcpy(entry,&series,sizeof(uint32_t));
   186         memcpy(&entry[sizeof(uint32_t)],tokenName,len);
   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 }
   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;
   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 }
   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;
   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 }
   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);
   240   nsresult rv;
   241   nsCOMPtr<nsINSSComponent> 
   242                     nssComponent(do_GetService(kNSSComponentCID, &rv));
   243   if (NS_FAILED(rv))
   244     return rv;
   246   // NSS returns actual UTF8, not ASCII
   247   nssComponent->PostEvent(eventType, NS_ConvertUTF8toUTF16(tokenName));
   248   return NS_OK;
   249 }
   251 //
   252 // This is the main loop.
   253 //
   254 void SmartCardMonitoringThread::Execute()
   255 {
   256   PK11SlotInfo *slot;
   257   const char *tokenName = nullptr;
   259   //
   260   // populate token names for already inserted tokens.
   261   //
   262   PK11SlotList *sl =
   263             PK11_FindSlotsByNames(mModule->dllName, nullptr, nullptr, true);
   264   PK11SlotListElement *sle;
   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   }
   275   // loop starts..
   276   do {
   277     slot = SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1)  );
   278     if (!slot) {
   279       break;
   280     }
   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);
   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);
   316   } while (1);
   317 }
   319 // accessor to help searching active Monitoring threads
   320 const SECMODModule * SmartCardMonitoringThread::GetModule() 
   321 {
   322   return mModule;
   323 }
   325 // C-like calling sequence to glue into PR_CreateThread.
   326 void SmartCardMonitoringThread::LaunchExecute(void *arg)
   327 {
   328   PR_SetCurrentThreadName("SmartCard");
   330   ((SmartCardMonitoringThread*)arg)->Execute();
   331 }

mercurial