Back to index

lightning-sunbird  0.9+nobinonly
nsSmartCardMonitor.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Contributed by Red Hat Inc.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Red Hat, Inc.
00018  * Portions created by the Initial Developer are Copyright (C) 2005
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 #include "nspr.h"
00037 
00038 #include "pk11func.h"
00039 #include "nsNSSComponent.h"
00040 #include "nsSmartCardMonitor.h"
00041 #include "nsSmartCardEvent.h"
00042 
00043 //
00044 // The SmartCard monitoring thread should start up for each module we load
00045 // that has removable tokens. This code calls an NSS function which waits
00046 // until there is a change in the token state. NSS uses the 
00047 // C_WaitForSlotEvent() call in PKCS #11 if the module implements the call,
00048 // otherwise NSS will poll the token in a loop with a delay of 'latency' 
00049 // between polls. Note that the C_WaitForSlotEvent() may wake up on any type
00050 // of token event, so it's necessary to filter these events down to just the
00051 // insertion and removal events we are looking for.
00052 //
00053 // Once the event is found, It is passed to nsNSSComponent for dispatching
00054 // on the UI thread, and forwarding to any interested listeners (including
00055 // javascript).
00056 //
00057 
00058 
00059 static NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
00060 
00061 #include <assert.h>
00062 
00063 // self linking and removing double linked entry
00064 // adopts the thread it is passed.
00065 class SmartCardThreadEntry {
00066 public:
00067  SmartCardThreadEntry *next;
00068  SmartCardThreadEntry *prev;
00069  SmartCardThreadEntry **head;
00070  SmartCardMonitoringThread *thread;
00071  SmartCardThreadEntry(SmartCardMonitoringThread *thread_,
00072    SmartCardThreadEntry *next_, SmartCardThreadEntry *prev_,
00073    SmartCardThreadEntry **head_) : 
00074    next(next_), prev(prev_), head(head_), thread(thread_) { 
00075     if (prev) { prev->next = this; } else { *head = this; }
00076     if (next) { next->prev = this; }
00077   }
00078   ~SmartCardThreadEntry() {
00079     if (prev) { prev->next = next; } else { *head = next; }
00080     if (next) { next->prev = prev; }
00081     // NOTE: automatically stops the thread
00082     delete thread;
00083   }
00084 };
00085 
00086 //
00087 // SmartCardThreadList is a class to help manage the running threads.
00088 // That way new threads could be started and old ones terminated as we
00089 // load and unload modules.
00090 //
00091 SmartCardThreadList::SmartCardThreadList() : head(0)
00092 {
00093 }
00094 
00095 SmartCardThreadList::~SmartCardThreadList()
00096 {
00097   // the head is self linking and unlinking, the following
00098   // loop removes all entries on the list.
00099   // it will also stop the thread if it happens to be running
00100   while (head) {
00101     delete head;
00102   }
00103 }
00104 
00105 void
00106 SmartCardThreadList::Remove(SECMODModule *aModule)
00107 {
00108   SmartCardThreadEntry *current;
00109   for (current = head; current; current=current->next) {
00110     if (current->thread->GetModule() == aModule) {
00111       // NOTE: automatically stops the thread and dequeues it from the list
00112       delete current;
00113       return;
00114     }
00115   }
00116 }
00117 
00118 // adopts the thread passwd to it. Starts the thread as well
00119 nsresult
00120 SmartCardThreadList::Add(SmartCardMonitoringThread *thread)
00121 {
00122   SmartCardThreadEntry *current = new SmartCardThreadEntry(thread, head, nsnull,
00123                                                            &head);
00124   if (current) {  
00125      // OK to forget current here, it's on the list
00126     return thread->Start();
00127   }
00128   return NS_ERROR_OUT_OF_MEMORY;
00129 }
00130 
00131 
00132 // We really should have a Unity PL Hash function...
00133 static PR_CALLBACK PLHashNumber
00134 unity(const void *key) { return PLHashNumber(NS_PTR_TO_INT32(key)); }
00135 
00136 SmartCardMonitoringThread::SmartCardMonitoringThread(SECMODModule *module_)
00137   : mThread(nsnull)
00138 {
00139   mModule = SECMOD_ReferenceModule(module_);
00140   // simple hash functions, most modules have less than 3 slots, so 10 buckets
00141   // should be plenty
00142   mHash = PL_NewHashTable(10, unity, PL_CompareValues, 
00143                            PL_CompareStrings, nsnull, 0);
00144 }
00145 
00146 //
00147 // when we shutdown the thread, be sure to stop it first. If not, it just might
00148 // crash when the mModule it is looking at disappears.
00149 //
00150 SmartCardMonitoringThread::~SmartCardMonitoringThread()
00151 {
00152   Stop();
00153   SECMOD_DestroyModule(mModule);
00154   if (mHash) {
00155     PL_HashTableDestroy(mHash);
00156   }
00157 }
00158 
00159 nsresult
00160 SmartCardMonitoringThread::Start()
00161 {
00162   if (!mThread) {
00163     mThread = PR_CreateThread(PR_SYSTEM_THREAD, LaunchExecute, this,
00164                               PR_PRIORITY_NORMAL, PR_LOCAL_THREAD,
00165                               PR_JOINABLE_THREAD, 0);
00166   }
00167   return mThread ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
00168 }
00169 
00170 //
00171 // Should only stop if we are through with the module.
00172 // CancelWait has the side effect of losing all the keys and
00173 // current operations on the module!. (See the comment in
00174 // SECMOD_CancelWait for why this is so..).
00175 //
00176 void SmartCardMonitoringThread::Stop()
00177 {
00178   SECStatus rv;
00179 
00180   rv = SECMOD_CancelWait(mModule);
00181   if (rv != SECSuccess) {
00182     // we didn't wake up the Wait, so don't try to join the thread 
00183     // otherwise we will hang forever...
00184     return;
00185   }
00186  
00187   // confused about the memory model here? NSPR owns the memory for
00188   // threads. non-joinable threads are freed when the thread dies.
00189   // joinable threads are freed after the call to PR_JoinThread.
00190   // That means if SECMOD_CancelWait fails, we'll leak the mThread
00191   // structure. this is considered preferable to hanging (which is
00192   // what will happen if we try to join a thread that blocked).
00193   if (mThread) {
00194     PR_JoinThread(mThread);
00195     mThread = 0; 
00196   }
00197 }
00198 
00199 //
00200 // remember the name and series of a token in a particular slot.
00201 // This is important because the name is no longer available when
00202 // the token is removed. If listeners depended on this information,
00203 // They would be out of luck. It also is a handy way of making sure
00204 // we don't generate spurious insertion and removal events as the slot
00205 // cycles through various states.
00206 //
00207 void
00208 SmartCardMonitoringThread::SetTokenName(CK_SLOT_ID slotid, 
00209                                        const char *tokenName, PRUint32 series)
00210 {
00211   if (mHash) {
00212     if (tokenName) {
00213       int len = strlen(tokenName) + 1;
00214       char *entry = (char *)malloc(len+sizeof(PRUint32));
00215      
00216       if (entry) {  
00217         memcpy(entry,&series,sizeof(PRUint32));
00218         memcpy(&entry[sizeof(PRUint32)],tokenName,len);
00219 
00220         PL_HashTableAdd(mHash,(void *)slotid, entry); /* adopt */
00221         return;
00222       }
00223     } 
00224     // if tokenName was not provided, remove the old one (implicit delete)
00225     PL_HashTableRemove(mHash,(void *)slotid);
00226   }
00227 }
00228 
00229 // retrieve the name saved above
00230 const char *
00231 SmartCardMonitoringThread::GetTokenName(CK_SLOT_ID slotid)
00232 {
00233   const char *tokenName = nsnull;
00234   const char *entry;
00235 
00236   if (mHash) {
00237     entry = (const char *)PL_HashTableLookupConst(mHash,(void *)slotid);
00238     if (entry) {
00239       tokenName = &entry[sizeof(PRUint32)];
00240     }
00241   }
00242   return tokenName;
00243 }
00244 
00245 // retrieve the series saved in SetTokenName above
00246 PRUint32
00247 SmartCardMonitoringThread::GetTokenSeries(CK_SLOT_ID slotid)
00248 {
00249   PRUint32 series = 0;
00250   const char *entry;
00251 
00252   if (mHash) {
00253     entry = (const char *)PL_HashTableLookupConst(mHash,(void *)slotid);
00254     if (entry) {
00255       memcpy(&series,entry,sizeof(PRUint32));
00256     }
00257   }
00258   return series;
00259 }
00260 
00261 //
00262 // helper function to pass the event off to nsNSSComponent.
00263 //
00264 nsresult
00265 SmartCardMonitoringThread::SendEvent(const nsAString &eventType,
00266                                      const char *tokenName)
00267 {
00268   nsresult rv;
00269   nsCOMPtr<nsINSSComponent> 
00270                     nssComponent(do_GetService(kNSSComponentCID, &rv));
00271   if (NS_FAILED(rv))
00272     return rv;
00273 
00274   // NSS returns actual UTF8, not ASCII
00275   nssComponent->PostEvent(eventType, NS_ConvertUTF8toUTF16(tokenName));
00276   return NS_OK;
00277 }
00278 
00279 //
00280 // This is the main loop.
00281 //
00282 void SmartCardMonitoringThread::Execute()
00283 {
00284   PK11SlotInfo *slot;
00285   const char *tokenName = nsnull;
00286 
00287   //
00288   // populate token names for already inserted tokens.
00289   //
00290   PK11SlotList *sl =
00291             PK11_FindSlotsByNames(mModule->dllName, nsnull, nsnull, PR_TRUE);
00292   PK11SlotListElement *sle;
00293  
00294   if (sl) {
00295     for (sle=PK11_GetFirstSafe(sl); sle; 
00296                                       sle=PK11_GetNextSafe(sl,sle,PR_FALSE)) {
00297       SetTokenName(PK11_GetSlotID(sle->slot), 
00298                   PK11_GetTokenName(sle->slot), PK11_GetSlotSeries(sle->slot));
00299     }
00300     PK11_FreeSlotList(sl);
00301   }
00302 
00303   // loop starts..
00304   do {
00305     slot = SECMOD_WaitForAnyTokenEvent(mModule, 0, PR_SecondsToInterval(1)  );
00306     if (slot == nsnull) {
00307       break;
00308     }
00309 
00310     // now we have a potential insertion or removal event, see if the slot
00311     // is present to determine which it is...
00312     if (PK11_IsPresent(slot)) {
00313       // insertion
00314       CK_SLOT_ID slotID = PK11_GetSlotID(slot);
00315       PRUint32 series = PK11_GetSlotSeries(slot);
00316 
00317       // skip spurious insertion events...
00318       if (series != GetTokenSeries(slotID)) {
00319         // if there's a token name, then we have not yet issued a remove
00320         // event for the previous token, do so now...
00321         tokenName = GetTokenName(slotID);
00322         if (tokenName) {
00323           SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
00324         }
00325         tokenName = PK11_GetTokenName(slot);
00326         // save the token name and series
00327         SetTokenName(slotID, tokenName, series);
00328         SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_INSERT), tokenName);
00329       }
00330     } else {
00331       // retrieve token name 
00332       CK_SLOT_ID slotID = PK11_GetSlotID(slot);
00333       tokenName = GetTokenName(slotID);
00334       // if there's not a token name, then the software isn't expecting
00335       // a (or another) remove event.
00336       if (tokenName) {
00337         SendEvent(NS_LITERAL_STRING(SMARTCARDEVENT_REMOVE), tokenName);
00338         // clear the token name (after we send it)
00339         SetTokenName(slotID, nsnull, 0);
00340       }
00341     }
00342     PK11_FreeSlot(slot);
00343 
00344   } while (1);
00345 }
00346 
00347 // accessor to help searching active Monitoring threads
00348 const SECMODModule * SmartCardMonitoringThread::GetModule() 
00349 {
00350   return mModule;
00351 }
00352 
00353 // C-like calling sequence to glue into PR_CreateThread.
00354 void SmartCardMonitoringThread::LaunchExecute(void *arg)
00355 {
00356   ((SmartCardMonitoringThread*)arg)->Execute();
00357 }
00358