LCOV - code coverage report
Current view: top level - security/manager/ssl - nsSmartCardMonitor.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 137 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 25 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
       5             : #include "nsSmartCardMonitor.h"
       6             : 
       7             : #include "ScopedNSSTypes.h"
       8             : #include "mozilla/Services.h"
       9             : #include "mozilla/Unused.h"
      10             : #include "nsIObserverService.h"
      11             : #include "nsServiceManagerUtils.h"
      12             : #include "nsThreadUtils.h"
      13             : #include "GeckoProfiler.h"
      14             : #include "nspr.h"
      15             : #include "pk11func.h"
      16             : 
      17             : using namespace mozilla;
      18             : 
      19             : //
      20             : // The SmartCard monitoring thread should start up for each module we load
      21             : // that has removable tokens. This code calls an NSS function which waits
      22             : // until there is a change in the token state. NSS uses the
      23             : // C_WaitForSlotEvent() call in PKCS #11 if the module implements the call,
      24             : // otherwise NSS will poll the token in a loop with a delay of 'latency'
      25             : // between polls. Note that the C_WaitForSlotEvent() may wake up on any type
      26             : // of token event, so it's necessary to filter these events down to just the
      27             : // insertion and removal events we are looking for.
      28             : //
      29             : // Once the event is found, it is dispatched to the main thread to notify
      30             : // any window where window.crypto.enableSmartCardEvents is true.
      31             : // Additionally, all observers of the topics |kSmartcardInsert| and
      32             : // |kSmartcardRemove| are notified by the observer service of the appropriate
      33             : // event.
      34             : //
      35             : 
      36             : #define kSmartcardInsert "smartcard-insert"
      37             : #define kSmartcardRemove "smartcard-remove"
      38             : 
      39             : class nsTokenEventRunnable : public nsIRunnable {
      40             : public:
      41           0 :   nsTokenEventRunnable(const char* aType, const nsAString& aTokenName)
      42           0 :     : mType(aType)
      43           0 :     , mTokenName(aTokenName)
      44             :   {
      45           0 :   }
      46             : 
      47             :   NS_DECL_THREADSAFE_ISUPPORTS
      48             :   NS_DECL_NSIRUNNABLE
      49             : 
      50             : private:
      51           0 :   virtual ~nsTokenEventRunnable() {}
      52             : 
      53             :   const char* mType;
      54             :   nsString mTokenName;
      55             : };
      56             : 
      57           0 : NS_IMPL_ISUPPORTS(nsTokenEventRunnable, nsIRunnable)
      58             : 
      59             : NS_IMETHODIMP
      60           0 : nsTokenEventRunnable::Run()
      61             : {
      62           0 :   MOZ_ASSERT(NS_IsMainThread());
      63             : 
      64             :   nsCOMPtr<nsIObserverService> observerService =
      65           0 :     mozilla::services::GetObserverService();
      66           0 :   if (!observerService) {
      67           0 :     return NS_ERROR_FAILURE;
      68             :   }
      69           0 :   return observerService->NotifyObservers(nullptr, mType, mTokenName.get());
      70             : }
      71             : 
      72             : // self linking and removing double linked entry
      73             : // adopts the thread it is passed.
      74             : class SmartCardThreadEntry
      75             : {
      76             : public:
      77             :   friend class SmartCardThreadList;
      78           0 :   SmartCardThreadEntry(SmartCardMonitoringThread *thread,
      79             :                        SmartCardThreadEntry *next,
      80             :                        SmartCardThreadEntry *prev,
      81             :                        SmartCardThreadEntry **head)
      82           0 :     : next(next)
      83             :     , prev(prev)
      84             :     , head(head)
      85           0 :     , thread(thread)
      86             :   {
      87           0 :     if (prev) {
      88           0 :       prev->next = this;
      89             :     } else {
      90           0 :       *head = this;
      91             :     }
      92           0 :     if (next) {
      93           0 :       next->prev = this;
      94             :     }
      95           0 :   }
      96             : 
      97           0 :   ~SmartCardThreadEntry()
      98           0 :   {
      99           0 :     if (prev) {
     100           0 :       prev->next = next;
     101             :     } else {
     102           0 :       *head = next;
     103             :     }
     104           0 :     if (next) {
     105           0 :       next->prev = prev;
     106             :     }
     107             :     // NOTE: automatically stops the thread
     108           0 :     delete thread;
     109           0 :   }
     110             : 
     111             : private:
     112             :   SmartCardThreadEntry *next;
     113             :   SmartCardThreadEntry *prev;
     114             :   SmartCardThreadEntry **head;
     115             :   SmartCardMonitoringThread *thread;
     116             : };
     117             : 
     118             : //
     119             : // SmartCardThreadList is a class to help manage the running threads.
     120             : // That way new threads could be started and old ones terminated as we
     121             : // load and unload modules.
     122             : //
     123           0 : SmartCardThreadList::SmartCardThreadList() : head(0)
     124             : {
     125           0 : }
     126             : 
     127           0 : SmartCardThreadList::~SmartCardThreadList()
     128             : {
     129             :   // the head is self linking and unlinking, the following
     130             :   // loop removes all entries on the list.
     131             :   // it will also stop the thread if it happens to be running
     132           0 :   while (head) {
     133           0 :     delete head;
     134             :   }
     135           0 : }
     136             : 
     137             : void
     138           0 : SmartCardThreadList::Remove(SECMODModule *aModule)
     139             : {
     140           0 :   for (SmartCardThreadEntry* current = head; current;
     141           0 :        current = current->next) {
     142           0 :     if (current->thread->GetModule() == aModule) {
     143             :       // NOTE: automatically stops the thread and dequeues it from the list
     144           0 :       delete current;
     145           0 :       return;
     146             :     }
     147             :   }
     148             : }
     149             : 
     150             : // adopts the thread passed to it. Starts the thread as well
     151             : nsresult
     152           0 : SmartCardThreadList::Add(SmartCardMonitoringThread* thread)
     153             : {
     154             :   SmartCardThreadEntry* current = new SmartCardThreadEntry(thread, head,
     155           0 :                                                            nullptr, &head);
     156             :   // OK to forget current here, it's on the list.
     157             :   Unused << current;
     158             : 
     159           0 :   return thread->Start();
     160             : }
     161             : 
     162             : 
     163             : // We really should have a Unity PL Hash function...
     164             : static PLHashNumber
     165           0 : unity(const void* key) { return PLHashNumber(NS_PTR_TO_INT32(key)); }
     166             : 
     167           0 : SmartCardMonitoringThread::SmartCardMonitoringThread(SECMODModule* module_)
     168           0 :   : mThread(nullptr)
     169             : {
     170           0 :   mModule = SECMOD_ReferenceModule(module_);
     171             :   // simple hash functions, most modules have less than 3 slots, so 10 buckets
     172             :   // should be plenty
     173           0 :   mHash = PL_NewHashTable(10, unity, PL_CompareValues, PL_CompareStrings,
     174             :                           nullptr, 0);
     175           0 : }
     176             : 
     177             : //
     178             : // when we shutdown the thread, be sure to stop it first. If not, it just might
     179             : // crash when the mModule it is looking at disappears.
     180             : //
     181           0 : SmartCardMonitoringThread::~SmartCardMonitoringThread()
     182             : {
     183           0 :   Stop();
     184           0 :   SECMOD_DestroyModule(mModule);
     185           0 :   if (mHash) {
     186           0 :     PL_HashTableDestroy(mHash);
     187             :   }
     188           0 : }
     189             : 
     190             : nsresult
     191           0 : SmartCardMonitoringThread::Start()
     192             : {
     193           0 :   if (!mThread) {
     194           0 :     mThread = PR_CreateThread(PR_SYSTEM_THREAD, LaunchExecute, this,
     195             :                               PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
     196             :                               PR_JOINABLE_THREAD, 0);
     197             :   }
     198           0 :   return mThread ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
     199             : }
     200             : 
     201             : //
     202             : // Should only stop if we are through with the module.
     203             : // CancelWait has the side effect of losing all the keys and
     204             : // current operations on the module!. (See the comment in
     205             : // SECMOD_CancelWait for why this is so..).
     206             : //
     207           0 : void SmartCardMonitoringThread::Stop()
     208             : {
     209             :   SECStatus rv;
     210             : 
     211           0 :   rv = SECMOD_CancelWait(mModule);
     212           0 :   if (rv != SECSuccess) {
     213             :     // we didn't wake up the Wait, so don't try to join the thread
     214             :     // otherwise we will hang forever...
     215           0 :     return;
     216             :   }
     217             : 
     218             :   // confused about the memory model here? NSPR owns the memory for
     219             :   // threads. non-joinable threads are freed when the thread dies.
     220             :   // joinable threads are freed after the call to PR_JoinThread.
     221             :   // That means if SECMOD_CancelWait fails, we'll leak the mThread
     222             :   // structure. this is considered preferable to hanging (which is
     223             :   // what will happen if we try to join a thread that blocked).
     224           0 :   if (mThread) {
     225           0 :     PR_JoinThread(mThread);
     226           0 :     mThread = 0;
     227             :   }
     228             : }
     229             : 
     230             : //
     231             : // remember the name and series of a token in a particular slot.
     232             : // This is important because the name is no longer available when
     233             : // the token is removed. If listeners depended on this information,
     234             : // They would be out of luck. It also is a handy way of making sure
     235             : // we don't generate spurious insertion and removal events as the slot
     236             : // cycles through various states.
     237             : //
     238             : void
     239           0 : SmartCardMonitoringThread::SetTokenName(CK_SLOT_ID slotid,
     240             :                                        const char* tokenName, uint32_t series)
     241             : {
     242           0 :   if (mHash) {
     243           0 :     if (tokenName) {
     244           0 :       int len = strlen(tokenName) + 1;
     245             :       // Use PR_Malloc() because PLHashAllocOps.freeEntry for mHash is
     246             :       // DefaultFreeEntry(), which uses PR_Free().
     247           0 :       char* entry = (char*)PR_Malloc(len + sizeof(uint32_t));
     248             : 
     249           0 :       if (entry) {
     250           0 :         memcpy(entry, &series, sizeof(uint32_t));
     251           0 :         memcpy(&entry[sizeof(uint32_t)], tokenName, len);
     252             : 
     253           0 :         PL_HashTableAdd(mHash, (void*)(uintptr_t)slotid, entry); /* adopt */
     254           0 :         return;
     255             :       }
     256             :     } else {
     257             :       // if tokenName was not provided, remove the old one (implicit delete)
     258           0 :       PL_HashTableRemove(mHash, (void*)(uintptr_t)slotid);
     259             :     }
     260             :   }
     261             : }
     262             : 
     263             : // retrieve the name saved above
     264             : const char*
     265           0 : SmartCardMonitoringThread::GetTokenName(CK_SLOT_ID slotid)
     266             : {
     267           0 :   const char* tokenName = nullptr;
     268             :   const char* entry;
     269             : 
     270           0 :   if (mHash) {
     271           0 :     entry = (const char*)PL_HashTableLookupConst(mHash,
     272           0 :                                                  (void*)(uintptr_t)slotid);
     273           0 :     if (entry) {
     274           0 :       tokenName = &entry[sizeof(uint32_t)];
     275             :     }
     276             :   }
     277           0 :   return tokenName;
     278             : }
     279             : 
     280             : // retrieve the series saved in SetTokenName above
     281             : uint32_t
     282           0 : SmartCardMonitoringThread::GetTokenSeries(CK_SLOT_ID slotid)
     283             : {
     284           0 :   uint32_t series = 0;
     285             :   const char* entry;
     286             : 
     287           0 :   if (mHash) {
     288           0 :     entry = (const char*)PL_HashTableLookupConst(mHash,
     289           0 :                                                  (void*)(uintptr_t)slotid);
     290           0 :     if (entry) {
     291           0 :       memcpy(&series, entry, sizeof(uint32_t));
     292             :     }
     293             :   }
     294           0 :   return series;
     295             : }
     296             : 
     297             : //
     298             : // helper function to pass the event off to nsNSSComponent.
     299             : //
     300             : void
     301           0 : SmartCardMonitoringThread::SendEvent(const char* eventType,
     302             :                                      const char* tokenName)
     303             : {
     304             :   // The token name should be UTF8, but it's not clear that this is enforced
     305             :   // by NSS. To be safe, we explicitly check here before converting it to
     306             :   // UTF16. If it isn't UTF8, we just use an empty string with the idea that
     307             :   // consumers of these events should at least be notified that something
     308             :   // happened.
     309           0 :   nsAutoString tokenNameUTF16(NS_LITERAL_STRING(""));
     310           0 :   if (IsUTF8(nsDependentCString(tokenName))) {
     311           0 :     tokenNameUTF16.Assign(NS_ConvertUTF8toUTF16(tokenName));
     312             :   }
     313             :   nsCOMPtr<nsIRunnable> runnable(new nsTokenEventRunnable(eventType,
     314           0 :                                                           tokenNameUTF16));
     315           0 :   NS_DispatchToMainThread(runnable);
     316           0 : }
     317             : 
     318             : //
     319             : // This is the main loop.
     320             : //
     321           0 : void SmartCardMonitoringThread::Execute()
     322             : {
     323             :   const char* tokenName;
     324             : 
     325             :   //
     326             :   // populate token names for already inserted tokens.
     327             :   //
     328           0 :   PK11SlotList* sl = PK11_FindSlotsByNames(mModule->dllName, nullptr, nullptr,
     329           0 :                                            true);
     330             : 
     331             :   PK11SlotListElement* sle;
     332           0 :   if (sl) {
     333           0 :     for (sle = PK11_GetFirstSafe(sl); sle;
     334             :          sle = PK11_GetNextSafe(sl, sle, false)) {
     335           0 :       SetTokenName(PK11_GetSlotID(sle->slot), PK11_GetTokenName(sle->slot),
     336           0 :                    PK11_GetSlotSeries(sle->slot));
     337             :     }
     338           0 :     PK11_FreeSlotList(sl);
     339             :   }
     340             : 
     341             :   // loop starts..
     342             :   do {
     343             :     UniquePK11SlotInfo slot(
     344           0 :       SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1)));
     345           0 :     if (!slot) {
     346           0 :       break;
     347             :     }
     348             : 
     349             :     // now we have a potential insertion or removal event, see if the slot
     350             :     // is present to determine which it is...
     351           0 :     if (PK11_IsPresent(slot.get())) {
     352             :       // insertion
     353           0 :       CK_SLOT_ID slotID = PK11_GetSlotID(slot.get());
     354           0 :       uint32_t series = PK11_GetSlotSeries(slot.get());
     355             : 
     356             :       // skip spurious insertion events...
     357           0 :       if (series != GetTokenSeries(slotID)) {
     358             :         // if there's a token name, then we have not yet issued a remove
     359             :         // event for the previous token, do so now...
     360           0 :         tokenName = GetTokenName(slotID);
     361           0 :         if (tokenName) {
     362           0 :           SendEvent(kSmartcardRemove, tokenName);
     363             :         }
     364           0 :         tokenName = PK11_GetTokenName(slot.get());
     365             :         // save the token name and series
     366           0 :         SetTokenName(slotID, tokenName, series);
     367           0 :         SendEvent(kSmartcardInsert, tokenName);
     368             :       }
     369             :     } else {
     370             :       // retrieve token name
     371           0 :       CK_SLOT_ID slotID = PK11_GetSlotID(slot.get());
     372           0 :       tokenName = GetTokenName(slotID);
     373             :       // if there's not a token name, then the software isn't expecting
     374             :       // a (or another) remove event.
     375           0 :       if (tokenName) {
     376           0 :         SendEvent(kSmartcardRemove, tokenName);
     377             :         // clear the token name (after we send it)
     378           0 :         SetTokenName(slotID, nullptr, 0);
     379             :       }
     380           0 :     }
     381             :   } while (1);
     382           0 : }
     383             : 
     384             : // accessor to help searching active Monitoring threads
     385           0 : const SECMODModule* SmartCardMonitoringThread::GetModule()
     386             : {
     387           0 :   return mModule;
     388             : }
     389             : 
     390             : // C-like calling sequence to glue into PR_CreateThread.
     391           0 : void SmartCardMonitoringThread::LaunchExecute(void* arg)
     392             : {
     393           0 :   AutoProfilerRegisterThread registerThread("SmartCard");
     394           0 :   NS_SetCurrentThreadName("SmartCard");
     395             : 
     396           0 :   ((SmartCardMonitoringThread*)arg)->Execute();
     397           0 : }

Generated by: LCOV version 1.13