security/manager/ssl/src/nsSmartCardMonitor.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/manager/ssl/src/nsSmartCardMonitor.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,332 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +#include "nspr.h"
     1.8 +
     1.9 +#include "pk11func.h"
    1.10 +#include "nsNSSComponent.h"
    1.11 +#include "nsSmartCardMonitor.h"
    1.12 +#include "nsIDOMSmartCardEvent.h"
    1.13 +#include "nsServiceManagerUtils.h"
    1.14 +#include "mozilla/unused.h"
    1.15 +
    1.16 +using namespace mozilla;
    1.17 +
    1.18 +//
    1.19 +// The SmartCard monitoring thread should start up for each module we load
    1.20 +// that has removable tokens. This code calls an NSS function which waits
    1.21 +// until there is a change in the token state. NSS uses the 
    1.22 +// C_WaitForSlotEvent() call in PKCS #11 if the module implements the call,
    1.23 +// otherwise NSS will poll the token in a loop with a delay of 'latency' 
    1.24 +// between polls. Note that the C_WaitForSlotEvent() may wake up on any type
    1.25 +// of token event, so it's necessary to filter these events down to just the
    1.26 +// insertion and removal events we are looking for.
    1.27 +//
    1.28 +// Once the event is found, It is passed to nsNSSComponent for dispatching
    1.29 +// on the UI thread, and forwarding to any interested listeners (including
    1.30 +// javascript).
    1.31 +//
    1.32 +
    1.33 +
    1.34 +// self linking and removing double linked entry
    1.35 +// adopts the thread it is passed.
    1.36 +class SmartCardThreadEntry {
    1.37 +public:
    1.38 + SmartCardThreadEntry *next;
    1.39 + SmartCardThreadEntry *prev;
    1.40 + SmartCardThreadEntry **head;
    1.41 + SmartCardMonitoringThread *thread;
    1.42 + SmartCardThreadEntry(SmartCardMonitoringThread *thread_,
    1.43 +   SmartCardThreadEntry *next_, SmartCardThreadEntry *prev_,
    1.44 +   SmartCardThreadEntry **head_) : 
    1.45 +   next(next_), prev(prev_), head(head_), thread(thread_) { 
    1.46 +    if (prev) { prev->next = this; } else { *head = this; }
    1.47 +    if (next) { next->prev = this; }
    1.48 +  }
    1.49 +  ~SmartCardThreadEntry() {
    1.50 +    if (prev) { prev->next = next; } else { *head = next; }
    1.51 +    if (next) { next->prev = prev; }
    1.52 +    // NOTE: automatically stops the thread
    1.53 +    delete thread;
    1.54 +  }
    1.55 +};
    1.56 +
    1.57 +//
    1.58 +// SmartCardThreadList is a class to help manage the running threads.
    1.59 +// That way new threads could be started and old ones terminated as we
    1.60 +// load and unload modules.
    1.61 +//
    1.62 +SmartCardThreadList::SmartCardThreadList() : head(0)
    1.63 +{
    1.64 +}
    1.65 +
    1.66 +SmartCardThreadList::~SmartCardThreadList()
    1.67 +{
    1.68 +  // the head is self linking and unlinking, the following
    1.69 +  // loop removes all entries on the list.
    1.70 +  // it will also stop the thread if it happens to be running
    1.71 +  while (head) {
    1.72 +    delete head;
    1.73 +  }
    1.74 +}
    1.75 +
    1.76 +void
    1.77 +SmartCardThreadList::Remove(SECMODModule *aModule)
    1.78 +{
    1.79 +  for (SmartCardThreadEntry *current = head; current; current = current->next) {
    1.80 +    if (current->thread->GetModule() == aModule) {
    1.81 +      // NOTE: automatically stops the thread and dequeues it from the list
    1.82 +      delete current;
    1.83 +      return;
    1.84 +    }
    1.85 +  }
    1.86 +}
    1.87 +
    1.88 +// adopts the thread passed to it. Starts the thread as well
    1.89 +nsresult
    1.90 +SmartCardThreadList::Add(SmartCardMonitoringThread *thread)
    1.91 +{
    1.92 +  SmartCardThreadEntry *current = new SmartCardThreadEntry(thread, head, nullptr,
    1.93 +                                                           &head);
    1.94 +  // OK to forget current here, it's on the list.
    1.95 +  unused << current;
    1.96 +
    1.97 +  return thread->Start();
    1.98 +}
    1.99 +
   1.100 +
   1.101 +// We really should have a Unity PL Hash function...
   1.102 +static PLHashNumber
   1.103 +unity(const void *key) { return PLHashNumber(NS_PTR_TO_INT32(key)); }
   1.104 +
   1.105 +SmartCardMonitoringThread::SmartCardMonitoringThread(SECMODModule *module_)
   1.106 +  : mThread(nullptr)
   1.107 +{
   1.108 +  mModule = SECMOD_ReferenceModule(module_);
   1.109 +  // simple hash functions, most modules have less than 3 slots, so 10 buckets
   1.110 +  // should be plenty
   1.111 +  mHash = PL_NewHashTable(10, unity, PL_CompareValues, 
   1.112 +                           PL_CompareStrings, nullptr, 0);
   1.113 +}
   1.114 +
   1.115 +//
   1.116 +// when we shutdown the thread, be sure to stop it first. If not, it just might
   1.117 +// crash when the mModule it is looking at disappears.
   1.118 +//
   1.119 +SmartCardMonitoringThread::~SmartCardMonitoringThread()
   1.120 +{
   1.121 +  Stop();
   1.122 +  SECMOD_DestroyModule(mModule);
   1.123 +  if (mHash) {
   1.124 +    PL_HashTableDestroy(mHash);
   1.125 +  }
   1.126 +}
   1.127 +
   1.128 +nsresult
   1.129 +SmartCardMonitoringThread::Start()
   1.130 +{
   1.131 +  if (!mThread) {
   1.132 +    mThread = PR_CreateThread(PR_SYSTEM_THREAD, LaunchExecute, this,
   1.133 +                              PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
   1.134 +                              PR_JOINABLE_THREAD, 0);
   1.135 +  }
   1.136 +  return mThread ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   1.137 +}
   1.138 +
   1.139 +//
   1.140 +// Should only stop if we are through with the module.
   1.141 +// CancelWait has the side effect of losing all the keys and
   1.142 +// current operations on the module!. (See the comment in
   1.143 +// SECMOD_CancelWait for why this is so..).
   1.144 +//
   1.145 +void SmartCardMonitoringThread::Stop()
   1.146 +{
   1.147 +  SECStatus rv;
   1.148 +
   1.149 +  rv = SECMOD_CancelWait(mModule);
   1.150 +  if (rv != SECSuccess) {
   1.151 +    // we didn't wake up the Wait, so don't try to join the thread 
   1.152 +    // otherwise we will hang forever...
   1.153 +    return;
   1.154 +  }
   1.155 + 
   1.156 +  // confused about the memory model here? NSPR owns the memory for
   1.157 +  // threads. non-joinable threads are freed when the thread dies.
   1.158 +  // joinable threads are freed after the call to PR_JoinThread.
   1.159 +  // That means if SECMOD_CancelWait fails, we'll leak the mThread
   1.160 +  // structure. this is considered preferable to hanging (which is
   1.161 +  // what will happen if we try to join a thread that blocked).
   1.162 +  if (mThread) {
   1.163 +    PR_JoinThread(mThread);
   1.164 +    mThread = 0; 
   1.165 +  }
   1.166 +}
   1.167 +
   1.168 +//
   1.169 +// remember the name and series of a token in a particular slot.
   1.170 +// This is important because the name is no longer available when
   1.171 +// the token is removed. If listeners depended on this information,
   1.172 +// They would be out of luck. It also is a handy way of making sure
   1.173 +// we don't generate spurious insertion and removal events as the slot
   1.174 +// cycles through various states.
   1.175 +//
   1.176 +void
   1.177 +SmartCardMonitoringThread::SetTokenName(CK_SLOT_ID slotid, 
   1.178 +                                       const char *tokenName, uint32_t series)
   1.179 +{
   1.180 +  if (mHash) {
   1.181 +    if (tokenName) {
   1.182 +      int len = strlen(tokenName) + 1;
   1.183 +      /* this must match the allocator used in
   1.184 +       * PLHashAllocOps.freeEntry DefaultFreeEntry */
   1.185 +      char *entry = (char *)PR_Malloc(len+sizeof(uint32_t));
   1.186 +     
   1.187 +      if (entry) {  
   1.188 +        memcpy(entry,&series,sizeof(uint32_t));
   1.189 +        memcpy(&entry[sizeof(uint32_t)],tokenName,len);
   1.190 +
   1.191 +        PL_HashTableAdd(mHash,(void *)(uintptr_t)slotid, entry); /* adopt */
   1.192 +        return;
   1.193 +      }
   1.194 +    } 
   1.195 +    else {
   1.196 +      // if tokenName was not provided, remove the old one (implicit delete)
   1.197 +      PL_HashTableRemove(mHash,(void *)(uintptr_t)slotid);
   1.198 +    }
   1.199 +  }
   1.200 +}
   1.201 +
   1.202 +// retrieve the name saved above
   1.203 +const char *
   1.204 +SmartCardMonitoringThread::GetTokenName(CK_SLOT_ID slotid)
   1.205 +{
   1.206 +  const char *tokenName = nullptr;
   1.207 +  const char *entry;
   1.208 +
   1.209 +  if (mHash) {
   1.210 +    entry = (const char *)PL_HashTableLookupConst(mHash,(void *)(uintptr_t)slotid);
   1.211 +    if (entry) {
   1.212 +      tokenName = &entry[sizeof(uint32_t)];
   1.213 +    }
   1.214 +  }
   1.215 +  return tokenName;
   1.216 +}
   1.217 +
   1.218 +// retrieve the series saved in SetTokenName above
   1.219 +uint32_t
   1.220 +SmartCardMonitoringThread::GetTokenSeries(CK_SLOT_ID slotid)
   1.221 +{
   1.222 +  uint32_t series = 0;
   1.223 +  const char *entry;
   1.224 +
   1.225 +  if (mHash) {
   1.226 +    entry = (const char *)PL_HashTableLookupConst(mHash,(void *)(uintptr_t)slotid);
   1.227 +    if (entry) {
   1.228 +      memcpy(&series,entry,sizeof(uint32_t));
   1.229 +    }
   1.230 +  }
   1.231 +  return series;
   1.232 +}
   1.233 +
   1.234 +//
   1.235 +// helper function to pass the event off to nsNSSComponent.
   1.236 +//
   1.237 +nsresult
   1.238 +SmartCardMonitoringThread::SendEvent(const nsAString &eventType,
   1.239 +                                     const char *tokenName)
   1.240 +{
   1.241 +  static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
   1.242 +
   1.243 +  nsresult rv;
   1.244 +  nsCOMPtr<nsINSSComponent> 
   1.245 +                    nssComponent(do_GetService(kNSSComponentCID, &rv));
   1.246 +  if (NS_FAILED(rv))
   1.247 +    return rv;
   1.248 +
   1.249 +  // NSS returns actual UTF8, not ASCII
   1.250 +  nssComponent->PostEvent(eventType, NS_ConvertUTF8toUTF16(tokenName));
   1.251 +  return NS_OK;
   1.252 +}
   1.253 +
   1.254 +//
   1.255 +// This is the main loop.
   1.256 +//
   1.257 +void SmartCardMonitoringThread::Execute()
   1.258 +{
   1.259 +  PK11SlotInfo *slot;
   1.260 +  const char *tokenName = nullptr;
   1.261 +
   1.262 +  //
   1.263 +  // populate token names for already inserted tokens.
   1.264 +  //
   1.265 +  PK11SlotList *sl =
   1.266 +            PK11_FindSlotsByNames(mModule->dllName, nullptr, nullptr, true);
   1.267 +  PK11SlotListElement *sle;
   1.268 + 
   1.269 +  if (sl) {
   1.270 +    for (sle=PK11_GetFirstSafe(sl); sle; 
   1.271 +                                      sle=PK11_GetNextSafe(sl,sle,false)) {
   1.272 +      SetTokenName(PK11_GetSlotID(sle->slot), 
   1.273 +                  PK11_GetTokenName(sle->slot), PK11_GetSlotSeries(sle->slot));
   1.274 +    }
   1.275 +    PK11_FreeSlotList(sl);
   1.276 +  }
   1.277 +
   1.278 +  // loop starts..
   1.279 +  do {
   1.280 +    slot = SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1)  );
   1.281 +    if (!slot) {
   1.282 +      break;
   1.283 +    }
   1.284 +
   1.285 +    // now we have a potential insertion or removal event, see if the slot
   1.286 +    // is present to determine which it is...
   1.287 +    if (PK11_IsPresent(slot)) {
   1.288 +      // insertion
   1.289 +      CK_SLOT_ID slotID = PK11_GetSlotID(slot);
   1.290 +      uint32_t series = PK11_GetSlotSeries(slot);
   1.291 +
   1.292 +      // skip spurious insertion events...
   1.293 +      if (series != GetTokenSeries(slotID)) {
   1.294 +        // if there's a token name, then we have not yet issued a remove
   1.295 +        // event for the previous token, do so now...
   1.296 +        tokenName = GetTokenName(slotID);
   1.297 +        if (tokenName) {
   1.298 +          SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
   1.299 +        }
   1.300 +        tokenName = PK11_GetTokenName(slot);
   1.301 +        // save the token name and series
   1.302 +        SetTokenName(slotID, tokenName, series);
   1.303 +        SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_INSERT), tokenName);
   1.304 +      }
   1.305 +    } else {
   1.306 +      // retrieve token name 
   1.307 +      CK_SLOT_ID slotID = PK11_GetSlotID(slot);
   1.308 +      tokenName = GetTokenName(slotID);
   1.309 +      // if there's not a token name, then the software isn't expecting
   1.310 +      // a (or another) remove event.
   1.311 +      if (tokenName) {
   1.312 +        SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
   1.313 +        // clear the token name (after we send it)
   1.314 +        SetTokenName(slotID, nullptr, 0);
   1.315 +      }
   1.316 +    }
   1.317 +    PK11_FreeSlot(slot);
   1.318 +
   1.319 +  } while (1);
   1.320 +}
   1.321 +
   1.322 +// accessor to help searching active Monitoring threads
   1.323 +const SECMODModule * SmartCardMonitoringThread::GetModule() 
   1.324 +{
   1.325 +  return mModule;
   1.326 +}
   1.327 +
   1.328 +// C-like calling sequence to glue into PR_CreateThread.
   1.329 +void SmartCardMonitoringThread::LaunchExecute(void *arg)
   1.330 +{
   1.331 +  PR_SetCurrentThreadName("SmartCard");
   1.332 +
   1.333 +  ((SmartCardMonitoringThread*)arg)->Execute();
   1.334 +}
   1.335 +

mercurial