devslot.c (9265B)
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 "pkcs11.h" 6 7 #ifndef DEVM_H 8 #include "devm.h" 9 #endif /* DEVM_H */ 10 11 #ifndef CKHELPER_H 12 #include "ckhelper.h" 13 #endif /* CKHELPER_H */ 14 15 #include "pkim.h" 16 #include "dev3hack.h" 17 #include "pk11func.h" 18 19 /* measured in seconds */ 20 #define NSSSLOT_TOKEN_DELAY_TIME 1 21 22 /* this should track global and per-transaction login information */ 23 24 #define NSSSLOT_IS_FRIENDLY(slot) \ 25 (slot->base.flags & NSSSLOT_FLAGS_FRIENDLY) 26 27 /* measured as interval */ 28 static PRIntervalTime s_token_delay_time = 0; 29 30 NSS_IMPLEMENT PRStatus 31 nssSlot_Destroy( 32 NSSSlot *slot) 33 { 34 if (slot) { 35 if (PR_ATOMIC_DECREMENT(&slot->base.refCount) == 0) { 36 PK11_FreeSlot(slot->pk11slot); 37 PZ_DestroyLock(slot->base.lock); 38 PZ_DestroyCondVar(slot->isPresentCondition); 39 PZ_DestroyLock(slot->isPresentLock); 40 return nssArena_Destroy(slot->base.arena); 41 } 42 } 43 return PR_SUCCESS; 44 } 45 46 void 47 nssSlot_EnterMonitor(NSSSlot *slot) 48 { 49 if (slot->lock) { 50 PZ_Lock(slot->lock); 51 } 52 } 53 54 void 55 nssSlot_ExitMonitor(NSSSlot *slot) 56 { 57 if (slot->lock) { 58 PZ_Unlock(slot->lock); 59 } 60 } 61 62 NSS_IMPLEMENT void 63 NSSSlot_Destroy( 64 NSSSlot *slot) 65 { 66 (void)nssSlot_Destroy(slot); 67 } 68 69 NSS_IMPLEMENT NSSSlot * 70 nssSlot_AddRef( 71 NSSSlot *slot) 72 { 73 PR_ATOMIC_INCREMENT(&slot->base.refCount); 74 return slot; 75 } 76 77 NSS_IMPLEMENT NSSUTF8 * 78 nssSlot_GetName( 79 NSSSlot *slot) 80 { 81 return slot->base.name; 82 } 83 84 NSS_IMPLEMENT void 85 nssSlot_ResetDelay( 86 NSSSlot *slot) 87 { 88 PZ_Lock(slot->isPresentLock); 89 slot->lastTokenPingState = nssSlotLastPingState_Reset; 90 PZ_Unlock(slot->isPresentLock); 91 } 92 93 static PRBool 94 token_status_checked(const NSSSlot *slot) 95 { 96 PRIntervalTime time; 97 int lastPingState = slot->lastTokenPingState; 98 /* When called from the same thread, that means 99 * nssSlot_IsTokenPresent() is called recursively through 100 * nssSlot_Refresh(). Return immediately in that case. */ 101 if (slot->isPresentThread == PR_GetCurrentThread()) { 102 return PR_TRUE; 103 } 104 /* Set the delay time for checking the token presence */ 105 if (s_token_delay_time == 0) { 106 s_token_delay_time = PR_SecondsToInterval(NSSSLOT_TOKEN_DELAY_TIME); 107 } 108 time = PR_IntervalNow(); 109 if ((lastPingState == nssSlotLastPingState_Valid) && ((time - slot->lastTokenPingTime) < s_token_delay_time)) { 110 return PR_TRUE; 111 } 112 return PR_FALSE; 113 } 114 115 NSS_IMPLEMENT PRBool 116 nssSlot_IsTokenPresent( 117 NSSSlot *slot) 118 { 119 CK_RV ckrv; 120 PRStatus nssrv; 121 NSSToken *nssToken = NULL; 122 /* XXX */ 123 nssSession *session; 124 CK_SLOT_INFO slotInfo; 125 void *epv; 126 PRBool isPresent = PR_FALSE; 127 PRBool doUpdateCachedCerts = PR_FALSE; 128 129 /* permanent slots are always present unless they're disabled */ 130 if (nssSlot_IsPermanent(slot)) { 131 return !PK11_IsDisabled(slot->pk11slot); 132 } 133 134 /* avoid repeated calls to check token status within set interval */ 135 PZ_Lock(slot->isPresentLock); 136 if (token_status_checked(slot)) { 137 CK_FLAGS ckFlags = slot->ckFlags; 138 PZ_Unlock(slot->isPresentLock); 139 return ((ckFlags & CKF_TOKEN_PRESENT) != 0); 140 } 141 PZ_Unlock(slot->isPresentLock); 142 143 /* First obtain the slot epv before we set up the condition 144 * variable, so we can just return if we couldn't get it. */ 145 epv = slot->epv; 146 if (!epv) { 147 return PR_FALSE; 148 } 149 150 /* set up condition so only one thread is active in this part of the code at a time */ 151 PZ_Lock(slot->isPresentLock); 152 while (slot->isPresentThread) { 153 PR_WaitCondVar(slot->isPresentCondition, PR_INTERVAL_NO_TIMEOUT); 154 } 155 /* if we were one of multiple threads here, the first thread will have 156 * given us the answer, no need to make more queries of the token. */ 157 if (token_status_checked(slot)) { 158 CK_FLAGS ckFlags = slot->ckFlags; 159 PZ_Unlock(slot->isPresentLock); 160 return ((ckFlags & CKF_TOKEN_PRESENT) != 0); 161 } 162 /* this is the winning thread, block all others until we've determined 163 * if the token is present and that it needs initialization. */ 164 slot->lastTokenPingState = nssSlotLastPingState_Update; 165 slot->isPresentThread = PR_GetCurrentThread(); 166 167 PZ_Unlock(slot->isPresentLock); 168 169 nssToken = PK11Slot_GetNSSToken(slot->pk11slot); 170 if (!nssToken) { 171 isPresent = PR_FALSE; 172 goto done; 173 } 174 175 if (PK11_GetSlotInfo(slot->pk11slot, &slotInfo) != SECSuccess) { 176 nssToken->base.name[0] = 0; /* XXX */ 177 isPresent = PR_FALSE; 178 goto done; 179 } 180 slot->ckFlags = slotInfo.flags; 181 /* check for the presence of the token */ 182 if ((slot->ckFlags & CKF_TOKEN_PRESENT) == 0) { 183 session = nssToken_GetDefaultSession(nssToken); 184 if (session) { 185 nssSession_EnterMonitor(session); 186 /* token is not present */ 187 if (session->handle != CK_INVALID_HANDLE) { 188 /* session is valid, close and invalidate it */ 189 CKAPI(epv) 190 ->C_CloseSession(session->handle); 191 session->handle = CK_INVALID_HANDLE; 192 } 193 nssSession_ExitMonitor(session); 194 } 195 if (nssToken->base.name[0] != 0) { 196 /* notify the high-level cache that the token is removed */ 197 nssToken->base.name[0] = 0; /* XXX */ 198 nssToken_NotifyCertsNotVisible(nssToken); 199 } 200 nssToken->base.name[0] = 0; /* XXX */ 201 /* clear the token cache */ 202 nssToken_Remove(nssToken); 203 isPresent = PR_FALSE; 204 goto done; 205 } 206 /* token is present, use the session info to determine if the card 207 * has been removed and reinserted. 208 */ 209 session = nssToken_GetDefaultSession(nssToken); 210 if (session) { 211 PRBool tokenRemoved; 212 nssSession_EnterMonitor(session); 213 if (session->handle != CK_INVALID_HANDLE) { 214 CK_SESSION_INFO sessionInfo; 215 ckrv = CKAPI(epv)->C_GetSessionInfo(session->handle, &sessionInfo); 216 if (ckrv != CKR_OK) { 217 /* session is screwy, close and invalidate it */ 218 CKAPI(epv) 219 ->C_CloseSession(session->handle); 220 session->handle = CK_INVALID_HANDLE; 221 } 222 } 223 tokenRemoved = (session->handle == CK_INVALID_HANDLE); 224 nssSession_ExitMonitor(session); 225 /* token not removed, finished */ 226 if (!tokenRemoved) { 227 isPresent = PR_TRUE; 228 goto done; 229 } 230 } 231 /* the token has been removed, and reinserted, or the slot contains 232 * a token it doesn't recognize. invalidate all the old 233 * information we had on this token, if we can't refresh, clear 234 * the present flag */ 235 nssToken_NotifyCertsNotVisible(nssToken); 236 nssToken_Remove(nssToken); 237 if (nssToken->base.name[0] == 0) { 238 doUpdateCachedCerts = PR_TRUE; 239 } 240 if (PK11_InitToken(slot->pk11slot, PR_FALSE) != SECSuccess) { 241 isPresent = PR_FALSE; 242 goto done; 243 } 244 if (doUpdateCachedCerts) { 245 nssTrustDomain_UpdateCachedTokenCerts(nssToken->trustDomain, 246 nssToken); 247 } 248 nssrv = nssToken_Refresh(nssToken); 249 if (nssrv != PR_SUCCESS) { 250 nssToken->base.name[0] = 0; /* XXX */ 251 slot->ckFlags &= ~CKF_TOKEN_PRESENT; 252 isPresent = PR_FALSE; 253 goto done; 254 } 255 isPresent = PR_TRUE; 256 done: 257 if (nssToken) { 258 (void)nssToken_Destroy(nssToken); 259 } 260 /* Once we've set up the condition variable, 261 * Before returning, it's necessary to: 262 * 1) Set the lastTokenPingTime so that any other threads waiting on this 263 * initialization and any future calls within the initialization window 264 * return the just-computed status. 265 * 2) Indicate we're complete, waking up all other threads that may still 266 * be waiting on initialization can progress. 267 */ 268 PZ_Lock(slot->isPresentLock); 269 /* don't update the time if we were reset while we were 270 * getting the token state */ 271 if (slot->lastTokenPingState == nssSlotLastPingState_Update) { 272 slot->lastTokenPingTime = PR_IntervalNow(); 273 slot->lastTokenPingState = nssSlotLastPingState_Valid; 274 } 275 slot->isPresentThread = NULL; 276 PR_NotifyAllCondVar(slot->isPresentCondition); 277 PZ_Unlock(slot->isPresentLock); 278 return isPresent; 279 } 280 281 NSS_IMPLEMENT void * 282 nssSlot_GetCryptokiEPV( 283 NSSSlot *slot) 284 { 285 return slot->epv; 286 } 287 288 NSS_IMPLEMENT NSSToken * 289 nssSlot_GetToken( 290 NSSSlot *slot) 291 { 292 NSSToken *rvToken = NULL; 293 294 if (nssSlot_IsTokenPresent(slot)) { 295 rvToken = PK11Slot_GetNSSToken(slot->pk11slot); 296 } 297 298 return rvToken; 299 } 300 301 NSS_IMPLEMENT PRStatus 302 nssSession_EnterMonitor( 303 nssSession *s) 304 { 305 if (s->lock) 306 PZ_Lock(s->lock); 307 return PR_SUCCESS; 308 } 309 310 NSS_IMPLEMENT PRStatus 311 nssSession_ExitMonitor( 312 nssSession *s) 313 { 314 return (s->lock) ? PZ_Unlock(s->lock) : PR_SUCCESS; 315 } 316 317 NSS_EXTERN PRBool 318 nssSession_IsReadWrite( 319 nssSession *s) 320 { 321 return s->isRW; 322 }